Quá tải chức năng PHP


195

Đến từ nền C ++;)
Làm cách nào tôi có thể quá tải các hàm PHP?

Một định nghĩa hàm nếu có bất kỳ đối số và một đối số khác nếu không có đối số? Có khả thi trong PHP không? Hoặc tôi nên sử dụng nếu khác để kiểm tra xem có bất kỳ tham số nào được truyền từ $ _GET và POST không ?? và liên quan đến họ?


1
Bạn chỉ có thể quá tải các phương thức lớp, nhưng không có chức năng. Xem php.net/manual/en/language.oop5.overloading.php
Spechal

1
Bạn có thể tạo một hàm kiểm tra số lượng đối số một cách rõ ràng và thực thi một hàm khác, từ một tập hợp được xác định trước của chúng. Bạn nên thiết kế lại giải pháp của mình hoặc sử dụng các lớp thực hiện giao diện của mình
kolypto

2
Như php.net/manual/en/lingu.oop5.overloading.php nói, định nghĩa về quá tải của PHP khác với ngôn ngữ OOP điển hình. Họ chỉ đề cập đến các phương thức ma thuật cho phép định tuyến động các thuộc tính và chức năng dựa trên X.
Edwin Daniels

Đối với độc giả tương lai: Những gì @Spechal đang đề cập đến, là một ý nghĩa khác cho từ này overloading, hơn là được hỏi trong câu hỏi. (Xem câu trả lời được chấp nhận để biết thêm chi tiết.)
ToolmakerSteve

2
Có gì thay đổi kể từ PHP 7 không? : o
nawfal

Câu trả lời:


217

Bạn không thể quá tải các chức năng PHP. Chữ ký hàm chỉ dựa trên tên của chúng và không bao gồm danh sách đối số, vì vậy bạn không thể có hai hàm có cùng tên. Quá tải phương thức lớp là khác nhau trong PHP so với nhiều ngôn ngữ khác. PHP sử dụng cùng một từ nhưng nó mô tả một mô hình khác.

Tuy nhiên, bạn có thể khai báo một hàm matrixdic có số lượng đối số thay đổi. Bạn sẽ sử dụng func_num_args()func_get_arg()để có được các đối số được thông qua, và sử dụng chúng bình thường.

Ví dụ:

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);

8
Có thể tôi đã phát triển C ++ quá nhiều, nhưng tôi sẽ gợi ý một gợi ý rằng điều này đang được thực hiện trong các tham số chức năng như thế nào myFunc(/*...*/).
doug65536

4
@ doug65536, PHP 5.6+ sẽ hỗ trợ "..." dưới dạng mã thông báo cú pháp , để giúp chúng tôi giải tỏa. ;)
Sz.

Hoặc xem câu trả lời của Adil , gần với tình trạng quá tải của C ++ - càng gần càng tốt, trong một ngôn ngữ được gõ lỏng lẻo như php. Nó thậm chí còn thích hợp hơn trong php 7, vì bạn có thể cung cấp gợi ý loại cho các tham số, nếu chúng cùng loại trong tất cả các tình trạng quá tải của bạn.
ToolmakerSteve

78

PHP không hỗ trợ nạp chồng phương thức truyền thống, tuy nhiên một cách bạn có thể đạt được những gì bạn muốn là sử dụng __callphương thức ma thuật:

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) {

    }

}

20
Tôi chưa từng thấy việc sử dụng này __call()trước đây. Khá sáng tạo (nếu một chút dài dòng)! +1
BoltClock

Công dụng thực sự đáng ngưỡng mộ của __call ()
Abhishek Gupta

2
Trên thực tế, không thể đồng ý với điều này và phải thực hiện đào tạo lại với đề xuất này. Đối với một, việc sử dụng __call () này là một mô hình chống. Thứ hai, có thể thực hiện quá tải trong PHP cho các phương thức lớp có khả năng hiển thị chính xác. Tuy nhiên, bạn không thể - quá tải các hàm đơn giản.
Oddman

1
Bạn có thể giải thích lý do tại sao bạn nghĩ sử dụng __call () là một mô hình chống? Quá tải phương thức PHP không phải là thứ mà OP đang tìm kiếm - họ muốn khả năng có nhiều chữ ký phương thức có cùng tên nhưng đầu vào / đầu ra khác nhau: en.wikipedia.org/wiki/Feft_overloading
Stephen

20
Không cần sử dụng __call (). Thay vào đó, hãy khai báo một phương thức có tên mà bạn mong muốn, không có bất kỳ tham số nào được liệt kê và sử dụng func_get_args () trong phương thức đó để gửi đến triển khai riêng tư thích hợp.
FantasticJamieBurns

30

Để tải quá mức một chức năng, chỉ cần thực hiện chuyển tham số là null theo mặc định,

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');

4
Tôi không coi tham số mặc định là quá tải hàm. chức năng [hoặc phương thức] quá tải có liên quan nhiều hơn đến việc gọi một triển khai khác dựa trên loại đối số được truyền. Sử dụng các tham số mặc định chỉ cho phép bạn gọi thực hiện tương tự với sự tiện lợi của ít tham số hơn.
Có thể mở rộng

1
vâng, bạn cũng có thể điều khiển nó dựa trên loại nhưng như thể bạn biết php gõ ngôn ngữ lỏng lẻo và xử lý nó đòi hỏi phải giải quyết điều đó.
Adil Abbasi

1
Tôi thích câu trả lời này cho câu trả lời được chấp nhận, vì nó làm cho nó rõ ràng số lượng tham số tối thiểu và tối đa là bao nhiêu. (Không cung cấp giá trị mặc định cho các tham số bắt buộc.) @Scalable - Tôi đồng ý với Adil rằng, vì php được gõ một cách lỏng lẻo, đây thực sự là tất cả những gì có thể có trong php đối với overloadmột hàm - không bao giờ, bạn tạo một điểm hữu ích mà độc giả nên biết.
ToolmakerSteve

11

Nó có thể bị hack với một số người, nhưng tôi đã học được cách này từ cách Cakephp thực hiện một số chức năng và đã điều chỉnh nó vì tôi thích sự linh hoạt mà nó tạo ra

Ý tưởng là bạn có các loại đối số, mảng, đối tượng khác nhau, sau đó bạn phát hiện ra những gì bạn đã được thông qua và đi từ đó

function($arg1, $lastname) {
    if(is_array($arg1)){
        $lastname = $arg1['lastname'];
        $firstname = $arg1['firstname'];
    } else {
        $firstname = $arg1;
    }
    ...
}

1
Không, tôi không thấy điều này là hackish, PHP thực hiện điều này cho nhiều chức năng tích hợp sẵn của nó.
BoltClock

Bởi vì php được gõ lỏng lẻo, đây chính xác là cách người ta phải xử lý tình huống này. Tính "hackishness" cần thiết của nó trong php.
ToolmakerSteve

11
<?php   
/*******************************
 * author  : hishamdalal@gmail.com 
 * 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;
}
//----------------------------------------------------------

4
Bạn có thể thêm một số giải thích làm thế nào để sử dụng lớp này?
Justus Romijn

1- tạo lớp mới 2- mở rộng quá tải. 3- tạo các hàm như funcname_ () => no args hoặc like funcname_s ($ s) => chuỗi arg </ li>
Hisham Dalal

1
Đây là giải pháp khá tuyệt vời. Tại sao bạn làm $ o = new $ obj ()? Tôi chưa thử nó, mặc dù tôi nghĩ nó nên là \ $ ​​o = \ $ này?
over_optimistic

Cảm ơn bạn vì thông báo quan trọng này và tôi sẽ sử dụng dấu gạch chéo ngược, nhưng nó hoạt động với dấu gạch chéo ngược và không có! - Tôi sử dụng phpEazy như một máy chủ cục bộ.
Hisham Dalal

4

Cái này thì sao:

function($arg = NULL) {

    if ($arg != NULL) {
        etc.
        etc.
    }
}

Có thể hoạt động, nhưng ít đọc hơn nếu quá tải sẽ có các tham số khác nhau với các tên và ý nghĩa khác nhau.
Mathias Lykkegaard Lorenzen

3

Trong PHP 5.6, bạn có thể sử dụng toán tử splat ... làm tham số cuối cùng và loại bỏ func_get_args()func_num_args():

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

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

Bạn cũng có thể sử dụng nó để giải nén các đối số:

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

Tương đương với:

example(1, 2, 3);

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);
    ?>

Cố gắng chỉnh sửa câu hỏi và sử dụng định dạng. Nó sẽ làm cho câu trả lời của bạn dễ đọc hơn và thu hút nhiều người dùng hơn.
Kashish Arora


0

Bây giờ PHP không hỗ trợ quá tải. Hy vọng điều này sẽ được thực hiện trong các phiên bản khác như các ngôn ngữ lập trình khác.

Kiểm tra thư viện này, Điều này sẽ cho phép bạn sử dụng Quá tải PHP về mặt đóng cửa. https://github.com/Sahil-Gulati/Overloading


1
nếu bạn định đưa ra một tuyên bố như thế này thì thực sự nên bao gồm các phiên bản mà bạn đang đề cập, theo cách đó mọi người sẽ biết nhận xét của bạn đã hết hạn như thế nào khi họ thấy nó vào một ngày trong tương lai
MikeT

0

Đáng buồn thay, không có quá tải trong PHP vì nó được thực hiện trong C #. Nhưng tôi có một mẹo nhỏ. Tôi khai báo các đối số với các giá trị null mặc định và kiểm tra chúng trong một hàm. Bằng cách đó, chức năng của tôi có thể làm những việc khác nhau tùy thuộc vào đối số. Dưới đây là ví dụ đơn giản:

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);

1
Xin vui lòng đọc qua tất cả các câu trả lời hiện có, trước khi viết một năm mới sau đó. Kỹ thuật này đã được hiển thị hai lần trong các câu trả lời ở trên. Một lần vào năm 2013, và một lần nữa vào năm 2014.
ToolmakerSteve
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.