Yii DI - фреймворконезависимый контейнер и инжектор

Выкладываем свои наработки
Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.25, 18:00

Выложил альфа-версию. Возможно, запилим на ней Yii 3.0 когда-нибудь:

https://github.com/yiisoft/di

Какие мысли? Что можно улучшить?

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.25, 20:52

Движение в правильном направлении.

Но почему не взять готовое, и при необходимости декорировать, подмешав функциональности?

Опять какая-то магия https://github.com/yiisoft/di/blob/mast ... r.php#L220
По-моему создание должно быть максимально простым - фабрики анонимками или invokable-классами для lazy load, готовые инстансы, и в последнюю очередь рефлексия (но я бы рефлексию вообще не включал - более явно самому фабрику прописать, тем более анонимкой это пара строк). Функциональность, как мне кажется, уже вся есть.

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.25, 21:10

Так и взято готовое. Просто зависимости убраны, инжектор выкинут в отдельный класс и чуть причёсано.

Анонимки тоже поддерживаются, но для большинства случае ими очень неудобно описывать зависимости. Много букв получается и конфиги невозможно описать не в PHP.

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.25, 21:12

Так да, есть два подхода как описывать зависимости в контейнерах:

1. Явно по id в анонимках их вынимать.
2. Менее явно, используя интерфейсы как id.

Часто встречается в остальных реализациях и то и то. У нас можно и так и так.

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.25, 22:07

samdark писал(а):
2017.11.25, 21:10
Так и взято готовое. Просто зависимости убраны, инжектор выкинут в отдельный класс и чуть причёсано.

Анонимки тоже поддерживаются, но для большинства случае ими очень неудобно описывать зависимости. Много букв получается и конфиги невозможно описать не в PHP.
в итоге получается жирный сложный компонент, который можно упростить, до приема только анонимок, а все остальное вынести в бутстрапирование приложения. Получаем конфиг, из конфига в рантайме собираем контейнер, оборачивая определения сервисов в анонимки. В итоге убираем сложный компонент, навязывающий свой флоу, а получаем простой компонент, 10-20 строк копипасты для бутстрапа плюс возможность копипасту расширить до чего угодно.

Код: Выделить всё

$config = ...;

$container = new Container();

if (isset($config['dependencies']['factories'])) {
    foreach ($config['dependencies']['factories'] as $id => $factoryName) {
        $container->addLazy($id, function (Container $container) use ($id, $factoryName) {
            if ($container->has($factoryName)) {
                $factory = $container->get($factoryName);
            } else {
                $factory = new $factoryName();
                $container->add($factoryName, $factory);
            }

            return $factory($container, $id);
        });
    }
}

if (isset($config['dependencies']['invokables'])) {
    foreach ($config['dependencies']['invokables'] as $id => $className) {
        $container->addLazy($id, function (Container $container) use ($className) {
            return new $className();
        });
    }
}

if (isset($config['dependencies']['aliases'])) {
    foreach ($config['dependencies']['aliases'] as $aliasId => $targetId) {
        $container->addLazy($aliasId, function (Container $container) use ($targetId) {
            return $container->get($targetId);
        });
    }
}

// что-то еще откуда угодно

return $container;
причем это бутстрапирование можно выделать в хэлпер в составе компонента, но не включать его непосредственно в контейнер.
$ontainer = \yii\di\buildFromConfig($config);

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.25, 22:12

samdark писал(а):
2017.11.25, 21:12
Так да, есть два подхода как описывать зависимости в контейнерах:

1. Явно по id в анонимках их вынимать.
2. Менее явно, используя интерфейсы как id.
я не разделяю эти два варианта. Id может быть какой угодно строкой - хоть смысловым id ('articleRepo') хоть именем интерфейса (\yii\ArticleRepositoryInterface::class, 'yii\\ArticleRepositoryInterface').
То есть не об этом говорил.

PS на самом деле многие не понимают, что вызывая $container->get(\yii\ArticleRepository::class) мы вызываем не реализацию интерфейса, а объект класса, который мы для удобства пометили именем интерфейса. Тонкая грань, но она есть - это просто идентификатор.

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.25, 22:56

из конфига в рантайме собираем контейнер, оборачивая определения сервисов в анонимки
Ох, боюсь даже представить, насколько не быстро это всё будет работать с анонимками. Это центральный компонент и обязан быть максимально шустрым, но, в то же время, простым. Но так да, с анонимками красиво.

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.25, 23:01

я не разделяю эти два варианта. Id может быть какой угодно строкой - хоть смысловым id ('articleRepo') хоть именем интерфейса (\yii\ArticleRepositoryInterface::class, 'yii\\ArticleRepositoryInterface').
То есть не об этом говорил.
Это я про автовайринг. Вайрить можно или по именам интерфейсов из сигнатуры конструктора или метода или по каким-то аннотациям. Имена переменных использовать категорически нехорошо, рефакторинг пострадает очень сильно.

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.25, 23:29

samdark писал(а):
2017.11.25, 22:56
из конфига в рантайме собираем контейнер, оборачивая определения сервисов в анонимки
Ох, боюсь даже представить, насколько не быстро это всё будет работать с анонимками. Это центральный компонент и обязан быть максимально шустрым, но, в то же время, простым. Но так да, с анонимками красиво.
да нет, быстро. плюс такой вариант очень удобно можно закэшировать в продакшн-режиме, формируя класс с методами вида public function getArticleRepository(), хотя не вижу смысла - контейнер будет билдиться миллисекунды.

Еще раз: я за то, чтобы сам контейнер был максимально простым, чуть ли не реестром, а все остальное бы шло дополнительными классами.

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.25, 23:32

samdark писал(а):
2017.11.25, 23:01
Имена переменных использовать категорически нехорошо, рефакторинг пострадает очень сильно.
но иногда необходимо - в проекте например может быть несколько разных сериалайзеров для разных кейсов. Но так да, если выдерживается соотношение один интерфейс - одна реализация, но называть лучше интерфейсом.

Аватара пользователя
ElisDN
Сообщения: 4730
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение ElisDN » 2017.11.25, 23:39

Данному yiisoft/di для полной декларативности стоит добавить поддержку ссылок вроде [42, new Reference('otherService')].

Пока он уступает приведённому подходу из zend/service-manager с factories, invokables и aliases в паре с ReflectionBasedAbstractFactory и другим PSR- контейнерам тем, что всё рассматривает как фабрику, не позволяя хранить скалярные параметры не оборачивая их в анонимку.
Не забудьте пройти мастер-класс по Yii2.

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.26, 00:35

надо исходить из того, что нужно создать гибкую абстракцию. А гибкая она будет только если будет минималистична. Часть абстракции на себя взял psr-11 (get/has).
Сформулируем оставшиеся пункты: очевидно мы должны уметь в контейнер что-то засеттить, и очевидно, контейнер должен поддерживать lazy load. Вот все что должен уметь конкретный контейнер. Все остальное имеет место быть, но дополнительно.

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.26, 08:46

Ссылки есть и поддерживаются. Наверное, забыл их в readme описать...

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.26, 08:49

На тему скалярных параметров думал, но что-то не нашёл им нормального применения.

Автоматически вайрить их по имени переменной — это, опять же, убить возможность простейшего рефакторинга типа rename. Да и конфликты возможны.

Аннотации не хочется потому что мы начинаем знать про наш контейнер там, где не должны, хоть формально это и не код.

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.26, 08:51

На тему сделать основу только на анонимках надо попробовать. Подозреваю, что всё-таки выйдет значительная просадка производительности.

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.26, 14:34

samdark писал(а):
2017.11.26, 08:49
На тему скалярных параметров думал, но что-то не нашёл им нормального применения.
https://gist.github.com/xtreamwayz/b8d7 ... ry-php-L18
samdark писал(а):
2017.11.26, 08:49
Автоматически вайрить их по имени переменной — это, опять же, убить возможность простейшего рефакторинга типа rename.
"ну давай, расскажи мне про рефакторинг". Мы же про контейнер или про рефакторинг?

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.26, 14:42

samdark писал(а):
2017.11.26, 08:51
На тему сделать основу только на анонимках надо попробовать. Подозреваю, что всё-таки выйдет значительная просадка производительности.
https://rawgit.com/kocsismate/php-di-co ... hmark.html
pimple для примера работает на анонимках. Может еще что-то. Но если посмотреть тхолодные тесты даже на хороших объемах, то видим, что время незначительно мало (можно принебречь), а потребление памяти у всех плюс минус одинаково.

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.26, 16:49

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

Аватара пользователя
samdark
Администратор
Сообщения: 8660
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение samdark » 2017.11.26, 16:51

"ну давай, расскажи мне про рефакторинг". Мы же про контейнер или про рефакторинг?
Одно влияет на другое.

zelenin
Сообщения: 10260
Зарегистрирован: 2013.04.20, 11:30

Re: Yii DI - фреймворконезависимый контейнер и инжектор

Сообщение zelenin » 2017.11.26, 17:57

samdark писал(а):
2017.11.26, 16:49
В примере по ссылке на тему конфига не ясно, почему конфиг должен быть именно в DI-контейнере.
di-контейнер - это контейнер зависимостей. Зависимость - это не объект, а некая сущность, от которой зависит зависимое. Фабрика БД-коннекшна например зависит от параметров соединения с базой.
DoctrineFactory мог бы его затягивать себе или в конструктор или брать из отдельного компонента с конфигом.
можно зассеттить параметры, прописанные в DbConnectionConfig, можно прописанные в обычном массиве в конфиге. Без разницы. Первый вариант даже лучше, но это не значит что не может существовать второй.

Ответить