Я смотрел это видео, и в 7:10 он добавляет зависимость db и использует закрытие для назначения значения.
Мой вопрос, почему бы не просто использовать прямое назначение вместо этого, я имею в виду, что не делаю этого:
$container['db'] = $capsule;
эквивалент этого:
$container['db'] = function ($container) use ($capsule) { return $capsule; }
Если нет, какая разница и какой способ лучше?
TLDR это потому, что определение зависимостей как закрытий позволяет контейнеру инъекции зависимости создавать их по требованию, поэтому вам не нужно беспокоиться о порядке их определения и управлении их зависимостями вручную.
Pimple – это контейнер для инъекций зависимостей , он должен помочь вам настроить ваши объекты, управляя их зависимостями простым и удобным способом.
Если вы напрямую назначаете значение ключу, Pimple вызывает это значение параметра , и когда вам нужно получить доступ к этому ключу, он просто возвращает это точное значение:
$container['sample-param'] = 'foo'; echo $container['sample-param']; //output: foo
Но дело в том, что этот sample-param
не требует какой-либо настройки, это просто значение, и мы в порядке с этим. Но предположим, что следующая настройка сервиса:
$container['myService'] = function($c) { $service = new \Some\Complicated\Service(); //this service depends on cache service $service->setCache($c['cache']); return $service; } $container['cache'] = function($c) { $cache = new \Cache\Driver(); //our cache driver needs a db connection $cache->setDbConnection($c['db']); return $cache; } $container['db'] = function($c) { //our database connection requires some parameters $dbConnection = new \Database\Connection($c['db-config']); return $dbConnection; } $container['db-config'] = [ 'username' => 'foo', 'password' => 'bar', 'host' => 'localhost' ]; //Now we want to user our service: $container['myService']->doSomething();
Пожалуйста, обратите внимание на порядок, который я использовал для определения разных ключей в контейнере.
myService
нужен cache
но определение кеша происходит после определения myService. И здесь помогает Pimple, и именно поэтому мы пропускаем контейнер для каждого закрытия, потому что Pimple собирается строить наши зависимости по требованию . Когда нам нужно получить доступ к myService
Pimple смотрит на свое внутреннее хранилище данных, если он ранее построил и сохранил myService
успешно, он вернет тот же самый экземпляр, иначе он будет называть наше закрытие для его создания. И когда будет вызвано наше закрытие, он попросит Pimple ($ c – контейнер Pimple) дать ему свои зависимости (что в данном случае является службой cache
). Pimple применяет то же самое к кешу, если он еще не построен, он будет строить его и т. Д. … до тех пор, пока он не получит часть, требующую простых параметров, таких как db-config
которые немедленно вернутся. И в этой цепочке вызовов замыкания строится наш объект и все его зависимости.
Теперь представьте, что произойдет, если мы будем использовать простые значения вместо закрытия? В этом случае, когда мы хотим построить myService
мы должны заботиться о его зависимостях. Мы должны убедиться, что его зависимости определены до того, как мы определим саму службу, и нам приходится иметь дело с другими проблемами, связанными с управлением зависимостями. В этом случае мы не смогли бы просто определить наш myService
и предположить, что имеется доступ к cache
, который будет определен позже.
В этом видео он использует Slim framework для контейнера, который по умолчанию использует Pimple для своего контейнера.
В документации на этой странице упоминается следующее
Определение служб Служба – это объект, который делает что-то как часть более крупной системы. Примеры услуг: подключение к базе данных, механизм шаблонов или почтовая программа. Почти любой глобальный объект может быть сервисом.
Сервисы определяются анонимными функциями, которые возвращают экземпляр объекта:
// define some services $container['session_storage'] = function ($container) { return new SessionStorage('SESSION_ID'); }; $container['session'] = function ($container) { return new Session($container['session_storage']); };
Обратите внимание, что анонимная функция имеет доступ к текущему экземпляру контейнера, что позволяет ссылаться на другие службы или параметры.
По дизайну контейнеры ожидают вызова функции
Контейнеры Pimple реализуют интерфейс \ArrayAccess
(см. Здесь) , а это означает, что они могут заставить объект действовать как массив, но не обязательно быть одним. В исходном коде вы увидите offsetSet
и offsetGet
, что и происходит, когда вы делаете:
$container['a'] = function($c){ return new Blah();} $thing = $container['a'];
tl; dr Это конкретная вещь контейнера Pimple, а не специфическая для PHP вещь.
Это совсем другое дело. Первый $container['db'] = $capsule;
означает, что $container
array имеет ключ db
и этот ключ равен $capsule
, во-вторых, у вас есть $container
а ключ db
имеет закрытие значения. Вы можете увидеть разницу при сбросе двух результатов:
Пример закрытия:
$capsule = 1; $container['db'] = function ($container) use ($capsule) { return $capsule; }; var_dump($container);
Результат:
Array(1) { ["db"]=> object(Closure)#1 (2) { ["static"]=> array(1) { ["capsule"]=> int(1) } ["parameter"]=> array(1) { ["$container"]=> string(10) "<required>" } } }
Пример без закрытия:
$capsule = 1; $container['db'] = $capsule; var_dump($container);
Результат:
result array(1) { ["db"]=> int(1) }
Вы можете прочитать о закрытии в руководстве php для лучшего понимания их роли. Руководство по закрытию PHP