2009-10-22 13 views
11

Tengo una instancia SoapClient generada para un archivo WSDL. Todas, excepto una de las invocaciones al método, requieren el nombre de usuario y la contraseña para pasar el id.¿Es posible curry llamadas de método en PHP?

¿Hay alguna manera de currying las llamadas al método para que pueda omitir el nombre de usuario y la contraseña?

+0

¿Puede dar un ejemplo de lo que estás pensando? – Gumbo

+0

Ver la respuesta de VolkerK. –

Respuesta

16

A partir de php 5.3 puede almacenar un anonymous function en una variable. Esta función anónima puede llamar a la función "original" con algunos parámetros predefinidos.

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

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

$bar('C'); 

edición: También puede utilizar un cierre a parametrizar la creación de la función anónima

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: Esto también funciona con objetos

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

pero las otras sugerencias utilizando __call() y una clase contenedora puede ser mejor si desea "mejorar" una clase completa.

+0

¡Eso está bien! Estoy un poco oxidado en PHP. Sé que puedo enumerar los métodos de un objeto, pero ¿puedo interceptarlos? –

+0

Te refieres a algo como 'magicTrampoline (array ($ obj, 'methodName'), $ myInterceptor)' para que siempre que se llame '$ obj-> methodName' se invoque tu devolución de llamada en $ myInterceptor (sin $ obj implementando explícitamente dicha función)? ¿Se le permitiría a 'magicTrampoline()' devolver un nuevo objeto? – VolkerK

+0

Si el 'magicTrampoline()' podría agregarse a los argumentos pasados ​​a $ obj-> methodName (...), yes. –

2

Aunque no es una solución muy buena, puede escribir una clase contenedora básica que usó PHPs magic methods (específicamente __call) para llamar a la función real pero anexar el nombre de usuario y la contraseña a la lista de argumentos.

Ejemplo básico:

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); 
    } 
} 
+0

Gracias. ¿Es ese el método '__call' IIRC? –

+0

call_user_func acepta una lista de parámetros, no una matriz. Su solución simplemente enviará los parámetros al curry a la función como una matriz, no una lista de argumentos. Sin embargo, usar call_user_func_array() funcionará :) –

+1

@JohnKurlak Se corrigió eso para ti. ;-) – Potherca

3

PHP no tiene currying per se, pero se puede hacer algo por el estilo de varias maneras. En su caso específico, algo así como esto puede funcionar:

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

Aunque aquí es una solución más general para ganarse en PHP> = 5.3:

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

$result = $curriedMethod('some argument'); 
3

hice una investigación relacionada en esto hoy. Esto es lo más cerca que pude conseguir:

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

El principal problema es PHP no le permitirá ejecutar un cierre directamente en un valor de retorno (mucho de la misma manera PHP no permitirá la ejecución de un método en un objeto independiente) Sin embargo, dado que los cierres son un objeto de tipo Cierre, que tienen un método incorporado __invoke(), lo anterior funcionará.

5

Aquí está una clase implementa currificación automático y aplicación parcial:

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

Ejemplo:

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

Incluso se puede hacer esto:

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

Según lo mencionado por Ihor, la biblioteca PHP no estándar es interesante. que ya han aplicado el mismo método, es un poco diferente a la función de 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)); 
    }; 
} 

Uso:

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