web-dev-qa-db-ja.com

コマンドラインからZend Frameworkアクションを実行する

Zend Frameworkアクションを実行して、コマンドラインからいくつかのファイルを生成したいと思います。これは可能ですか?ZFを使用している既存のWebプロジェクトにどの程度の変更を加える必要がありますか?

ありがとう!

62
Ariod

実際には、思っているよりもずっと簡単です。ブートストラップ/アプリケーションコンポーネントと既存の構成をCLIスクリプトで再利用しながら、HTTP要求で呼び出されるMVCスタックと不要な重みを回避できます。これは、wgetを使用しないことの利点の1つです。

公開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'));

MVCアプリケーションの場合と同様に、ZFリソースの使用に進むことができます。

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

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

CLIスクリプトに設定可能な引数を追加する場合は、 Zend_Console_Getopt をご覧ください

MVCアプリケーションでも呼び出す共通コードがある場合は、それをオブジェクトにラップし、MVCアプリケーションとコマンドラインアプリケーションの両方からそのオブジェクトのメソッドを呼び出すことを見てください。これは一般的な良い習慣です。

64

[〜#〜] update [〜#〜]

必要に応じて、 https://github.com/akond/zf-cli からこのコードをすべてZF 1.12に適合させることができます。

解決策#1は問題ありませんが、より複雑なものが必要な場合があります。特に、複数のCLIスクリプトがあると予想される場合。あなたが私を許可するなら、私は別の解決策を提案します。

まず、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を優先して、デフォルトルーターからディスパッチ制御を奪います。

ちなみに、Webインターフェイスの_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

この場合、BackupControllerコントローラーのcliActionメソッドが呼び出されます。

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

先に進んでApplication_Router_Cliクラスを変更して、毎回「cli」アクションを実行するのではなく、ユーザーが追加のパラメーターを通じて選択したアクションを実行することもできます。

最後にもう1つ。画面に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";
        }
    }
}
66
akond

これが私の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',
                    ),
                ),
            ),
        ),    
    ),
),

上記の設定を使用して、Applicationモジュールの下のIndexController.phpでdoCliActionを定義します。コマンドラインから実行するのは簡単です:

php index.php do cli

できた!よりスムーズに。

8
Saeven

上記のakondのソリューションは最良の軌道に乗っていますが、ご使用の環境ではスクリプトが機能しない可能性のある微妙な点がいくつかあります。彼の答えに対するこれらの微調整を考慮してください:

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() );
    }
}

上記のように初期化エラーが発生する可能性があります。デフォルトの設定を変更しない限り、エラーハンドラはおそらくまだインスタンス化されていません。

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

より堅牢なフィルタリング/エスケープを実装する必要がありますが、それは簡単な構成要素です。お役に立てれば!

6
Saeven

Wgetの-Oオプションを使用して出力を保存することはできません。しかし、wgetは明らかに解決策ではありません。代わりにCLIを使用することをお勧めします。

0
antoineg

1つのオプションは、望ましいアクションを呼び出すために使用するURLでwgetを実行することにより、それをごまかすことができるということです。

0
marsbomber

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で、echoおよびdieステートメントをコメントアウトします

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