Когда пишут ООП-код, каждый класс помещают в отдельный файл. Класс может использовать какие-то другие классы. В примере ниже класс A ссылается на классы SomeClass и OtherClass. Если класс SomeClass не известен PHP, то на строчке class ... extends ... интерпретатор выдаст ошибку. Аналогично, ошибка будет выдана, если мы вызвали метод test(), но PHP в этот момент не знает о классе OtherClass:

class A extends SomeClass
{
    public function test()
    {
        $o = new OtherClass;
        // ...
    }
}

Чтобы код не выдал ошибку, мы должны в начале файла подключить (выполнить) файлы, где описаны классы SomeClass и OtherClass. Выглядит это примерно так:

// встроенная в PHP константа __DIR__ содержит путь к папке с этим файлом
require_once __DIR__ . '/Some/Class.php';
require_once __DIR__ . '/Some/Other/Class.php';

class A extends SomeClass
{ 
...

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


Автозагрузка

PHP позволяет зарегистрировать функцию-автозагрузчик с помощью функции spl_autoload_register. Можно зарегистрировать любое число автозагрузчиков. В случае обращения к несуществующему в данный момент классу (о котором PHP ничего не знает), PHP будет вызывать по очереди все зарегистрированные автозагрузчики, передавая им имя класса. Если автозагрузчик знает, где лежит этот класс, он должен подключить файл с ним, PHP увидит, что класс появился, и продолжит выполнение программы. Иначе PHP вызовет следующий автозагрузчик. Если ни один автозагрузчик не подключит файл с классом, то будет выведена ошибка об обращении к несуществующему классу.

Вот примеры кода, который вызовет срабатывание автозагрузки:

// Будут вызваны все автозагрузчики для загрузки класса A
// Если ни один из них не найдет класс, то произойдет ошибка и 
// выполнение скрипта прервется
$a = new A; 

// Будут вызваны автозагрузчики для загрузки класса B
B::someMethod();

// Будут вызваны автозагрузчики для загрузки класса D
class C extends D 
{
}

// Здесь тоже будут вызваны автозагрузчики для проверки, существует такой
// класс или нет, но даже если класс не будет найден, ошибки не произойдет, 
// просто class_exists вернет false и код внутри if не выполнится
if (class_exists('E')) {
// ...

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

function test(F $f) { } 

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

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

Функция-автозагрузчик имеет такой вид:

function ($className) {
    // ...
}

Она получает от PHP только имя класса. Для того, чтобы по имени класса можно было понять какой файл надо подключить, обычно используют либо заранее подготовленный список соответствия классов и файлов, либо придумывают договоренность, по которой из имени класса можно получить имя файла. Например, файлы называют так же, как и класс, с точностью до регистра букв, и кладут в заранее оговоренную папку: class SomeClass описывается в файле SomeClass.php. Одна из таких договоренностей называется PSR-4, о ней ниже.

Запомнить:

  • Автозагрузчик не должен выдавать ошибку, если он не может найти файл с классом - может быть, этот класс подгрузит следующий автозагрузчик;
  • Пиши автозагрузчик только для своих файлов и не пытайся подгружать классы от сторонних библиотек - они сами об этом позаботятся;
  • Не используй функцию __autoload(), она устарела много лет назад (например, она не позволяет сделать больше одного автозагрузчика);
  • В Линуксе регистр букв в именах файлов имеет значение;
  • В PHP уже есть готовая реализация функции-автозагрузчика spl_autoload;
  • Не изобретай свои правила сопоставления имен классов и файлов, а используй общепринятый стандарт PSR-4 (о нем ниже);
  • Вообще, автозагрузчик не обязан создавать класс именно за счет подключения файла, он может создать класс каким-то другим способом, например: искать код класса в базе данных, скачивать его с гитхаба, генерировать и выполнять с помощью конструкции eval. Но не стоит так делать, так как скорее всего твой код будет медленно работать и в нем потом будет очень сложно разобраться;

Пример функции-автозагрузчика:

spl_autoload_register(function ($class) {
    // Получаем путь к файлу из имени класса
    $path = __DIR__ . $class . '.php';

    // Если в текущей папке есть такой файл, то выполняем код из него
    if (file_exists($path)) {
        require_once $path;
    }

    // Если файла нет, то ничего не делаем - может быть, класс 
    // загрузит какой-то другой автозагрузчик или может быть, 
    // такого класса нет
});

Неймспейсы (пространства имен)

Классы можно помещать в так называемые пространства имен (неймспейсы), иначе говоря, полное имя класса может состоять из нескольких частей, которые разделены бекслешем \ (не путать с прямым слешем /). Например: в полном названии Megasoft\Megacms\SomeClass слово SomeClass - это короткое название класса, а Megasoft\Megacms - это пространство имен, в котором он находится. Уникальным должно быть только полное имя класса, а короткие названия могут совпадать, например: SomeLibrary\User и OtherLibrary\User.

Если в полном названии класса нет пространства имен, то полное название совпадает с коротким и говорят, что класс находится в корневом неймспейсе (например: ExampleClass).

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

Пространства имен позволяют записывать длинные имена классов в более удобном виде. Длинные имена появляются в коде по 2 причинам:

1. Борьба с конфликтом имен. Представьте, что Вася и Маша пишут каждый свою библиотеку и оба решают написать в них класс с одинаковым названием, например User. Или функцию getUser(). Если мы попробуем подключить в проекте обе этих библиотеки, произойдет ошибка, так как нельзя создать два класса с одинаковыми именами. Чтобы избежать таких конфликтов, нужно, чтобы имена классов были уникальными, потому библиотеки обычно приписывают к классам и функциям свое название в начало:

Masha_User
mashaGetUser()
Vasya_User
vasyaGetUser()
Zend_Mail
sfController

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

2. Большие библиотеки и фреймворки состоят из большого числа классов (сотни и тысячи). Чтобы не запутаться, эти классы собирают в отдельные модули и добавляют имя модуля в начало имени класса. При этом сами модули могут быть дополнительно разделены на подмодули. В итоге получаются длинные имена классов:

- Zend_Db_Table_Row_Abstract - это класс фреймворка Zend Framework 1, модуль Db, подмодуль Table, подмодуль Row;
- sfDatabaseConfigHandler - класс фреймворка Symfony1, модуль Database, подмодуль Config, класс Handler;

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

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

Symfony\Component\Routing\Router
Doctrine\ORM\Mapping\Id
Masha\User
Vasya\User

Однако, писать полное имя в конструкции class X нельзя. Чтобы поместить класс в неймспейс, мы должны указать в начале файла с ним название неймспейса с помощью ключевого слова namespace. Неймспейс задается для всего файла сразу (это не проблема, так как мы обычно описываем в одном файле только один класс), и относится ко всем упомянутым в нем именам классов:

// Задаем пространство имен для этого файла
namespace MegaLibrary\Database;

// Указываем в конструкции class короткое имя класса
// Этот класс получит полное имя MegaLibrary\Database\User
class User 
{ 
} 

// PHP попытается создать объект класса MegaLibrary\Database\SomeClass
$x = new SomeClass;

Если в этом файле указаны другие имена классов, то PHP припишет название неймспейса перед ними: SomeClass превратится в MegaLibrary\Database\SomeClass, а название Connector\Db\Mysql будет воспринято как указание на класс MegaLibrary\Database\Connector\Db\Mysql.

Если мы хотим использовать класс из другого неймспейса, то мы должны либо указать его полное имя, добавив перед ним бекслеш (например $x = new \Other\Class(...) или $y = new \Example), либо задать для него синоним с помощью специальной конструкции use, которая пишется в начале файла. Вот пример конструкции use:

namespace MegaLibrary\Database;
// в этом файле Router - это синоним для длинного имени класса
use Symfony\Component\Routing\Router as Router;

$router = new Router();

Эта строчка говорит о том, что далее в коде название Router будет обозначать класс Symfony\Component\Routing\Router (иногда это еще называют "импортируем класс Router"). Если выбранный синоним совпадает с коротким названием класса, то конструкцию as можно не писать, и код получается еще проще:

namespace MegaLibrary\Database;
use Symfony\Component\Routing\Router;

$router = new Router();

Конструкция use ... as позволяет задать произвольный синоним для класса. Эта возможность используется, когда надо подключить классы с одинаковыми короткими именами из разных неймспейсов:

// Объявляем, что имя User будет обозначать класс \Some\Library\User
use Some\Library\User;
// Объявляем, что имя OtherUser будет обозначать класс \Other\User
use Other\User as OtherUser; 

$user1 = new User;
$user2 = new OtherUser;

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

Часто неймспейс составляется из названия библиотеки или приложения, к которому добавляется группа или модуль, к которому относится класс. Неймспейс может состоять из произвольного числа частей, но на практике больше 2-3 уровней вложенности (не считая короткого имени) не требуется. Например:

  • Drupal\Core\Cache\MemoryBackend - приложение Drupal, модуль Core, подмодуль Cache, класс MemoryBackend;
  • Symfony\Component\Routing\Router - класс Router в фреймворке Symfony, в компоненте роутинга;
  • Hashids\Hashids - основной класс Hashids из библиотеки с таким же именем;

Также, иногда главный класс может помещаться в корневой неймспейс, и обычно его название совпадает с названием приложения (чтобы гарантировать уникальность имен). Например, в Друпале есть класс Drupal.

Если не указывать в начале файла неймспейс, то твои классы создаются в глобальном, или корневом неймспейсе (то есть класс SomeClass получает полное имя SomeClass).

Повторим особенности неймспейсов:

  • Имена неймспейсов принято начинать с большой буквы;
  • Слова namespace и use пишутся в самом начале файла и действуют только в пределах этого файла. Они не влияют на другие файлы, которые могут вызываться из текущего;
  • namespace задает пространство имен для файла;
  • use задает короткий синоним для длинного имени класса;
  • В коде можно использовать полные имена классов, но перед ними надо поставить дополнительный бекслеш, чтобы указать, что к ним не надо приписывать текущий неймспейс;
  • Ради читаемости после слов namespace и группы слов use оставляют одну пустую строчку;
  • Слово use не вызывает срабатывания автозагрузки и даже не проверяет, существует ли указанный в нем класс. Эта конструкция просто задает короткий синоним для длинного имени класса;

Современные IDE (вроде PhpStorm, Eclipse PDT или Netbeans) умеют вставлять в файл строку use автоматически при использовании короткого имени класса. Не требуется писать её руками. Некоторые IDE также умеют автоматически вставлять строку namespace, определяя неймспейс по папкам, в которых хранится файл.

Хотя это редко используется, но функции и константы так же могут помещаться в неймспейсы и иметь длинные имена. Начиная с PHP5.6, их стало можно импортировать (создавать короткие синонимы) с помощью конструкций use function \Namespace\functionName и use const \Namespace\CONSTANT.

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


PSR-0 - Стандарт автозагрузки

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

- Обязательные требования:

  • Полностью определённое пространство имён и имя класса должны иметь следующую структуру: \<Vendor Name>\(<Namespace>\)*<Class Name>.
  • Каждое пространство имён должно начинаться с пространства имён высшего уровня, указывающего на разработчика кода.
  • Каждое пространство имён может включать в себя неограниченное количество вложенных подпространств имён.
  • Каждый разделитель пространства имён при обращении к файловой системе преобразуется в РАЗДЕЛИТЕЛЬ_ИМЁН_КАТАЛОГОВ.
  • Каждый символ _ в ИМЕНИ_КЛАССА преобразуется в РАЗДЕЛИТЕЛЬ_ИМЁН_КАТАЛОГОВ. При этом символ _ не обладает никаким особенным значением в имени пространства имён (и не претерпевает преобразований).
  • При обращении к файловой системе полностью определённое пространство имён и имя класса дополняются суффиксом .php.
  • В имени производителя, имени пространства имён и имени класса допускается использование буквенных символов в любых комбинациях нижнего и верхнего регистров.

- Примеры:

  • \Doctrine\Common\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
  • \Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php
  • \Zend\Acl => /path/to/project/lib/vendor/Zend/Acl.php
  • \Zend\Mail\Message => /path/to/project/lib/vendor/Zend/Mail/Message.php

- Знак подчёркивания в именах пространств имён и классов:

  • \namespace\package\Class_Name => /path/to/project/lib/vendor/namespace/package/Class/Name.php
  • \namespace\package_name\Class_Name => /path/to/project/lib/vendor/namespace/package_name/Class/Name.php

PSR-1 - Базовый стандарт оформления кода

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

1. Общие положения

  • В файлах НЕОБХОДИМО использовать только теги <?php и <?=.
  • Файлы НЕОБХОДИМО представлять только в кодировке UTF-8 без BOM-байта.
  • В файлах СЛЕДУЕТ либо объявлять структуры (классы, функции, константы и т.п.), либо генерировать побочные эффекты (выполнять действия) (например: передавать данные в выходной поток, модифицировать настройки и т.п.), но НЕ СЛЕДУЕТ делать одновременно и то, и другое.
  • Имена пространств имён и имена классов ДОЛЖНЫ следовать стандарту PSR-0.
  • Имена классов ДОЛЖНЫ быть объявлены с использованием т.н. "StudlyCaps" (каждое слово начинается с большой буквы, между словами нет разделителей).
  • Константы классов ДОЛЖНЫ быть объявлены исключительно в верхнем регистре с использованием символа подчёркивания для разделения слов.
  • Имена методов ДОЛЖНЫ быть объявлены с использованием т.н. "camelCase" (первое слово пишется в нижнем регистре, далее каждое слово начинается с большой буквы, а между словами нет разделителей).

2. Файлы

2.1. PHP-теги

PHP-код ОБЯЗАТЕЛЬНО следует заключать в полную версию (<?php ?>) тегов или укороченную (сокращённую запись echo) версию (<?= ?>) тегов и НЕДОПУСТИМО заключать ни в какие иные разновидности тегов.

2.2. Кодировка символов

PHP-код ДОЛЖЕН быть представлен только в кодировке UTF-8 без BOM-байта.

2.3. Побочные эффекты

В файлах СЛЕДУЕТ либо объявлять структуры (классы, функции, константы и т.п.) и не создавать побочных эффектов (например: передавать данные в выходной поток, модифицировать настройки и т.п.), либо реализовывать логику, порождающую побочные эффекты, но НЕ СЛЕДУЕТ делать одновременно и то, и другое.

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

"Побочные эффекты" включают (но не ограничиваются этим перечнем): передачу данных в выходной поток, явное использование require или include, изменение настроек, генерирование ошибочных ситуаций или порождение исключений, изменение глобальных или локальных переменных, чтение из файла или запись в файл и т.п.

3. Имена пространств имён и имена классов

Имена пространств имён и имена классов ДОЛЖНЫ следовать стандарту PSR-0. В конечном итоге это означает, что каждый класс должен располагаться в отдельном файле и в пространстве имён с хотя бы одним верхним уровнем (именем производителя).

Имена классов ДОЛЖНЫ быть объявлены с использованием т.н. "StudlyCaps" (каждое слово начинается с большой буквы, между словами нет разделителей).

4. Константы, свойства и методы классов

Здесь под "классом" следует понимать также интерфейсы (interface) и примеси (trait).

4.1. Константы

Константы классов ДОЛЖНЫ быть объявлены в верхнем регистре с использованием символа подчёркивания в качестве разделителя слов.

4.2. Свойства

В данном руководстве намеренно не приводится никаких рекомендаций относительно использования $StudlyCaps, $camelCase или $under_score вариантов именования свойств.

Какой бы вариант именования ни был выбран, СЛЕДУЕТ сохранять его неизменным в рамках некоторого разумного объёма кода (например, на уровне производителя, пакета, класса или метода).

4.3. Методы

Имена методов ДОЛЖНЫ быть объявлены с использованием т.н. "camelCase" (первое слово пишется в нижнем регистре, далее каждое слово начинается с большой буквы, а между словами нет разделителей).


PSR-2 - Рекомендации по оформлению кода

Данные рекомендации расширяют и дополняют базовый стандарт оформления кода PSR-1.

Цель данных рекомендаций - снижение сложности восприятия кода, написанного разными авторами; она достигается путём рассмотрения серии правил и ожиданий относительно форматирования PHP-кода.

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

1. Общие положения

  • Код ДОЛЖЕН быть оформлен согласно стандарту PSR-1.
  • Для оформления отступов ДОЛЖНЫ использоваться четыре пробела (но не знак табуляции).
  • НЕДОПУСТИМО жёстко ограничивать длину строки; мягкое ограничение ДОЛЖНО составлять 120 символов; СЛЕДУЕТ стараться, чтобы длина строки составляла 80 символов или менее.
  • После определения пространства имён (namespace) и после блока импорта пространств имён (use) ДОЛЖНА быть одна пустая строка.
  • Открывающая фигурная скобка в определении класса ДОЛЖНА располагаться на новой строке, а закрывающая фигурная скобка ДОЛЖНА располагаться на следующей строке после тела класса.
  • Открывающая фигурная скобка в определении метода ДОЛЖНА располагаться на новой строке, а закрывающая фигурная скобка ДОЛЖНА располагаться на следующей строке после тела метода.
  • Область видимости ДОЛЖНА быть указана явно для всех свойств и методов; модификаторы abstract и final ДОЛЖНЫ располагаться перед модификаторами области видимости; модификатор static ДОЛЖЕН располагаться после модификаторов области видимости.
  • После ключевых слов в управляющих конструкциях ДОЛЖЕН располагаться один пробел, а после вызовов функций и методов - НЕ ДОЛЖЕН.
  • Открывающая фигурная скобка в управляющих конструкциях ДОЛЖНА располагаться в той же строке, что и сама конструкция, а закрывающая фигурная скобка ДОЛЖНА располагаться на следующей строке после тела конструкции.
  • После открывающей круглой скобки и перед закрывающей круглой скобкой в управляющих конструкциях НЕ ДОЛЖНО быть пробела.

2. Основные положения

2.1. Базовый стандарт оформления кода

Код ДОЛЖЕН быть оформлен согласно всем правилам, указанным в стандарте PSR-1.

2.2. Файлы

  • Во всех файлах с PHP-кодом ДОЛЖЕН быть использован Unix-вариант переноса строк (Unix linefeed, т.е. \n).
  • В конце каждого файла с PHP-кодом ДОЛЖНА быть одна пустая строка.
  • Закрывающий тег ?> ДОЛЖЕН отсутствовать в файлах, содержащих только PHP-код.

2.3. Строки

  • НЕ ДОЛЖНО быть жёсткого ограничения длины строки.
  • Мягкое ограничение длины строки ДОЛЖНО составлять 120 символов; автоматические системы проверки стиля ДОЛЖНЫ выдавать предупреждение при превышении этого ограничения, но НЕ ДОЛЖНЫ считать это ошибочной ситуацией.
  • СЛЕДУЕТ стараться, чтобы длина строки составляла 80 символов или менее; более длинные строки СЛЕДУЕТ разбивать на несколько отдельных строк, длина каждой из которых не превышала бы 80 символов.
  • В конце непустых строк НЕ ДОЛЖНО быть пробелов.
  • Пустые строки МОГУТ быть добавлены в код для повышения удобочитаемости и разделения блоков кода.
  • В одной строке НЕ ДОЛЖНО быть более одного выражения.

2.4. Отступы

Для оформления отступов ДОЛЖНЫ использоваться четыре пробела (но не знак табуляции).

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

2.5. Ключевые слова и константы true / false / null

  • Ключевые слова PHP ДОЛЖНЫ быть написаны в нижнем регистре.
  • Константы PHP true, false и null ДОЛЖНЫ быть написаны в нижнем регистре.

3. Определение пространств имён и блоков импорта

  • В случае наличия определения пространства имён, после него ДОЛЖНА располагаться одна пустая строка.
  • В случае наличия импорта пространств имён, он ДОЛЖЕН располагаться после определения пространства имён.
  • При реализации импорта каждое пространство имён ДОЛЖНО импортироваться отдельно (со своим ключевым словом use).
  • После блока импорта ДОЛЖНА быть одна пустая строка.

4. Классы, свойства и методы

Здесь под "классом" следует понимать также интерфейсы (interface) и примеси (trait).

4.1. Наследование и реализация

  • Ключевые слова extends и implements ДОЛЖНЫ находиться на той же строке, на которой находится имя класса.
  • Открывающая фигурная скобка в определении класса ДОЛЖНА располагаться на новой строке, а закрывающая фигурная скобка ДОЛЖНА располагаться на следующей строке после тела класса.
  • Список реализуемых интерфейсов МОЖЕТ быть разделён на несколько строк, каждая из которых дополнена слева одним отступом (четырьмя пробелами). В таком случае первый элемент списка интерфейсов ДОЛЖЕН начинаться с новой строки, и в каждой строке ДОЛЖЕН быть указан только один интерфейс.

4.2. Свойства

  • Область видимости ДОЛЖНА быть явно указана для каждого свойства.
  • При определении свойства НЕ ДОЛЖНО применяться ключевое слово var.
  • В одном выражении НЕ ДОЛЖНО быть определено более одного свойства.
  • Одиночный знак подчёркивания в начале имени свойства НЕ СЛЕДУЕТ использовать как признак защищённой (protected) или приватной (private) области видимости.

4.3. Методы

  • Область видимости ДОЛЖНА быть явно указана для каждого метода.
  • Одиночный знак подчёркивания в начале имени метода НЕ СЛЕДУЕТ использовать как признак защищённой (protected) или приватной (private) области видимости.
  • После имени метода НЕ ДОЛЖНО быть пробела. Открывающая фигурная скобка ДОЛЖНА находиться на отдельной строке, а закрывающая фигурная скобка ДОЛЖНА находиться на следующей за телом метода строке. НЕ ДОЛЖНО быть пробелов после открывающей и перед закрывающей круглыми скобками в определении метода.

4.4. Аргументы методов

  • В списке аргументов НЕ ДОЛЖНО быть пробела перед запятыми, но ДОЛЖЕН быть пробел после каждой запятой.
  • Аргументы со значениями по умолчанию ДОЛЖНЫ располагаться в конце списка (после аргументов без значений по умолчанию).
  • Список аргументов МОЖЕТ быть разделён на несколько строк, каждая из которых дополнена слева одним отступом (четырьмя пробелами). В таком случае первый элемент списка аргументов ДОЛЖЕН начинаться с новой строки, и в каждой строке ДОЛЖЕН быть указан только один аргумент.
  • В случае, если список аргументов разделён на несколько строк, закрывающая круглая скобка и открывающая фигурная скобка ДОЛЖНЫ располагаться вместе на своей отдельной строке, а между ними должен быть один пробел.

4.5. Ключевые слова abstract, final и static

  • Ключевые слова abstract и final, в случае их наличия, ДОЛЖНЫ располагаться перед указанием области видимости.
  • Ключевое слово static, в случае его наличия, ДОЛЖНО располагаться после указания области видимости.

4.6. Вызовы методов и функций

  • В коде вызова функций и методов НЕ ДОЛЖНО быть пробела между именем функции или метода и открывающей круглой скобкой, НЕ ДОЛЖНО быть пробела после открывающей круглой скобки, НЕ ДОЛЖНО быть пробела перед закрывающей круглой скобкой. В списке аргументов НЕ ДОЛЖНО быть пробелов перед запятыми, но ДОЛЖЕН быть пробел после каждой запятой.
  • Список аргументов МОЖЕТ быть разделён на несколько строк, каждая из которых дополнена слева одним отступом (четырьмя пробелами). В таком случае первый элемент списка аргументов ДОЛЖЕН начинаться с новой строки, и в каждой строке ДОЛЖЕН быть указан только один аргумент.

5. Управляющие конструкции

Общие правила оформления управляющих конструкций:

  • После ключевого слова, определяющего управляющую конструкцию, ДОЛЖЕН быть один пробел.
  • После открывающих круглых скобок НЕ ДОЛЖНО быть пробелов.
  • Перед закрывающими круглыми скобками НЕ ДОЛЖНО быть пробелов.
  • Между закрывающей круглой скобкой и открывающей фигурной скобкой ДОЛЖЕН быть один пробел.
  • Тело конструкции ДОЛЖНО быть дополнено одним отступом (четырьмя пробелами).
  • Закрывающая фигурная скобка ДОЛЖНА располагаться на следующей строке после тела конструкции.

Тело каждой управляющей конструкции ДОЛЖНО быть заключено в фигурные скобки. Это позволяет стандартизировать внешний вид управляющих конструкций с снизить риск возникновения ошибок при добавлении новых строк в тело конструкции.

5.1. Конструкции if, elseif и else

Конструкция if выглядит следующим образом. Обратите внимание на круглые скобки, пробелы и фигурные скобки, а также на тот факт, что слова else и elseif располагаются в той же строке, что и закрывающая фигурная скобка предшествующего тела конструкции. Ключевое слово elseif СЛЕДУЕТ использовать вместо отдельного сочетания else и if. Так конструкция будет представлять собой одно слово.

if ($expr1) {
    // тело if
} elseif ($expr2) {
    // тело elseif
} else {
    // тело else
}

5.2. Конструкции switch и case

Конструкция switch выглядит следующим образом. Обратите внимание на круглые скобки, пробелы и фигурные скобки. Выражение case ДОЛЖНО быть смещено на один отступ (четыре пробела) от switch, а ключевое слово break (или иное слово, обозначающее выход из конструкции) ДОЛЖНО располагаться на том же уровне отступов, что и тело case. В том случае, когда в непустом теле case умышленно не используется break, ДОЛЖЕН быть комментарий в стиле // no break.

switch ($expr) {
    case 0:
        echo 'First case, with a break';
        break;
    case 1:
        echo 'Second case, which falls through';
        // no break
    case 2:
    case 3:
    case 4:
        echo 'Third case, return instead of break';
        return;
    default:
        echo 'Default case';
        break;
}

5.3. Конструкции while и do while

Конструкция while выглядит следующим образом. Обратите внимание на круглые скобки, пробелы и фигурные скобки.

while ($expr) {
    // тело конструкции
}

Соответственно, конструкция do while выглядит следующим образом. Обратите внимание на круглые скобки, пробелы и фигурные скобки.

do {
    // тело конструкции
} while ($expr);

5.4. Конструкция for

Конструкция for выглядит следующим образом. Обратите внимание на круглые скобки, пробелы и фигурные скобки.

for ($i = 0; $i < 10; $i++) {
    // тело for
}

5.5. Конструкция foreach

Конструкция foreach выглядит следующим образом. Обратите внимание на круглые скобки, пробелы и фигурные скобки.

foreach ($iterable as $key => $value) {
    // тело foreach
}

5.6. Конструкция try catch

Блоки конструкции try catch выглядят следующим образом. Обратите внимание на круглые скобки, пробелы и фигурные скобки.

try {
    // тело try
} catch (FirstExceptionType $e) {
    // тело catch
} catch (OtherExceptionType $e) {
    // тело catch
}

6. Замыкания

  • Замыкания ДОЛЖНЫ описываться с использованием пробела после ключевого слова function и пробелами до и после ключевого слова use.
  • Открывающая фигурная скобка ДОЛЖНА располагаться на одной строке с именем замыкания строке, а закрывающая фигурная скобка ДОЛЖНА располагаться на следующей строке после тела замыкания.
  • После открывающей круглой скобки и перед закрывающей круглой скобкой в списке аргументов или переменных НЕ ДОЛЖНО быть пробела.
  • В списке аргументов или переменных НЕ ДОЛЖНО быть пробелов перед запятыми, но ДОЛЖЕН быть один пробел после каждой запятой.
  • Аргументы замыкания со значениями по умолчанию ДОЛЖНЫ располагаться в конце списка (после аргументов без значений по умолчанию).

Описание замыкания выглядит следующим образом. Обратите внимание на круглые скобки, запятые, пробелы и фигурные скобки.

$closureWithArgs = function ($arg1, $arg2) {
    // тело
};
 
$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // тело
};

7. Заключение

В этом руководстве намеренно не рассмотрены правила и лучшие практики по оформлению многих элементов, список которых включает в себя, но не ограничивается следующим:

  • Определение глобальных переменных и констант.
  • Определение функций.
  • Использование операторов и присваивание.
  • Межстрочное выравнивание.
  • Блоки комментариев и документации.
  • Префиксы и суффиксы в именах классов.
  • Лучшие практики.

В будущем данные рекомендации МОГУТ быть пересмотрены и расширены, чтобы охватить те или иные элементы кода и практики оформления.


PSR-3 - Интерфейс протоколирования

Данный документ описывает общий интерфейс библиотек протоколирования.

Основная цель данного документа - позволить библиотекам получать объект Psr\Log\LoggerInterface и использовать его простым и универсальным образом для реализации протоколирования. В случае, если некий фреймворк или CMS нуждается в расширенном функционале, МОЖНО расширять данный интерфейс, но СЛЕДУЕТ сохранять совместимость с описанными в данном документе правилами. Это позволит сторонним библиотекам, применяемым при разработке приложения, использовать централизованную систему протоколирования.

1. Спецификации

1.1 Основы

Интерфейс LoggerInterface предоставляет восемь методов протоколирования, соответствующих восьми уровням протоколирования, описанным в RFC 5424 (отладка (debug), информация (info), замечание (notice), предупреждение (warning), ошибка (error), критическая ошибка (critical), тревога (alert), авария (emergency)).

Девятый метод, "протокол" (log) принимает в качестве первого аргумента уровень протоколирования. Вызов этого метода с передачей константы одного из уровней протоколирования ДОЛЖЕН приводить к тому же результату, что и вызов соответствующего переданному уровню протоколирования специального метода.

Вызов этого метода с передачей уровня протоколирования, не описанного в данной спецификации, ДОЛЖЕН приводить к порождению исключения Psr\Log\InvalidArgumentException в случае, если конкретная реализация системы протоколирования не поддерживает переданный уровень протоколирования.

Пользователям НЕ СЛЕДУЕТ использовать собственные уровни протоколирования без полной уверенности в том, что конкретная реализация системы протоколирования их поддерживает.

1.2 Сообщения

  • Каждый метод протоколирования должен принимать строку-сообщение или объект с методом __toString(). Разработчики МОГУТ использовать специальные обработчики переданных объектов, но если этого не сделано, объект ДОЛЖЕН быть приведён к строке.
  • Строка-сообщение МОЖЕТ содержать плейсхолдеры, которые МОГУТ быть заменены на конкретные значения из массива context.
  • Имена плейсхолдеров ДОЛЖНЫ совпадать со значениями ключей массива context.
  • Плейсхолдеры ДОЛЖНЫ быть заключены в одиночные фигурные скобки, при этом НЕ ДОЛЖНО быть пробелов между фигурными скобками и именем плейсхолдера.
  • Имена плейсхолдеров НУЖНО составлять только из символов A-Z, a-z, 0-9, знак подчёркивания (_) и точка (.). Остальные символы зарезервированы для будущих изменений в спецификации плейсхолдеров.
  • Разработчики МОГУТ реализовывать со значениями плейсхолдеров различные стратегии экранирования и преобразования при отображении протокола. Пользователям НЕ НУЖНО предварительно экранировать данные в значениях плейсхолдеров, т.к. заранее не известно, как и в каком контексте содержащаяся в них информация может быть использована.

1.3 Контекст

  • Каждый метод получает массив сопутствующих данных (context), содержащих дополнительную информацию, представление которой в виде строки не является оптимальным. На содержимое массива не налагается никаких ограничений. Разработчики ДОЛЖНЫ обрабатывать данные массива context максимально гибко. Переданные в массиве context данные НЕ ДОЛЖНЫ порождать исключений, вызывать сообщений об ошибках, предупреждений или замечаний от интерпретатора PHP.
  • Если в массив context передан объект исключения, он ДОЛЖЕН находиться в элементе с ключом exception. Протоколирование исключений является распространённой практикой и позволяет разработчикам извлекать данные трассировки стека, если система протоколирования поддерживает такую функциональность. Разработчики ДОЛЖНЫ удостовериться, что в элементе с ключом exception на самом деле находится объект исключения, т.к. в реальности там МОЖЕТ оказаться что угодно.

1.4 Вспомогательные классы и интерфейсы

  • Класс Psr\Log\AbstractLogger позволяет очень легко реализовать интерфейс LoggerInterface - достаточно создать свой класс-наследник и реализовать там метод log. Остальные восемь методов будут передавать сообщения и контекст в этот метод.
  • Аналогично, использование Psr\Log\LoggerTrait требует всего лишь реализовать метод log. Однако в связи с тем, что примеси не могут реализовывать интерфейсы, вам всё равно придётся реализовать LoggerInterface.
  • Класс Psr\Log\NullLogger, поставляемый с соответствующим интерфейсом, МОЖЕТ быть использован для реализации "записи протокола в пустоту", однако условное протоколирование может оказаться лучшим решением в случае, если создание контекстной информации является затратной операцией.
  • Интерфейс Psr\Log\LoggerAwareInterface содержит только метод setLogger(LoggerInterface $logger) и может быть использован для автоматического связывания необходимых сущностей с системой протоколирования.
  • Примесь Psr\Log\LoggerAwareTrait может быть легко использована для реализации соответствующего интерфейса. Она предоставляет доступ к $this->logger.
  • Класс Psr\Log\LogLevel содержит константы восьми уровней протоколирования.

PSR-4 - Улучшенная автозагрузка

1. Обзор

Данный стандарт описывает спецификацию автозагрузки классов на основе путей к файлам. Он полностью совместим (и может использоваться как дополнение) с любой другой спецификацией автозагрузки, включая PSR-0. Данный стандарт также описывает правила размещения файлов, предназначенных для автозагрузки.

2. Спецификация

  • Здесь под "классом" следует понимать также интерфейсы (interface), примеси (trait) и иные подобные конструкции.
  • Полностью определённое имя класса должно иметь следующую структуру: \<ПространствоИмён>(\<ПодпространствоИмён>)*\<ИмяКласса>.
  • Полностью определённое имя класса ДОЛЖНО начинаться с пространства имён высшего уровня, указывающего на разработчика кода ("имя производителя").
  • Полностью определённое имя класса МОЖЕТ включать в себя одно или более подпространств имён.
  • Полностью определённое имя класса ДОЛЖНО заканчиваться именем класса.
  • Символ _ (знак подчёркивания) не обладает никаким особенным значением в полностью определённом имени класса.
  • В полностью определённом имени класса МОЖНО использовать буквенные символы в любых комбинациях нижнего и верхнего регистров.
  • Все имена классов ДОЛЖНЫ быть использованы с соблюдением регистрочувствительности.

При загрузке файла, соответствующего полностью определённому имени класса, используются следующие правила:

  • Последовательность из одного и более пространств и подпространств имён (не включая ведущий разделитель пространств имён) в полностью определённом имени класса (т.н. префикс пространств имён) должна соответствовать хотя бы одному "базовому каталогу".
  • Последовательность подпространств имён после "префикса пространства имён" соответствует подкаталогу в "базовом каталоге", при этом разделители пространств имён \ соответствуют разделителям каталогов /. Имя подкаталога и имя подпространства имён ДОЛЖНЫ совпадать вплоть до регистра символов.
  • Имя класса, завершающее собой полностью определённое имя, соответствует имени файла с расширением .php. Имя файла и имя класса ДОЛЖНЫ совпадать вплоть до регистра символов.

В реализации автозагрузчика НЕДОПУСТИМО порождать исключения, ошибочные ситуации любого уровня и НЕ СЛЕДУЕТ возвращать какое бы то ни было значение.