Intereting Posts
Как я могу использовать PHP для динамического опубликования файла iical для чтения в Календаре Google? PHP-код для получения выделенного текста комбинированного блока получить список источников данных ODBC в локальном компьютере с помощью PHP Использование javascript для назначения переменной php Макет почты в окне разработки xampp Извлечение десятичного или целочисленного из строки в php oAuth с PHP (для google api) получение значения радиокамера в php Проблема структуры PHP OOP, имитировать множественное наследование php: // ввод возвращает пустую строку Codeigniter Cart – сохранение данных в базе данных – как подойти? Использование PHP в качестве механизма шаблона Установите корень документа в приложении Zend Framework 1.12.x в службе Azure Cloud Service Загрузка ссылки на базу данных (из upload.php) и обновление загруженной ссылки на iframe src (в index.php) YII – как мы можем получить имя метода в функции init () в контроллере?

Как создать контейнер для инъекций зависимостей PHP

Недавно я узнал о преимуществах использования Dependency Injection (DI) в моем приложении PHP. Тем не менее, я по-прежнему не знаю, как создать контейнер для зависимостей, или я должен использовать DI вообще для онлайн-форума, который я создаю.

Следующий код – это моя версия контейнера DI, который я сделал на примере, который я узнал отсюда .

class ioc { var $db; var $session; var $user_id; static function newUser(static::$db, static::$user_id) { $user = new User($db, $user_id); return $user; } static function newLogin(static::$db, static::$session) { $login = new Login($db, $session); return $login; } } $user = ioc::newUser(); $login = ioc::newLogin(); 

У меня есть несколько вопросов:

1) Где мне следует создавать экземпляры зависимостей, например $ database, $ session и т. Д.? Будет ли это за пределами класса контейнера или внутри конструктора контейнера.

2) Что делать, если мне нужно создать несколько экземпляров класса User внутри других классов? Я не могу ввести ранее созданный объект $ user, потому что этот экземпляр уже используется. Однако создание нескольких экземпляров пользователя внутри другого класса нарушило бы правила DI. Например:

 class Users { function __construct($db, $user_id) { $this->db = $db; $this->user_id = $user_id; } function create_friends_list() { $st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = $this->user_id"); $st->execute(); while($row = $st->fetch()) { $friend = ioc::newUser($row['user_id']); $friend->get_user_name(); $friend->get_profile_picture(); } } } - class Users { function __construct($db, $user_id) { $this->db = $db; $this->user_id = $user_id; } function create_friends_list() { $st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = $this->user_id"); $st->execute(); while($row = $st->fetch()) { $friend = ioc::newUser($row['user_id']); $friend->get_user_name(); $friend->get_profile_picture(); } } } - class Users { function __construct($db, $user_id) { $this->db = $db; $this->user_id = $user_id; } function create_friends_list() { $st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = $this->user_id"); $st->execute(); while($row = $st->fetch()) { $friend = ioc::newUser($row['user_id']); $friend->get_user_name(); $friend->get_profile_picture(); } } } 

3) Мне интересно, должен ли я даже принять DI, зная, что мне нужно переписать весь мой предыдущий код. Я ранее полагался на глобальные переменные, которые я создавал в своем файле initialize.php, который включен во все мои файлы.

Мне кажется, что DI создает много накладных расходов, и есть ситуации, когда они непригодны (как в моем примере # 2). Следующий сайт принадлежит разработчику, который ссылается на многие веские причины не использовать DI. Есть ли у его аргументов какие-то заслуги? Или я просто использую DI неправильно? проверьте эту ссылку .

Solutions Collecting From Web of "Как создать контейнер для инъекций зависимостей PHP"

Где я должен создавать экземпляры зависимостей, например $ database, $ session и т. Д.? Будет ли это за пределами класса контейнера или внутри конструктора контейнера.

В идеале ваше подключение к базе данных и сеанс будут загружены. Правильный DI требует экземпляра базового объекта, для которого все зарегистрировано. Поэтому, взяв ваш класс IOC в качестве примера, вам нужно сделать его экземпляр ( $ioc = new IOC(); ), тогда вам нужен какой-то класс поставщика услуг

 $ioc->register('database', new DatabaseServiceProvider($host, $user, $pass)) 

Теперь каждый раз, когда вы хотите подключиться к базе данных, вам просто нужно передать в $ioc->get('database'); очень грубый пример, но я думаю, что вы можете понять, что идея состоит в том, чтобы хранить все в реестре, и ничего не статически привязано, что означает, что вы можете создать еще один экземпляр $ioc с совершенно разными настройками, что упростит создание подключений, чтобы сказать другую базу данных для в целях тестирования.

Что делать, если мне нужно создать несколько экземпляров класса User внутри других классов? Я не могу ввести ранее созданный объект $ user, потому что этот экземпляр уже используется. Однако создание нескольких экземпляров пользователя внутри другого класса нарушило бы правила DI.

Это обычная проблема, и есть несколько разных решений. Во-первых, ваш DI должен показать разницу между зарегистрированным пользователем и просто пользователем. Вероятно, вы захотите зарегистрировать зарегистрированного пользователя, но не только любого пользователя. сделайте свой пользовательский класс просто нормальным и используйте

 $ioc->register('login-user', User::fetch($ioc->get('database'), $user_id)); 

поэтому теперь $ioc->get('login-user') возвращает зарегистрированного пользователя. Затем вы можете использовать User->fetchAll($ioc->get('database')); чтобы получить всех ваших пользователей.

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

Если вам нужно переписать весь свой код для использования DI, вы, вероятно, не должны этого делать. Возможно, посмотрите, как создать новый проект и работать в каком-то из вашего старого кода, если у вас есть время. Если ваша кодовая база большая, я бы рекомендовал разбить ее на более мелкие проекты и использовать RESTFUL apis для получения и сохранения данных. Хорошие примеры написания API-интерфейсов были бы для поиска вашего форума в его собственном приложении / поиске / имени? Partial-name = bob вернул бы всех пользователей со словом bob в нем. вы можете создать его и сделать его лучше с течением времени и использовать его на своем основном форуме

Надеюсь, вы понимаете мои ответы, но если вам нужна дополнительная информация, дайте мне знать.

Вместо globals в вашем init.php определите ваши объекты как:

 ioc::register('user', function() { return new User(); }); 

И не используйте метод create_friends_list:

 ioc::get('user')->newUser($user_id); 

Это действительно простая реализация. Выезд: http://net.tutsplus.com/tutorials/php/dependency-injection-huh/?search_index=2

или

http://net.tutsplus.com/tutorials/php/dependency-injection-in-php/?search_index=1

Чтобы получить больше информации.

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

Вы ответили :

Это кажется немного сложным (и совсем другим) и большим количеством работы, чем это стоит реализовать на данный момент. Я просто пытаюсь взломать что-то реальное быстро и посмотреть, получает ли он тягу.

Помните, что вам нужно выполнить следующие действия, если вы хотите правильно выполнить DI с контейнером IoC:

  • Внедрить контейнер IoC и реестр при загрузке
  • Измените все существующие классы, у которых есть зависимости, чтобы разрешить инъекцию установщика, но не в зависимости от самого контейнера IoC
  • Повторная запись / реструктуризация ваших тестов по мере необходимости

Заметка о второй пуле, которая отражает мое собственное личное мнение и предпочтение, развиваясь с умением думать в уме, может быть трудной, и вы хотите получить полную награду, которую она может дать. Одна из самых больших наград – полностью развязанные объекты, вы не получаете полной части этого, если все еще зависит от контейнера IoC.

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

Вместо этого я предлагаю вам сделать правильный выбор, поскольку вы пишете новый код. Предоставлять методы установки для инъекций зависимостей, которые могут быть использованы вручную или через контейнер IoC. Тем не менее, ваши классы продолжают создавать их как раз вовремя, если ни один из них не был введен, и избегать композиции в конструкторе.

В этот момент у вас будет коллекция классов, которые значительно улучшают обращение к управлению, если это шаблон, который вы хотите продолжить в будущем. Если это хорошо работает для вас, и это то, что вы действительно хотите включить в свои проектные решения, вы можете легко перераспределить, чтобы удалить логику JIT позже.

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

Я собирался написать этот комментарий, но он слишком долгое время. Я не эксперт, поэтому я просто даю свою точку зрения из того, что я узнал через несколько лет, практикуя и здесь, в SO. Не стесняйтесь использовать или подвергать сомнению любую часть моего ответа (или ничего).

1. Вне. Что делает контейнер? Ответ должен быть одним. Он не должен нести ответственность за инициализацию классов, подключение к базе данных, обработку сеанса и другие вещи. Каждый класс делает только одно.

 class ioc { public $db; // Only pass here the things that the class REALLY needs static public function set($var, $val) { return $this->$var = $val; } static function newDB($user, $pass) { return new PDO('mysql:host=localhost;dbname=test', $user, $pass); } static function newUser($user_id) { return new User($db, $user_id); } static function newLogin($session) { return new Login($this->db, $session); } } if (ioc::set('db',ioc::newDB($user, $pass))) { $user = ioc::newUser($user_id); $login = ioc::newLogin($session); } 

2.- Вы не должны делать $friend = ioc::newUser($row['user_id']); внутри вашего класса. Там вы предполагаете, что существует класс ioc с методом newUser() , в то время как каждый класс должен действовать самостоятельно, а не на основе [возможно] других существующих классов. Это называется жесткой связью . В принципе, вы также не должны использовать глобальные переменные. Все, что используется в классе, должно быть передано ему, а не предполагается в глобальной области. Даже если вы знаете, что он есть, и ваш код работает, он делает класс не повторно используемым для других проектов и гораздо сложнее проверить. Я не буду расширять себя (PUN?), Но поместил отличное видео, которое я открыл здесь в SO, чтобы вы могли больше копать: «Чистые кодовые разговоры» – «Не смотрите на вещи» .

Я не уверен в том, как ведет себя User класса, но я так и сделал (не обязательно правильно):

 // Allow me to change the name to Friends to avoid confusions class Friends { function __construct($db) { $this->db = $db; } function create_friends_list($user_id) { if (!empty(id)) { // Protect it from injection if your $user_id MIGHT come from a $_POST or whatever $st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = ?"); $st->execute(array($user_id)); $AllData = $st->fetchAll() return $AllData; } else return null; } // Pass the $friend object function get_friend_data($friend) { $FriendData = array ('Name' => $friend->get_user_name(), 'Picture' => $friend->get_profile_picture()); return $FriendData; } } $User = ioc::newUser($user_id); $Friends = new Friends($db); $AllFriendsIDs = array(); if ($AllFriendsIDs = $Friends->create_friends_list($User->get('user_id'))) foreach ($AllFriendsIDs as $Friend) { // OPTION 1. Return the name, id and whatever in an array for the user object passed. $FriendData = $Friends->get_friend_data(ioc::newUser($Friend['user_id'])); // Do anything you want with $FriendData // OPTION 2. Ditch the get_friend_data and work with it here directly. You're already in a loop. // Create the object (User) Friend. $Friend = ioc::newUser($Friend['user_id']); $Friend->get_user_name(); $Friend->get_profile_picture(); } - // Allow me to change the name to Friends to avoid confusions class Friends { function __construct($db) { $this->db = $db; } function create_friends_list($user_id) { if (!empty(id)) { // Protect it from injection if your $user_id MIGHT come from a $_POST or whatever $st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = ?"); $st->execute(array($user_id)); $AllData = $st->fetchAll() return $AllData; } else return null; } // Pass the $friend object function get_friend_data($friend) { $FriendData = array ('Name' => $friend->get_user_name(), 'Picture' => $friend->get_profile_picture()); return $FriendData; } } $User = ioc::newUser($user_id); $Friends = new Friends($db); $AllFriendsIDs = array(); if ($AllFriendsIDs = $Friends->create_friends_list($User->get('user_id'))) foreach ($AllFriendsIDs as $Friend) { // OPTION 1. Return the name, id and whatever in an array for the user object passed. $FriendData = $Friends->get_friend_data(ioc::newUser($Friend['user_id'])); // Do anything you want with $FriendData // OPTION 2. Ditch the get_friend_data and work with it here directly. You're already in a loop. // Create the object (User) Friend. $Friend = ioc::newUser($Friend['user_id']); $Friend->get_user_name(); $Friend->get_profile_picture(); } - // Allow me to change the name to Friends to avoid confusions class Friends { function __construct($db) { $this->db = $db; } function create_friends_list($user_id) { if (!empty(id)) { // Protect it from injection if your $user_id MIGHT come from a $_POST or whatever $st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = ?"); $st->execute(array($user_id)); $AllData = $st->fetchAll() return $AllData; } else return null; } // Pass the $friend object function get_friend_data($friend) { $FriendData = array ('Name' => $friend->get_user_name(), 'Picture' => $friend->get_profile_picture()); return $FriendData; } } $User = ioc::newUser($user_id); $Friends = new Friends($db); $AllFriendsIDs = array(); if ($AllFriendsIDs = $Friends->create_friends_list($User->get('user_id'))) foreach ($AllFriendsIDs as $Friend) { // OPTION 1. Return the name, id and whatever in an array for the user object passed. $FriendData = $Friends->get_friend_data(ioc::newUser($Friend['user_id'])); // Do anything you want with $FriendData // OPTION 2. Ditch the get_friend_data and work with it here directly. You're already in a loop. // Create the object (User) Friend. $Friend = ioc::newUser($Friend['user_id']); $Friend->get_user_name(); $Friend->get_profile_picture(); } 

Я не тестировал его, поэтому у него, вероятно, небольшие ошибки.

3.- Если вы учитесь во время кодирования, вам придется переписывать МНОГИЕ вещи. Попробуйте сделать что-то с самого начала, поэтому вам не нужно переписывать все, а только классы / методы и принимать некоторые условные обозначения для всего вашего кода. Например, никогда не эхо изнутри функции / метода, всегда возвращайте и эхо извне. Я бы сказал, что да, это того стоит. Плохо, что вам нужно потерять 1 или 2 часа, просто переписывая что-то, но если это нужно сделать, сделайте это.

PS, извините, я повсюду менял свой стиль.


EDIT Чтение других ответов, в то время как вы не должны подключаться к базе данных с вашим объектом ioc, это должно быть совершенным, создавая с ним новый объект. Отредактировано выше, чтобы понять, что я имею в виду.