Pregunta

Tengo una instancia de SoapClient generada para un archivo WSDL. Todas, excepto una de las invocaciones de métodos, requieren que el nombre de usuario y la contraseña se pasen como id.

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

¿Fue útil?

Solución

A partir de php 5.3, puede almacenar una función anónima en una variable. Esta función anónima puede llamar al " original " funciona con algunos parámetros predefinidos.

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

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

$bar('C');

editar: también puede utilizar un cierre para 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 que usan __call () y una clase de envoltura pueden ser mejores si desea " mejorar " una clase completa.

Otros consejos

Aquí hay una clase que implementa aplicación automática y 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 puedes 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);

PHP no tiene curry per se, pero puedes hacer algo así de varias maneras. En su caso específico, algo 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í hay una solución más general para hacer currys en PHP > = 5.3:

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

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

Hice algunas investigaciones relacionadas con 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 que PHP no le permitirá ejecutar un cierre directamente en un valor de retorno (de la misma manera que PHP no le permitirá ejecutar un método en un objeto no vinculado). Sin embargo, dado que los cierres son un objeto de tipo Cierre, que tienen un método incorporado __invoke (), lo anterior funcionará.

Aunque no es una solución muy buena, puede escribir una clase de contenedor básico que use PHP métodos mágicos (Específicamente __call) para llamar a la función real pero agregar 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);
    }
}

Como lo mencionó Ihor, la biblioteca de PHP no estándar es interesante. Ya he implementado el mismo método, es un poco diferente a la función curried () de Ihor

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

Esta respuesta ¿Es posible curry ¿Las llamadas de método en PHP? no muestran Currying. Esa respuesta muestra aplicación parcial. Un buen tutorial que explica la diferencia entre esos conceptos se puede ver aquí: http://allthingsphp.blogspot.com/2012/02/currying-vs-partial-application.html

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

Invocando la función al curry en PHP 7

$result = curried_sum3(1)(2)(3); 
var_dump($result); // int 6

Invocando la función al curry en PHP 5

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

//OUTPUT:
int 18

Esta es una aplicación parcial:

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top