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

А именно - начать использовать менеджер зависимостей для PHP - Composer. Он позволит нам легко устанавливать разные полезные вещи на сайт, обновлять их и даже удалять. Если вы уже работали с менеджером пакетов GNU/Linux - то вам всё это будет очень знакомо.

В качестве разминки, удалим Fenom из директории core и установим через Composer. Для этого нужно в корне сайта создать файл composer.json и прописать в нём требуемые пакеты (подробнее можно прочитать на Хабре):


{
    "require": {
        "php":">=5.3.0",
        "fenom/fenom": "2.*"
    }
}

Теперь нужно только скачать composer.phar и запустить его в корне сайта:

php composer.phar install

На https://modhost.pro и качать ничего не нужно - просто запускаем composer в директории:

composer install

И волшебным образом у нас появляется директория /vendor/ с установленным Fenom. Что же с ним делать дальше?

Принцип работы

Давайте немного остановимся, и я объясню, как именно функционирует Composer, если вы еще не знаете.

Composer - это менеджер пакетов, который работает с ними согласно правил в composer.json. То, что мы там напишем, и будет устанавливаться.

При установке пакетов Composer пробегается по их правилам и генерирует самый важный файл - /vendor/autoload.php, который загружает все скачанные классы. Нам, выходит, нужно только подключить этот файл в своём проекте, и с этих пор мы можем запускать свои классы без require:

$Fenom = new Fenom::factory($tplDir, $compileDir);

То есть, система уже знает, где физически находится Fenom и нам не нужно делать reuire_once('путь к fenom'). Также нам не нужно держать сам Fenom в своём проекте, достаточно хранить там наш composer.json.

Еще раз, для закрепления: 1. Пишем composer.json 2. Устанавливаем пакеты через composer.phar 3. Подключаем в проект /vendor/autoload.php 4. Используем любые установленные классы через new Class()

Лишних файлов в проекте нет, лишних require нет, установленные пакеты обновляются через тот же composer:

php composer.phar update

Всё очень просто и удобно. Стоит только раз использовать такую схему работы и велосипедить дальше уже просто невозможно!

Подключаем Fenom

Для работы с установленными пакетами нам нужно просто добавить autoload.php в наш класс Core:


    function __construct(array $config = array()) {
        $this->config = array_merge(
            array(
                'controllersPath' => dirname(__FILE__) . '/Controllers/',
                'templatesPath' => dirname(__FILE__) . '/Templates/',
                'cachePath' => dirname(__FILE__) . '/Cache/',
                'fenomOptions' => array(
                    'auto_reload' => true,
                    'force_verify' => true,
                ),
            ),
            $config
        );
        // Например, вот тут
        require_once dirname(dirname(__FILE__)) . '/vendor/autoload.php';
    }

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


    public function getFenom() {
        if (!$this->fenom) {
            try {
                if (!file_exists($this->config['cachePath'])) {
                    mkdir($this->config['cachePath']);
                }
                $this->fenom = Fenom::factory($this->config['templatesPath'], $this->config['cachePath'], $this->config['fenomOptions']);
            }
            catch (Exception $e) {
                $this->log($e->getMessage());
                return false;
            }
        }

        return $this->fenom;
    }

Просто вызываем Fenom::factrory - и он сам загрузится. Вот коммит с изменениями - смотрите, сколько кода мы убрали сразу из своего проекта!

Зачем нам таскать с собой Fenom и обновлять его вручную, если давно придуман Composer, который об этом позаботится гораздо лучше? Всё, что нам нужно - положить в корень проекта composer.json.

Давайте теперь отрефакторим и наш собственный проект, чтобы он также подключался в autoload.php.

Рефакторинг проекта

Вся эта замечательная автозагрузка работает при помощи пространств имён - namespaces. Это такая фишка для гарантированного отделения имён классов друг от друга. Все классы, как бы, лежат в своих виртуальных директориях, и чтобы их использовать, нужно указывать полное имя или задавать псевдоним. Подробнее можно снова прочитать на Хабре.

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

Отсюда следует два вывода: Composer можно использовать только в PHP 5.3+, и наш проект нужно переписать так, чтобы он использовал пространства имён.

Первым делом, нужно определить имя для нашего проекта. Пусть будет Brevis - от латинского "короткий". Теперь наша директория /core/ - это пространство имён Brevis. Значит: - Класс Core становится \Brevis\Core и находится в core\Core.php

  • Класс Controller становится \Brevis\Controller и находится в core\Controller.php
  • Класс Controlles_Home становится \Brevis\Controllers\Home и находится в core\Controllers\Home.php
  • Класс Controlles_Test становится \Brevis\Controllers\Test и находится в core\Controllers\Test.php

Обратите внимание, что физическое расположение файлов совпадает с пространством имён. Это потому, что мы уже следуем стандарту PSR-4.

Теперь в начале каждого класса пишем используемое пространство имён. У основных классов это:


<?php 
namespace Brevis;

А у контроллеров:


<?php 
namespace Brevis\Controllers;

PhpStorm сразу начинает подсвечивать нам все использования Exception и Fenom как неправильные, потому что они вызываются внутри пространства имён, но без указания полного имени. Тут 2 варианта исправления: 1. Пробежаться по всему коду и добавить к именам этих классов \, чтобы было \Exception и \Fenom 2. Не страдать фигнёй, а использовать псевдонимы - возможность указать короткое имя для класса

Конечно, выбираем второй вариант:


<?php 
namespace Brevis;

use \Fenom as Fenom;
use \Exception as Exception;

И ничего в коде менять не нужно - все вызовы Fenom теперь автоматически будут разрешены в \Fenom на этапе работы.

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


<?php
namespace Brevis\Controllers;

use Brevis\Controller as Controller;

class Home extends Controller {

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

Перемещаем autoload.php из класса \Brevis\Core в index.php, потому что пространство имён Brevis у нас скоро и само будет работать через автозагрузку и лишние require ему не нужны.

Index.php теперь выглядит вот так:


require_once dirname(__FILE__) . '/vendor/autoload.php';
$Core = new \Brevis\Core();

$req = !empty($_REQUEST['q'])
    ? trim($_REQUEST['q'])
    : '';
$Core->handleRequest($req);

Метод \Brevis\Core::handleRequest() переписываем, убирая всякие проверки файлов. Нам нужно проверять только наличие класса, используя get_class():


    public function handleRequest($uri) {
        $request = explode('/', $uri);

        $className = '\Brevis\Controllers\\' . ucfirst(array_shift($request));
        /** @var Controller $controller */
        if (!class_exists($className)) {
            $controller = new Controllers\Home($this);
        }
        else {
            $controller = new $className($this);
        }

        $initialize = $controller->initialize($request);
        if ($initialize === true) {
            $response = $controller->run();
        }
        elseif (is_string($initialize)) {
            $response = $initialize;
        }
        else {
            $response = 'Возникла неведомая ошибка при загрузке страницы';
        }

        echo $response;
    }

Как видите, мы определяем полное имя загружаемого контроллера, проверяем его наличие, а если такого контроллера нет - загружаем \Brevis\Controllers\Home.

В общем, вся логика осталась прежней, а количество кода сократилось. Нет ручной работы с файлами, нет никаких require, вся работа по подключению классов лежит на autoload.php. Соотвественно, и параметр controllersPath из системного конфига пропадает.

Осталось добавить и наш собственный проект в автозагрузку. Для этого меняем composer.json вот так:


{
  "name": "Brevis",
  "autoload": {
    "psr-4": {
      "Brevis\\": "core/"
    }
  },
  "require": {
    "php": ">=5.3.0",
    "fenom/fenom": "2.*"
  }
}

Блок autoload указывает, что загружать дополнительно, кроме установленных пакетов. Ключ psr-4 указывает, как именно загружать наш проект, и указывает директорию core/ как основную для пространства имён Brevis.

Запускаем

php composer.phar update

и наши классы попадают в автозагрузку. Всё должно работать, смотрим изменения на GitHub.

Заключение

Вот мы и переписали наш проект в соотвествии с последними стандартами в мире программирования на PHP. Согласитесь, было не так уж и страшно?

Зато теперь наш код чистенький, опрятненький, соотвествует стандартам и отлично работает!

На следующем занятии мы установим через Composer xPDO 3.0 (да-да, он уже переписан и поддерживает namespaces, хоть пока и только в dev-ветке) и подключим его к нашей БД.

Напишем схему, сгенерируем по ней модель и создадим пару таблиц.

← Предыдущая заметка
Расширение и наследование шаблонов Fenom
Комментарии (16)
Семён Лобачевский
07.06.2015 15:19

Спасибо за урок! Понял, что со стандартизацией и автоматизацией в разработке гораздо проще и удобней.

bezumkinВасилий Наумкин
07.06.2015 15:33

Согласен! Причём, чем больше используешь готовых пакетов, тем лучше это выглядит. Вот, вчера так за день переписал один проект - аж на душе полегчало!

Семён Лобачевский
07.06.2015 15:32

Вопрос по алгоритму работы приложения, как вы запоминаете что и с чем взаимодействует? Какие классы и методы нужны в том или ином случае? Я так подозреваю, что для создания даже такого мини фреймворка, всё равно требуется сначала составить логику работы этого фреймворка или всё проще?

bezumkinВасилий Наумкин
07.06.2015 15:36

А не нужно ничего запоминать - всё подскажет PhpStorm. Для того мы и пишем ему комментарии у методов и переменных, чтобы быстро переходить по ним. Вся работа разбивается на отдельные мелкие методы, помещается в контроллеры и запутаться сложно.

Ну а в целом, всегда есть любимые решения: - Fenom для шаблонизации - xPDO для работы с БД - PhpMailer для почты - Parsedown для работы с Markdown.

Если понадобится что-то еще, просто гуглю, смотрю документацию и устанавливаю через Composer. Не понравилось - удаляю и ищу что-то новое.

Сергей
22.09.2015 17:30

Теперь нужно только скачать composer.phar и запустить его в корне сайта: php composer.phar installНа modhost.pro и качать ничего не нужно — просто запускаем composer в директории: composer install

Вот тут непонятно...каким образом запустить?

bezumkinВасилий Наумкин
23.09.2015 00:10

Зайти в консоль сервера, перейти в директорию с проектом и набрать

composer install
Сергей
23.09.2015 06:09

Василий, видимо я совсем новичок)) А как можно попасть в консоль сервера? - гуглил, выдает только консоль контрстрайка...У меня как раз modhost.pro.

bezumkinВасилий Наумкин
23.09.2015 06:21

Вторая ссылка в Яндексе по запросу как можно попасть в консоль сервера.

Имя пользователя, сервер и пароль для SSH показываются в окне информации сайта на modhost.

Сергей
23.09.2015 12:08

Спасибо, буду разбираться!)

biz87Николай Савин
05.01.2016 08:08

Василий, А если у меня типовой виртуальный хостинг без доступа к консоли и ssh не будет работать? Есть ли альтернативные способы запустить установку Composer? Если нет, сильно ли мне это затруднит изучение дальнейших материалов курса?

bezumkinВасилий Наумкин
05.01.2016 08:18

Честно говоря, виртуальный хостинг без консоли - это сегодня не совсем типовое. Бывает, что SSH отключен по умолчанию, но можно включить из панели управления.

В принципе, можно скачать всё нужное из репозиториев на GitHub и залить на сервер, но через Composer, конечно, было бы в разы удобнее.

Есть еще вариант запускать composer из php скрипта через браузер. Ну и можно, конечно, купить сайт для разработки на https://modhost.pro за 75 руб - там всё есть.

biz87Николай Савин
05.01.2016 09:17

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

biz87Николай Савин
05.01.2016 10:47

В общем в итоге купил хостинг на modstore. Все установил, код переписал, с GitHub сверился, все нормально. Но почему то сайт не работает, и вот эта строка в конце урока

php composer.phar update

выдает ошибку **Could not open file: composer.**Видимо что то не туда положил, но в итоге застрял и что делать не знаю. Нужна подсказка.

bezumkinВасилий Наумкин
05.01.2016 11:01

У нас там всё проще:

composer update

И еще обрати внимание, что ты положил composer.json выше директории core (рядом с www), а не внутри. Так тоже можно, но нужно будет поменять путь к autoload.php.

biz87Николай Савин
05.01.2016 11:04

А вот этот файл .gitignore зачем нужен? Про него речи в уроке не было но в GitHub он есть.

bezumkinВасилий Наумкин
05.01.2016 11:04
bezumkin
Василий Наумкин
04.07.2022 23:34
Что-то странное у тебя произошло: миграция есть, и вроде как выполнена, но таблицы при этом отсутств...
inetlover
Александр Наумов
03.07.2022 20:36
Василий, спасибо! Все понятно!
bezumkin
Василий Наумкин
02.07.2022 20:28
Спасибо, поправил!
bezumkin
Василий Наумкин
30.06.2022 03:58
Есть ли возможность формировать &quot;friendly URL aliases&quot;, используя аналог translit MODx? ...
bezumkin
Василий Наумкин
27.06.2022 03:32
Спасибо за исправления, очень выручаешь =) Но учитывая количество не описаных в заметке дополнительн...
bezumkin
Василий Наумкин
27.06.2022 03:10
что будет использоваться для вывода многоуровневого меню Посмотри как работают комментарии на этом ...
bezumkin
Василий Наумкин
25.06.2022 11:56
Поправил, спасибо!
bezumkin
Василий Наумкин
21.06.2022 01:58
onLoad(data) { this.total = data.total }, и onLoad({total}) { this.total = total }, В нашем случ...
bezumkin
Василий Наумкин
20.06.2022 14:01
Прекрасно тебя понимаю, я когда сам в этом разбирался - голова дымилась. Но зато теперь прямо-таки п...
bezumkin
Василий Наумкин
20.06.2022 09:30
Не надо, оно по умолчанию так - я просто чуть более подробно написал.