inilim2
@inilim2
Intern PHP

Возможно ли добавить use переменные после объявления анонимной функции?

Что-то вроде:
$a = function()
{

};

$b = 'value';

$a = $a use ($b);
  • Вопрос задан
  • 73 просмотра
Пригласить эксперта
Ответы на вопрос 3
iMedved2009
@iMedved2009
Не люблю людей
ага :)
$a = function($b)
{
echo $b;
};

$a('value1');
$a('value2');
Ответ написан
Комментировать
gzhegow
@gzhegow
aka "ОбнимиБизнесмена"
Процесс, который вы хотите называется "функции высшего порядка"

// функция "высшего" порядка - что в ней высшего - надо спросить у того кто придумал, высший человек наверное

$fnPow = function ($x, $y) {
  return pow($x, $y);
};

$fnPowExtended = function () use ($fnPow, $b, $c) {
  var_dump($b);
  var_dump($c);
  return $fnPow($b, $c);
};

$fnPowExtended(2, 3);
// 2
// 3
// 8


Таким образом чтобы прокинуть что-то в уже созданную функцию вам нужно создать новую функцию и прокинуть туда существующую и что-то ещё. Более того, вы можете прокинуть функцию саму в себя, чтобы сделать рекурсию.

$fnRecursion = function () use (&$fnRecursion) {
  $fnRecursion(); // infinite recursion here
}


Я полагаю что ваш интерес вызван желанием создать функцию bind()/call()/apply(), как в javascript. В ПХП этим путем не пошли, т.к. ООП все же хоть и дороже в разработке, но понижает порог входа новых людей в проект. Решение спорное, т.к. в ООП надо уметь, а функционалка может быть лишь бы было. И понимая что "старый код не трогать, а заменять" это вроде не так уж плохо. Но в пхп часто старый код именно трогают, доделывают, подпиливают. Функционалка усложняет процесс понимания "что происходит", поэтому ей редко пользуются. К тому же в пхп до недавнего времени вообще короткой записи не было, и писать function () use () {}, и потом переживать, что там чото с памятью может быть и пересылом часто сложнее, чем нажать CTRL+N в IDE и создать новый класс.

bind() должна бы выглядеть примерно так в PHP (но ей здесь никто не будет пользоваться):

/**
     * bind
     * копирует тело функции и присваивает аргументы на их места в переданном порядке
     * bind('is_array', [], 1, 2) -> Closure of (function is_array($var = []))
     *
     * @param null|object $newthis
     * @param callable    $func
     * @param mixed       ...$arguments
     *
     * @return \Closure
     */
    public function bind(?object $newthis, callable $func, ...$arguments) : \Closure
    {
        if (! is_string($func)) {
            $bind = $arguments;

        } else {
            // string
            $bind = [];

            $rf = new \ReflectionFunction($func);

            $requiredCnt = $rf->getNumberOfRequiredParameters();

            while ( $requiredCnt-- ) {
                $bind[] = null !== key($arguments)
                    ? current($arguments)
                    : null;

                next($arguments);
            }

            $func = \Closure::fromCallable($func);
        }

        if (null !== $newthis) {
            if (! $func instanceof \Closure) {
                $func = \Closure::fromCallable($func); // throws exception if not possible
            }

            $func->bindTo($newthis); // throws exception if not possible
        }

        $result = static function (...$args) use ($func, $bind) {
            $bind = array_replace(
                $bind,
                array_slice($args, 0, count($bind))
            );

            return call_user_func_array($func, $bind);
        };

        return $result;
    }


Почему она такая "странная"? Потому в php есть еще одна проблема. Созданные функции (не безымянные, вот только не помню - кажется только встроенные, свои - не такие строгие) требуют строгое число аргументов на своих местах. Если вы передадите больше или меньше - они ломаются и не работают. Рефлексия считывает "а скока надо" и передает ровно столько, на остальное забивает.

===

В ООП для подобных вещей используют декорирование (decoration) (не путать с кАррирование (currying)). Вы пишете класс, который на вход ожидает обьект другого класса, чтобы нарастить его функционал, не влазя в детали.

Чуть позже в python поняли, что можно не только готовые объекты заворачивать, но и декорировать любую функцию уже на этапе описания. На псевдокоде это выглядит так:

[send_telegram] // use result to call new function
[send_email] // use result to call new function
[push_event] // use result to call new function
function register() {
  // do some
}


В среде разработчиков ООП этот процесс не нашел большого распространения (как бы Симфони не старался продвигать написание кода и валидаций в аттрибутах и аннотациях - это все равно чертова магия, которая требует высокого порога входа и даже специальной теории баз данных, которую не понимает никто кроме программистов - что создает барьер между бизнес-аналитиками, даже теми, кому выставляли порог входа - знание SQL, так и барьер между опытными разработчиками, которые привыкли прослеживать ход программы, а им пытаются навязать изучение какой-то штуки, которая читает приписки и делает что-то описанное черти-где).

Причина процесса - вы все равно переписывается старые файлы. ООП и принцип солида отталкивается от того, что старый модуль вынимается и выбрасывается, а не дорабатывается, т.к. вызовет новые баги.

1) Я стараюсь не увлекаться функциями, потому что у них проблемы с "сериализацией" - сохранить их в файл или передать по сети проблематично. Вы буквально передадите номер строки в коде, который завтра может быть другой и вторая сторона должна иметь точно такой же код, это ненадежно.

2) Функции работают с памятью несколько по особенному. То есть если не писать перед функцией слово `static`, что означает "не хранить внутри $this того места где её создали", можно получить интересную картинку если эту функцию вызовут в рекурсии. Оригинальный класс, который для неё $this, накопив данные и начав копировать эту функцию в момент того как вы пишете $a = function () {}, будет каждому экземпляру копировать данные этого объекта. И случайно в коде выполнив циклом 300 раз $a = function () {} вы 300 раз скопируете данные, которые возможно представляли собой "много текста" и ушатаете память.

С функциями нужно быть осторожным. Чем реже, тем лучше. Но это не значит что они плохие. Так например прослушку постоянного процесса в скрипте, который никогда не завершится с возможностью подключать свои действия сильно проще написать на функциях-обработчиках. Хотя ООП рекомендовал бы использовать имена классов и фабрику их создающую, гарантируя что конфиг будет содержать текст а не объекты.

Не уверен, что понимаю как работает ядро яваскрипт, но что-то мне посказывает, что там есть общий скоуп и ничего никуда не копируется, а просто берется то что есть либо везде, либо конкретно здесь, т.к. передали. В пхп у каждой функции свой скоуп. С одной стороны это обрезает руки, с другой стороны не позволяет совершить глупых ошибок по типу "вывести то, чего тут может и не быть".
Ответ написан
Комментировать
SilenceOfWinter
@SilenceOfWinter Куратор тега PHP
та еще зажигалка...
1. можно импортировать переменные по ссылке use (&$b)
2. можно привязать замыкание к другой области видимости используя один из методов Closure и вместо импорта через use использовать свойства объекта
class Scope
{
      public int  $b = 1;
}
class Scope2
{
      public int $b = 2;
}
$callback = function (int $a) {
     return $a + $this->b;
};

$scope = new Scope();
$scope2 = new Scope2();
var_dump($callback->call($scope, 3), $callback->call($scope2, 3));
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы