Перегрузка функций PHP

196

Исходя из фона C ++;)
Как я могу перегрузить функции PHP?

Одно определение функции, если есть аргументы, и другое, если нет аргументов? Это возможно в PHP? Или я должен использовать if else, чтобы проверить, есть ли какие-либо параметры, переданные из $ _GET и POST ?? и связать их?

Вамси Кришна Б
источник
1
Вы можете перегружать только методы класса, но не функции. См. Php.net/manual/en/language.oop5.overloading.php
Spechal
1
Вы можете создать функцию, которая явно проверяет количество аргументов и выполняет другую функцию из предопределенного набора из них. Как бы вам ни было лучше переделать свое решение или использовать классы, реализующие ваш интерфейс
kolypto
2
Как говорит php.net/manual/en/language.oop5.overloading.php , определение перегрузки в PHP отличается от типичного языка ООП. Они просто ссылаются на магические методы, которые позволяют динамическую маршрутизацию свойств и функций на основе X.
Эдвин Дэниелс
Для будущих читателей: То, на что ссылается @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);
BoltClock
источник
8
Возможно, я слишком много занимался разработкой на C ++, но я бы предложил намек на то, что это делается в таких параметрах функции, как myFunc(/*...*/).
doug65536
4
@ doug65536, PHP 5.6+ будет поддерживать это "..." в качестве синтаксического токена , к нашему большому облегчению. ;)
Сз.
Или посмотрите ответ Адиля , который ближе к перегрузке 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()раньше. Довольно креативно (если немного многословно)! +1
BoltClock
Действительно замечательное использование __call ()
Абхишек Гупта
2
На самом деле, не может согласиться с этим, и должен осуществить переобучение с этим предложением. Во-первых, использование __call () является анти-паттерном. Во-вторых, в PHP возможна перегрузка для методов классов, которые имеют правильную видимость. Вы не можете, однако, перегрузить обычные функции.
Оддман
1
Можете ли вы объяснить, почему вы думаете, что использование __call () является антишаблоном? Перегрузка метода PHP - это не то, что ищет OP - они хотят иметь возможность иметь несколько сигнатур методов с одним и тем же именем, но с разным вводом / выводом: en.wikipedia.org/wiki/Function_overloading
Стивен,
20
Нет необходимости использовать __call (). Вместо этого объявите метод с желаемым именем без перечисленных параметров и используйте func_get_args () в этом методе для отправки в соответствующую частную реализацию.
FantasticJamieBurns
30

Чтобы перегрузить функцию, просто передайте параметр как ноль по умолчанию,

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, и для его решения необходимо решить эту проблему.
Адиль Аббаси
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;
    }
    ...
}
SeanDowney
источник
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
Не могли бы вы объяснить, как использовать этот класс?
Юстус Ромийн
1 - создать новый класс 2 - расширяет возможности перегрузки. 3- создавать функции, такие как funcname_ () => без аргументов или как funcname_s ($ s) => строковый аргумент </ li>
Хишам Далал,
1
Это довольно крутое решение. Почему вы делаете $ o = new $ obj ()? Я еще не пробовал, хотя думаю, что это должно быть \ $ o = \ $ this?
сверхоптимистичный
Спасибо за это важное уведомление, и я буду использовать обратную косую черту, но она работает с обратной косой чертой и без нее! - Я использую phpEazy в качестве локального сервера.
Хишам Далал
4

Как насчет этого:

function($arg = NULL) {

    if ($arg != NULL) {
        etc.
        etc.
    }
}
Марлин Уверсон
источник
Может работать, но менее читабельно, если перегрузка будет иметь разные параметры с разными именами и значениями.
Матиас Ликкегор Лоренцен
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);
mtpultz
источник
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);
    ?>
Раман Глубокая Баджва
источник
Попробуйте отредактировать вопрос и использовать форматирование. Это сделает ваш ответ более читабельным и привлечет больше пользователей.
Кашиш Арора
Эта техника показана в предыдущем ответе .
ToolmakerSteve
0

PHP пока не поддерживает перегрузку. Надеюсь, что это будет реализовано в других версиях, как и другие языки программирования.

Оформить заказ этой библиотеки, это позволит вам использовать перегрузку PHP с точки зрения замыканий. https://github.com/Sahil-Gulati/Overloading

Сахил Гулати
источник
1
если вы собираетесь сделать такое заявление, оно должно действительно включать версии, на которые вы ссылаетесь, таким образом, люди узнают, насколько устаревшим является ваш комментарий, когда они увидят его в будущем
MikeT
0

К сожалению, в PHP нет перегрузки, как в C #. Но у меня есть маленькая хитрость. Я объявляю аргументы с нулевыми значениями по умолчанию и проверяю их в функции. Таким образом, моя функция может делать разные вещи в зависимости от аргументов. Ниже приведен простой пример:

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