Web

Какие шаблоны существуют в проекте PHP для хранения, доступа и организации вспомогательных объектов?

Как организовать и управлять вспомогательными объектами, такими как движок базы данных, уведомления пользователей, обработка ошибок и так далее, в объектно-ориентированном проекте на базе PHP?

Допустим, у меня есть большая PHP CMS. CMS организована в виде различных классов. Несколько примеров:

  1. объект базы данных

  2. управление пользователями

  3. API для создания/изменения/удаления элементов

  4. объект обмена сообщениями для отображения сообщений конечному пользователю

  5. обработчик контекста, который переводит пользователя на нужную страницу

  6. класс навигационной панели, отображающий кнопки

  7. объект протоколирования

  8. возможно, пользовательская обработка ошибок

Как лучше сделать эти объекты доступными для каждой части системы, которая в них нуждается? Мой первый подход заключался в том, чтобы иметь суперглобальный массив $application, который содержал бы инициализированные экземпляры этих классов:

global $application;

$application->messageHandler->addMessage("Item successfully inserted");

Затем я перешел к шаблону Singleton и функции фабрики:

$mh =&factory("messageHandler");

$mh->addMessage("Item successfully inserted");

 

Но и это меня не устраивает. Юнит-тесты и инкапсуляция становятся для меня все более важными, а в моем понимании логика, стоящая за глобальными/синглтонами, разрушает основную идею ООП.

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

 Большинство PHP-фреймворков, которые я изучал, используют либо паттерн синглтон, либо функции, которые обращаются к инициализированным объектам. Оба подхода хороши, но, как я уже сказал, меня не устраивает ни один из них.

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

Ответ 1

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

Синглон

Не рекомендуется, поскольку его трудно тестировать и он подразумевает глобальное состояние (singletonitis).

Реестр

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

Наследование

Жаль, но в PHP нет множественного наследования, поэтому все ограничивается цепочкой.

Инъекция зависимостей

Это лучший подход, но более серьезная тема.

Традиционный подход

Самый простой способ сделать это использовать инъекцию конструктора или сеттера (передать объект зависимости с помощью сеттера или в конструкторе класса).

Фреймворки

Вы можете создать свой собственный инжектор зависимостей или использовать некоторые фреймворки для инъекции зависимостей, например, Yadif.

Ресурсы приложения

Вы можете инициализировать каждый из ваших ресурсов в бутстрапе приложения (который действует как контейнер) и обращаться к ним в любом месте приложения, используя объект бутстрапа.

Такой подход реализован в Zend Framework 1.x

Загрузчик ресурсов

Разновидность статического объекта, который загружает (создает) нужный ресурс только тогда, когда это необходимо. Это очень умный подход. Вы можете посмотреть это в действии, например, при реализации компонента Dependency Injection в Symfony.

Инъекция на определенный слой

Ресурсы не всегда нужны в любом месте приложения. Иногда они нужны только в контроллерах (MVC). Тогда вы можете инжектировать ресурсы только там.

Обычный подход к этому использование комментариев docblock для добавления метаданных инъекции.

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

В целом, несмотря на выбранную вами технику, вы должны подумать о том, как будут кэшироваться ресурсы. Кэшем будет сам ресурс.

Приложения могут быть очень большими и загружать все ресурсы при каждом запросе очень дорого. Существует множество подходов, в том числе и этот appserver-in-php Проект Хостинг на Google Code.

Ответ 2

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

  1. Объект может иметь много полезных методов или должен вызываться более одного раза, в этом случае я реализую синглтон/реестр:

$object = load::singleton('classname');

//или

$object = classname::instance(); // which sets self::$instance = $this

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

$object = new Class();

Хранение временных объектов В ЛЮБОМ МЕСТЕ может привести к утечке памяти, так как ссылки на них могут быть забыты при сохранении объекта в памяти до конца сценария.

Ответ 3

Мне нравится концепция Dependency Injection:

«Dependency Injection это когда компоненты получают свои зависимости через конструкторы, методы или непосредственно в поля. (С сайта Pico Container)».

Фабьен Потенсье написал действительно хорошую серию статей о Dependency Injection и необходимости их использования. Он также предлагает хороший и небольшой контейнер Dependency Injection Container под названием Pimple, который можно использовать (больше информации на github).

 

Схожие статьи

Web

Почему move_uploaded_file выдает ошибку «Не удалось открыть поток: в доступе отказано»

Web

Как заставить PHP перестать заменять символы «.» в массивах $_GET или $_POST

Как произвести бесплатное сканирование сайта на ссылки, ошибки и позицию
Web

Как произвести бесплатное сканирование сайта на ссылки, ошибки и позицию

Web

Tailwind CSS — что это такое и какие возможности он дает?

×