Возможно ли выполнять вызовы методов curry в PHP?

StackOverflow https://stackoverflow.com/questions/1609985

  •  05-07-2019
  •  | 
  •  

Вопрос

У меня есть Мыльный клиент экземпляр, созданный для файла WSDL.Для всех вызовов метода, кроме одного, требуется, чтобы имя пользователя и пароль были переданы id.

Есть ли какой-нибудь способ отключить вызовы метода, чтобы я мог опустить имя пользователя и пароль?

Это было полезно?

Решение

Начиная с php 5.3, вы можете хранить анонимную функцию в переменной. Эта анонимная функция может вызывать «оригинал» функция с некоторыми предопределенными параметрами.

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

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

$bar('C');

edit: вы также можете использовать замыкание для параметризации создания анонимной функции

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-библиотека интересна. Я уже реализовал тот же метод, он немного отличается от функции 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? не показывает Curry. Этот ответ показывает частичное применение. Хороший учебник, который объясняет разницу между этими понятиями, можно увидеть здесь: > 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

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

Вызов функции карри в PHP 5

$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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top