среда, 15 июля 2015 г.

Автозагрузка классов в PHP

Разрабатывая более-менее серьёзное web приложение с использованием OOP, очень часто приходится подключать классы используя инструкции require, require_once, include, include_once. При этом, как правило, каждый скрипт начинается из длинного списка подключаемых классов. Безусловно это проблема. Что если, например, изменится путь к какому то классу? В таком случае придется пройтись по всем файлам в котором используется этот класс и внести соответствующие изменения. К счастью для нас, разработчиков, предусмотрено решение этой проблемы.

В этой статье я очень подробно опишу процесс автозагрузки с практической точки зрения.

Немного истории

Давайте вспомним как мы делали в самом начале нашего пути изучения PHP, как мы только начинали использовать классы:

Файл index.php
<?php
  require_once 'src/User.php'
  $user = new User();

Как то так! При этом, мы подразумевали, что папка с нашими классами (src) находится в том же каталоге что и файл index.php. Это очень простой и бездумный вариант реализации, но очень трудно поддерживаемый в дальнейшем.

Функция __autoload()

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

Файл index.php
  <?php
  function __autoload($class) {
    require_once "src/$class.php";
  }
  // далее можно просто создавать объекты
  $user = new User('Victor');

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

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

Быть может устаревший метод __autoload() Вам придется очень даже по душе (по своей простоте). Тем не менее, предлагаю пойти дальше и воспользоватся более продвинутыми возможностями версии PHP 5.3 — пространствами имен.

Автозагрузка классов из пространств имен подразумевает четко определенную структуру каталогов. К примеру, вот такая структура:

Здесь у нас объявлено два класса User и UserRepository. Описывая эти классы, мы должны указать пространство имен с помощью ключевого слова namespace.

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

В этом, конкретном случае, классы выглядят так:

Файл User.php
<?php
  namespace Project\Entity;
  class User implements \JsonSerializable {  
    private $id;
    ...
Файл UserRepository.php
<?php
  namespace Project\Repository;
  use Project\Entity\User;
  class UserRepository {
    protected $connect;
    ...

  public function all(){
    $user = new User();
  }

Теперь, давайте посмотрим, что представляет из себя класс NamespaceAutoloader. Вообще то ничего сложного:

Файл NamespaceAutoloader.php
<?php
class NamespaceAutoloader {

    protected $namespaceMap = array();

    public function addNamespace($namespace, $rootDir){
        if (is_dir($rootDir)){
            $this->namespaceMap[$namespace] = $rootDir;
            return true;
        }
        return false;
    }

    public function register(){
        spl_autoload_register(array($this, 'autoload'));
    }

    protected function autoload($class){
        $pathParts = explode('\\', $class);
        if (is_array($pathParts)){
            $namespace = array_shift($pathParts);
            if (!empty($this->namespaceMap[$namespace])){
                $filePath = $this->namespaceMap[$namespace].'/'.implode('/', $pathParts).'.php';
                require_once $filePath;
                return true;
            }
        }
        return false;
    }
}

Принцип действия приведенного автозагружчика следующий:

  1. Сперва мы регистрируем пространство имен. Для этой цели используется метод addNamespace(). Для регистрации пространства имен, нужно передать два параметра: название пространсва имен и путь к папке в которой размещены классы.
  2. Далее, вызовом метода register(), мы запускаем процесс автозагрузки.

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

Файл index.php
<?php
header('Content-type: application/json');

// Подключаем класс автозагружчика и создаем его экземпляр
require_once 'NamespaceAutoloader.php';
$autoloader = new NamespaceAutoloader();

// Регистрируем пространство имен и запускаем автозагрузку
$autoloader->addNamespace('Project', 'ServerSide');
$autoloader->register();

// Используем классы из пространства имен
use Project\Repository\UserRepository;

$userRepo = new UserRepository($connect);
echo json_encode($userRepo->all());

В приведенном примере, мы зарегистрировали пространство имен Project. Путь к папке с классами указываем относительно файла index.php.

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

При создании поста использовались такие ресурсы:

  1. Механизм автозагрузки классов в PHP
  2. Автозагрузка классов в PHP с использованием SPL

0 коммент.:

Отправить комментарий

Ярлыки

Популярные записи