ฟังก์ชั่นการโอเวอร์โหลดของ PHP


196

มาจากพื้นหลัง C ++;)
ฉันจะโอเวอร์โหลดฟังก์ชัน PHP ได้อย่างไร

นิยามฟังก์ชันหนึ่งถ้ามีอาร์กิวเมนต์ใด ๆ และอีกอันถ้าไม่มีอาร์กิวเมนต์? เป็นไปได้ใน PHP? หรือฉันควรใช้ถ้าอื่นเพื่อตรวจสอบว่ามีพารามิเตอร์ใด ๆ ที่ส่งผ่านจาก $ _GET และ POST ?? และเกี่ยวข้องกับพวกเขา?


1
คุณสามารถโอเวอร์โหลดคลาสเมธอดได้ แต่ไม่สามารถใช้งานได้ ดูphp.net/manual/en/language.oop5.overloading.php
Spechal

1
คุณสามารถสร้างฟังก์ชั่นที่ตรวจสอบจำนวนข้อโต้แย้งอย่างชัดเจนและดำเนินการฟังก์ชั่นอื่นจากชุดที่กำหนดไว้ล่วงหน้าของพวกเขา ไม่ว่าคุณจะต้องการออกแบบโซลูชันของคุณดีขึ้นหรือใช้คลาสที่ใช้อินเทอร์เฟซของคุณ
kolypto

2
ดังที่php.net/manual/en/language.oop5.overloading.phpกล่าวว่าคำจำกัดความการโอเวอร์โหลดของ PHP แตกต่างจากภาษา OOP ทั่วไป พวกเขาอ้างถึงวิธีการทางเวทมนตร์ที่อนุญาตให้มีการกำหนดเส้นทางคุณสมบัติและฟังก์ชั่นแบบไดนามิกโดยใช้ X
Edwin Daniels

สำหรับผู้อ่านในอนาคต: สิ่งที่ @Spechal หมายถึงคือความหมายที่แตกต่างกันสำหรับคำoverloadingนี้มากกว่าที่จะถูกถามในคำถาม (ดูคำตอบที่ยอมรับสำหรับรายละเอียดเพิ่มเติม)
ToolmakerSteve

2
มีอะไรเปลี่ยนแปลงตั้งแต่ PHP 7? : o
nawfal

คำตอบ:


219

คุณไม่สามารถโอเวอร์โหลดฟังก์ชั่น PHP ได้ ฟังก์ชันลายเซ็นจะขึ้นอยู่กับชื่อของพวกเขาเท่านั้นและไม่มีรายการอาร์กิวเมนต์ดังนั้นคุณจึงไม่สามารถมีสองฟังก์ชันที่ใช้ชื่อเดียวกันได้ วิธีการบรรทุกเกินพิกัดระดับแตกต่างกันใน PHP กว่าในภาษาอื่น ๆ PHP ใช้คำเดียวกัน แต่อธิบายรูปแบบที่แตกต่าง

อย่างไรก็ตามคุณสามารถประกาศฟังก์ชัน Variadicที่รับจำนวนตัวแปรได้ คุณจะใช้func_num_args()และfunc_get_arg()เพื่อให้ข้อโต้แย้งผ่านไปและใช้ตามปกติ

ตัวอย่างเช่น:

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 ++ มากเกินไป myFunc(/*...*/)แต่ฉันขอแนะนำให้คำแนะนำที่ว่านี้จะถูกดำเนินการในพารามิเตอร์ของฟังก์ชั่นเช่น
doug65536

4
@ doug65536, PHP 5.6+ จะสนับสนุน"... " เป็นโทเค็นของไวยากรณ์เพื่อการบรรเทาที่ยอดเยี่ยมของเรา ;)
Sz.

หรือดูคำตอบของ Adilซึ่งอยู่ใกล้กับการบรรทุกเกินพิกัดของ C ++ - ใกล้เคียงที่สุดที่จะได้รับในภาษาที่พิมพ์อย่างหลวม ๆ เช่น php มีความเหมาะสมมากกว่าใน php 7 เนื่องจากคุณสามารถให้คำแนะนำประเภทสำหรับพารามิเตอร์หากเป็นประเภทเดียวกันในโอเวอร์โหลดทั้งหมดของคุณ
ToolmakerSteve

78

PHP ไม่สนับสนุนการใช้วิธีการมากไปกว่าเดิม แต่วิธีหนึ่งที่คุณสามารถบรรลุสิ่งที่คุณต้องการคือการใช้ประโยชน์จาก__callวิธีเวทย์มนตร์:

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
ฉันไม่เคยเห็นการใช้สิ่งนี้__call()มาก่อน ความคิดสร้างสรรค์ (ถ้า verbose น้อย) +1
BoltClock

การใช้ __call () ที่น่าชื่นชมจริงๆ
Abhishek Gupta

2
ที่จริงแล้วไม่สามารถเห็นด้วยกับเรื่องนี้และจะต้องใช้ความอดทนกับข้อเสนอแนะนี้ สำหรับหนึ่งการใช้ __call () นี้เป็นรูปแบบการต่อต้าน ประการที่สองเป็นไปได้ที่จะทำการโอเวอร์โหลดใน PHP สำหรับวิธีการเรียนที่มีทัศนวิสัยที่ถูกต้อง อย่างไรก็ตามคุณไม่สามารถ - ฟังก์ชั่นโอเวอร์เพลนเจนมากเกินไป
Oddman

1
คุณช่วยอธิบายได้ไหมว่าทำไมคุณคิดว่าการใช้ __call () เป็นรูปแบบต่อต้าน วิธีการบรรทุกเกินพิกัด PHP ไม่ได้เป็นสิ่ง OP ที่กำลังมองหา - พวกเขาต้องการความสามารถในการที่จะมีลายเซ็นวิธีการหลายรายที่มีชื่อเหมือนกัน แต่แตกต่างกันใส่ / เอาท์พุท: en.wikipedia.org/wiki/Function_overloading
สตีเฟ่น

20
ไม่จำเป็นต้องใช้ __call () แทนที่จะประกาศวิธีการที่มีชื่อที่คุณต้องการโดยไม่มีพารามิเตอร์ใด ๆ ในรายการและใช้ func_get_args () ภายในวิธีการนั้นเพื่อส่งไปยังการใช้งานส่วนตัวที่เหมาะสม
FantasticJamieBurns

30

หากต้องการโหลดฟังก์ชั่นมากเกินไปให้ทำพารามิเตอร์ Pass เป็นค่าเริ่มต้น

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
ฉันไม่พิจารณาพารามิเตอร์เริ่มต้นเป็นฟังก์ชั่นมากไป ฟังก์ชั่น [หรือวิธีการ] การบรรทุกเกินพิกัดมีมากขึ้นจะทำอย่างไรกับการเรียกใช้งานที่แตกต่างกันขึ้นอยู่กับประเภทของการโต้แย้งผ่าน การใช้พารามิเตอร์เริ่มต้นอนุญาตให้คุณเรียกใช้การดำเนินการเดียวกันเท่านั้นโดยสะดวกกับพารามิเตอร์ที่น้อยลง
Scalable

1
ใช่คุณสามารถจัดการกับมันตามประเภท แต่ถ้าคุณรู้ภาษา PHP พิมพ์อย่างหลวม ๆ และจัดการกับมันต้องจัดการกับมัน
Adil Abbasi

1
ฉันชอบคำตอบนี้กับคำตอบที่ยอมรับเนื่องจากทำให้ชัดเจนว่าพารามิเตอร์ขั้นต่ำและจำนวนสูงสุดควรเป็นเท่าไหร่ (ไม่ต้องระบุค่าเริ่มต้นสำหรับพารามิเตอร์ที่จำเป็น) @Scalable - ฉันเห็นด้วยกับ Adil ว่าเนื่องจาก php ถูกพิมพ์อย่างหลวม ๆ นี่เป็นสิ่งที่มีประสิทธิภาพทั้งหมดที่สามารถมีความหมายใน php ต่อoverloadฟังก์ชั่น - ไม่เป็นไรคุณทำให้เป็นประโยชน์ ที่ผู้อ่านควรระวัง
ToolmakerSteve

11

มันอาจจะแฮ็กไปบ้าง แต่ฉันได้เรียนรู้วิธีนี้จากวิธีที่ Cakephp ทำหน้าที่บางอย่างและปรับมันเพราะฉันชอบความยืดหยุ่นที่มันสร้างขึ้น

แนวคิดคือคุณมีอาร์กิวเมนต์ประเภทต่างๆอาร์เรย์วัตถุ ฯลฯ จากนั้นคุณจะตรวจสอบสิ่งที่คุณผ่านและไปจากที่นั่น

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

1
ไม่ฉันไม่เห็นว่านี่เป็นการแฮ็กข้อมูล PHP ทำสิ่งนี้กับฟังก์ชั่นในตัวหลายอย่าง
BoltClock

เนื่องจาก php ถูกพิมพ์แบบหลวม ๆ นี่เป็นวิธีที่เราต้องจัดการกับสถานการณ์นี้ "การแฮ็คที่จำเป็น" ใน 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
คุณสามารถเพิ่มคำอธิบายวิธีใช้คลาสนี้ได้ไหม?
Justus Romijn

1- สร้างคลาสใหม่ 2- ขยายเกินความสามารถ 3- สร้างฟังก์ชั่นเช่น funcname_ () => ไม่ต้องใส่ args หรือชอบ funcname_s ($ s) => string arg </li>
Hisham Dalal

1
นี่เป็นทางออกที่ยอดเยี่ยม ทำไมคุณถึงทำ $ o = new $ obj () ฉันยังไม่ได้ลองเลยแม้ว่าฉันจะคิดว่ามันควรเป็น \ $ o = \ $ อันนี้ไหม
over_optimistic

ขอบคุณสำหรับประกาศสำคัญนี้และฉันจะใช้แบ็กสแลช แต่มันใช้ได้กับแบ็กสแลชและไม่ใช้! - ฉันใช้ phpEazy เป็นเซิร์ฟเวอร์ท้องถิ่น
Hisham Dalal

4

เกี่ยวกับสิ่งนี้:

function($arg = NULL) {

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

สามารถใช้งานได้ แต่อ่านได้น้อยกว่าหากโอเวอร์โหลดจะมีพารามิเตอร์ต่างกันที่มีชื่อแตกต่างกันและมีความหมายต่างกัน
Mathias Lykkegaard Lorenzen

3

ใน PHP 5.6 คุณสามารถใช้ตัวดำเนินการ splat ...เป็นพารามิเตอร์สุดท้ายและทำได้ด้วย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);

คุณสามารถใช้มันเพื่อคลายข้อโต้แย้งได้เช่นกัน:

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

เทียบเท่ากับ:

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

ลองแก้ไขคำถามและใช้การจัดรูปแบบ มันจะทำให้คำตอบของคุณอ่านง่ายขึ้นและดึงดูดผู้ใช้มากขึ้น
Kashish Arora

เทคนิคนี้จะแสดงให้เห็นในคำตอบก่อนหน้านี้
ToolmakerSteve

0

PHP ไม่รองรับการโหลดมากเกินไปในตอนนี้ หวังว่าจะมีการใช้งานในเวอร์ชันอื่น ๆ เช่นภาษาการเขียนโปรแกรมอื่น ๆ

ชำระเงินห้องสมุดนี้จะช่วยให้คุณใช้ PHP มากไปในแง่ของการปิด https://github.com/Sahil-Gulati/Overloading


1
หากคุณกำลังจะทำแถลงการณ์เช่นนี้ควรรวมถึงรุ่นที่คุณอ้างถึงด้วยวิธีการที่ผู้คนรู้ว่าล้าสมัยความคิดเห็นของคุณคือเมื่อพวกเขาเห็นมันในวันที่ในอนาคต
MikeT

0

น่าเศร้าที่ไม่มี PHP มากเกินไปเนื่องจากทำใน C # แต่ฉันมีเคล็ดลับเล็กน้อย ฉันประกาศข้อโต้แย้งที่มีค่า null เริ่มต้นและตรวจสอบพวกเขาในฟังก์ชั่น ด้วยวิธีการทำงานของฉันสามารถทำสิ่งที่แตกต่างกันขึ้นอยู่กับข้อโต้แย้ง ด้านล่างเป็นตัวอย่างง่ายๆ:

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
โปรดอ่านคำตอบที่มีอยู่ทั้งหมดก่อนเขียนใหม่ในอีกหนึ่งปีต่อมา เทคนิคนี้แสดงให้เห็นแล้วสองครั้งในคำตอบข้างต้น ครั้งเดียวในปี 2013 และอีกครั้งในปี 2014
ToolmakerSteve
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.