我有一个为 WSDL 文件生成的SoapClient实例。除了一个方法调用之外,所有方法调用都需要传递用户名和密码 id。
有没有办法对方法调用进行柯里化,以便我可以省略用户名和密码?
我有一个为 WSDL 文件生成的SoapClient实例。除了一个方法调用之外,所有方法调用都需要传递用户名和密码 id。
有没有办法对方法调用进行柯里化,以便我可以省略用户名和密码?
从 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');
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 本身没有柯里化,但您可以通过多种方式执行类似的操作。在您的特定情况下,这样的事情可能会起作用:
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');
我今天对此做了一些相关的研究。这是我能得到的最接近的:
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 库中的部分应用程序和curry函数。
正如 Ihor 所提到的,非标准 PHP 库很有趣。我已经实现了相同的方法,它与Ihor的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);
这个答案是否可以在 PHP 中进行方法调用?没有显示柯里化。该答案显示了部分应用。一个很好的教程解释了这些概念之间的区别可以在这里看到:http: //allthingsphp.blogspot.com/2012/02/currying-vs-partial-application.html
这是柯里化:
function sum3($x, $y, $z) {
return $x + $y + $z;
}
// The curried function
function curried_sum3($x) {
return function ($y) use ($x) {
return function ($z) use ($x, $y) {
return sum3($x, $y, $z);
};
};
}
在 PHP 7 中调用 curried 函数
$result = curried_sum3(1)(2)(3);
var_dump($result); // int 6
在 PHP 5 中调用 curried 函数
$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