Kelebihan fungsi PHP

195

Berasal dari latar belakang C ++;)
Bagaimana saya bisa membebani fungsi PHP?

Satu definisi fungsi jika ada argumen, dan yang lain jika tidak ada argumen? Apakah mungkin di PHP? Atau haruskah saya menggunakan if lagi untuk memeriksa apakah ada parameter yang lulus dari $ _GET dan POST ?? dan menghubungkannya?

Vamsi Krishna B
sumber
1
Anda hanya bisa membebani metode kelas, tetapi tidak berfungsi. Lihat php.net/manual/en/language.oop5.overloading.php
Spechal
1
Anda dapat membuat fungsi yang memeriksa jumlah argumen secara eksplisit dan mengeksekusi fungsi lain, dari kumpulan yang telah ditentukan sebelumnya. Namun, Anda sebaiknya mendesain ulang solusi Anda, atau menggunakan kelas yang mengimplementasikan antarmuka Anda
kolypto
2
Seperti yang dikatakan oleh php.net/manual/en/language.oop5.overloading.php , definisi PHP tentang kelebihan beban berbeda dari bahasa OOP pada umumnya. Mereka hanya merujuk pada metode ajaib yang memungkinkan untuk routing dinamis properti dan fungsi berdasarkan X.
Edwin Daniels
Untuk pembaca masa depan: Apa yang dimaksud @Spechal, adalah arti yang berbeda untuk kata tersebut overloading, daripada yang ditanyakan dalam pertanyaan. (Lihat jawaban yang diterima untuk lebih jelasnya.)
ToolmakerSteve
2
Adakah yang berubah sejak PHP 7? : o
nawfal

Jawaban:

218

Anda tidak dapat membebani fungsi PHP secara berlebihan. Tanda tangan fungsi hanya didasarkan pada namanya dan tidak menyertakan daftar argumen, jadi Anda tidak dapat memiliki dua fungsi dengan nama yang sama. Metode overloading kelas berbeda dalam PHP dibandingkan dengan banyak bahasa lainnya. PHP menggunakan kata yang sama tetapi menggambarkan pola yang berbeda.

Anda bisa, bagaimanapun, mendeklarasikan fungsi variadic yang mengambil sejumlah variabel argumen. Anda akan menggunakan func_num_args()dan func_get_arg()untuk mendapatkan argumen yang berlalu, dan menggunakannya secara normal.

Sebagai contoh:

function myFunc() {
    for ($i = 0; $i < func_num_args(); $i++) {
        printf("Argument %d: %s\n", $i, func_get_arg($i));
    }
}

/*
Argument 0: a
Argument 1: 2
Argument 2: 3.5
*/
myFunc('a', 2, 3.5);
BoltClock
sumber
8
Mungkin aku sudah melakukan C ++ pengembangan terlalu banyak, tapi saya akan menyarankan petunjuk bahwa ini sedang dilakukan dalam parameter fungsi seperti myFunc(/*...*/).
doug65536
4
@ doug65536, PHP 5.6+ akan mendukung "..." itu sebagai token sintaks , untuk bantuan besar kami. ;)
Sz.
Atau lihat jawaban Adil , yang lebih dekat dengan kelebihan C ++ - sedekat mungkin, dalam bahasa yang diketik longgar seperti php. Ini bahkan lebih sesuai di php 7, karena Anda dapat memberikan petunjuk tipe untuk parameter, jika mereka adalah tipe yang sama di semua kelebihan Anda.
ToolmakerSteve
78

PHP tidak mendukung metode overload tradisional, namun satu cara Anda mungkin dapat mencapai apa yang Anda inginkan, adalah dengan menggunakan __callmetode ajaib:

class MyClass {
    public function __call($name, $args) {

        switch ($name) {
            case 'funcOne':
                switch (count($args)) {
                    case 1:
                        return call_user_func_array(array($this, 'funcOneWithOneArg'), $args);
                    case 3:
                        return call_user_func_array(array($this, 'funcOneWithThreeArgs'), $args);
                 }
            case 'anotherFunc':
                switch (count($args)) {
                    case 0:
                        return $this->anotherFuncWithNoArgs();
                    case 5:
                        return call_user_func_array(array($this, 'anotherFuncWithMoreArgs'), $args);
                }
        }
    }

    protected function funcOneWithOneArg($a) {

    }

    protected function funcOneWithThreeArgs($a, $b, $c) {

    }

    protected function anotherFuncWithNoArgs() {

    }

    protected function anotherFuncWithMoreArgs($a, $b, $c, $d, $e) {

    }

}
Stephen
sumber
20
Saya belum pernah melihat penggunaan ini __call()sebelumnya. Cukup kreatif (jika sedikit bertele-tele)! +1
BoltClock
Penggunaan __call () yang sangat mengagumkan
Abhishek Gupta
2
Sebenarnya, tidak bisa setuju dengan ini, dan harus melakukan pelatihan ulang dengan saran ini. Untuk satu, penggunaan __call () ini merupakan anti-pola. Kedua, itu mungkin untuk melakukan overloading di PHP untuk metode kelas yang memiliki visibilitas yang benar. Anda tidak bisa, bagaimanapun - kelebihan fungsi plain-jane.
Oddman
1
Bisakah Anda menjelaskan mengapa menurut Anda menggunakan __call () adalah anti-pola? Metode PHP overloading bukan yang dicari OP - mereka menginginkan kemampuan untuk memiliki beberapa tanda tangan metode dengan nama yang sama tetapi input / output berbeda: en.wikipedia.org/wiki/Function_overloading
Stephen
20
Tidak perlu menggunakan __call (). Alih-alih mendeklarasikan metode yang memiliki nama yang Anda inginkan, tanpa parameter apa pun terdaftar, dan gunakan func_get_args () dalam metode itu untuk mengirim ke implementasi pribadi yang sesuai.
FantasticJamieBurns
30

Untuk over load suatu fungsi cukup lakukan pass parameter sebagai null secara default,

class ParentClass
{
   function mymethod($arg1 = null, $arg2 = null, $arg3 = null)  
     {  
        if( $arg1 == null && $arg2 == null && $arg3 == null ){ 
           return 'function has got zero parameters <br />';
        }
        else
        {
           $str = '';
           if( $arg1 != null ) 
              $str .= "arg1 = ".$arg1." <br />";

           if( $arg2 != null ) 
              $str .= "arg2 = ".$arg2." <br />";

           if( $arg3 != null ) 
              $str .= "arg3 = ".$arg3." <br />";

           return $str;
         }
     }
}

// and call it in order given below ...

 $obj = new ParentClass;

 echo '<br />$obj->mymethod()<br />';
 echo $obj->mymethod();

 echo '<br />$obj->mymethod(null,"test") <br />';
 echo $obj->mymethod(null,'test');

 echo '<br /> $obj->mymethod("test","test","test")<br />';
 echo $obj->mymethod('test','test','test');
Adil Abbasi
sumber
4
Saya tidak menganggap parameter default sebagai fungsi yang berlebihan. fungsi [atau metode] kelebihan beban lebih berkaitan dengan memanggil implementasi yang berbeda berdasarkan jenis argumen yang diteruskan. Menggunakan parameter default hanya memungkinkan Anda untuk memanggil implementasi yang sama dengan kenyamanan parameter yang lebih sedikit.
Scalable
1
ya, Anda dapat memanipulasinya berdasarkan tipe juga tetapi seolah-olah Anda tahu php dengan bahasa yang diketik longgar dan berurusan dengannya perlu untuk mengatasinya.
Adil Abbasi
1
Saya lebih suka jawaban ini daripada yang diterima, karena menjelaskan secara eksplisit berapa jumlah parameter minimum dan maksimum. (Jangan berikan nilai default untuk parameter yang diperlukan.) @Scalable - Saya setuju dengan Adil bahwa, karena php diketik secara longgar, ini adalah semua yang dapat diartikan dalam php untuk overloadsuatu fungsi - walaupun begitu, Anda membuat poin berguna bahwa pembaca harus sadar.
ToolmakerSteve
11

Ini mungkin diretas bagi sebagian orang, tetapi saya belajar dengan cara ini dari bagaimana Cakephp melakukan beberapa fungsi dan mengadaptasinya karena saya menyukai fleksibilitas yang diciptakannya.

Idenya adalah Anda memiliki berbagai jenis argumen, array, objek dll, maka Anda mendeteksi apa yang Anda berikan dan pergi dari sana

function($arg1, $lastname) {
    if(is_array($arg1)){
        $lastname = $arg1['lastname'];
        $firstname = $arg1['firstname'];
    } else {
        $firstname = $arg1;
    }
    ...
}
SeanDowney
sumber
1
Nah saya tidak melihat ini sebagai peretasan, PHP melakukan ini untuk banyak fungsi bawaannya.
BoltClock
Karena php diketik secara longgar, beginilah cara seseorang harus menangani situasi ini. "Peretasan yang diperlukan" dalam php.
ToolmakerSteve
11
<?php   
/*******************************
 * author  : [email protected] 
 * version : 3.8
 * create on : 2017-09-17
 * updated on : 2020-01-12
 *****************************/

#> 1. Include Overloadable class

class Overloadable
{
    static function call($obj, $method, $params=null) {
        $class = get_class($obj);
        // Get real method name
        $suffix_method_name = $method.self::getMethodSuffix($method, $params);

        if (method_exists($obj, $suffix_method_name)) {
            // Call method
            return call_user_func_array(array($obj, $suffix_method_name), $params);
        }else{
            throw new Exception('Tried to call unknown method '.$class.'::'.$suffix_method_name);
        }
    }

    static function getMethodSuffix($method, $params_ary=array()) {
        $c = '__';
        if(is_array($params_ary)){
            foreach($params_ary as $i=>$param){
                // Adding special characters to the end of method name 
                switch(gettype($param)){
                    case 'array':       $c .= 'a'; break;
                    case 'boolean':     $c .= 'b'; break;
                    case 'double':      $c .= 'd'; break;
                    case 'integer':     $c .= 'i'; break;
                    case 'NULL':        $c .= 'n'; break;
                    case 'object':
                        // Support closure parameter
                        if($param instanceof Closure ){
                            $c .= 'c';
                        }else{
                            $c .= 'o'; 
                        }
                    break;
                    case 'resource':    $c .= 'r'; break;
                    case 'string':      $c .= 's'; break;
                    case 'unknown type':$c .= 'u'; break;
                }
            }
        }
        return $c;
    }
    // Get a reference variable by name
    static function &refAccess($var_name) {
        $r =& $GLOBALS["$var_name"]; 
        return $r;
    }
}
//----------------------------------------------------------
#> 2. create new class
//----------------------------------------------------------

class test 
{
    private $name = 'test-1';

    #> 3. Add __call 'magic method' to your class

    // Call Overloadable class 
    // you must copy this method in your class to activate overloading
    function __call($method, $args) {
        return Overloadable::call($this, $method, $args);
    }

    #> 4. Add your methods with __ and arg type as one letter ie:(__i, __s, __is) and so on.
    #> methodname__i = methodname($integer)
    #> methodname__s = methodname($string)
    #> methodname__is = methodname($integer, $string)

    // func(void)
    function func__() {
        pre('func(void)', __function__);
    }
    // func(integer)
    function func__i($int) {
        pre('func(integer '.$int.')', __function__);
    }
    // func(string)
    function func__s($string) {
        pre('func(string '.$string.')', __function__);
    }    
    // func(string, object)
    function func__so($string, $object) {
        pre('func(string '.$string.', '.print_r($object, 1).')', __function__);
        //pre($object, 'Object: ');
    }
    // func(closure)
    function func__c(Closure $callback) {

        pre("func(".
            print_r(
                array( $callback, $callback($this->name) ), 
                1
            ).");", __function__.'(Closure)'
        );

    }   
    // anotherFunction(array)
    function anotherFunction__a($array) {
        pre('anotherFunction('.print_r($array, 1).')', __function__);
        $array[0]++;        // change the reference value
        $array['val']++;    // change the reference value
    }
    // anotherFunction(string)
    function anotherFunction__s($key) {
        pre('anotherFunction(string '.$key.')', __function__);
        // Get a reference
        $a2 =& Overloadable::refAccess($key); // $a2 =& $GLOBALS['val'];
        $a2 *= 3;   // change the reference value
    }

}

//----------------------------------------------------------
// Some data to work with:
$val  = 10;
class obj {
    private $x=10;
}

//----------------------------------------------------------
#> 5. create your object

// Start
$t = new test;

#> 6. Call your method

// Call first method with no args:
$t->func(); 
// Output: func(void)

$t->func($val);
// Output: func(integer 10)

$t->func("hello");
// Output: func(string hello)

$t->func("str", new obj());
/* Output: 
func(string str, obj Object
(
    [x:obj:private] => 10
)
)
*/

// call method with closure function
$t->func(function($n){
    return strtoupper($n);
});

/* Output:
func(Array
(
    [0] => Closure Object
        (
            [parameter] => Array
                (
                    [$n] => 
                )

        )

    [1] => TEST-1
)
);
*/

## Passing by Reference:

echo '<br><br>$val='.$val;
// Output: $val=10

$t->anotherFunction(array(&$val, 'val'=>&$val));
/* Output:
anotherFunction(Array
(
    [0] => 10
    [val] => 10
)
)
*/

echo 'Result: $val='.$val;
// Output: $val=12

$t->anotherFunction('val');
// Output: anotherFunction(string val)

echo 'Result: $val='.$val;
// Output: $val=36







// Helper function
//----------------------------------------------------------
function pre($mixed, $title=null){
    $output = "<fieldset>";
    $output .= $title ? "<legend><h2>$title</h2></legend>" : "";
    $output .= '<pre>'. print_r($mixed, 1). '</pre>';
    $output .= "</fieldset>";
    echo $output;
}
//----------------------------------------------------------
Hisyam Dalal
sumber
4
Bisakah Anda menambahkan beberapa penjelasan cara menggunakan kelas ini?
Justus Romijn
1- membuat kelas baru 2- memperluas kelebihan beban. 3- membuat fungsi seperti funcname_ () => no args atau suka funcname_s ($ s) => string arg </li>
Hisham Dalal
1
Ini solusi yang sangat keren. Mengapa Anda melakukan $ o = new $ obj ()? Saya belum mencobanya, meskipun saya pikir seharusnya \ $ o = \ $ ini?
over_optimistic
Terima kasih atas pemberitahuan penting ini, dan saya akan menggunakan backslash, tetapi ini berfungsi dengan backslash dan tanpa! - Saya menggunakan phpEazy sebagai server lokal.
Hisham Dalal
4

Bagaimana dengan ini:

function($arg = NULL) {

    if ($arg != NULL) {
        etc.
        etc.
    }
}
Marlin Ouverson
sumber
Bisa bekerja, tetapi kurang dapat dibaca jika kelebihan akan memiliki parameter yang berbeda dengan nama dan makna yang berbeda.
Mathias Lykkegaard Lorenzen
3

Di PHP 5.6 Anda dapat menggunakan operator percikan ... sebagai parameter terakhir dan menghapus func_get_args()dan func_num_args():

function example(...$args)
{
   count($args); // Equivalent to func_num_args()
}

example(1, 2);
example(1, 2, 3, 4, 5, 6, 7);

Anda dapat menggunakannya untuk membongkar argumen juga:

$args[] = 1;
$args[] = 2;
$args[] = 3;
example(...$args);

Setara dengan:

example(1, 2, 3);
mtpultz
sumber
1
<?php

    class abs
    {
        public function volume($arg1=null, $arg2=null, $arg3=null)
        {   
            if($arg1 == null && $arg2 == null && $arg3 == null)
        {
            echo "function has no arguments. <br>";
        }

        else if($arg1 != null && $arg2 != null && $arg3 != null)
            {
            $volume=$arg1*$arg2*$arg3;
            echo "volume of a cuboid ".$volume ."<br>";
            }
            else if($arg1 != null && $arg2 != null)
            {
            $area=$arg1*$arg2;
            echo "area of square  = " .$area ."<br>";
            }
            else if($arg1 != null)
            {
            $volume=$arg1*$arg1*$arg1; 
            echo "volume of a cube = ".$volume ."<br>";
            }


        }


    }

    $obj=new abs();
    echo "For no arguments. <br>";
    $obj->volume();
    echo "For one arguments. <br>";
    $obj->volume(3);
    echo "For two arguments. <br>";
    $obj->volume(3,4);
    echo "For three arguments. <br>";
    $obj->volume(3,4,5);
    ?>
Raman Deep Bajwa
sumber
Cobalah untuk mengedit pertanyaan dan gunakan pemformatan. Ini akan membuat jawaban Anda lebih mudah dibaca dan menarik lebih banyak pengguna.
Kashish Arora
Teknik ini ditunjukkan dalam jawaban sebelumnya .
ToolmakerSteve
0

PHP tidak mendukung kelebihan muatan untuk saat ini. Semoga ini akan diimplementasikan dalam versi lain seperti bahasa pemrograman lainnya.

Lihat pustaka ini, ini memungkinkan Anda untuk menggunakan PHP Overloading dalam hal penutupan. https://github.com/Sahil-Gulati/Overloading

Sahil Gulati
sumber
1
jika Anda akan membuat pernyataan seperti ini harus benar-benar menyertakan versi yang Anda maksudkan, dengan cara itu orang tahu seberapa lama komentar Anda ketika mereka melihatnya di masa mendatang
MikeT
0

Sayangnya tidak ada kelebihan dalam PHP seperti yang dilakukan di C #. Tetapi saya punya sedikit trik. Saya mendeklarasikan argumen dengan nilai null default dan memeriksanya dalam suatu fungsi. Dengan begitu fungsi saya dapat melakukan hal-hal yang berbeda tergantung pada argumen. Di bawah ini adalah contoh sederhana:

public function query($queryString, $class = null) //second arg. is optional
{
    $query = $this->dbLink->prepare($queryString);
    $query->execute();

    //if there is second argument method does different thing
    if (!is_null($class)) { 
        $query->setFetchMode(PDO::FETCH_CLASS, $class);
    }

    return $query->fetchAll();
}

//This loads rows in to array of class
$Result = $this->query($queryString, "SomeClass");
//This loads rows as standard arrays
$Result = $this->query($queryString);
Rovshan Mamedov
sumber
1
Harap baca semua jawaban yang ada, sebelum menulis yang baru beberapa tahun kemudian. Teknik ini sudah ditunjukkan dua kali dalam jawaban di atas. Sekali pada 2013, dan sekali lagi pada 2014.
ToolmakerSteve