Skip to content

Latest commit

 

History

History
executable file
·
167 lines (113 loc) · 15 KB

autoload.md

File metadata and controls

executable file
·
167 lines (113 loc) · 15 KB

Автозагрузка, неймспейсы и PSR-4

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

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;
    }
});

Подробнее про автозагрузку можно почитать тут:

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

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

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

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

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

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

  1. большие библиотеки и фреймворки состоят из вложенных друг в друга модулей, чтобы не было совпадений имен классов в разных модулях, они добавляют название модуля в имя класса и они получаются длинными:
  • 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

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

Эту проблему (как назвать файл с классом) решает рекомендация 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 и использование композера позволяет нам не писать свой автозагрузчик.

Официальная документация: