Зачем вам нужна автозагрузка дисков?

Обновлено: 02.07.2024

В этом руководстве описано, как работает автозагрузка и перезагрузка в режиме zeitwerk.

Прочитав это руководство, вы узнаете:

  • Связанная конфигурация Rails
  • Структура проекта
  • Автозагрузка, перезагрузка и активная загрузка
  • Наследование одной таблицы
  • И многое другое

Главы

1 Введение

В этом руководстве описаны автозагрузка, перезагрузка и немедленная загрузка в приложениях Rails.

В обычной программе Ruby зависимости необходимо загружать вручную. Например, в следующем контроллере используются классы ApplicationController и Post , и обычно для них нужно поместить вызовы require:

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

Чтобы обеспечить эту функцию, Rails управляет парой загрузчиков Zeitwerk от вашего имени.

2 Структура проекта

В приложении Rails имена файлов должны соответствовать константам, которые они определяют, а каталоги действуют как пространства имен.

Например, файл app/helpers/users_helper.rb должен определять UsersHelper, а файл app/controllers/admin/payments_controller.rb должен определять Admin::PaymentsController .

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

Дополнительную информацию см. в документации Zeitwerk.

3 config.autoload_paths

Мы ссылаемся на список каталогов приложений, содержимое которых должно быть автоматически загружено и (необязательно) перезагружено как пути автозагрузки. Например, приложение/модели. Такие каталоги представляют собой корневое пространство имен: Object .

Пути автозагрузки называются корневыми каталогами в документации Zeitwerk, но в этом руководстве мы остановимся на «пути автозагрузки».

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

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

Например, если UsersHelper реализован в app/helpers/users_helper.rb , модуль является автозагружаемым, вам не нужен (и не следует писать) вызов require для него:

Rails автоматически добавляет пользовательские каталоги в app в пути автозагрузки. Например, если в вашем приложении есть app/presenters , вам не нужно ничего настраивать для автоматической загрузки докладчиков, все работает сразу.

Массив путей автозагрузки по умолчанию можно расширить, добавив config.autoload_paths в config/application.rb или config/environments/*.rb. Например:

Кроме того, движки могут помещать в тело класса движка и в свои собственные config/environments/*.rb .

Не изменяйте ActiveSupport::Dependencies.autoload_paths ; общедоступный интерфейс для изменения путей автозагрузки — config.autoload_paths .

Вы не можете автоматически загружать код в пути автозагрузки во время загрузки приложения. Конкретно, прямо в config/initializers/*.rb . Проверьте Автозагрузка при загрузке приложения ниже, чтобы узнать, как это сделать.

Пути автозагрузки управляются автозагрузчиком Rails.autoloaders.main.

4 config.autoload_once_paths

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

По умолчанию эта коллекция пуста, но вы можете расширить ее, передав config.autoload_once_paths . Вы можете сделать это в config/application.rb или config/environments/*.rb . Например:

Кроме того, движки могут помещать в тело класса движка и в свои собственные config/environments/*.rb .

Если app/serializers помещается в config.autoload_once_paths , Rails больше не считает этот путь автозагрузкой, несмотря на то, что он является пользовательским каталогом в app . Этот параметр имеет приоритет над этим правилом.

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

Например, сериализаторы Active Job хранятся внутри Active Job:

и само активное задание не перезагружается при перезагрузке, перезагружается только код приложения и движков в путях автозагрузки.

Сделать MoneySerializer перезагружаемым было бы запутанно, потому что повторная загрузка отредактированной версии не повлияет на этот объект класса, хранящийся в Active Job. Действительно, если бы MoneySerializer был перезагружаемым, начиная с Rails 7 такой инициализатор вызывал бы ошибку NameError .

Еще один вариант использования — механизмы, украшающие классы фреймворка:

Там объект модуля, хранящийся в MyDecoration к моменту запуска инициализатора, становится предком ActionController::Base , и перезагружать MyDecoration бессмысленно, это не повлияет на эту цепочку предков.

Классы и модули из путей автозагрузки один раз могут быть автоматически загружены в config/initializers . Итак, с этой конфигурацией это работает:

Технически вы можете автоматически загружать классы и модули, управляемые автозагрузчиком Once, в любом инициализаторе, который запускается после :bootstrap_hook .

Пути автозагрузки один раз управляются Rails.autoloaders.once .

5 $LOAD_PATH

Пути автозагрузки добавляются в $LOAD_PATH по умолчанию. Однако внутри Zeitwerk использует абсолютные имена файлов, и ваше приложение не должно вызывать вызовы require для автозагружаемых файлов, поэтому эти каталоги там фактически не нужны. Вы можете отказаться с этим флагом:

Это может немного ускорить законные вызовы require, так как поиск выполняется реже. Кроме того, если ваше приложение использует Bootsnap, это избавляет библиотеку от создания ненужных индексов и экономит ОЗУ, которое им потребуется.

6 Перезагрузка

Rails автоматически перезагружает классы и модули, если файлы приложений в путях автозагрузки изменяются.

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

Повторная загрузка может быть включена или отключена. Параметр, управляющий этим поведением, называется config.cache_classes , который по умолчанию имеет значение false в режиме разработки (перезагрузка включена) и true по умолчанию в рабочем режиме (перезагрузка отключена).

Rails по умолчанию использует событийный файловый монитор для обнаружения изменений в файлах. Вместо этого его можно настроить для обнаружения изменений файлов путем обхода путей автозагрузки. Это контролируется настройкой config.file_watcher.

В консоли Rails нет активного наблюдателя за файлами независимо от значения config.cache_classes . Это связано с тем, что обычно перезагрузка кода в середине сеанса консоли может привести к путанице. Как и в случае с отдельным запросом, обычно требуется, чтобы сеанс консоли обслуживался согласованным, неизменным набором классов и модулей приложения.

Однако вы можете принудительно выполнить перезагрузку в консоли, выполнив команду reload! :

Как видите, объект класса, хранящийся в константе User, после перезагрузки отличается.

6.1 Перезагрузка и устаревшие объекты

Очень важно понимать, что в Ruby нет способа по-настоящему перезагрузить классы и модули в памяти и отразить это везде, где они уже используются. Технически «выгрузка» класса User означает удаление константы User с помощью Object.send(:remove_const, «User») .

Например, посмотрите этот сеанс консоли Rails:

joe — это экземпляр исходного класса User. Когда происходит перезагрузка, константа User оценивается как другой перезагруженный класс. alice является экземпляром только что загруженного User , а joe — нет — его класс устарел. Вы можете снова определить joe, запустить подсессию IRB или просто запустить новую консоль вместо вызова перезагрузки! .

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

если пользователь перезагружается, а VipUser нет, суперкласс VipUser является исходным устаревшим объектом класса.

Вывод: не кэшируйте перезагружаемые классы или модули.

7 Автозагрузка при загрузке приложения

Во время загрузки приложения могут автоматически загружаться из путей автозагрузки один раз, которые управляются однократным автозагрузчиком. Пожалуйста, проверьте раздел config.autoload_once_paths выше.

Однако вы не можете выполнять автоматическую загрузку из путей автозагрузки, которые управляются основным автозагрузчиком. Это относится к коду в config/initializers, а также к инициализаторам приложений или движков.

Почему? Инициализаторы запускаются только один раз при загрузке приложения. Если вы перезагрузите сервер, они снова запустятся в новом процессе, но перезагрузка не перезагрузит сервер, и инициализаторы больше не запустятся. Давайте рассмотрим два основных варианта использования.

7.1 Вариант использования 1. Во время загрузки загружайте перезагружаемый код

Давайте представим, что ApiGateway — это перезагружаемый класс из приложений/служб, управляемых основным автозагрузчиком, и вам нужно настроить его конечную точку во время загрузки приложения:

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

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

По историческим причинам этот обратный вызов может выполняться дважды. Исполняемый код должен быть идемпотентным.

7.2 Вариант использования 2. Во время загрузки загружайте код, остающийся в кэше

Некоторые конфигурации берут объект класса или модуля и сохраняют его в месте, которое не перезагружается.

Другой пример — сериализаторы Active Job:

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

Еще одним примером являются railties или движки, украшающие классы фреймворка путем включения модулей. Например, turbo-rails украшает ActiveRecord::Base следующим образом:

Это добавляет объект модуля в цепочку предков ActiveRecord::Base . Изменения в Turbo::Broadcastable не будут иметь никакого эффекта при перезагрузке, цепочка предков останется исходной.

Следствие: эти классы или модули нельзя перезагружать.

Самый простой способ обратиться к этим классам или модулям во время загрузки — определить их в каталоге, который не относится к путям автозагрузки. Например, lib — идиоматический выбор. По умолчанию он не принадлежит путям автозагрузки, но принадлежит $LOAD_PATH. Просто выполните регулярный запрос, чтобы загрузить его.

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

8 нетерпеливых загрузок

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

Неотложная загрузка контролируется флагом config.eager_load , который включен по умолчанию в рабочем режиме.

Порядок, в котором загружаются файлы, не определен.

Если определена константа Zeitwerk, Rails вызывает Zeitwerk::Loader.eager_load_all независимо от режима автозагрузки приложения. Это гарантирует загрузку зависимостей, которыми управляет Zeitwerk.

9 Наследование одной таблицы

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

В некотором смысле приложениям необходимо быстро загружать иерархии STI независимо от режима загрузки.

Конечно, если приложение загружается при загрузке, это уже выполнено. Когда это не так, на практике достаточно создать экземпляры существующих типов в базе данных, что обычно хорошо в режимах разработки или тестирования. Один из способов сделать это — включить модуль предварительной загрузки STI в каталог lib:

а затем включите его в корневые классы STI вашего проекта:

10 настраиваемых интонаций

Возможно, имя какого-то конкретного файла или каталога не изменяется так, как вы хотите. Например, ожидается, что html_parser.rb по умолчанию определяет HtmlParser. Что, если вы предпочитаете, чтобы класс был HTMLParser? Есть несколько способов настроить это.

Самый простой способ — определить аббревиатуры в config/initializers/inflections.rb:

Это повлияет на глобальные изменения Active Support. В некоторых приложениях это может подойти, но вы также можете настроить преобразование отдельных базовых имен независимо от Active Support, передав набор переопределений в инфлекторы по умолчанию:

Нет глобальной конфигурации, которая может повлиять на указанные экземпляры; они детерминированы.

Для полной гибкости вы даже можете определить собственный инфлектор. Дополнительные сведения см. в документации Zeitwerk.

11 Автозагрузка и движки

Двигатели работают в контексте родительского приложения, и их код автоматически загружается, перезагружается и загружается родительским приложением. Если приложение работает в режиме zeitwerk, код движка загружается в режиме zeitwerk. Если приложение работает в классическом режиме, код движка загружается в классическом режиме.

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

Например, это приложение использует Devise:

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

Однако, если движок поддерживает Rails 6 или Rails 6.1 и не контролирует свои родительские приложения, он должен быть готов к работе в классическом режиме или в режиме Zeitwerk. Что нужно учитывать:

Если в классическом режиме потребуется вызов require_dependency, чтобы обеспечить загрузку какой-либо константы в какой-то момент, напишите его. Пока zeitwerk не понадобится, не помешает, будет работать и в режиме zeitwerk.

классический режим подчеркивает имена констант ("Пользователь" -> "user.rb"), а режим zeitwerk делает имена файлов камелизированными ("user.rb" -> "User"). Они совпадают в большинстве случаев, но не совпадают, если есть ряд последовательных заглавных букв, как в "HTMLParser". Самый простой чтобы обеспечить совместимость, нужно избегать таких имен. В этом случае выберите "HtmlParser".

В классическом режиме файл app/model/concerns/foo.rb может определять как Foo, так и Concerns::Foo . В режиме zeitwerk есть только один вариант: он должен определить Foo. Для совместимости определите Foo .

12 Тестирование

12.1 Ручное тестирование

Задача zeitwerk:check проверяет, соответствует ли дерево проекта ожидаемым соглашениям об именах, и удобна для ручной проверки. Например, если вы переходите с классического режима на zeitwerk или что-то исправляете:

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

12.2 Автоматическое тестирование

Рекомендуется проверить в наборе тестов правильность загрузки проекта.

Это касается соответствия именования Zeitwerk и других возможных ошибок. Ознакомьтесь с разделом о тестировании нетерпеливой загрузки в руководстве Тестирование приложений Rails.

13 Устранение неполадок

Лучший способ проследить, что делают загрузчики, — проверить их активность.

Самый простой способ сделать это – включить

в config/application.rb после загрузки настроек фреймворка по умолчанию. Это выведет трассировку на стандартный вывод.

Если вы предпочитаете вести журнал в файл, вместо этого настройте это:

Регистратор Rails еще недоступен, когда выполняется config/application.rb. Если вы предпочитаете использовать регистратор Rails, вместо этого настройте этот параметр в инициализаторе:

14 Rails.autoloaders

Экземпляры Zeitwerk, управляющие вашим приложением, доступны по адресу

по-прежнему доступен в приложениях Rails 7 и возвращает true .

Отзыв

Призываем вас помочь улучшить качество этого руководства.

Пожалуйста, внесите свой вклад, если заметите какие-либо опечатки или фактические ошибки. Чтобы приступить к работе, вы можете ознакомиться с нашим разделом документации.

Вы также можете найти неполный контент или устаревшие материалы. Пожалуйста, добавьте недостающую документацию для main. Обязательно сначала проверьте Edge Guides, чтобы убедиться, что проблемы уже исправлены или нет в основной ветке. Обратитесь к Руководству по Ruby on Rails, чтобы узнать о стиле и условных обозначениях.

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

И последнее, но не менее важное: любое обсуждение документации по Ruby on Rails приветствуется в списке рассылки rubyonrails-docs.

Rails, Ruby on Rails и логотип Rails являются товарными знаками Дэвида Хайнемайера Ханссона. Все права защищены.

В PHP 5 в этом больше нет необходимости. Вы можете определить функцию __autoload(), которая вызывается автоматически, если вы пытаетесь использовать класс/интерфейс, который еще не определен. При вызове этой функции обработчик сценариев получает последний шанс загрузить класс до того, как PHP завершит работу с ошибкой.

Теперь я хочу знать, является ли плохой практикой использование только __autoload для загрузки соответствующих классов на динамическом сайте?

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


4 ответа 4

Плохо? Нет. __autoload() — одно из моих любимых дополнений к PHP 5. Оно снимает ответственность (и раздражение) с необходимостью вручную включать/требовать файлы классов, необходимые вашему приложению. При этом вы, как разработчик, должны убедиться, что загружаются только «соответствующие классы». Это легко сделать с помощью структурированной схемы именования и структуры каталогов. В Интернете есть множество примеров того, как правильно использовать __autoload(), выполните поиск в Google, и вы найдете много информации.

@jason: Вам, вероятно, следует пояснить это в своем вопросе, потому что сейчас вы ничего не говорите о производительности.

@musicfreak, вы правы, но если бы это было не очень хорошо для производительности, то, скорее всего, это было бы расценено как «плохая практика»

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

Автозагрузка — это хороший способ загрузить только те классы, которые необходимы.

В PHP 5 >= 5.1.2 большинство проблем со старой функцией __autoload() исчезли благодаря spl_autoload_register().

Я только что просмотрел указанную страницу spl_autoload_register(), но я не очень понимаю, нужна ли она или используется только базовая функция __autoload?

Если вы хотите иметь более одного автозагрузчика, вы должны использовать spl_autoload_register, если нет, __autoload вполне подойдет.

Главное преимущество spl_autoload_register() заключается в том, что библиотека классов может определить свою собственную функцию автозагрузки без возникновения ошибки во время выполнения, поскольку __autoload() определяется дважды.

Теперь я хочу знать, является ли плохой практикой использование только __autoload для загрузки соответствующих классов на динамический сайт?

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

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

Вот цитата из руководства пользователя Zend Guard:

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

Программная инженерия, психология, звездолет, белок, женщины и мотоциклисты.

Информационный бюллетень: 1 раз в неделю

Стать инженером

Вы создаете больше ошибок, чем исправляете?

Неудобно ли вам получать отзывы о вашем коде?

Вы застряли со сложным программным обеспечением, требующим рефакторинга?

Хотите улучшить свои навыки, чтобы не отставать от лучших практик?

Баннер курса PHP корпоративного уровня

Article Blog Image

Магия автоматической загрузки php-файлов с помощью Composer

Предисловие

Уже несколько лет я не начинал проект с нуля. Я имею в виду полностью с нуля. Все мои проекты по умолчанию являются свежими установками Symfony. 2 недели назад я записывал одну из лекций этого курса, и когда я готовил супер простой пример, я столкнулся с ошибкой Class not found. Это напомнило мне о старых добрых временах использования метода PHP require_once, поэтому я решил написать короткую статью о том, как Composer упрощает нашу жизнь и как он на самом деле автоматически загружает файлы за кулисами.

Спасибо, Нильс и Джорди, за прекрасного менеджера зависимостей, и с днем ​​рождения, Джорди!

Базовая линия

Не задумывайтесь над тем, что такое пространство имен.

Пространство имен — это просто префикс класса (например, каталог в операционной системе), обеспечивающий уникальность пути к классу.

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

Проблема

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

Автозагрузка в PHP значительно экономит время. Он позволяет вам писать краткие сценарии, не зная точной структуры каталогов используемых вами библиотек. Но с появлением пространств имен в PHP 5.3 и влиянием Java на PHP-фреймворки нового поколения автозагрузка меняется. В ближайшем будущем явная автозагрузка станет повсеместной, но не будет иметь никаких преимуществ старой автозагрузки.

До автозагрузки были пути к файлам

До автозагрузки каждый файл класса должен был явно объявлять путь к своим зависимостям. Исходный код, взятый из библиотеки PEAR, будет выглядеть следующим образом:

Зависимости четко отображались в начале каждого класса. В этом фрагменте кода, даже если первым использованием класса PEAR_DependencyDB является скрытая строка 328 класса PEAR_Registry, зависимость очевидна.

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

Затем появилась автозагрузка SPL

require_once работал очень медленно. На серверах без быстрого диска или кеша опкода лучше вообще не использовать require_once. Затем нам очень пригодилась функция spl_autoload_register() из библиотеки PHP SPL. Это позволило полностью удалить вызовы require_once из исходного кода. Это ускорило работу приложений.

Но самым большим преимуществом было то, что вы могли использовать класс, фактически не зная, где в структуре каталогов находится его исходный файл. Вот выдержка из руководства «Мой первый проект» для фреймворка Symfony:

Здесь вообще нет require_once, даже если этот класс зависит от классов sfActions, PostPeer и Criteria. Разработчики могли сразу погрузиться в бизнес-логику, не тратя ни секунды на выяснение, где находятся зависимости. Это была быстрая разработка приложений в действии.

Реализации автозагрузки

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

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

Более того, техника автозагрузки Symfony позволяла переопределять классы фреймворка пользовательскими. Итератор файлов просматривал структуру каталогов в определенном порядке: сначала пользовательские каталоги, затем каталоги проекта, затем каталоги плагинов и, наконец, каталоги фреймворка. Таким образом, разработчик может создать собственный класс PostPeer, который переопределит другой класс PostPeer, предоставленный подключаемым модулем.

Автозагрузка была на высоте: быстрая, мощная, лаконичная.

Автозагрузка пространств имен

Появление пространств имен в PHP 5.3 заставило изменить методы автозагрузки. Инициатива, начатая некоторыми авторами фреймворка, попыталась обеспечить техническую совместимость между библиотеками и реализациями автозагрузчика. Это сообщество, получившее название «Рабочая группа по стандартам PHP», согласилось с тем, что явное лучше, чем неявное, и что полное имя класса будет относительным путем к исходному файлу класса:

Библиотеки, согласные с этой инициативой, должны следовать принципам именования и структуры файлов и предоставлять реализацию автозагрузки, совместимую с примером класса SplClassLoader. Это относится к большинству фреймворков «нового поколения» в 2011 году. Например, вот выдержка из нового туториала «Мой первый проект» в Symfony2:

В этом коде по-прежнему нет require_once — работает автозагрузка. Автозагрузка PHP ищет класс Symfony\Framework\WebBundle\Controller в файле Symfony/Framework/WebBundle/Controller.php. Путь к файлу больше не относится к include_path , так как автозагрузчик должен быть инициализирован базовым путем к каталогу библиотеки.

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

Это больше не быстрая разработка приложений

Разве первое использование в предыдущем примере ничего вам не напоминает? Правильно, это очень похоже на вызовы require_once из первого примера без автозагрузки:

Дополнительная многословность автозагрузки пространства имен снижает простоту использования, в первую очередь связанную с автозагрузкой SPL.

Проблема не только в необходимости писать больше кода. Рассмотрим, что нужно сделать, чтобы использовать класс из фреймворка «нового поколения»:

  1. Проанализируйте структуру каталогов фреймворка и найдите исходный файл класса для использования
  2. Откройте исходный файл и скопируйте объявление пространства имен.
  3. Вставьте объявление пространства имен в инструкцию использования пользовательского кода.

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

В PHP нет лучшего способа

Было бы здорово, если бы вы могли использовать код фреймворка нового поколения, не зная, где в файловой системе лежат необходимые зависимости? Что, если бы вы могли написать контроллер Symfony2 следующим образом:

Умный автозагрузчик может перехватить вызов класса Controller, открыть реализацию по умолчанию (в Symfony/Framework/WebBundle/Controller.php) и динамически создать псевдоним Symfony\Framework\WebBundle\Controller для Controller . За исключением того, что в PHP use создает псевдонимы во время компиляции, так что это не работает.Есть возможность реализовать такой автозагрузчик с помощью eval() , но это, вероятно, хуже, чем требовать файлы вручную.

Кроме того, использование псевдонимов для всех классов на уровне удобства использования поверх платформы также невозможно. Это предотвратит ленивую загрузку основных классов и сбой при дублировании имен классов (например, Symfony\Framework\WebBundle\Command и Symfony\Components\Console\Command\Command ).

Если авторы фреймворков не изменят свое мнение об автозагрузке, будущее PHP будет многословным.

Решение проблемы

Во втором примере автозагрузка мешает и усложняет задачу.

Разработчики фреймворков нового поколения объясняют, что дополнительная многословность — это плата за более качественный код. Я не уверен, что готов заплатить эту цену. Мне не нравится рассматривать PHP как следующую Java, где код великолепен с точки зрения выпускника CS, но очень дорог в написании. Это вызывает у меня желание переключиться на другие языки, где это обсуждение автозагрузки пространств имен никогда не проводилось и где все еще возможна быстрая разработка приложений.

Возьмите, к примеру, Ruby. Он предлагает микрофреймворк под названием Sinatra, который делает приложение «Hello, world» очень лаконичным:

О, смотрите, в этом скрипте есть инструкции require. И все же, это так быстро и просто в использовании.

Нашли опечатку? Что-то не так в этой документации? Просто создайте форк и отредактируйте его!

Читайте также: