PSR Стандарты
Как показала практика, многие PHP-разработчики знакомы с аббревиатурой PSR. Однако большинство все еще ограничены знанием, что PSR это стандарт оформления кода.
Ребята из PHP-FIG (PHP Framework Interop Group), группа концепций совместимости PHP, которые занимаются развитием PSR (PHP Standards Recommendations) шагнули далеко вперед. Поэтому давайте разберемся, что из себя представляет PSR…
За последние полгода мне пришлось провести множество собеседований на позицию Middle PHP-Developer.
Один из вопросов, который я задавал всем кандидатам, был:- «Знаете ли Вы, что такое PSR?»
Как оказалось, практически всем кандидатам была знакома эта аббревиатура. Однако пытаясь рассказать подробнее, почти все разработчики указывали в основном на то, что это стандарт оформления кода (code style). Только некоторые упоминали про PSR-4 автозагрузку (использует Composer) и PSR-3 Logger Interface (в сети очень много однородных статей про PSR-0-1-2-3-4).
Мне кажется это весьма странным, так как термин PSR существует достаточно давно (уже более 10 лет) и каждый год набирает все большую популярность. Поэтому, думаю, будет неплохо собрать все в кучу и сделать обзор PSR стандартов.
PHP-FIG и PSR
PHP-FIG (PHP Framework Interop Group) — организованная в 2009 году группа разработчиков, основная идея которой находить способы совместной работы, выделяя общие концепции в разработке проектов на PHP.
Проще говоря, ребята стараются выделить что-то общее между различными проектами на разных фреймворках и сформировать некие стандарты (рекомендации) для дальнейшего периспользования.
Участники PHP-FIG
ReactPHP, Composer, Laminas Project (переименованный Zend Framework), Yii framework, CakePHP, Slim, Joomla, Magento, Drupal, phpBB, phpDocumentor и другие.
PSR (PHP Standards Recommendations) — описывает общие концепции, которые уже были проверены и отработаны. Вероятно при создании PSR, группа PHP-FIG вдохновлялась Java Community Process, а первый стандарт был принят в 2010 году.
Список PSR стандартов расширяется новыми, а сами стандарты делятся на категории:
Автозагрузка, Интерфейсы, HTTP и Стиль кодирования,
каждому из которых присваивается определенный статус:
Принят, Устаревший, Черновик и Заброшенный.
Далее мы рассмотрим принятые PSR стандарты по категориям:
Автозагрузка
Разработчики, которые «недолго» работают с PHP, вероятно не знакомы с проблемами импорта классов, ведь есть пространства имен, а сейчас вообще трудно представить проект без менеджера зависимостей Composer, которой решает все вопросы с автозагрузкой классов.
Однако так было не всегда, пространства имен появились только в PHP 5.3 (это был 2009 год), а первый релиз Composer состоялся в марте 2012 года. Но вот сам PHP существует гораздо дольше, как Personal Home Page с 1995 года и как Hypertext Preprocessor с 1998 года, однако только PHP 5 включает «полноценную объектную модель», релиз которого был в июле 2004 года. Бородатым разработчикам того времени приходилось как-то сражаться со всеми возникшими проблемами при импорте классов.
Не самый плохой пример импорта классов на PHP 14-ти летней давности:
class MyLib < private $CI; public function __construct() < $this->CI = &get_instance(); $this->CI->load->library('encrypt'); $this->CI->load->library('session'); $this->CI->load->library('url'); $this->CI->load->helper('captcha'); $this->CI->load->helper('cookie'); $this->CI->load->helper('email'); > . >
(Напишите в комментариях, если узнали где использовался данный подход).
PSR-0 — Autoloading StandardУстарел
После релиза пространства имен в 2009 году, в 2010 году был опубликован первый стандарт, который стал революцией в решении проблем автозагрузки классов и стал первым шагом на пути объединения фреймворков — наличие общей структуры директорий.
Пример реализации- PSR-4 — Autoloading Standard
Прогресс не стоит на месте и в конце 2013 года PHP-FIG публикуют новый стандарт автозагрузки классов. Он может использоваться в дополнение к PSR-0, а также любой другой спецификации автозагрузки. Стандарт также описывает, где размещать файлы, которые будут автоматически загружаться в соответствии со спецификацией. Данный стандарт решает некоторые проблемы/ограничения PSR-0 и используется по умолчанию в Composer.
Пример реализации
В дополнение, оставлю ссылку на статью, которая подробно описывает работу с пространством имен, решенные проблемы и PSR-4.
Интерфейсы
PHP развивается, набирает все большую популярность, а его инфраструктура пополняется огромным количеством различных инструментов.
Появляются проблемы с изучением и переиспользованием однотипного функционала, а менеджер зависимостей может затянуть целую кучу несвязанных однотипных зависимостей. (тут JavaScript разработчик улыбнется).
Поэтому принято решение стандартизировать некоторые общие концепции:
- PSR-3: Logger Interface
Основная цель данного интерфейса – простым и универсальным способом стандартизировать реализацию логирования. К данному интерфейсу прилагается спецификация, которая описывает в каких случаях какой из методов рекомендуется использовать.
interface LoggerInterface
Если Ваш проект нуждается в расширенном функционале, МОЖНО расширять данный интерфейс, но СЛЕДУЕТ сохранять совместимость с описанными в данном стандарте правилами. Это позволит сторонним библиотекам, применяемых при разработке приложения, использовать централизованную систему логирования.
Это привело к ситуации, когда многие библиотеки реализуют свои собственные системы кэширования с различными уровнями функциональности. Эти различия заставляют разработчиков изучать несколько систем, которые могут предоставлять или не предоставлять необходимую им функциональность. Кроме того, разработчики кеширующих библиотек сами сталкиваются с выбором между поддержкой только ограниченного числа платформ или созданием большого количества классов адаптеров.
Чтобы решить эти проблемы был принят общий стандарт для библиотек реализующих кэширование, он включает в себя несколько интерфейсов:
interface CacheItemInterface
interface CacheItemPoolInterface
interface ContainerInterface
Спецификация PSR-11 не описывает то, как необходимо регистрировать зависимости в проекте, однако дает четкую рекомендацию как делать не нужно:
Пользователи НЕ ДОЛЖНЫ передавать контейнер в объект, чтобы объект мог получить свои собственные зависимости. Это означает, что контейнер используется в качестве Service Locator, который обычно не рекомендуется использовать.
Отсюда, возникает простой вопрос: «Как это вообще работает»?
На самом деле все просто, на помощь приходит паттерн Factory, который возьмет на себя задачу создания объекта. А вот сам класс фабрики уже может принимать ContainerInterface и передавать в создаваемый объект необходимые зависимости.
[ 'invokables' => [ // можно обойтись без фабрики, если класс не требует зависимостей ], 'factories' => [ MyHandler::class => MyHandlerFactory::class, ], ];
class MyHandlerFactory < public function __invoke(ContainerInterface $container) : RequestHandlerInterface < $dep = $container->get(MyDepInterface::class); return new MyHandler($dep); > >
class MyHandler < private $dep; public function __construct(MyDepInterface $dep) < $this->dep = $dep; > public function __invoke(ServerRequestInterface $request): RequestHandlerInterface < // >>
interface EvolvableLinkInterface extends LinkInterface
interface LinkProviderInterface
interface EvolvableLinkProviderInterface extends LinkProviderInterface
interface EventDispatcherInterface
interface ListenerProviderInterface
interface StoppableEventInterface
Поэтому был принят PSR-16. Этот более простой подход направлен на создание стандартизированного оптимизированного интерфейса для общих случаев.
HTTP
Если речь идет о WEB-приложении, то не важно на сколько сложная в нем бизнес-логика, по факту оно делает всего 2 вещи — принимает Request и отдает Response. Это значит, что так или иначе, приходится реализовывать эти объекты у себя в проекте.
Пожалуй, одной из самых сложных задач, которая нередко возникает является переиспользование кода между различными проектами. Если хорошо абстрагированные участки бизнес логики, некоторые компоненты и модули перенести возможно (с минимальными затратами), то с переносом более высокого уровня фреймворков (например контроллеров) возникают сложности.
Группа PHP-FIG пытается исправить данную проблему и предоставляет стандарты абстракции над HTTP.
- PSR-7: HTTP Message Interfaces
Цель данного стандарта, предоставить общий набор интерфейсов для фреймворков, чтобы последние могли использовать одинаковые абстракции над Request и Response объектами. Это позволит разработчикам писать переиспользуемый, независимый от фреймворка код. Спецификация данного стандарта достаточно объемна:
MessageInterface, RequestInterface, ServerRequestInterface, ResponseInterface, StreamInterface, UriInterface и UploadedFileInterface
interface MessageInterface
interface RequestInterface extends MessageInterface
interface ServerRequestInterface extends RequestInterface
interface ResponseInterface extends MessageInterface
interface StreamInterface
interface UploadedFileInterface
А более детальное описание с примерами можно разобрать в статье «PSR-7 в примерах».
interface RequestHandlerInterface
interface MiddlewareInterface
Если не вдаваться во все тонкости, то по сути это возможность писать некие абстрактные контроллеры для последующего переиспользования между различными проектами.
PSR-7 не содержит рекомендации о том, как создавать HTTP-объекты. Это может приводить к трудностям при необходимости их создания внутри компонентов, которые не привязаны к конкретной реализации PSR-7.
Интерфейсы, описанные в этой спецификации, описывают методы, с помощью которых можно создавать PSR-7 объекты.
RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface и UriFactoryInterface
interface RequestFactoryInterface
interface ResponseFactoryInterface
interface ServerRequestFactoryInterface
interface StreamFactoryInterface
interface UploadedFileFactoryInterface
interface UriFactoryInterface
interface ClientInterface
interface RequestExceptionInterface extends ClientExceptionInterface
interface NetworkExceptionInterface extends ClientExceptionInterface
Это может сделать библиотеки более пригодными для повторного использования, так как уменьшает количество зависимостей и снижает вероятность конфликтов версий.
Также в спецификации указано, что HTTP-клиенты могут быть заменены согласно принципу подстановки Лисков. Это означает, что все клиенты ДОЛЖНЫ вести себя одинаково при отправке запроса.
На практике, конечно все на много сложнее и есть свои нюансы и подводные камни, однако PHP-FIG делает значительный шаг вперед в этом направлении.
Стиль кодирования
До появления стандартов стиля кодирования, каждый из разработчиков оформлял свой код по-разному: одни писали CLASSNAME, другие ClassName, а третьи Class_Name, вечный спор относительно табов и пробелов, а еще StudlyCaps vs сamelCase vs snake_case и так далее.
Цель следующих PSR стандартов уменьшить когнитивное искажение при чтении кода от разных авторов.
- PSR-1: Basic Coding Standard
PSR-2 — Coding Style GuideУстарел- PSR-12: Extended Coding Style Guide
- Использование только тэгов
- Только UTF-8 без BOM для php кода
- Не стоит мешать разный функционал в одном файле (1 файл = 1 класс)
- Пространство имен и классы должны следовать [
PSR-0, PSR-4] - Классы объявляются в `StudlyCase`
- Константы объявляются в ТАКОМ_ВИДЕ
- Методы объявляются в `camelCase`
На самом деле, нет смысла помнить про кажный пункт о переносе скобки, пробеле, табе и т.п., так как существует различный функционал, который позволяет автоматически проверить и отформатировать кодовую базу по стандарту PSR-2/PSR-12:
- Ручной режим: можно использовать reformat code в phpStorm.
- Более продвинутый вариант: использовать какой-нибуть кодснифер, например PHP CS Fixer (часто используется в CI, чтобы не принимать коммиты с неотформатированным кодом).
А для достижения максимального профита можно расширить стандарты стиля кодирования руководством по написанию чистого кода.
Преимущества использования PSR
Сама аббревиатура PSR вышла за рамки простых рекомендаций относительно стиля кодирования. А группа PHP-FIG предлагает стандарты, которые предоставляют действительно мощную абстракцию для решения базовых проблем с переиспользованием различных инструментов и позволяет сделать код более независимым от конкретных реализаций и фреймворков (на практике все куда сложнее, но фундамент уже заложен).
Для демонстрации хорошей абстракции рассмотрим установку middleware framework Mezzio:
Стандартизация в виде интерфейсов предоставляет возможность выбора любой имплементированной реализации. Также можно существенно облегчить написание документации ссылаясь на хорошо описанные PSR спецификации.
И в заключение
PSR — это действительно годные стандарты, которые созданы для того, чтобы решать проблемы переиспользования кода и облегчить совместную разработку. Однако использование PSR остается рекомендацией, поэтому Вы можете использовать как отдельные стандарты, так и не использовать их вообще.
Лично я рекомендую использовать и делиться своим опытом с сообществом PHP.
Спасибо, что дочитали до конца!