【问题标题】:Running a Zend Framework action from command line从命令行运行 Zend Framework 操作
【发布时间】:2011-01-20 11:40:32
【问题描述】:

我想从命令行运行 Zend Framework 操作来生成一些文件。这可能吗?我需要对使用 ZF 的现有 Web 项目进行多少更改?

谢谢!

【问题讨论】:

    标签: php zend-framework command-line


    【解决方案1】:

    刚刚看到这个在我的 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做cli

    完成! 更流畅。

    【讨论】:

    【解决方案2】:

    akond idea 效果很好,除了错误控制器没有呈现错误异常。

    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 and die 语句

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

    【讨论】:

      【解决方案3】:

      更新

      如果您愿意,您可以从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。

      顺便说一句,如果您在 _initRoutes 中为您的 Web 界面定义了自己的路由,您可能希望在命令行模式下取消它们。

      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”操作,而是用户通过附加参数选择的操作。

      最后一件事。为命令行界面定义自定义错误处理程序,这样您就不会在屏幕上看到任何 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";
              }
          }
      }
      

      【讨论】:

      • 这对我来说非常有效。我创建了一个单独的 CLI 引导程序,因此我可以只加载重要的组件,而无需任何通常的 Web 视图初始化。谢谢
      • 我必须在/library 中创建一个Application/router/Cli.php 对吗?
      • 是的。不要忘记通过 autoloadernamespaces[] = Application 为 application.ini 中的 Application 前缀打开自动加载
      • 我通常在我的 .htaccess 中设置我的 APPLICATION_ENV,这似乎绕过了它。使用此代码进行设置的最佳位置在哪里?
      【解决方案4】:

      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
      

      您会想要实现更强大的过滤/转义,但是,它是一个快速构建块。希望这会有所帮助!

      【讨论】:

      • 您能解释一下为什么禁用输出缓冲吗? $front->setParam('disableOutputBuffering', true);
      【解决方案5】:

      我用过 wget 命令

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

      -O /dev/null如果你不想保存输出

      【讨论】:

        【解决方案6】:

        您不能使用 wget 的 -O 选项来保存输出。但 wget 显然不是解决方案。更喜欢使用 CLI。

        【讨论】:

          【解决方案7】:

          如果您的操作系统是 Linux,您可以使用 wget 命令。例如:

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

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

          更新:

          你可以像这样编写一个简单的 bash 脚本:

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

          然后你可以在 PHP 中做:

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

          希望这会有所帮助。

          【讨论】:

          • wget 是可能的,但是你的脚本很容易超时
          • @David 您可以使用 if else 编写一个 bash 脚本,将 wget 的返回值保存到文件中,并使用 php 检查该文件是否存在。但这似乎有点过于复杂,我同意。不过,我实际上会更新我的答案。也许会有所帮助。
          【解决方案8】:

          实际上比您想象的要容易得多。引导程序/应用程序组件和您现有的配置可以通过 CLI 脚本重用,同时避免在 HTTP 请求中调用的 MVC 堆栈和不必要的权重。这是不使用 wget 的一大优势。

          像你的公共 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 和命令行应用程序调用该对象的方法。这是一般的良好做法。

          【讨论】:

          • 感谢您的广泛回答。您如何建议我在此类 CLI 应用程序中使用视图?例如,假设我想通过视图生成 CSV 或 XML 文件?在这种情况下,我可以利用完整的 MCV 模式吗?
          • Zend_View 实际上只是一个模板组件,它可以单独使用。字面意思是 $view = new Zend_View(); $view->var = '一些数据';然后 $view->render('/path/to/script.phtml');您还可以正常引导视图以设置路径/帮助程序/选项并从应用程序中检索它。如果您希望简单地替换 XML 文档中的一些细节,您可以使用视图。为了生成高度动态的文档,我只建议 SimpleXML 或 XMLWriter without a view
          • 谢谢 - 我现在在davidcaunt.co.uk/2010/02/25/…发布了一篇内容大致相同的博客文章
          【解决方案9】:

          您可以像往常一样从命令行使用 PHP。如果您从 PHP 调用脚本并在脚本中设置操作,那么您可以运行任何您想要的。

          真的很简单。 它并不是真正的预期用途,但是如果您愿意,它可以这样工作。

          例如

           php script.php 
          

          在这里阅读:http://php.net/manual/en/features.commandline.php

          【讨论】:

            【解决方案10】:

            一种选择是您可以通过对用于调用所需操作的 URL 执行 wget 来伪造它

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-01-22
              • 1970-01-01
              • 1970-01-01
              • 2022-12-29
              • 2011-01-29
              • 1970-01-01
              • 2017-12-11
              相关资源
              最近更新 更多