PHP 함수 오버로딩


196

C ++ background에서 온다;)
어떻게 PHP 함수를 오버로드 할 수 있습니까?

인수가있는 경우 하나의 함수 정의, 인수가없는 경우 다른 함수 정의? PHP에서 가능합니까? 아니면 $ _GET 및 POST에서 전달 된 매개 변수가 있는지 확인하려면 다른 방법을 사용해야합니까 ?? 그들과 관련이 있습니까?


1
클래스 메서드 만 오버로드 할 수 있지만 함수는 오버로드 할 수 없습니다. 참조 php.net/manual/en/language.oop5.overloading.php
Spechal

1
인수 수를 명시 적으로 확인하고 사전 정의 된 인수 세트에서 다른 함수를 실행하는 함수를 작성할 수 있습니다. 그러나 솔루션을 다시 디자인하거나 인터페이스를 구현하는 클래스를 사용하는 것이
좋습니다

2
는 AS 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는 같은 단어를 사용하지만 다른 패턴을 설명합니다.

그러나 가변 개수의 인수를 사용 하는 가변 함수 를 선언 할 수 있습니다 . 당신이 사용하는 것이 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.

또는 C ++의 오버로드에 더 가까운 Adil의 답변을 참조하십시오 . 모든 과부하에서 동일한 유형 인 경우 매개 변수에 대한 유형 힌트를 제공 할 수 있으므로 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()이전 에이 사용을 보지 못했습니다 . 꽤 창의적입니다 (약간의 장황한 경우)! +1
BoltClock

__call () 정말 대단한 사용
Abhishek Gupta

2
실제로, 이것에 동의 할 수 없으며,이 제안으로 재교육을 받아야합니다. 우선, 이러한 __call () 사용은 안티 패턴입니다. 둘째, 올바른 가시성을 가진 클래스 메소드에 대해 PHP에서 오버로드를 수행 할 수 있습니다. 그러나 일반 제인 기능에는 과부하가 걸리지 않습니다.
Oddman

1
__call () 사용이 안티 패턴이라고 생각하는 이유를 설명 할 수 있습니까? PHP 메소드 오버로딩은 OP가 찾는 것이 아닙니다. 그들은 같은 이름이지만 다른 입력 / 출력을 가진 여러 메소드 서명을 가질 수있는 기능을 원합니다. en.wikipedia.org/wiki/Function_overloading
Stephen

20
__call ()을 사용할 필요가 없습니다. 대신 매개 변수없이 원하는 이름을 가진 메소드를 선언하고 해당 메소드 내에서 func_get_args ()를 사용하여 적절한 개인 구현으로 디스패치하십시오.
FantasticJamieBurns

30

함수를 오버로드하려면 단순히 기본적으로 매개 변수를 null로 전달하십시오.

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
기본 매개 변수를 함수 오버로드로 간주하지 않습니다. 함수 [또는 메소드] 오버로딩은 전달 된 인수의 유형에 따라 다른 구현을 호출하는 것과 더 관련이 있습니다. 기본 매개 변수를 사용하면 더 적은 수의 매개 변수로 동일한 구현을 호출 할 수 있습니다.
확장 가능

1
그렇습니다. 유형을 기반으로 유형을 조작 할 수는 있지만 PHP가 느슨하게 유형이 지정된 언어를 알고 처리하는 경우이를 해결해야합니다.
Adil Abbasi

1
최소 및 최대 매개 변수 수를 명시 적으로 지정하므로 허용 된 답변 보다이 대답을 선호합니다. (필요한 매개 변수에 기본값을 제공하지 마십시오.) @Scalable-Adil에 동의합니다. PHP는 느슨하게 입력 overload되었으므로 PHP에서 함수에 의미 할 수있는 모든 것입니다. 독자들이 알고 있어야합니다.
ToolmakerSteve

11

일부 사람들에게는 해킹 일 수 있지만 Cakephp가 일부 기능을 수행하는 방법 에서이 방법을 배웠고 유연성이 마음에 들기 때문에 그것을 적응 시켰습니다.

아이디어는 다른 유형의 인수, 배열, 객체 등을 가지고 있으며 전달 된 것을 감지하고 거기에서 나갑니다.

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

1
아니, 나는 이것을 hackish로 보지 않는다 .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-새로운 클래스를 만듭니다. 3- funcname_ () => 인수 없음 또는 funcname_s ($ s) => 문자열 인수 </ li>와 같은 함수 만들기
Hisham Dalal

1
이것은 매우 멋진 솔루션입니다. 왜 $ o = new $ obj ()를합니까? 아직 시도하지는 않았지만 \ $ o = \ $ this 여야한다고 생각합니까?
over_optimistic

이 중요한 통지에 감사드립니다. 나는 백 슬래시를 사용할 것이지만 백 슬래시와 함께 작동합니다! -phpEazy를 로컬 서버로 사용합니다.
Hisham Dalal


3

PHP 5.6에서 당신은 사용할 수있는 플랫 연산자를 ... 마지막 매개 변수로와 페지 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

슬프게도 C #에서와 같이 PHP에는 과부하가 없습니다. 하지만 약간의 트릭이 있습니다. 기본 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
1 년 후 새로운 글을 쓰기 전에 기존의 모든 답을 읽으십시오. 이 기술은 위의 답변에서 이미 두 번 표시되었습니다. 2013 년에 한 번 그리고 2014 년에 다시 한 번
ToolmakerSteve
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.