Осваиваем Composer

Ну что, на прошлом уроке мы фактически закончили писать ядро нашего сайта, и с этого момента я предлагаю завязать с велосипедостроением и припасть к достижениям мирового сообщества.
А именно - начать использовать менеджер зависимостей для 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
  1. Устанавливаем пакеты через composer.phar
  2. Подключаем в проект /vendor/autoload.php
  3. Используем любые установленные классы через 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
  1. Не страдать фигнёй, а использовать псевдонимы - возможность указать короткое имя для класса
Конечно, выбираем второй вариант:

<?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-ветке) и подключим его к нашей БД.
Напишем схему, сгенерируем по ней модель и создадим пару таблиц.

16 комментариев

Семён Лобачевский
Спасибо за урок! Понял, что со стандартизацией и автоматизацией в разработке гораздо проще и удобней.
Василий Наумкин
Согласен! Причём, чем больше используешь готовых пакетов, тем лучше это выглядит. Вот, вчера так за день переписал один проект - аж на душе полегчало!
Семён Лобачевский
Вопрос по алгоритму работы приложения, как вы запоминаете что и с чем взаимодействует? Какие классы и методы нужны в том или ином случае? Я так подозреваю, что для создания даже такого мини фреймворка, всё равно требуется сначала составить логику работы этого фреймворка или всё проще?
Василий Наумкин
А не нужно ничего запоминать - всё подскажет PhpStorm. Для того мы и пишем ему комментарии у методов и переменных, чтобы быстро переходить по ним. Вся работа разбивается на отдельные мелкие методы, помещается в контроллеры и запутаться сложно.
Ну а в целом, всегда есть любимые решения: - Fenom для шаблонизации - xPDO для работы с БД - PhpMailer для почты - Parsedown для работы с Markdown.
Если понадобится что-то еще, просто гуглю, смотрю документацию и устанавливаю через Composer. Не понравилось - удаляю и ищу что-то новое.
Теперь нужно только скачать composer.phar и запустить его в корне сайта: php composer.phar installНа modhost.pro и качать ничего не нужно — просто запускаем composer в директории: composer install
Вот тут непонятно...каким образом запустить?
Василий Наумкин
Зайти в консоль сервера, перейти в директорию с проектом и набрать
composer install
Василий, видимо я совсем новичок)) А как можно попасть в консоль сервера? - гуглил, выдает только консоль контрстрайка...У меня как раз modhost.pro.
Василий Наумкин
Имя пользователя, сервер и пароль для SSH показываются в окне информации сайта на modhost.
Спасибо, буду разбираться!)
Николай Савин
Василий, А если у меня типовой виртуальный хостинг без доступа к консоли и ssh не будет работать? Есть ли альтернативные способы запустить установку Composer? Если нет, сильно ли мне это затруднит изучение дальнейших материалов курса?
Василий Наумкин
Честно говоря, виртуальный хостинг без консоли - это сегодня не совсем типовое. Бывает, что SSH отключен по умолчанию, но можно включить из панели управления.
В принципе, можно скачать всё нужное из репозиториев на GitHub и залить на сервер, но через Composer, конечно, было бы в разы удобнее.
Есть еще вариант запускать composer из php скрипта через браузер. Ну и можно, конечно, купить сайт для разработки на https://modhost.pro за 75 руб - там всё есть.
Николай Савин
Вот нашел скрипт NoConsoleComposer, который все устанавливает через бразузер. Может кому пригодится. Теперь осталось разобраться, что он мне там поустанавливал.
Николай Савин
В общем в итоге купил хостинг на modstore. Все установил, код переписал, с GitHub сверился, все нормально. Но почему то сайт не работает, и вот эта строка в конце урока
php composer.phar update
выдает ошибку Could not open file: composer.Видимо что то не туда положил, но в итоге застрял и что делать не знаю. Нужна подсказка.
Василий Наумкин
У нас там всё проще:
composer update
И еще обрати внимание, что ты положил composer.json выше директории core (рядом с www), а не внутри. Так тоже можно, но нужно будет поменять путь к autoload.php.
Николай Савин
А вот этот файл .gitignore зачем нужен? Про него речи в уроке не было но в GitHub он есть.
bezumkin.ru
Personal website of Vasily Naumkin
Прямой эфир
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так. А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен. Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. ...
Futuris
04.04.2024, 08:56:12
Я просто немного запутался. Когда в абзаце "Vesp/Core" ты пишешь про "новый trait FileModel", я поду...
Василий Наумкин
20.03.2024, 21:21:52
Volledig!
Андрей
14.03.2024, 13:47:10
Василий! Как всегда очень круто! Моё почтение!
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо! Извини, тупанул.
Василий Наумкин
22.01.2024, 07:43:20
Давай-давай!
Василий Наумкин
24.12.2023, 14:26:13
Спасибо!