Je voudrais exécuter une action Zend Framework pour générer des fichiers, à partir de la ligne de commande. Est-ce possible et combien de changements devrais-je apporter à mon projet Web existant qui utilise ZF?
Merci!
C'est en fait beaucoup plus facile que vous ne le pensez. Les composants d'amorçage/d'application et vos configurations existantes peuvent être réutilisés avec des scripts CLI, tout en évitant la pile MVC et le poids inutile qui est invoqué dans une demande HTTP. C'est un avantage de ne pas utiliser wget.
Démarrez votre script comme le ferait votre index.php public:
<?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'));
Vous pouvez ensuite utiliser les ressources ZF comme vous le feriez dans une application MVC:
$db = $application->getBootstrap()->getResource('db');
$row = $db->fetchRow('SELECT * FROM something');
Si vous souhaitez ajouter des arguments configurables à votre script CLI, jetez un œil à Zend_Console_Getopt
Si vous constatez que vous disposez d'un code commun que vous appelez également dans les applications MVC, envisagez de l'envelopper dans un objet et d'appeler les méthodes de cet objet à partir du MVC et des applications en ligne de commande. Il s'agit d'une bonne pratique générale.
[~ # ~] mise à jour [~ # ~]
Bien que la solution n ° 1 soit correcte, vous souhaitez parfois quelque chose de plus élaboré. Surtout si vous vous attendez à avoir plus d'un seul script CLI. Si vous me le permettez, je proposerais une autre solution.
Tout d'abord, ayez dans votre 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 ());
}
}
Cette méthode privera le contrôle de répartition du routeur par défaut au profit de notre propre routeur Application_Router_Cli.
Par ailleurs, si vous avez défini vos propres routes dans _initRoutes pour votre interface Web, vous souhaiterez probablement les neutraliser en mode ligne de commande.
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
}
}
La classe Application_Router_Cli (je suppose que le chargement automatique est activé pour le préfixe d'application) peut ressembler à:
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;
}
}
Vous pouvez maintenant simplement exécuter votre application en exécutant
php index.php backup
Dans ce cas, la méthode cliAction du contrôleur BackupController sera appelée.
class BackupController extends Zend_Controller_Action
{
function cliAction ()
{
print "I'm here.\n";
}
}
Vous pouvez même aller de l'avant et modifier la classe Application_Router_Cli afin que l'action "cli" ne soit pas exécutée à chaque fois, mais quelque chose que l'utilisateur a choisi via un paramètre supplémentaire.
Et une dernière chose. Définissez un gestionnaire d'erreurs personnalisé pour l'interface de ligne de commande afin que vous ne voyiez aucun code html sur votre écran
Dans 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');
}
}
Dans ErrorController.php
function cliAction ()
{
$this->_helper->viewRenderer->setNoRender (true);
foreach ($this->_getParam ('error_handler') as $error)
{
if ($error instanceof Exception)
{
print $error->getMessage () . "\n";
}
}
}
Je viens de voir celui-ci se taguer dans mon CP. Si vous êtes tombé sur ce post et que vous utilisez ZF2, c'est BEAUCOUP plus facile. Modifiez simplement les routes de votre module.config.php comme ceci:
/**
* 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',
),
),
),
),
),
),
En utilisant la configuration ci-dessus, vous définiriez doCliAction dans votre IndexController.php sous votre module d'application. L'exécuter est un gâteau, à partir de la ligne de commande:
php index.php do cli
Terminé! Beaucoup plus fluide.
la solution d'Akond ci-dessus est sur la meilleure piste, mais il existe certaines subtilités qui peuvent empêcher son script de fonctionner dans votre environnement. Considérez ces ajustements à sa réponse:
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() );
}
}
L'erreur d'initialisation serait probablement de la manière indiquée ci-dessus, le gestionnaire d'erreur n'est probablement pas encore instancié, sauf si vous avez modifié la configuration par défaut.
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');
}
}
Vous voulez probablement aussi munir plus d'un paramètre de la ligne de commande, voici un exemple de base:
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;
}
}
Enfin, dans votre contrôleur, l'action que vous invoquez utilise les paramètres rendus orphelins par la suppression du contrôleur et l'action du routeur 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 );
}
Vous pouvez ensuite invoquer votre commande CLI avec:
php index.php Controller Action ....
Par exemple, comme ci-dessus:
php index.php Controller echo database123 this,becomes,an,array
Vous voudrez implémenter un filtrage/échappement plus robuste, mais c'est un bloc de construction rapide. J'espère que cela t'aides!
Vous ne pouvez pas utiliser l'option -O de wget pour enregistrer la sortie. Mais wget n'est clairement PAS la solution. Préférez utiliser CLI à la place.
Une option est que vous pouvez le truquer en faisant un wget sur l'URL que vous utilisez pour appeler l'action souhaitable
akond idea fonctionne très bien, sauf que l'exception d'erreur n'est pas restituée par le contrôleur d'erreur.
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";
}
}
}
et dans Application_Router_Cli, commentez l'instruction echo and die
public function assemble($userParams, $name = null, $reset = false, $encode = true) {
//echo "Not implemented\n";
}