Можно ли выполнять вызовы методов в PHP?


У меня есть экземпляр SoapClient, созданный для файла WSDL. Все, кроме одного из вызовов метода, требуют, чтобы имя пользователя и пароль были переданы id.

Есть ли какой-либо способ выполнить вызовы метода, чтобы я мог опустить имя пользователя и пароль?

Author: Robert Munteanu, 2009-10-23

8 answers

Начиная с php 5.3, вы можете хранить анонимную функцию в переменной. Эта анонимная функция может вызывать "оригинальную" функцию с некоторыми предопределенными параметрами.

function foo($x, $y, $z) {
  echo "$x - $y - $z";
}

$bar = function($z) {
  foo('A', 'B', $z);
};

$bar('C');

Изменить: Вы также можете использовать замыкание для параметризации создания анонимной функции

function foo($x, $y, $z) {
  echo "$x - $y - $z";
}

function fnFoo($x, $y) {
  return function($z) use($x,$y) {
    foo($x, $y, $z);
  };
}

$bar = fnFoo('A', 'B');
$bar('C');

Правка2: Это также работает с объектами

class Foo {
  public function bar($x, $y, $z) {
    echo "$x - $y - $z";
  }
}

function fnFoobar($obj, $x, $z) {
  return function ($y) use ($obj,$x,$z) {
    $obj->bar($x, $y, $z);
  };
}

$foo = new Foo;
$bar = fnFoobar($foo, 'A', 'C');
$bar('B');

Но другие предложения с использованием __call() и класса-оболочки могут быть лучше, если вы хотите "улучшить" полный класс.

 19
Author: VolkerK, 2009-10-22 21:36:24

Вот класс, реализующий автоматическое каррирование и частичное применение:

class lambda
{
    private $f;
    private $args;
    private $count;
    public function __construct($f, $args = [])
    {
        if ($f instanceof lambda) {
            $this->f = $f->f;
            $this->count = $f->count;
            $this->args = array_merge($f->args, $args);
        }
        else {
            $this->f = $f;
            $this->count = count((new ReflectionFunction($f))->getParameters());
            $this->args = $args;
        }
    }

    public function __invoke()
    {
        if (count($this->args) + func_num_args() < $this->count) {
            return new lambda($this, func_get_args());
        }
        else {
            $args = array_merge($this->args, func_get_args());
            $r = call_user_func_array($this->f, array_splice($args, 0, $this->count));
            return is_callable($r) ? call_user_func(new lambda($r, $args)) : $r;
        }
    }
}
function lambda($f)
{
    return new lambda($f);
}

Пример:

$add = lambda(function($a, $b) { 
    return $a + $b; 
});
$add1 = $add(1);
echo $add1(2); // 3

Даже вы можете сделать это:

$int1 = lambda(function($f, $x) {
    return $f($x);
});

$successor = lambda(function($p, $f, $x) {
    return $f($p($f, $x));
}); 

$add = lambda(function($p, $q, $f, $x) {
    return $p($f, $q($f, $x));
}); 

$mul = lambda(function($p, $q, $x) {
    return $p($q($x));
}); 

$exp = lambda(function($m, $n) {
    return $n($m);
});

$int2 = $successor($int1);
$int3 = $add($int1, $int2);
$int6 = $mul($int3, $int2);
$int8 = $exp($int2, $int3);
 6
Author: ouyangde, 2014-11-05 05:57:05

В PHP нет карринга как такового, но вы можете сделать что-то подобное несколькими способами. В вашем конкретном случае может сработать что-то вроде этого:

class MySoapClient extends SoapClient {
  ...
  public function __call($meth,$args) {
    if (substr($method,0,5) == 'curry') {
      array_unshift($args,PASSWORD);
      array_unshift($args,USERNAME);
      return call_user_func_array(array($this,substr($meth,5)),$args);
    } else {
      return parent::__call($meth,$args);
    }
  }
}
$soapClient = new MySoapClient();
...
// now the following two are equivalent
$soapClient->currysomeMethod($additionalArg);
$soapClient->someMethod(USERNAME,PASSWORD,$additionalArg);

Хотя вот более общее решение для карринга в PHP>=5.3:

$curriedMethod = function ($additionalArg) use ($soapClient) { return $soapClient->method(USERNAME,PASSWORD,$additionalArg); }

$result = $curriedMethod('some argument');
 3
Author: Lucas Oman, 2009-10-22 21:24:41

Сегодня я провел некоторые связанные с этим исследования. Это настолько близко, насколько я мог подойти:

function curryAdd($x)
{
  return function($y = null) use ($x)
  {
    if (is_null($y)) return $x;
    else return curryAdd($x + $y);
  };
}

// echo curryAdd(1)(2)(3)(4);
echo curryAdd(1)
  ->__invoke(2)
  ->__invoke(3)
  ->__invoke(4)
  ->__invoke();

Основная проблема заключается в том, что PHP не позволит вам выполнить замыкание непосредственно на возвращаемом значении (во многом таким же образом PHP не позволит выполнить метод на несвязанном объекте). Однако, поскольку замыкания являются объектом типа Closure, который имеет встроенный метод __invoke(), вышеописанное будет работать.

 3
Author: , 2012-01-20 01:58:51

Хотя это и не очень хорошее решение, вы могли бы написать базовый класс-оболочку, который использовал бы волшебные методы PHPs (в частности __call) для вызова фактической функции, но добавил имя пользователя и пароль в список аргументов.

Основной пример:

class SC
{
    private $user;
    private $pass;

    public function __construct($user, $pass)
    {
        $this->user = $user;
        $this->pass = $pass;
    }

    public function __call($name, $arguments) 
    {
        $arguments = array_merge(array($this->user, $this->pass), $arguments);  
        call_user_func_array($name, $arguments);
    }
}
 2
Author: Yacoby, 2015-08-01 21:48:19

Как упоминал Игорь, нестандартная библиотека PHP интересна. Я уже реализовал тот же метод, он немного отличается от функции Игоря curried()

function curryfy($f, $args = []) {
    $reflexion = new ReflectionFunction($f);
    $nbParams = $reflexion->getNumberOfParameters();

    return function (...$arguments) use ($f, $reflexion, $nbParams, $args) {
        if (count($args) + count($arguments) >= $nbParams) {
            return $reflexion->invokeArgs(array_merge($args, $arguments));
        }

        return curryfy($f, array_merge($args, $arguments));
    };
}

Использование:

function display4 ($a, $b, $c, $d) {
    echo "$a, $b, $c, $d\n";
};
$curry4 = curryfy('display4');
display4(1, 2, 3, 4);
$curry4(1)(2)(3)(4);
 2
Author: Arno, 2017-10-09 16:07:06

Этот ответ Можно ли выполнять вызовы методов карри в PHP? не показывает Каррирование. Этот ответ показывает частичное применение. Хороший учебник, который объясняет разницу между этими понятиями, можно увидеть здесь: http://allthingsphp.blogspot.com/2012/02/currying-vs-partial-application.html

Это Каррирование:

function sum3($x, $y, $z) {
    return $x + $y + $z;
}

function curried_sum3($x) {
    return function ($y) use ($x) {
        return function ($z) use ($x, $y) {
            return sum3($x, $y, $z);
        };
    };
}

//$result = curried_sum3(1)(2)(3); // Not possible in PHP

$f1 = curried_sum3(6);
$f2 = $f1(6);
$result = $f2(6);
var_dump($result);

//OUTPUT:
int 18

Это частичное применение:

function sum3($x, $y, $z) {
    return $x + $y + $z;
}

function partial_sum3($x) {
    return function($y, $z) use($x) {
        return sum3($x, $y, $z);
    };
}

//create the partial
$f1 = partial_sum3(6);
//execute the partial with the two remaining arguments
$result = $f1(6, 6);

var_dump($result);

//OUTPUT:
int 18
 0
Author: Julian, 2018-08-23 12:49:06