Когда пишут ООП-код, каждый класс помещают в отдельный файл. Чтобы использовать эти классы, мы должны подключить файлы с ними. Выглядит это примерно так:
require_once __DIR__ . '/Some/Class.php';
require_once __DIR__ . '/Some/Other/Class.php';
(__DIR__
содержит путь к текущей папке). Когда число классов увеличивается, писать эти require становится неудобно. К счастью, в PHP есть решение проблемы — автозагрузка.
PHP позволяет тебе зарегистрировать функцию-автозагрузчик с помощью функции spl_autoload_register. В случае обращения к несуществующему в данный момент классу, PHP будет вызывать по очереди все зарегистрированные автозагрузчики, передавая им имя класса. Если автозагрузчик знает где лежит этот класс, он должен подключить файл с ним, PHP увидит что класс появился, и продолжит выполнение программы. Если ни один автозагрузчик не подключит класс, будет выведена ошибка о обращении к несуществующему классу.
Вот примеры кода, который вызовет срабатывание автозагрузки:
$a = new A;
B::someMethod();
// Это также вызовет попытку автозагрузки, но если класс не будет
// найден, ошибки не произойдет
if (class_exists('C')) {
// ..
Автозагрузчиков может быть несколько. Например, если ты подключаешь стороннюю библиотеку, то у она может зарегистрировать свой автозагрузчик для загрузки своих классов. Таким образом, каждая библиотека может устанавливать свои правила для поиска файлов со своими классами.
Функция-автозагрузчик имеет примерно такой вид:
function ($className) {
// ...
}
Она получает от PHP только имя класса. Для того, чтобы по имени класса можно было понять какой файл надо подключить, обычно используют договоренность. Например, файлы называют так же, как и класс, с точностью до регистра букв: class SomeClass
-> файл SomeClass.php
. Одна из таких договоренностей называется PSR-4, о ней ниже.
Помни:
- автозагрузчик не должен выдавать ошибку если он не может найти файл с классом - может быть, этот класс подгрузит другой автозагрузчик
- пиши автозагрузчик только для своих файлов и не пытайся подгружать классы от сторонних библиотек - они сами об этом позаботятся
- не используй функцию
__autoload()
, она устарела много лет назад (например, она не позволяет сделать больше одного автозагрузчика) - в линуксе регистр букв в именах файлов имеет значение
- в PHP уже есть готовая реализация функции-автозагрузчика spl_autoload
Пример функции-автозагрузчика:
spl_autoload_register(function ($class) {
// Получаем путь к файлу из имени класса
$path = __DIR__ . $class . '.php';
if (file_exists($path)) {
require_once $path;
}
});
Подробнее про автозагрузку можно почитать тут:
- мануал: http://php.net/manual/ru/function.spl-autoload-register.php
- http://victor.4devs.io/ru/php/classes-autoload-php.html
- http://habrahabr.ru/post/136761/
Классы можно помещать в так называемые пространства имен (неймспейсы), говоря иначе, полное имя класса может состоять из нескольких частей, которые разделены бекслешем \
(не путай с прямым слешем /
). Например: полное имя Megasoft\Megacms\SomeClass
, где Megasoft\Megamcs
- это пространство имен, а SomeClass
- краткое имя класса.
Неймспейсы, как и имена классов, принято писать с большой буквы.
Пространства имен решают проблему слишком длинных имен классов. Длинные имена появляются по 2 причинам:
- борьба с конфликтом имен. Представь что Вася и Петя каждый пишет свои библиотеки и оба решают использовать класс с одинаковым названием, например User. Или функцию getUser(). Если мы попробуем подключить в проекте обе этих библиотеки, будет ошибка, так как нельзя создать 2 класса с одинаковыми именами. Чтобы этого избежать, библиотеки приписывают к классам и функциям свое название в начало:
Petya_User
petyaGetUser()
Vasya_User
vasyaGetUser()
Zend_Mail
sfController
Если не приписывать в начало имя библиотеки, то конфликт обязательно будет, так как в мире тысячи библиотек и в каких-нибудь двух обязательно будут совпадающие названия.
- большие библиотеки и фреймворки состоят из вложенных друг в друга модулей, чтобы не было совпадений имен классов в разных модулях, они добавляют название модуля в имя класса и они получаются длинными:
Zend_Db_Table_Row_Abstract
— это класс фреймворка Zend Framework 1, модуль Db.sfDatabaseConfigHandler
— класс фреймворка Symfony1, модуль Database
Таким образом, имена классов получаются длинными. Это не очень удобно, так как они увеличивают длину строк и ухудшают читаемость кода, но без этого были бы конфликты.
Неймспейсы решают эту проблему тем, что дают возможность помещать класс (и функции) в неймспейсы произвольной глубины вложенности. Названия неймспейсов разделены бекслешем \
:
\Symfony\Routing\Router
\Doctrine\ORM\Mapping\Id
\Petya\User
\Vasya\User
И теперь мы можем либо писать в коде полное имя класса со всеми неймспейсами, либо создать синоним через специальную конструкцию use
(она должна идти в самом начале файла):
use \Symfony\Routing\Router;
и писать просто Router
. Таким образом мы получаем и защиту от совпадений имен классов, и возможность использовать короткие имена классов в коде за счет того, что неймспейс для класса указывается в конструкции use
. Конструкция use
действует только в пределах одного файла. Если нам надо использовать в файле 2 класса из разных неймспейсов с одинаковыми названиями, мы должны дать одному из них отличающеся короткое имя с помощью конструкции use ... as
:
use \Some\Library\User;
// так как синоним User уже использован, дадим этому классу другой синоним
use \Other\User as OtherUser;
По умолчанию, если ты не указываешь неймспейс, то твои классы создаются в глобальном неймспейсе (без добавления префиксов перед именем класса). Чтобы поместить свой класс в неймспейс, надо написать в начале файла с ним namespace \Some\Name\Space;
. Подробнее неймспейсы и правила работы с ними описаны в мануале: http://php.net/manual/ru/language.namespaces.php
Когда ты пишешь автозагрузчик, нужны какие-то правила, чтобы узнать по полному имени класса путь к файлу с ним. Желательно, чтобы правила были общие, чтобы каждый разработчик не придумывал свой велосипед.
Эту проблему (как назвать файл с классом) решает рекомендация PSR-4 (англ). Она советует называть файл в соответствие с кратким именем класса и класть его в папки, имена которых совпадают с неймспейсами. Например, класс
MyLibrary\A\B\C\D
нужно поместить в файл
MyLibrary/A/B/C/D.php
Также, если все наши классы находятся в одном неймспейсе (например MyLibrary), мы можем не создавать для него папку и класть файл с классом в A/B/C/D.php
Сам PHP не требует чтобы имя класса совпадало с именем файла, а неймспейсов с именами папок. Ему не важно, где хранится класс. PSR-4 это лишь договоренность, чтобы все использовали общий стандарт.
Обычно в качестве неймспейса верхнего уровня выбирают название приложения, иногда вместе с названием компании или ником разработчика (например в фреймворке Symfony все классы лежат внутри неймспейса \Symfony\
). Если приложение маленькое, то все классы можно сложить в один неймспейс, а по мере роста добавляются дополнительные уровни вложенности и получается что-то вроде \Symfony\Component\HttpFoundation\Request
.
Если ты придерживаешься этого стандарта, то во-первых, ты молодец, во-вторых ты можешь не писать свой автозагрузчик, а взять любой готовый автозагрузчик PSR-4 (например встроенный в композер). Единственное, что тебе надо указать — это корневой неймспейс твоего проекта и корневую папку. Вот пример кода, который надо вписать в composer.json
, чтобы сказать что классы из неймспейса MyLibrary
находятся в папке src
, а классы из MyApp\Plugins
в plugins
:
{
"autoload": {
"psr-4": {
"MyLibrary\\": "src/",
"MyApp\\Plugins\\": "plugins/"
}
}
}
Обрати внимание, что регистр тут имеет значение! Если ты напишешь PSR-4
вместо psr-4
, то композер просто тихо проигнорирует указанную тобой настройку, а ты будешь долго искать, в чем же проблема.
Мы пишем бекслеш 2 раза, так как таковы правила формата JSON (чтобы в строку вставить бекслеш, его надо написать 2 раза, бекслеш это экранирующий символ). Бекслеш нужен, так как без него (если написать MyLibrary
) правило будет относиться к любым классам, имена которых начинаются с MyLibrary
, например MyLibrarySomething\Class
.
В примере выше классы будут искаться так:
MyLibrary\Some\Class -> src/Some/Class.php
MyApp\Plugins\Some\Class -> plugins/Some/Class.php
После того, как ты добавишь информацию в composer.json
, надо выполнить команду php composer.phar dump-autoload
или composer dump-autoload
(разумеется композер должен быть у тебя уже установлен). Она сгенерирует файлы автозагрузчика и положит их в папку vendor
. Тебе остается только подключить их в своей программе через require_once __DIR__ . '/vendor/autoload.php;'
. Таким образом, следование PSR-4 и использование композера позволяет нам не писать свой автозагрузчик.
Официальная документация: