Следом ещё одна заметка о Symfony 3.4 и мучениях при работе с его сервис-контейнером. Допустим, есть задача проинтегрировать в приложение библиотеку от стороннего разработчика. Архитектура библиотеки конечно не внушает доверия и просить автора привести её в порядок — бесполезно. Иначе говоря, нужно через Service Container Symfony работать с библиотекой, автор которой нарушил максимальное количество принципов объектно-ориентированного программирования и проектирования.
Например, в некой библиотеке имеется один единственный класс через который происходит работа. Однако, в некоторых случаях нужно вызывать методы объекта являющегося свойством «главного» класса, например:
$Object->part->call($args);
Нет, можно конечно прокидывать родительский объект во все части приложения, но это не гибко и не всегда удобно, т.к. некий объект получает доступ ко всему родительскому объекту и может сильно повлиять на его состояние.
В Laravel такие вещи делаются элементарно просто:
$this->app->bind(SomeInterface::class, function($app) { return $app[Object::class]->part; });
Но в Symfony ничего подобного нет! Приходится выкручиваться так:
$expr = new \Symfony\Component\ExpressionLanguage\Expression("service('MyBundle\\\\Object').part"); $container->autowire(\MyBundle\SomeInterface::class) ->setArguments(['$part' => $expr]);
Обратите внимание на четыре слэша, именно такая последовательность символов служит разделителем неймспейсных директорий. А вместо привычного -> свойство объекта нужно извлекать через точку. После чего полученный экспрешен можно использовать аналогично референсу.
Умные люди говорят что есть и другое более красивое решение: создать фабрику, которая бы возвращала объект из свойства другого объекта. Но на мой взгляд это ещё более сложное решение для такой банальной задачи.