Phalcon — скомпилированный PHP MVC Framework

в 14:45, , рубрики: framework, mvc, phalcon, php, метки: , , ,

Phalcon — скомпилированный PHP MVC Framework
Создание скомпилированных MVC фреймворков для PHP не раз приходила на ум кодерам.

Достоинства такого подхода:

  • Высокая производительность
  • Малая нагрузка файловой системы
  • Меньший расход памяти (при строгой типизированности)
  • Частичная обработка данных без интерпритации

И само собой не менее явные недостатки:

  • Если Вы не знаете C, то Вы полностью зависите от разработчиков
  • Проект может в любую секунду сдуться
  • В зависимости от архитектуры, часть модулей все равно приходится писать самому, что уменьшает выигрыш

В этой статье я попытаюсь познакомить с еще одной попыткой сделать скомпилированный PHP MVC Framework.

Знакомство

Сам я в поисках быстрого, надежного и способного фреймворка. Гуляя по зарубежным форумам и блогам, наткнулся на интересный бенчмарк, где некий фреймворк на «Hello World!» уделывал пузатых знаменитостей в несколько раз.
Phalcon — скомпилированный PHP MVC Framework

Пройти мимо такого я не смог и принялся узнавать, как это работает и развивается.
Проекту менее года. Первый коммит на github — 10 января 2012. И да, это open source проект. Насколько я понял, сейчас им более менее активно занимаются 3 человека.

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

Пример

Лучший способ изучить framework — сделать что-нибудь на нем, параллельно изучая документацию.
Я сделал 3-х страничное приложение, показывающее некоторые возможности phalcon, а именно:

  • Роутинг
  • Работа с БД, на внутреннем языке PHQL
  • Работа с events
  • Фильтрация данных
  • Кеширование
  • DI (dependency injection)
  • Возможности встроенного движка шаблонов Volt

Кроме того, разработчики заявляют гораздо большее:

  • ODM — работа с документо-ориентированными БД
  • Многоязычность
  • Автоматическое создание приложений CRUD из консоли или веб-морды
  • Поддержка нескольких популярных движков шаблонов
  • Микро приложения (для создания REST API)
  • Создание логгера любой конфигурации
  • Создание своего event manager с fire и catch
  • Встроенный обработчик html тегов для шаблонов
  • Поддержка nemespace и мульти-модульности
  • Обработка сессий и flash сообщений
  • ACL
Иерархия проекта:

xmpl/
    apps/
        app/config/
        app/controllers/
        app/models/
        app/views/
        app/My/
    public/
        public/img/
        public/css/
        public/js/

Весь код будет лежать по пути /apps/, инициализация приложения /public/index.php. На последний файл собственно и нужно настроить редирект несуществующих страниц, через apache (.htaccess в папках / и /public/ ) или nginx.

Инициализация:

Про инициализацию вкратце под спойлером.

<?php

//загружаем конфигурационные данные из ini файла
$config = new PhalconConfigAdapterIni( '../apps/config/config.ini' );

//Подключаем загрузчик, показывая ему, где будут лежать вызываемые классы
$loader = new PhalconLoader();
$loader->registerDirs(array(
	$config->application->controllersDir,
	$config->application->modelsDir,
	$config->application->myDir,
));
$loader->register();

//Подкючаем DI
$di = new PhalconDI();

//Компонент, отвечающий за обработку url.
$di->set('url', function() use ($config){
	$url = new PhalconMvcUrl();
	return $url;
});

//Подключаем модель соединения с БД, доступны ( Mysql, PostgreSQL, SQLite)
$di->set('db', function() use ($config) {
    $connection = new PhalconDbAdapterPdoMysql(array(
        "host" => $config->database->host,
        "username" => $config->database->username,
        "password" => $config->database->password,
        "dbname" => $config->database->name
    ));

    return $connection;
});

//2 вспомогательных компонента для работы с БД
$di->set('modelsManager', function(){
	return new PhalconMvcModelManager();
});
//Указываем, где хранится мета-данным из БД, доступны (Apc, Files, Memory, Session)
$di->set('modelsMetadata', function(){
	return new PhalconMvcModelMetadataMemory();
});

//Подключаем роутер
$di->set('router', 'PhalconMvcRouter');	

//Подключаем диспетчер, чтобы иметь доступ к методам view из контроллера. Можно назначить свой обработчик.
$di->set('dispatcher', function() use ($di) {
	$dispatcher = new PhalconMvcDispatcher();
	return $dispatcher;
});

//Обработка ответов и запросов
$di->set('response', 'PhalconHttpResponse');
$di->set('request', 'PhalconHttpRequest');

//Подключение фильтра данных
$di->set('filter', function(){
    return new PhalconFilter();
});

//Подключаем сервис внутреннего движка Volt с настройками
$di->set('voltService', function($view, $di) use ($config) {
    $volt = new PhalconMvcViewEngineVolt($view, $di);
    $volt->setOptions(array(
        "compiledPath" => $config->application->templCompDir,
        "compiledExtension" => ".compiled"
    ));
    return $volt;
});

//Подключаем компонент, отвечающий за вид. Также назначаем уме eventManager, 
//который, получает несколько событий выбрасываемых view. Таким образом мы 
//можем манипулировать с шаблонами как захотим
$di->set('view', function() use ($config) {

    $eventsManager = new PhalconEventsManager();
    $viewManager = new ViewManager();
    $eventsManager->attach('view', $viewManager);

    $view = new PhalconMvcView();
    $view->setViewsDir( $config->application->viewsDir );
    $view->registerEngines(array(
    	".phtml" => 'voltService'
    ));

    $view->setEventsManager($eventsManager);

    return $view;
});

//Подключаем фронт и бакенд кеширование, доступны front (Base64, Data, None, Output) и backend (Apc, File, Memcache, Mongo), само собой, можно дописать и свои
$di->set('cache', function(){

    $frontCache = new PhalconCacheFrontendData(array(
        "lifetime" => 60
    ));

    $cache = new PhalconCacheBackendApc($frontCache);
    return $cache;
});


//собственно инициализация и вывод ошибок как есть на экран
try {
	$application = new PhalconMvcApplication();
	$application->setDI($di);
	echo $application->handle()->getContent();
}
catch(PhalconException $e){
	echo $e->getMessage();
}

Большую часть можно подключить автоматически, вот так:

$di = new PhalconDIFactoryDefault();
Контроллеры и свои классы:

Теперь, в Вашем приложении доступен стандартный роутинг /baseUri/ControllerName/ActionName/, подключены методы, которые через di наследуют контроллеры $this->cache, $this->dispatcher, $this->db

Также Вы можете вызывать свои классы, находящиеся в папке /apps/My/
Класс ViewManager получит все события, генерируемые View. На интересует beforeRender. В нем, мы передадим в вид переменные, содержащие название класса и действия.

Также мы создадим класс BaseController

class BaseController extends PhalconMvcController
{
	public function initialize()
    {
        PhalconTag::prependTitle('Example | ');
    }
}

class IndexController extends BaseController {

	public function initialize()
    {
        //Устанавливаем текст в тег title, также можно добавлять постфикс (сделано в контроллере Poll)
        PhalconTag::setTitle('Index');
        parent::initialize();
    }

    public function indexAction(){

    }
}

Он наследует стандартный класс. Наши контроллеры, будут уже наследовать BaseController и получать префикс к html тегу title.

View

Phalcon предоставляет довольно удобный метод шаблонов, где существует иерархия.
Шаблоны хранятся в папке /apps/views/
Корневой шаблон /apps/views/index.phtm рендерится всеми контроллерами, кроме тех, где явно задан другой путь.
Этот файл может содержать в себе дальнейший путь иерархии:

{{ content() }}

Я использовал синтаксис встроенного движка шаблонов. Для других движков тоже есть свои аналоги.
Эта функция вызывает следующий шаблон, расположенный по пути:
/apps/view/layer/ControllerName.phtml

По сути, задавая особое оформление для определенного контроллера, как я и сделал в своем приложении. В нем также можно вызвать content(), тем самым подгрузив шаблон /apps/views/ControllerName/ActionName.phtml

В шаблонах можно подгружать другие шаблоны, заменять предустановленные блоки, вызывать пользовательские и предопределенные движком функции, обращаться к методам Models.
Только Volt их молодой проект, поэтому многих функций в нем нет, но они запланированы на ближайшие релизы.
Из коробки, поддерживают Slim, Smarty, Mustache

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

Модели

Подключение к БД, осуществляется с помощью PDO. Имеется 2 реализации работы с базой данных, объектно-ориентированный путь и PHQL, свой обработчик псевдо SQL строк.
К сожалению, первый подходит только для довольно простых запросов, а второй будет сложным, для тех, кто привык к первому.

Модель в приложении

class Poll extends PhalconMvcModel
{
    //объявление используемых переменных в строках, явное указание, какие доступны напрямую public
    public $id;
    public $gender;
    public $age;
    public $read_modern;
    public $authors;
    public $genre;
    public $method;
    public $timestamp;

    //метод вызывается при инициализации БД, должен возвращать имя используемой таблицы
    public function getSource()
    {
        return 'answers';
    }

    //выбор метода соединения при инициализации
    public function initialize()
    {
        $this->setConnectionService('db');
    }
}

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

Недостатки

Я не до конца разобрался во всем, что может этот фреймворк, но все же заметил недостатки:

  • У меня так и не получилось собрать dll под windows. Разработчики так и не ответили.
  • На vps не смог собрать версию 0.6.1, сообщал разработчикам, так и не смогли разобраться.
  • Не нашел способа настроить роутинг для статичного файла example.com/tp/score.json Файла нет — роутер к контроллеру и методу, иначе статичный файл. Буду разбираться.
  • Очень мало возможностей у встроенного шаблонизатора Volt.
  • Не нашел способа вывода Flash сообщений без использования сессий, не отключая рендеринг.
  • Сильно разбитая документации по моделям — 3 отдельных раздела, нет структурности.
  • В большинстве случаев невозможно использовать на shared хостингах.

Трудно назвать это недостатками. Разве что нет прямого общения с разработчиками, кроме issue list на гитхабе.

Функционал приложения

Имеем приложение с главной страницей, страницей опроса и вывода результатов. Отсюда работа с БД, отправка данных с фильтрацией, и получение с минутным кешированием в Apc.

Железо: VPS хостинг. Процессор х1 500 МГц, ОП 256 Мб. Лошадка слабая.
Подключен APC. Настройки все дефолтные. Сервер: Nginx — PHP-FPM — PHP — MySQL

Синтетические тесты:
1. -c50 -d5 -r10
2. -c100 -d2 -r10
3. -c300 -d2 -r20 (LA — 0.7)
4. -c400 -d1 -r20 (LA — 0.47)
5. -c800 -d1 -r30 (LA — 1.1)
6. -c1000 -b -r20 (LA — 2)
(-с: конкуренты, -d: задержка перед повторным вызовом, -r: количество повторений, -b: вызов без задержки, LA: load average)
Phalcon — скомпилированный PHP MVC Framework

Siege делает в рандомном порядке 3 запроса, все динамические. 2 из них с запросами к БД, с кешированием в 1 минуту.
При большой конкурентности появились ошибки (до 2% запросов).
Максимально обрабатывал 516 запросов в секунду. Довольно недурно. Если хранить запрашиваемые json в файлах и обновлять их по cron, то можно нагрузку в разы сократить.

Ссылки

Сайт Phalcon
Репозиторий Phalcon

Работающий пример
Исходники примера на GitHub

P.S.

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

Автор: Assorium

Источник


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js