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

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

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

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

function foo($x, $y, $z) { echo "$x - $y - $z"; } $bar = function($z) { foo('A', 'B', $z); }; $bar('C'); 

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

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

edit2: Это также работает с объектами

 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 () и класс-оболочку, могут быть лучше, если вы хотите «улучшить» полный класс.

Здесь класс реализует автоматическое каррирование и частичное применение:

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

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

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

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

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

Сегодня я провел некоторые исследования. Это как можно ближе:

 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 (), выше будет работать.

Хотя это не очень хорошее решение, вы можете написать базовый класс-оболочку, который использовал магические методы PHP (конкретно __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); } } 

Вы можете использовать функции частичного приложения и карри из нестандартной библиотеки PHP .

Как отметил Игорь, интересна нестандартная библиотека 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);