Запуск Zend Framework MVC из командной строки

25 августа 2010 г.

Хочу поделиться своим решение по запуску Zend Framework MVC из командной строки. Я ставил цель получить возможность легко и быстро работать как с новым проектом, так и с уже готовым приложением, к тому же используя все возможности последних версий фреймворка (Zend_Application, resources loading). Скрипт будет работать с проектами, базирующимися на Zend Framework 1.8 и дальше.

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

Создаем скрипт играющий роль точки входа для приложения.

$ touch ./scripts/zf-cli.php

Далее посмотрим код скрипта с подробными комментариями по коду:

// можно удалить строчку начиная с PHP старше версии >= 5.3.0
defined('__DIR__') || define('__DIR__', dirname(__FILE__));
// стандартный код инициализации (путь к приложению, библиотека классов и автозагрузка)
defined('APPLICATION_PATH') || define('APPLICATION_PATH', realpath(__DIR__ . '/../application'));
$paths = explode(PATH_SEPARATOR, get_include_path());
$paths[] = realpath(__DIR__.'/../library');
set_include_path(implode(PATH_SEPARATOR, $paths));
unset($paths);
require_once 'Zend/Loader/Autoloader.php';
$loader = Zend_Loader_Autoloader::getInstance();
// нам нужен этод код регистритующий отдельное пространство имен для загрузки специального класса
$loader->registerNamespace('Custom_');
// наброски скелета CLI приложения
$getopt = new Zend_Console_Getopt(array( 'action|a=s' => 'action to perform in format of "module/controller/action"', 'env|e-s' => 'defines application environment (defaults to "production")', 'help|h' => 'displays usage information', ));
try {
$getopt->parse();
}
catch (Zend_Console_Getopt_Exception $e) {
// пользователь передал неверные параметры: показать инструкцию по использованию echo
$e->getUsageMessage();
return false;
} // покажем справку в случае запроса последней или параметры (модуль, контролер, действие) упущены if (
$getopt->getOption('h') || !$getopt->getOption('a')) { echo $getopt->getUsageMessage();
return true;
} // инициализируем дополнительные параметры если они есть
$env = $getopt->getOption('e');
defined('APPLICATION_ENV') || define('APPLICATION_ENV', (null === $env) ? 'production' : $env);
// инициализируем обьект приложения Zend_Application
$application = new Zend_Application ( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' );
// начинаем загрузку приложения, загружаем и извлекаем ресурс FrontController-а
$front = $application->getBootstrap() ->bootstrap('frontController') ->getResource('frontController');
// дальше начинается магия! //
// мы будем использовать Zend_Controller_Request_Simple и немного дополнительного кода
// чтоб компенсировать отсутствие в еко-системе Zend Framework решения
// "Zend_Controller_Request_Cli" которое сейчас находиться на стадии Предложения (англ. Proposal):
// http://framework.zend.com/wiki/display/ZFPROP/Zend_Controller_Request_Cli //
// Мне нравиться идея задавать параметры MVC через "/"
// например "module/controller/action" //
// NOTE: в соответствии с имеющейся реализацией мы не можем пропускать
// параметры module/controller/action //
// TODO: добавить возможность пропуска параметров "module", "action"
// и давать им значения по умолчанию "default" и "index" соответственно //
// давайте разделим параметры полученые из CLI
// и сформируем их в обьект запроса //
// TODO: думаю в будущем этот функционал должен будет перемещен в маршрутизацию (Routing)
// FIXME: пока мы не будем заниматься обработкой других параметров кроме module/controller/action //
$params = array_reverse(explode('/', $getopt->getOption('a')));
$module = array_pop($params);
$controller = array_pop($params);
$action = array_pop($params);
$request = new Zend_Controller_Request_Simple ($action, $controller, $module);
// зададим параметры FrontController чтоб подружить его с интерфейсом командной строки
$front->setRequest($request) ->setResponse(new Zend_Controller_Response_Cli()) ->setRouter(new Custom_Controller_Router_Cli())
// наш отдельный класс для маршрутизации ->throwExceptions(true);
// давайте загрузим остальные ресурсы приложения и насладимся работой!
$application->bootstrap() ->run();

Я думаю для людей знакомых с Zend Framewrk — этот код предельно прост. Вы заметили, что я использовал свой класс-заглушку для маршрутизации. Это костыль без которого запустится стандартная система маршрутизации, работающая с HTTP обьектами запроса, результатом работы которой будет вызов метода getRequestUri() и соответственно сообщение PHP Fatal error: Call to undefined method Zend_Controller_Request_Simple::getRequestUri(). Для обхода проблемы я использую свой клас-маршрутизатора, который просто не осуществляет этот вызов.

$ mkdir -p ./library/Custom/Controller/Router $ touch ./library/Custom/Controller/Router/Cli.php

Собственно наш маршрутизатор наследует Zend_Controller_Router_Abstract и реализует интерфейс Zend_Controller_Router_Interface используя методы-заглушки, поскольку при работе через командную строку маршрутизация очень простая и мы ее по сути уже сделали выше.

/** * Клас маршрутизатора-заглушки * его задача ничего не делать на этапе маршрутизации (все уже сделано до нас) */
class Custom_Controller_Router_Cli
extends Zend_Controller_Router_Abstract
implements Zend_Controller_Router_Interface {
public function route(Zend_Controller_Request_Abstract $dispatcher){}
public function assemble($userParams, $name = null, $reset = false, $encode = true){}
public function getFrontController(){}
public function setFrontController(Zend_Controller_Front $controller){}
public function setParam($name, $value){}
public function setParams(array $params){}
public function getParam($name){}
public function getParams(){}
public function clearParams($name = null){} };

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

# справка
$ php ./scripts/zf-cli.php
$ php ./scripts/zf-cli.php --help
# запуск модуля, контролера и действия по умолчанию
$ php ./scripts/zf-cli.php -a default/index/index
# запуск модуля "blog", контролера "users", действия "list" используя настройки среды "development"
$ php ./scripts/zf-cli.php -a blog/users/list -e development

Я рекомендую использовать отдельную среду (environment) для запуска приложения с командной строки. Такой подход позволит в отдельном разделе конфигурационного файла исключить ненужные в работе из под CLI ресурсы, осуществить более тонкую настройку приложения.

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

Выводы

Работа с значениями запроса и маршрутизацией из под командной строки в Zend Framework всегда с ухищрениями. Я нашел кучу разных решений, но большинство из них создана народными умельцами и могут быть рассмотрены только как нестандартные подходы к решению. У нас все есче нет официального пути запуска каркаса MVC с командной строки в полном соответствии с идеологией Zend Framework. Обратите внимание на обсуждения проблемы по ссылке в низу, для детального ознакомления и понимания сложившейся ситуации. Кажеться что многообещающий компонент Zend_Controller_Request_Cli можеш сдвинуть решение проблемы, но есче нет единой точки зрения о интерпретации параметров CLI при маршрутизации.

Конечно все в мире open source движеться добровольцами, и я думаю, что волонтерская активность (если такая будет) приведет к решению.

Ссылки на ресурсы

Вот вам ссылки на материалы из Интернета, которые мне показалить полезными при освещении этой темы.

Using Zend Framework from the Command Line — статья созданая есче 2008 году автором по имени «Jed», и собственно единственная статья на его блоге. За несколько лет автора достали коментарии и он сам неоднократно заявлял, что за это время статья устарела в плане использования новых возможностей Zend Framework. Статья вдохновила меня написать обновленую версию его решения;

Programmer’s Reference Guide — Zend_Console_Getopt — прекрасный компонент делающий жизнь разработчика CLI приложений легче;

Zend_Controller_Request_Cli Component Proposal — многообещающие предложение для решения проблемы работы с параметрами командной строки в Zend Framework, но дальше обсуждения дело не пошло.

Zend Framework Quick Start — последняя версия инстурции для разработчика «Quick Start» показывающая эталоное решение запуска скриптов из CLI в Zend framework (без MVC);

The Mysteries Of Asynchronous Processing With PHP — Part 2: Making Zend Framework Applications CLI Accessible — самая интересная статья, в которой описано очень хорошое решение.