Ist es möglich, Methodenaufrufe in PHP Curry?
Frage
Ich habe ein Soapclient Beispiel für eine WSDL-Datei erzeugt. Alle außer einem der Methodenaufrufe benötigen Sie den Benutzernamen und das Passwort-ID übergeben werden.
Gibt es eine Möglichkeit, die Methodenaufrufe von Striegeln, so dass ich den Benutzernamen und das Kennwort weglassen können?
Lösung
Ab PHP 5.3 Sie eine in einer Variablen rel="noreferrer">. Diese anonymen Funktion kann die „Original“ Funktion mit einigen vordefinierten Parametern aufrufen.
function foo($x, $y, $z) {
echo "$x - $y - $z";
}
$bar = function($z) {
foo('A', 'B', $z);
};
$bar('C');
bearbeiten: Sie können auch einen Verschluss verwenden, um die Erstellung der anonymen Funktion parametrisieren
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: Dies funktioniert auch mit Objekten
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');
Aber auch die anderen Vorschläge mit __call () und eine Wrapper-Klasse kann besser sein, wenn Sie wollen eine komplette Klasse „verbessern“.
Andere Tipps
Hier ist eine Klasse implementiert automatisches currying und teilweise Anwendung:
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);
}
Beispiel:
$add = lambda(function($a, $b) {
return $a + $b;
});
$add1 = $add(1);
echo $add1(2); // 3
Auch können Sie dies tun:
$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 muss nicht per se currying, aber Sie können auf verschiedene Weise etwas tun. In Ihrem speziellen Fall, so etwas wie dies funktionieren kann:
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);
Obwohl hier eine allgemeinere Lösung für currying in PHP> = 5.3:
$curriedMethod = function ($additionalArg) use ($soapClient) { return $soapClient->method(USERNAME,PASSWORD,$additionalArg); }
$result = $curriedMethod('some argument');
Ich habe einige relevante Forschung in diese heute. Das ist so nah, wie ich bekommen konnte:
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();
Das Hauptproblem ist PHP erhalten Sie einen Verschluss direkt auf einem Rückgabewert nicht zulassen, execute (viel in der gleichen Art und Weise PHP wird nicht zulassen, dass eine Methode auf einem ungebundenes Objekt ausgeführt wird). Da jedoch Schließungen ein Objekt vom Typ Schließung sind, die eine integrierte Methode __invoke hat (), wird die oben arbeiten.
Obwohl es nicht eine sehr gute Lösung, könnten Sie eine einfache Wrapper-Klasse schreiben, die PHPs verwendet
Sie können mit Teil Anwendung und Curry Funktionen von nicht-Standard-PHP-Bibliothek .
Wie von Ihor erwähnt, die Nicht-Standard-PHP-Bibliothek ist interessant.
Ich habe implementiert bereits die gleiche Methode, es ist ein bisschen anders als Ihor der ist curried()
Funktion
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));
};
}
Verbrauch:
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);
Diese Antwort Ist es möglich, Curry Methodenaufrufe in PHP? nicht Currying nicht zeigen. Diese Antwort zeigt teilweise Anwendung. Ein schönes Tutorial, das den Unterschied zwischen diesen Begriffen erklärt ist hier zu sehen: http://allthingsphp.blogspot.com/2012/02/currying-vs-partial-application.html
Dies ist Currying:
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);
};
};
}
die curried Funktion in PHP 7 Aufrufe
$result = curried_sum3(1)(2)(3);
var_dump($result); // int 6
die curried Funktion in PHP 5 Aufrufe
$f1 = curried_sum3(6);
$f2 = $f1(6);
$result = $f2(6);
var_dump($result);
//OUTPUT:
int 18
Dies ist teilweise Anwendung:
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