File indexing completed on 2025-01-19 05:21:35

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Tool
0017  * @subpackage Framework
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Console_GetOpt
0025  */
0026 // require_once 'Zend/Console/Getopt.php';
0027 
0028 /**
0029  * @see Zend_Tool_Framework_Registry_EnabledInterface
0030  */
0031 // require_once 'Zend/Tool/Framework/Registry/EnabledInterface.php';
0032 
0033 /**
0034  * @category   Zend
0035  * @package    Zend_Tool
0036  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0037  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0038  */
0039 class Zend_Tool_Framework_Client_Console_ArgumentParser implements Zend_Tool_Framework_Registry_EnabledInterface
0040 {
0041 
0042     /**
0043      * @var Zend_Tool_Framework_Registry_Interface
0044      */
0045     protected $_registry = null;
0046 
0047     /**
0048      * Holds the manifest repository taken from the registry.
0049      *
0050      * @var Zend_Tool_Framework_Manifest_Repository
0051      */
0052       protected $_manifestRepository = null;
0053 
0054     /**
0055      * @var Zend_Tool_Framework_Client_Request
0056      */
0057     protected $_request = null;
0058 
0059     /**
0060      * @var Zend_Tool_Framework_Client_Response
0061      */
0062     protected $_response = null;
0063 
0064     /**#@+
0065      * @var array
0066      */
0067     protected $_argumentsOriginal = null;
0068     protected $_argumentsWorking  = null;
0069     /**#@-*/
0070 
0071     /**
0072      * @var bool
0073      */
0074     protected $_help = false;
0075     protected $_helpKnownAction = false;
0076     protected $_helpKnownProvider = false;
0077     protected $_helpKnownSpecialty = false;
0078 
0079 
0080     /**
0081      * setArguments
0082      *
0083      * @param array $arguments
0084      * @return Zend_Tool_Framework_Client_Console_ArgumentParser
0085      */
0086     public function setArguments(Array $arguments)
0087     {
0088         $this->_argumentsOriginal = $this->_argumentsWorking = $arguments;
0089         return $this;
0090     }
0091 
0092     /**
0093      * setRegistry()
0094      *
0095      * @param Zend_Tool_Framework_Registry_Interface $registry
0096      * @return Zend_Tool_Framework_Client_Console_ArgumentParser
0097      */
0098     public function setRegistry(Zend_Tool_Framework_Registry_Interface $registry)
0099     {
0100         // get the client registry
0101         $this->_registry = $registry;
0102 
0103         // set manifest repository, request, response for easy access
0104         $this->_manifestRepository = $this->_registry->getManifestRepository();
0105         $this->_request  = $this->_registry->getRequest();
0106         $this->_response = $this->_registry->getResponse();
0107         return $this;
0108     }
0109 
0110     /**
0111      * Parse() - This method does the work of parsing the arguments into the enpooint request,
0112      * this will also (during help operations) fill the response in with information as needed
0113      *
0114      * @return null
0115      */
0116     public function parse()
0117     {
0118 
0119         if ($this->_request == null || $this->_response == null) {
0120             // require_once 'Zend/Tool/Framework/Client/Exception.php';
0121             throw new Zend_Tool_Framework_Client_Exception('The client registry must have both a request and response registered.');
0122         }
0123 
0124         // setup the help options
0125         $helpResponseOptions = array();
0126 
0127         // check to see if the first cli arg is the script name
0128         if ($this->_argumentsWorking[0] == $_SERVER['SCRIPT_NAME' ]) {
0129             array_shift($this->_argumentsWorking);
0130         }
0131 
0132         // process global options
0133         try {
0134             $this->_parseGlobalPart();
0135         } catch (Zend_Tool_Framework_Client_Exception $exception) {
0136             $this->_createHelpResponse(array('error' => $exception->getMessage()));
0137             return;
0138         }
0139 
0140         // ensure there are arguments left
0141         if (count($this->_argumentsWorking) == 0) {
0142             $this->_request->setDispatchable(false); // at this point request is not dispatchable
0143 
0144             // check to see if this was a help request
0145             if ($this->_help) {
0146                 $this->_createHelpResponse();
0147             } else {
0148                 $this->_createHelpResponse(array('error' => 'An action and provider is required.'));
0149             }
0150             return;
0151         }
0152 
0153         // process the action part of the command line
0154         try {
0155             $this->_parseActionPart();
0156         } catch (Zend_Tool_Framework_Client_Exception $exception) {
0157             $this->_request->setDispatchable(false);
0158             $this->_createHelpResponse(array('error' => $exception->getMessage()));
0159             return;
0160         }
0161 
0162         if ($this->_helpKnownAction) {
0163             $helpResponseOptions = array_merge(
0164                 $helpResponseOptions,
0165                 array('actionName' => $this->_request->getActionName())
0166                 );
0167         }
0168 
0169         /* @TODO Action Parameter Requirements */
0170 
0171         // make sure there are more "words" on the command line
0172         if (count($this->_argumentsWorking) == 0) {
0173             $this->_request->setDispatchable(false); // at this point request is not dispatchable
0174 
0175             // check to see if this is a help request
0176             if ($this->_help) {
0177                 $this->_createHelpResponse($helpResponseOptions);
0178             } else {
0179                 $this->_createHelpResponse(array_merge($helpResponseOptions, array('error' => 'A provider is required.')));
0180             }
0181             return;
0182         }
0183 
0184 
0185         // process the provider part of the command line
0186         try {
0187             $this->_parseProviderPart();
0188         } catch (Zend_Tool_Framework_Client_Exception $exception) {
0189             $this->_request->setDispatchable(false);
0190             $this->_createHelpResponse(array('error' => $exception->getMessage()));
0191             return;
0192         }
0193 
0194         if ($this->_helpKnownProvider) {
0195             $helpResponseOptions = array_merge(
0196                 $helpResponseOptions,
0197                 array('providerName' => $this->_request->getProviderName())
0198                 );
0199         }
0200 
0201         if ($this->_helpKnownSpecialty) {
0202             $helpResponseOptions = array_merge(
0203                 $helpResponseOptions,
0204                 array('specialtyName' => $this->_request->getSpecialtyName())
0205                 );
0206         }
0207 
0208         // if there are arguments on the command line, lets process them as provider options
0209         if (count($this->_argumentsWorking) != 0) {
0210             $this->_parseProviderOptionsPart();
0211         }
0212 
0213         // if there is still arguments lingering around, we can assume something is wrong
0214         if (count($this->_argumentsWorking) != 0) {
0215             $this->_request->setDispatchable(false); // at this point request is not dispatchable
0216             if ($this->_help) {
0217                 $this->_createHelpResponse($helpResponseOptions);
0218             } else {
0219                 $this->_createHelpResponse(array_merge(
0220                     $helpResponseOptions,
0221                     array('error' => 'Unknown arguments left on the command line: ' . implode(' ', $this->_argumentsWorking))
0222                     ));
0223             }
0224             return;
0225         }
0226 
0227         // everything was processed and this is a request for help information
0228         if ($this->_help) {
0229             $this->_request->setDispatchable(false); // at this point request is not dispatchable
0230             $this->_createHelpResponse($helpResponseOptions);
0231         }
0232 
0233         return;
0234     }
0235 
0236     /**
0237      * Internal routine for parsing global options from the command line
0238      *
0239      * @return null
0240      */
0241     protected function _parseGlobalPart()
0242     {
0243         $getoptOptions = array();
0244         $getoptOptions['help|h']    = 'HELP';
0245         $getoptOptions['verbose|v'] = 'VERBOSE';
0246         $getoptOptions['pretend|p'] = 'PRETEND';
0247         $getoptOptions['debug|d']   = 'DEBUG';
0248         $getoptParser = new Zend_Console_Getopt($getoptOptions, $this->_argumentsWorking, array('parseAll' => false));
0249 
0250         // @todo catch any exceptions here
0251         $getoptParser->parse();
0252 
0253         foreach ($getoptParser->getOptions() as $option) {
0254             if ($option == 'pretend') {
0255                 $this->_request->setPretend(true);
0256             } elseif ($option == 'debug') {
0257                 $this->_request->setDebug(true);
0258             } elseif ($option == 'verbose') {
0259                 $this->_request->setVerbose(true);
0260             } else {
0261                 $property = '_'.$option;
0262                 $this->{$property} = true;
0263             }
0264         }
0265 
0266         $this->_argumentsWorking = $getoptParser->getRemainingArgs();
0267 
0268         return;
0269     }
0270 
0271     /**
0272      * Internal routine for parsing the action name from the arguments
0273      *
0274      * @return null
0275      */
0276     protected function _parseActionPart()
0277     {
0278         // the next "word" should be the action name
0279         $consoleActionName = array_shift($this->_argumentsWorking);
0280 
0281         if ($consoleActionName == '?') {
0282             $this->_help = true;
0283             return;
0284         }
0285 
0286         $actionSearchCriteria = array(
0287             'type'       => 'Tool',
0288             'name'       => 'actionName',
0289             'value'      => $consoleActionName,
0290             'clientName' => 'console'
0291             );
0292 
0293         // is the action name valid?
0294         $actionMetadata = $this->_manifestRepository->getMetadata($actionSearchCriteria);
0295 
0296         // check for normalized names as well (all lower, no separators)
0297         if (!$actionMetadata) {
0298             $actionSearchCriteria['name']  = 'normalizedActionName';
0299             $actionSearchCriteria['value'] = strtolower(str_replace(array('-', '_'), '', $consoleActionName));
0300             $actionSearchCriteria['clientName'] = 'all';
0301             $actionMetadata = $this->_manifestRepository->getMetadata($actionSearchCriteria);
0302         }
0303 
0304         // if no action, handle error
0305         if (!$actionMetadata) {
0306             // require_once 'Zend/Tool/Framework/Client/Exception.php';
0307             throw new Zend_Tool_Framework_Client_Exception('Action \'' . $consoleActionName . '\' is not a valid action.');
0308         }
0309 
0310         // prepare action request name
0311         $this->_helpKnownAction = true;
0312         $this->_request->setActionName($actionMetadata->getActionName());
0313         return;
0314     }
0315 
0316     /**
0317      * Internal routine for parsing the provider part of the command line arguments
0318      *
0319      * @return null
0320      */
0321     protected function _parseProviderPart()
0322     {
0323         // get the cli "word" as the provider name from command line
0324         $consoleProviderFull = array_shift($this->_argumentsWorking);
0325         $consoleSpecialtyName = '_global';
0326 
0327         // if there is notation for specialties? If so, break them up
0328         if (strstr($consoleProviderFull, '.')) {
0329             list($consoleProviderName, $consoleSpecialtyName) = explode('.', $consoleProviderFull);
0330         } else {
0331             $consoleProviderName = $consoleProviderFull;
0332         }
0333 
0334         if ($consoleProviderName == '?') {
0335             $this->_help = true;
0336             return;
0337         }
0338 
0339         $providerSearchCriteria = array(
0340             'type'       => 'Tool',
0341             'name'       => 'providerName',
0342             'value'      => $consoleProviderName,
0343             'clientName' => 'console'
0344             );
0345 
0346         // get the cli provider names from the manifest
0347         $providerMetadata = $this->_manifestRepository->getMetadata($providerSearchCriteria);
0348 
0349         // check for normalized names as well (all lower, no separators)
0350         if (!$providerMetadata) {
0351             $providerSearchCriteria['name']  = 'normalizedProviderName';
0352             $providerSearchCriteria['value'] = strtolower(str_replace(array('-', '_'), '', $consoleProviderName));
0353             $providerSearchCriteria['clientName'] = 'all';
0354             $providerMetadata = $this->_manifestRepository->getMetadata($providerSearchCriteria);
0355         }
0356 
0357         if (!$providerMetadata) {
0358             // require_once 'Zend/Tool/Framework/Client/Exception.php';
0359             throw new Zend_Tool_Framework_Client_Exception(
0360                 'Provider \'' . $consoleProviderFull . '\' is not a valid provider.'
0361                 );
0362         }
0363 
0364         $this->_helpKnownProvider = true;
0365         $this->_request->setProviderName($providerMetadata->getProviderName());
0366 
0367         if ($consoleSpecialtyName == '?') {
0368             $this->_help = true;
0369             return;
0370         }
0371 
0372         $providerSpecialtySearchCriteria = array(
0373             'type'         => 'Tool',
0374             'name'         => 'specialtyName',
0375             'value'        => $consoleSpecialtyName,
0376             'providerName' => $providerMetadata->getProviderName(),
0377             'clientName'   => 'console'
0378             );
0379 
0380         $providerSpecialtyMetadata = $this->_manifestRepository->getMetadata($providerSpecialtySearchCriteria);
0381 
0382         if (!$providerSpecialtyMetadata) {
0383             $providerSpecialtySearchCriteria['name'] = 'normalizedSpecialtyName';
0384             $providerSpecialtySearchCriteria['value'] = strtolower(str_replace(array('-', '_'), '', $consoleSpecialtyName));
0385             $providerSpecialtySearchCriteria['clientName'] = 'all';
0386             $providerSpecialtyMetadata = $this->_manifestRepository->getMetadata($providerSpecialtySearchCriteria);
0387         }
0388 
0389         if (!$providerSpecialtyMetadata) {
0390             // require_once 'Zend/Tool/Framework/Client/Exception.php';
0391             throw new Zend_Tool_Framework_Client_Exception(
0392                 'Provider \'' . $consoleSpecialtyName . '\' is not a valid specialty.'
0393                 );
0394         }
0395 
0396         $this->_helpKnownSpecialty = true;
0397         $this->_request->setSpecialtyName($providerSpecialtyMetadata->getSpecialtyName());
0398         return;
0399     }
0400 
0401     /**
0402      * Internal routine for parsing the provider options from the command line
0403      *
0404      * @return null
0405      */
0406     protected function _parseProviderOptionsPart()
0407     {
0408         if (current($this->_argumentsWorking) == '?') {
0409             $this->_help = true;
0410             return;
0411         }
0412 
0413         $searchParams = array(
0414             'type'          => 'Tool',
0415             'providerName'  => $this->_request->getProviderName(),
0416             'actionName'    => $this->_request->getActionName(),
0417             'specialtyName' => $this->_request->getSpecialtyName(),
0418             'clientName'    => 'console'
0419             );
0420 
0421         $actionableMethodLongParamsMetadata = $this->_manifestRepository->getMetadata(
0422             array_merge($searchParams, array('name' => 'actionableMethodLongParams'))
0423             );
0424 
0425         $actionableMethodShortParamsMetadata = $this->_manifestRepository->getMetadata(
0426             array_merge($searchParams, array('name' => 'actionableMethodShortParams'))
0427             );
0428 
0429         $paramNameShortValues = $actionableMethodShortParamsMetadata->getValue();
0430 
0431         $getoptOptions = array();
0432         $wordArguments = array();
0433         $longParamCanonicalNames = array();
0434 
0435         $actionableMethodLongParamsMetadataReference = $actionableMethodLongParamsMetadata->getReference();
0436         foreach ($actionableMethodLongParamsMetadata->getValue() as $parameterNameLong => $consoleParameterNameLong) {
0437             $optionConfig = $consoleParameterNameLong . '|';
0438 
0439             $parameterInfo = $actionableMethodLongParamsMetadataReference['parameterInfo'][$parameterNameLong];
0440 
0441             // process ParameterInfo into array for command line option matching
0442             if ($parameterInfo['type'] == 'string' || $parameterInfo['type'] == 'bool') {
0443                 $optionConfig .= $paramNameShortValues[$parameterNameLong]
0444                                . (($parameterInfo['optional']) ? '-' : '=') . 's';
0445             } elseif (in_array($parameterInfo['type'], array('int', 'integer', 'float'))) {
0446                 $optionConfig .= $paramNameShortValues[$parameterNameLong]
0447                                . (($parameterInfo['optional']) ? '-' : '=') . 'i';
0448             } else {
0449                 $optionConfig .= $paramNameShortValues[$parameterNameLong] . '-s';
0450             }
0451 
0452             $getoptOptions[$optionConfig] = ($parameterInfo['description'] != '') ? $parameterInfo['description'] : 'No description available.';
0453 
0454 
0455             // process ParameterInfo into array for command line WORD (argument) matching
0456             $wordArguments[$parameterInfo['position']]['parameterName'] = $parameterInfo['name'];
0457             $wordArguments[$parameterInfo['position']]['optional']      = $parameterInfo['optional'];
0458             $wordArguments[$parameterInfo['position']]['type']          = $parameterInfo['type'];
0459 
0460             // keep a translation of console to canonical names
0461             $longParamCanonicalNames[$consoleParameterNameLong] = $parameterNameLong;
0462         }
0463 
0464 
0465         if (!$getoptOptions) {
0466             // no options to parse here, return
0467             return;
0468         }
0469 
0470         // if non-option arguments exist, attempt to process them before processing options
0471         $wordStack = array();
0472         while (($wordOnTop = array_shift($this->_argumentsWorking))) {
0473             if (substr($wordOnTop, 0, 1) != '-') {
0474                 array_push($wordStack, $wordOnTop);
0475             } else {
0476                 // put word back on stack and move on
0477                 array_unshift($this->_argumentsWorking, $wordOnTop);
0478                 break;
0479             }
0480 
0481             if (count($wordStack) == count($wordArguments)) {
0482                 // when we get at most the number of arguments we are expecting
0483                 // then break out.
0484                 break;
0485             }
0486 
0487         }
0488 
0489         if ($wordStack && $wordArguments) {
0490             for ($wordIndex = 1; $wordIndex <= count($wordArguments); $wordIndex++) {
0491                 if (!array_key_exists($wordIndex-1, $wordStack) || !array_key_exists($wordIndex, $wordArguments)) {
0492                     break;
0493                 }
0494                 $this->_request->setProviderParameter($wordArguments[$wordIndex]['parameterName'], $wordStack[$wordIndex-1]);
0495                 unset($wordStack[$wordIndex-1]);
0496             }
0497         }
0498 
0499         $getoptParser = new Zend_Console_Getopt($getoptOptions, $this->_argumentsWorking, array('parseAll' => false));
0500         $getoptParser->parse();
0501         foreach ($getoptParser->getOptions() as $option) {
0502             $value = $getoptParser->getOption($option);
0503             $providerParamOption = $longParamCanonicalNames[$option];
0504             $this->_request->setProviderParameter($providerParamOption, $value);
0505         }
0506 
0507         $this->_argumentsWorking = $getoptParser->getRemainingArgs();
0508 
0509         return;
0510     }
0511 
0512     /**
0513      * _createHelpResponse
0514      *
0515      * @param unknown_type $options
0516      */
0517     protected function _createHelpResponse($options = array())
0518     {
0519         // require_once 'Zend/Tool/Framework/Client/Console/HelpSystem.php';
0520         $helpSystem = new Zend_Tool_Framework_Client_Console_HelpSystem();
0521         $helpSystem->setRegistry($this->_registry);
0522 
0523         if (isset($options['error'])) {
0524             $helpSystem->respondWithErrorMessage($options['error']);
0525         }
0526 
0527         if (isset($options['actionName']) && isset($options['providerName'])) {
0528             $helpSystem->respondWithSpecialtyAndParamHelp($options['providerName'], $options['actionName']);
0529         } elseif (isset($options['actionName'])) {
0530             $helpSystem->respondWithActionHelp($options['actionName']);
0531         } elseif (isset($options['providerName'])) {
0532             $helpSystem->respondWithProviderHelp($options['providerName']);
0533         } else {
0534             $helpSystem->respondWithGeneralHelp();
0535         }
0536 
0537     }
0538 
0539 }