Domanda

Ho un'istanza SoapClient generata per un file WSDL. Tutti tranne una delle invocazioni del metodo richiedono che il nome utente e la password siano passati id.

Esiste un modo per valutare le chiamate al metodo in modo da poter omettere nome utente e password?

È stato utile?

Soluzione

A partire da php 5.3 puoi memorizzare una funzione anonima in una variabile. Questa funzione anonima può chiamare "originale" funzione con alcuni parametri predefiniti.

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

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

$bar('C');

modifica: puoi anche usare una chiusura per parametrizzare la creazione della funzione anonima

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: funziona anche con oggetti

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

Ma gli altri suggerimenti che usano __call () e una classe wrapper potrebbero essere migliori se si desidera " migliorare " una classe completa.

Altri suggerimenti

Ecco una classe che implementa il curry automatico e l'applicazione parziale:

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

Esempio:

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

Anche tu puoi farlo:

$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 non ha curry in sé, ma puoi fare qualcosa del genere in diversi modi. Nel tuo caso specifico, qualcosa del genere potrebbe funzionare:

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

Anche se ecco una soluzione più generale per curry in PHP > = 5.3:

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

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

Oggi ho fatto alcune ricerche in merito. Questo è il più vicino possibile:

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

Il problema principale è che PHP non ti permetterà di eseguire una chiusura direttamente su un valore di ritorno (più o meno allo stesso modo in cui PHP non consentirà di eseguire un metodo su un oggetto non associato). Tuttavia, poiché le chiusure sono un oggetto di tipo Closure, che ha un metodo incorporato __invoke (), quanto sopra funzionerà.

Sebbene non sia un'ottima soluzione, potresti scrivere una classe wrapper di base che utilizzava PHPs metodi magici (in particolare __call) per chiamare la funzione effettiva ma aggiungere il nome utente e la password all'elenco degli argomenti.

Esempio di base:

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

Come menzionato da Ihor, la libreria PHP non standard è interessante. Ho già implementato lo stesso metodo, è un po 'diverso dalla funzione curried () di 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));
    };
}

Utilizzo:

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

Questa risposta È possibile curry il metodo chiama in PHP? non mostra Currying. Questa risposta mostra un'applicazione parziale. Un bel tutorial che spiega la differenza tra questi concetti può essere visto qui: http://allthingsphp.blogspot.com/2012/02/currying-vs-partial-application.html

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

Richiamo della funzione curry in PHP 7

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

Richiamare la funzione curry in PHP 5

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

//OUTPUT:
int 18

Questa è un'applicazione parziale:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top