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

Я хотел бы запустить действие Zend Framework для создания некоторых файлов из командной строки. Является ли это возможным и сколько изменений мне нужно сделать для моего существующего веб-проекта, который использует ZF?

Спасибо!

Ответ 1

На самом деле это намного проще, чем вы думаете. Компоненты bootstrap/application и ваши существующие конфигурации могут быть повторно использованы с помощью сценариев CLI, избегая стека MVC и ненужного веса, который вызывается в HTTP-запросе. Это одно из преимуществ использования wget.

Начните свой script, как ваш общедоступный index.php:

<?php

// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH',
              realpath(dirname(__FILE__) . '/../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV',
              (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
                                         : 'production'));

require_once 'Zend/Application.php';
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/config.php'
);

//only load resources we need for script, in this case db and mail
$application->getBootstrap()->bootstrap(array('db', 'mail'));

Затем вы можете использовать ресурсы ZF так же, как и в приложении MVC:

$db = $application->getBootstrap()->getResource('db');

$row = $db->fetchRow('SELECT * FROM something');

Если вы хотите добавить настраиваемые аргументы в свой CLI script, посмотрите Zend_Console_Getopt

Если вы обнаружите, что у вас есть общий код, который вы также вызываете в приложениях MVC, посмотрите на его обертку в объекте и вызовите эти методы объектов как из MVC, так и из приложений командной строки. Это общепринятая практика.

Ответ 2

UPDATE

Вы можете использовать весь этот код для ZF 1.12 от https://github.com/akond/zf-cli, если хотите.

Хотя решение № 1 в порядке, иногда вам нужно что-то более сложное. Особенно, если вы ожидаете иметь не один CLI script. Если вы позволите мне, я бы предложил другое решение.

Прежде всего, в вашем Bootstrap.php

protected function _initRouter ()
{
    if (PHP_SAPI == 'cli')
    {
        $this->bootstrap ('frontcontroller');
        $front = $this->getResource('frontcontroller');
        $front->setRouter (new Application_Router_Cli ());
        $front->setRequest (new Zend_Controller_Request_Simple ());
    }
}

Этот метод лишит диспетчерский контроль от маршрутизатора по умолчанию в пользу нашего собственного маршрутизатора Application_Router_Cli.

Кстати, если вы определили свои собственные маршруты в _initRoutes для своего веб-интерфейса, вы, вероятно, захотите их нейтрализовать в режиме командной строки.

protected function _initRoutes ()
{
    $router = Zend_Controller_Front::getInstance ()->getRouter ();
    if ($router instanceof Zend_Controller_Router_Rewrite)
    {
        // put your web-interface routes here, so they do not interfere
    }
}

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

class Application_Router_Cli extends Zend_Controller_Router_Abstract
{
    public function route (Zend_Controller_Request_Abstract $dispatcher)
    {
        $getopt = new Zend_Console_Getopt (array ());
        $arguments = $getopt->getRemainingArgs ();
        if ($arguments)
        {
            $command = array_shift ($arguments);
            if (! preg_match ('~\W~', $command))
            {
                $dispatcher->setControllerName ($command);
                $dispatcher->setActionName ('cli');
                unset ($_SERVER ['argv'] [1]);

                return $dispatcher;
            }

            echo "Invalid command.\n", exit;

        }

        echo "No command given.\n", exit;
    }


    public function assemble ($userParams, $name = null, $reset = false, $encode = true)
    {
        echo "Not implemented\n", exit;
    }
}

Теперь вы можете просто запустить свое приложение, выполнив

php index.php backup

В этом случае будет вызван метод cliAction в контроллере BackupController.

class BackupController extends Zend_Controller_Action
{
    function cliAction ()
    {
        print "I'm here.\n";
    }
}

Вы даже можете продолжить и модифицировать класс Application_Router_Cli, чтобы каждый раз не выполнялось действие "cli", а что-то, что пользователь выбрал с помощью дополнительного параметра.

И последнее. Определите настраиваемый обработчик ошибок для интерфейса командной строки, чтобы на вашем экране не отображался HTML-код.

В Bootstrap.php

protected function _initError ()
{
    $error = $frontcontroller->getPlugin ('Zend_Controller_Plugin_ErrorHandler');
    $error->setErrorHandlerController ('index');

    if (PHP_SAPI == 'cli')
    {
        $error->setErrorHandlerController ('error');
        $error->setErrorHandlerAction ('cli');
    }
}

В ErrorController.php

function cliAction ()
{
    $this->_helper->viewRenderer->setNoRender (true);

    foreach ($this->_getParam ('error_handler') as $error)
    {
        if ($error instanceof Exception)
        {
            print $error->getMessage () . "\n";
        }
    }
}

Ответ 3

Просто увидел, что в моем CP есть теги. Если вы наткнулись на этот пост и используете ZF2, ему стало намного проще. Просто отредактируйте маршруты module.config.php следующим образом:

/**
 * Router
 */

'router' => array(
    'routes' => array(

        // .. these are your normal web routes, look further down
    ),
),

/**
 * Console Routes
 */
'console' => array(
    'router' => array(
        'routes' => array(

            /* Sample Route */
            'do-cli' => array(
                'options' => array(
                    'route'    => 'do cli',
                    'defaults' => array(
                        'controller' => 'Application\Controller\Index',
                        'action'     => 'do-cli',
                    ),
                ),
            ),
        ),    
    ),
),

Используя приведенную выше конфигурацию, вы должны определить doCliAction в вашем IndexController.php в своем приложении. Запуск это торт из командной строки:

php index.php do cli

Готово! Путь более плавный.

Ответ 4

Решение akond выше находится на лучшем треке, но есть некоторые тонкости, которые могут его script не работать в вашей среде. Подумайте об этих настройках его ответа:

Bootstrap.php

protected function _initRouter()
{
    if( PHP_SAPI == 'cli' )
    {
        $this->bootstrap( 'FrontController' );
        $front = $this->getResource( 'FrontController' );
        $front->setParam('disableOutputBuffering', true);
        $front->setRouter( new Application_Router_Cli() );
        $front->setRequest( new Zend_Controller_Request_Simple() );
    }
}

Ошибка Init, вероятно, будет barf, как написано выше, обработчик ошибок, вероятно, еще не создан, если вы не изменили конфигурацию по умолчанию.

protected function _initError ()
{
    $this->bootstrap( 'FrontController' );
    $front = $this->getResource( 'FrontController' );
    $front->registerPlugin( new Zend_Controller_Plugin_ErrorHandler() );
    $error = $front->getPlugin ('Zend_Controller_Plugin_ErrorHandler');
    $error->setErrorHandlerController('index');

    if (PHP_SAPI == 'cli')
    {
        $error->setErrorHandlerController ('error');
        $error->setErrorHandlerAction ('cli');
    }
}

Возможно, вы также захотите вывести из командной строки более одного параметра, вот базовый пример:

class Application_Router_Cli extends Zend_Controller_Router_Abstract
{
    public function route (Zend_Controller_Request_Abstract $dispatcher)
    {
        $getopt     = new Zend_Console_Getopt (array ());
        $arguments  = $getopt->getRemainingArgs();

        if ($arguments)
        {
            $command = array_shift( $arguments );
            $action  = array_shift( $arguments );
            if(!preg_match ('~\W~', $command) )
            {
                $dispatcher->setControllerName( $command );
                $dispatcher->setActionName( $action );
                $dispatcher->setParams( $arguments );
                return $dispatcher;
            }

            echo "Invalid command.\n", exit;

        }

        echo "No command given.\n", exit;
    }


    public function assemble ($userParams, $name = null, $reset = false, $encode = true)
    {
        echo "Not implemented\n", exit;
    }
}

Наконец, в вашем контроллере действие, которое вы вызываете, использует параметры, которые были потеряны путем удаления контроллера и действия с помощью маршрутизатора CLI:

public function echoAction()
{
    // disable rendering as required
    $database_name     = $this->getRequest()->getParam(0);        
    $udata             = array();

    if( ($udata = $this->getRequest()->getParam( 1 )) )
        $udata         = explode( ",", $udata );

    echo $database_name;
    var_dump( $udata );
}

Затем вы можете вызвать команду CLI с помощью:

php index.php Controller Action ....

Например, как указано выше:

php index.php Controller echo database123 this,becomes,an,array

Вы хотите реализовать более надежную фильтрацию/экранирование, но это быстрый строительный блок. Надеюсь, это поможет!

Ответ 5

Один из вариантов заключается в том, что вы можете выманить его, выполнив wget по URL-адресу, который используется для вызова желаемого действия.

Ответ 6

Вы не можете использовать -O вариант wget для сохранения вывода. Но wget явно НЕ является решением. Предпочитаете использовать CLI вместо этого.

Ответ 7

Идея akond отлично работает, за исключением исключения ошибки, которое не выводится контроллером ошибок.

public function cliAction() {
  $this->_helper->layout->disableLayout();
  $this->_helper->viewRenderer->setNoRender(true);

  foreach ($this->_getParam('error_handler') as $error) {
    if ($error instanceof Exception) {
      print "cli-error: " . $error->getMessage() . "\n";
    }
  }
}

и в приложении Application_Router_Cli комментировать off echo and die statement

public function assemble($userParams, $name = null, $reset = false, $encode = true) {
//echo "Not implemented\n";
}

Ответ 8

Вы можете просто использовать PHP, как обычно из командной строки. Если вы вызываете script из PHP и устанавливаете действие в script, вы можете запускать все, что хотите.

Это было бы очень просто. На самом деле это не предполагаемое использование, но так оно и может работать, если вы хотите.

Например

 PHP скрипт.php 

Читайте здесь: http://php.net/manual/en/features.commandline.php

Ответ 9

Вы можете использовать команду wget, если ваша ОС - Linux. Например:

wget http://example.com/controller/action

См. http://linux.about.com/od/commands/l/blcmdl1_wget.htm

UPDATE:

Вы можете написать простой bash script следующим образом:

if wget http://example.com/controller/action
    echo "Hello World!" > /home/wasdownloaded.txt
else
    "crap, wget timed out, let remove the file."
    rm /home/wasdownloaded.txt
fi

Тогда вы можете сделать в PHP:

if (true === file_exists('/home/wasdownloaded.txt') {
    // to check that the 
}

Надеюсь, что это поможет.

Ответ 10

Я использовал команду wget

wget http://example.com/module/controller/action -O /dev/null

-O /dev/null, если вы не хотите сохранять вывод