Пишу свою реализацию dependency injection на php

Стоит ли её дальше писать

  • Да

    Голосов: 3 20,0%
  • Нет

    Голосов: 3 20,0%
  • Я не знаю что это и зачем

    Голосов: 2 13,3%
  • ЭОС

    Голосов: 7 46,7%

  • Всего проголосовало
    15

smpl

Новичок
Вообщем начал писать свою реализацию di под php (знаю о существующих но хотелось что то свое и более гибкое, примеры аналогов pimple, http://php-di.org/ и тд)

Вот что у меня получилось https://github.com/smpl/mydi можете попробовать по юзать у себя ну и также сообщать feedback о идеях и чем то полезном лучше на github заводить задачку, а обсуждать тут, спасибо мне важно ваше мнение

собственно там несколько релизов (использовал в паре проектов, очень доволен своей реализацией).

Просьба модераторам перенести в соответсвующий раздел (возможно её надо в Вопросы по теории программирования а возможно и нет пока я разместил в offtopic)
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
Я тебе запятых принес: ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

По ссылке - не вижу, чем это лучше того же php-di.
 

smpl

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

на тему чем лучше,
лично для меня это легкий способ создавать свои контейнеры для себя (смотри interface ContainerInterface) и использовать их, и менять поведения (базовые контейнеры созданны как в pimple)
и вторая прикольная штука это возможность загружать только те контейнеры которы необходимы (смотри LoaderInterface) через него же можно сделать и работу с анотациями которой сейчас нет да и в php анотация сделаны через одно место.
ах да ещё поддержка маргинальной версии 5.4
это что я с ходу назвал хотя именно этот di я мало использовал я больше знаком с pimple
 

AnrDaemon

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

smpl

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

AmdY

Пью пиво
Команда форума
По ссылке находится контейнер с уродским синтаксисом, в сравнении с тем же pimple, он очень неудобен и многословен. А вот заявленным DI там и не пахнет.
 

Фанат

oncle terrible
Команда форума
писать стоит в любом случае.
использовать ли - вот это вопрос.
 
  • Like
Реакции: smpl

smpl

Новичок
По ссылке находится контейнер с уродским синтаксисом, в сравнении с тем же pimple, он очень неудобен и многословен. А вот заявленным DI там и не пахнет.
насчет уродского синтаксиса я был бы не так категоричен, но у каждый обладает своим мнением и его выражает, я просто приведу для примера синтаксис pimple и синтаксис своей библиотеки

pimple объявление Service
PHP:
$container['session_storage'] = function ($c) {
    return new SessionStorage('SESSION_ID');
};

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};
$session = $container['session'];
Factory
PHP:
$container['sessionFactory'] = $container->factory(function ($c) {
return new Session($c['session_storage']);
});
$sessionByFactory = $container['sessionFactory'];
mydi объявление Service
PHP:
$container['session_storage'] = function () {
    return new SessionStorage('SESSION_ID');
};

$container['session'] = function () use ($container) {
    return new Session($container['session_storage']);
};
Factory
PHP:
$container['sessionFactory'] = new Factory(function () use ($container) {
return new Session($container['session_storage']);
});
$sessionByFactory = $container['sessionFactory'];
многословней? возможно, в pimple ещё есть всякие raw и прочее чего у меня нет, вопрос а зачем оно ? да я видел примеры на офф сайте
но обычно на проекте нужен auto complete, и авто дополнение по именам контейнеров, попробуем добиться этого я пишу в PHPSTORM поэтому примеры будут рабочие для него, но и также должны работать и в netbeans или других ide где есть нормальная поддержка phpdoc

pimple реальное использование так чтобы было удобно использовать в большом проекте и по возможности не допускать ошибки в именах контейнеров (вы заметили мое правописание страдает, для меня это как ни для кого актуально, чтобы ide подсвечивал проблемные места
одним куском кода
PHP:
/**
* Class App
* @property Session $session
* @property SessionStorage $session_storage
* @property Session $sessionFactory
*/
class App extends Container
{
    function __get($name)
    {
        return $this[$name];
    }

    function __set($name, $value)
    {
        $this[$name] = $value;
    }
}

$app = new App();
$app->session_storage = function () {
    return new SessionStorage('SESSION_ID');
};
$app->session = function (App $c) {
    return new Session($c->session_storage);
};
$app->sessionFactory = $app->factory(function (App $c) {
    return new Session($c->session_storage);
});
$session = $app->session;
$sessionByFactory = $app->sessionFactory;
mydi
PHP:
/**
* Class App
* @property Session $session
* @property SessionStorage $session_storage
* @property Session $sessionFactory
*/
class App extends Locator
{
}

$app = new App();
$app->session_storage = function () {
    return new SessionStorage('SESSION_ID');
};
$app->session = function () use ($app){
    return new Session($app->session_storage);
};
$app->sessionFactory = new Factory(function () use ($app) {
    return new Session($app->session_storage);
});
$session = $app->session;
$sessionByFactory = $app->sessionFactory;

да у меня использование свойств(property) deprecated и не очень рекомендуется по другим причинам, но было введено изначально специально для автокомплита в ide, сейчас для auto complete я думаю написать плагин для phpstorm (что то вроде dynamicReturnTypePlugin) кстате этот плагин можно будет использовать и с pimple, поэтому если кто писал свои плагины к phpstorm буду рад совету.

да возможно у меня чего то нет что есть в pimple но мне оно не пригождалось, но у меня есть и то чего нет в pimple
Во первых это легкий способ добавить свои контейнеры (просто имплементишь LoaderInterface) и создаешь объект mydi его подхватит, в pimple это надо делать через overwride метод.
Во вторых на больших проектах количество контейров чуть больше чем очень много, у меня есть возможность объявлять только те контейнеры которые необходимы и не объявлять остальное что может применятся редко (смотри LoaderInterface) подобного у pimple нету

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


Насчет заявленного di, смотря считать di, если придерживаться Файлера http://www.martinfowler.com/articles/injection.html то там черным по белому написано
The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface, resulting in a dependency diagram along the lines of Figure 2
так что все зависит от твоего использования и вполне её можно использовать как di.

получилось много букафффФ зато с аргументами и фактами.
 
Последнее редактирование:

Absinthe

жожо
Для начала убери все магические __get и __set.
Тогда и костыли @property не понядобятся.
 

smpl

Новичок
Для начала убери все магические __get и __set.
Тогда и костыли @property не понядобятся.
а как тогда получать auto complete по названиям контейров и получаемым значениям ?
PHP:
/** @var Service $service */
$service = $container['sercice'];
и так каждый раз писать что за объект у меня, а ещё для примера я специально опечатался в service заметил? а об этой ошибке будет известно только в runtime
а в моем случае phpstorm покажет warning при использование свойств, да я знаю о недостатках при использование магии (именно поэтому решил написать небольшой плагин для auto complete и return type в методах)

потому что в дальнейшем я хочу использовать refactoring из ide
 

Absinthe

жожо
а как тогда получать auto complete по названиям контейров и получаемым значениям ?
Прочитай определение DI. То, что у тебя в этом примере - это не DI, а Service Locator (многие считают антипатитерном).
В DI у тебя все типы и так будут описаны.
 

smpl

Новичок
Прочитай определение DI. То, что у тебя в этом примере - это не DI, а Service Locator (многие считают антипатитерном).
В DI у тебя все типы и так будут описаны.
по той же самой ссылочке http://www.martinfowler.com/articles/injection.html есть и определение service locator и даже отдельная часть service locator vs di
я просто про цитирую
The fundamental choice is between Service Locator and Dependency Injection. The first point is that both implementations provide the fundamental decoupling that's missing in the naive example - in both cases application code is independent of the concrete implementation of the service interface. The important difference between the two patterns is about how that implementation is provided to the application class. With service locator the application class asks for it explicitly by a message to the locator. With injection there is no explicit request, the service appears in the application class - hence the inversion of control.
опять же это если придерживаться мнения Фаулера, если в вашем понимание какие то свои di и service locator то ничего страшного покажите ссылку куда вы ссылаетесь, а если придумали статью то расскажите об этом людям и опять же ссылку туда

в примерах выше классы зависимостей (application class в формулировке Фаулера) (Session и SessionStorage) напрямую не обращаются к $container или $app а эти классы разрешают зависимость через анонимную функцию и проставляют зависимости в application class

Вот популярный пример service locator http://api.symfony.com/2.7/Symfony/Component/DependencyInjection/ContainerAware.html который как ты верно заметил может считаться анти паттерном, с точки зрения некоторых людей.

объясню довольно популярный пример (если можно в терминах Java spring)
предположим есть interface (I) и в проекте есть 2 его реализации (A, B)
и есть другой класс(C) который зависит от этого интерфейса (пусть constructor injection, или method injection, или property injection не важно)
и по началу вы используете C и в зависимости вы передаете то что указано в разрешение I (например экземпляр класса A)
через некоторое время появляется класс D который ожидает I но ему надо реализацию в виде B (например A хранит данные в memcache, а тут надо хранить в mongo или redis интерфейсы могут быть одинаковы а хранение разное)
такую проблему в spring решают с помощью именованных бинов (named bean)
но там это возможно так есть этап компиляции и чтение анотаций и дерево зависимостей строиться во время компиляции и объкеты создаются только те которые необходимо.

теперь вернемся в php
что мешает имя контейнера давать как I.class ? а получение объекта сделать с помошью анонимной функции это не service locator
не нравиться анонимная функция(которая выглядит красивей и понятней) сделайте свой ComponentImplementation
PHP:
class ComponentImplementation implement ContainerInterface {
   public function __constructor(Locator $locator, array $constructInjections = [], array $methodInjections = []) {
       $this->setLocator($locator);
       $this->constrcutInject($constructInjections);
       // и так далее
   }
   public function setLocator(Locator $locator) {
   }
   public function constructorInject(array $containers) {
   }
   public function methodInjection(array $containers) {
   }
   public function resolve() {
      // тут создаем объект с аргументами в конструкторе и вызываем методы и прочие штуки
   }
}
я код накидал примерный класс для создания своего контейнера(его можно сделать и красивей например убрать locator из конструктора и тд) теперь как использовать из примера выше (первую ситуацию без named bean, причем именовать придется оба bean, A и B иначе все сломается)
PHP:
$locator[A.class] = new ComponentImplementation($locator);
$locator[B.class] = new ComponentImplementation($locator);
$locator[I.class] = $locator['A.class'];   // когда понадобиться можно например присвоить B.class
$locator[C.class] = new ComponentImplementation($locator, [I.class]);
$locator[D.class] = new ComponentImplementation($locator, [B.class]);
собственно теперь управление выбором имплементации лежит в 3 строке
можно все это сделать изящней и красивей используя анотации и так далее но я не считаю анотации в php хорошей штукой.
а плюс ко всему в моей реализации можно сделать подгрузку определений динамической (частями) и довольно просто создали новый контейнер(хотя его не обязательно было делать анонимная функция гораздо более гибкий инструмент), а теперь как тоже самое сделать в pimple без overwride method ? :D

это очень черновой вариант и тут можно много к чему прикопаться и сделать лучше, собственно если будет потребность в такой штуке(вроде ComponentImplementation) можно зависти задачу на github и описать что примерно охото (с примерами) я в свободное время сяду и возможно реализую это, ну или можно самому и pull request (но это очень мало вероятный сценарий но я был бы искренне рад).

блин выглядит так будто я написал что что должно быть лучше всех и всего и универсально подходить ко всему, но это не так и это не возможно, я лишь написал довольно удобный и самое главное гибкий инструмент который можно использовать в своём проекте так как это вам надо, да мало примеров и best практик, да в документации много много ошибок в русском языке, да возможно что то существует и лучше (но я пока не знаю я уверен и в других библиотеках можно много чего интерсеного сделать), но в этой библиотеке основная возможность это создание своих контейнеров(ContainerInterface) и динамическая загрузка из любова удобного вам формата (LoaderInterafce) и это я сделал довольно не плохо, да её могут использовать как Service Locator или даже как Registor

я рад что вы задаете вопросы и ищите недостатки в примерах и даже указывате на них
 
Последнее редактирование:

Absinthe

жожо
то что в примерах выше это используется как service locator я не отрицаю, но это не значит что pimple и мою библиотеку нельзя использовать как di, о том как её использовать зависит от программиста
Зачем тогда сверху было про определения?
Просто не используй DI как SL и все. И костыли не понадобятся.

через некоторое время появляется класс D который ожидает I но ему надо реализацию в виде B
Это нарушение принципов ООП (принцип подстановки Лисков). Грубая ошибка. Если он ожидает I, то ему должна подходить и A. Или требуй явно B, или заводи I2, которому подходит B.
 

smpl

Новичок
насчет подстановки Лисков согласен и здесь моя неточность я хотел кое что другое показать(смотри следующий абзац) править сообщение выше не буду напишу тут.

но опять же в spring когда у одного интерфейса более одной реализации, необходимо каждую реализацию (bean) именовать уникальным именем и уже потом Autowired(иньектить) по этому имени, я это просто привел как пример

по поводу service locator я подправил сообщение выше тот текст что ты процитировал больше его нет теперь там
в примерах выше классы зависимостей (application class в формулировке Фаулера) (Session и SessionStorage) напрямую не обращаются к $container или $app а эти классы разрешают зависимость через анонимную функцию и проставляют зависимости в application class

Вот популярный пример service locator http://api.symfony.com/2.7/Symfony/Component/DependencyInjection/ContainerAware.html который как ты верно заметил может считаться анти паттерном, с точки зрения некоторых людей.
 

AmdY

Пью пиво
Команда форума
>>$container['sessionFactory'] = new Factory(function () use ($container) {
выделил жирным что мне не нравится. нужно лишнее знание о других классах и пробрасывание контейнера и это для библиотеки, которая как раз должна упростить пробрасывание сервисов.

У тебя как раз ioc, ты сравниваешь либу с pimple, которая тоже ioc.
 

Absinthe

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

И если несколько классов реализуют определенный тип (интерфейс или наследование), то они должны бить взаимозаменяемы при использовании по этому типу.
 

smpl

Новичок
>>$container['sessionFactory'] = new Factory(function () use ($container) {
выделил жирным что мне не нравится. нужно лишнее знание о других классах и пробрасывание контейнера и это для библиотеки, которая как раз должна упростить пробрасывание сервисов.

У тебя как раз ioc, ты сравниваешь либу с pimple, которая тоже ioc.
судя по сайту http://pimple.sensiolabs.org/ по title и главной странице там написано:
Pimple is a simple PHP Dependency Injection Container
но да я вынужден согласиться что это не совсем красиво но более гибко
я тебя понял тебе нравиться более декларативное описание зависимостей и такое можно сделать и у меня используя Loader и хранить описание в виде json, yaml, xml, анотаций
собственно я планирую сделать такой подход просто он пока не сильно необходим и не очень в приоритете но он будет (я планирую сделать описание в виде json)
я даже завел задачу https://github.com/smpl/mydi/issues/35 но не очень скоро она будет готова, такую штуку впринципе можно написать за часа 2 но я просто другими делами занят и задача по написанию плагина для phpstorm по выше.

Я правильно понял что тебе не особо понравилось и что хотелось бы в конечном счете?
 
Последнее редактирование:

smpl

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

И если несколько классов реализуют определенный тип (интерфейс или наследование), то они должны бить взаимозаменяемы при использовании по этому типу.
я согласен насчет взаимо заменямости это один из принципов SOLID
я говорю о том что как только появиться вторая имплементация придется именовать и первую и вторую и во всех местах автоварить по имени это пример из Java там иначе в Runtime будет Exception типо у меня запросили имплементацию такого интерфейса а тут их 2 подходящих а хз какую выбрать, паника паника паника и там как раз используется именование и берется нужная реализация
 

scorpion-ds

Новичок
Когда я работал над своей CMS, то ни про какие DI я не знал, даже в теории. Сначала очень делал массу инклудов, общие параметры передавались через обычные константы и глобальные переменные (имеется ввиду объявление global), код был ужасен. Позже создал базовый класс который отвечал за основной функционал и еще несколько вспомогательных, выглядело это примерно так:

PHP:
MAIN_CLASS::ProcessingSite()-> //...
MAIN_CLASS::ProcessingPage()-> //...

Users::getInstance() // создаем новый объект

Users::get($id) // получаем существующего пользователя
 

smpl

Новичок
опа синглетон best practices

а если серьзно то все мы через подобное проходили я помню свою первую реализацию mvc, где в контроллере жеско была зашита переменная модели и переменная view :D и контроллер работал с одной моделью и с одной view, потом узнал о registry радовался будто изобрел непонятно что, потом понял что на больших проектах объявлять все каждый раз не лучшая практика и надо подгружать только то что надо и тд и так велосипедил, ещё помню когда времена когда все было в php файлах :D без единой точки входа, помню ещё php4, все через это проходили главное двигаться дальше, вот я здесь по общавшись понял что надо доработать (надо больше синтаксического сахара и в начале в примерах надо простые примеры а не гибкие я так и передалаю и кое от чего откажусь, главное двигаться и не стоять на месте), ностальгия.
 
Сверху