PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Tool/Framework/Client/Console/ArgumentParser.php

http://github.com/zendframework/zf2
PHP | 542 lines | 327 code | 86 blank | 129 comment | 67 complexity | 44ce1617f46eb0c766efa121a7619592 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Tool
  17. * @subpackage Framework
  18. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /**
  22. * @namespace
  23. */
  24. namespace Zend\Tool\Framework\Client\Console;
  25. use Zend\Tool\Framework\Client,
  26. Zend\Tool\Framework\Registry,
  27. Zend\Tool\Framework\RegistryEnabled,
  28. Zend\Tool\Framework\Exception;
  29. /**
  30. * @uses Zend_Console_GetOpt
  31. * @uses \Zend\Tool\Framework\Client\Console\HelpSystem
  32. * @uses \Zend\Tool\Framework\Client\Exception
  33. * @uses \Zend\Tool\Framework\RegistryEnabled
  34. * @category Zend
  35. * @package Zend_Tool
  36. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  37. * @license http://framework.zend.com/license/new-bsd New BSD License
  38. */
  39. class ArgumentParser implements RegistryEnabled
  40. {
  41. /**
  42. * @var \Zend\Tool\Framework\Registry
  43. */
  44. protected $_registry = null;
  45. /**
  46. * Holds the manifest repository taken from the registry.
  47. *
  48. * @var Zend_Tool_Framework_Manifest_Repository
  49. */
  50. protected $_manifestRepository = null;
  51. /**
  52. * @var \Zend\Tool\Framework\Client\Request
  53. */
  54. protected $_request = null;
  55. /**
  56. * @var \Zend\Tool\Framework\Client\Response
  57. */
  58. protected $_response = null;
  59. /**#@+
  60. * @var array
  61. */
  62. protected $_argumentsOriginal = null;
  63. protected $_argumentsWorking = null;
  64. /**#@-*/
  65. /**
  66. * @var bool
  67. */
  68. protected $_help = false;
  69. protected $_helpKnownAction = false;
  70. protected $_helpKnownProvider = false;
  71. protected $_helpKnownSpecialty = false;
  72. /**
  73. * setArguments
  74. *
  75. * @param array $arguments
  76. * @return \Zend\Tool\Framework\Client\Console\ArgumentParser
  77. */
  78. public function setArguments(Array $arguments)
  79. {
  80. $this->_argumentsOriginal = $this->_argumentsWorking = $arguments;
  81. return $this;
  82. }
  83. /**
  84. * setRegistry()
  85. *
  86. * @param \Zend\Tool\Framework\Registry $registry
  87. * @return \Zend\Tool\Framework\Client\Console\ArgumentParser
  88. */
  89. public function setRegistry(Registry $registry)
  90. {
  91. // get the client registry
  92. $this->_registry = $registry;
  93. // set manifest repository, request, response for easy access
  94. $this->_manifestRepository = $this->_registry->getManifestRepository();
  95. $this->_request = $this->_registry->getRequest();
  96. $this->_response = $this->_registry->getResponse();
  97. return $this;
  98. }
  99. /**
  100. * Parse() - This method does the work of parsing the arguments into the enpooint request,
  101. * this will also (during help operations) fill the response in with information as needed
  102. *
  103. * @return null
  104. */
  105. public function parse()
  106. {
  107. if ($this->_request == null || $this->_response == null) {
  108. throw new Exception\RuntimeException('The client registry must have both a request and response registered.');
  109. }
  110. // setup the help options
  111. $helpResponseOptions = array();
  112. // check to see if the first cli arg is the script name
  113. if ($this->_argumentsWorking[0] == $_SERVER['SCRIPT_NAME' ]) {
  114. array_shift($this->_argumentsWorking);
  115. }
  116. // process global options
  117. try {
  118. $this->_parseGlobalPart();
  119. } catch (Client\Exception $exception) {
  120. $this->_createHelpResponse(array('error' => $exception->getMessage()));
  121. return;
  122. }
  123. // ensure there are arguments left
  124. if (count($this->_argumentsWorking) == 0) {
  125. $this->_request->setDispatchable(false); // at this point request is not dispatchable
  126. // check to see if this was a help request
  127. if ($this->_help) {
  128. $this->_createHelpResponse();
  129. } else {
  130. $this->_createHelpResponse(array('error' => 'An action and provider is required.'));
  131. }
  132. return;
  133. }
  134. // process the action part of the command line
  135. try {
  136. $this->_parseActionPart();
  137. } catch (Client\Exception $exception) {
  138. $this->_request->setDispatchable(false);
  139. $this->_createHelpResponse(array('error' => $exception->getMessage()));
  140. return;
  141. }
  142. if ($this->_helpKnownAction) {
  143. $helpResponseOptions = array_merge(
  144. $helpResponseOptions,
  145. array('actionName' => $this->_request->getActionName())
  146. );
  147. }
  148. /* @TODO Action Parameter Requirements */
  149. // make sure there are more "words" on the command line
  150. if (count($this->_argumentsWorking) == 0) {
  151. $this->_request->setDispatchable(false); // at this point request is not dispatchable
  152. // check to see if this is a help request
  153. if ($this->_help) {
  154. $this->_createHelpResponse($helpResponseOptions);
  155. } else {
  156. $this->_createHelpResponse(array_merge($helpResponseOptions, array('error' => 'A provider is required.')));
  157. }
  158. return;
  159. }
  160. // process the provider part of the command line
  161. try {
  162. $this->_parseProviderPart();
  163. } catch (Client\Exception $exception) {
  164. $this->_request->setDispatchable(false);
  165. $this->_createHelpResponse(array('error' => $exception->getMessage()));
  166. return;
  167. }
  168. if ($this->_helpKnownProvider) {
  169. $helpResponseOptions = array_merge(
  170. $helpResponseOptions,
  171. array('providerName' => $this->_request->getProviderName())
  172. );
  173. }
  174. if ($this->_helpKnownSpecialty) {
  175. $helpResponseOptions = array_merge(
  176. $helpResponseOptions,
  177. array('specialtyName' => $this->_request->getSpecialtyName())
  178. );
  179. }
  180. // if there are arguments on the command line, lets process them as provider options
  181. if (count($this->_argumentsWorking) != 0) {
  182. $this->_parseProviderOptionsPart();
  183. }
  184. // if there is still arguments lingering around, we can assume something is wrong
  185. if (count($this->_argumentsWorking) != 0) {
  186. $this->_request->setDispatchable(false); // at this point request is not dispatchable
  187. if ($this->_help) {
  188. $this->_createHelpResponse($helpResponseOptions);
  189. } else {
  190. $this->_createHelpResponse(array_merge(
  191. $helpResponseOptions,
  192. array('error' => 'Unknown arguments left on the command line: ' . implode(' ', $this->_argumentsWorking))
  193. ));
  194. }
  195. return;
  196. }
  197. // everything was processed and this is a request for help information
  198. if ($this->_help) {
  199. $this->_request->setDispatchable(false); // at this point request is not dispatchable
  200. $this->_createHelpResponse($helpResponseOptions);
  201. }
  202. return;
  203. }
  204. /**
  205. * Internal routine for parsing global options from the command line
  206. *
  207. * @return null
  208. */
  209. protected function _parseGlobalPart()
  210. {
  211. $getoptOptions = array();
  212. $getoptOptions['help|h'] = 'HELP';
  213. $getoptOptions['verbose|v'] = 'VERBOSE';
  214. $getoptOptions['pretend|p'] = 'PRETEND';
  215. $getoptOptions['debug|d'] = 'DEBUG';
  216. $getoptParser = new \Zend\Console\Getopt($getoptOptions, $this->_argumentsWorking, array('parseAll' => false));
  217. // @todo catch any exceptions here
  218. $getoptParser->parse();
  219. foreach ($getoptParser->getOptions() as $option) {
  220. if ($option == 'pretend') {
  221. $this->_request->setPretend(true);
  222. } elseif ($option == 'debug') {
  223. $this->_request->setDebug(true);
  224. } elseif ($option == 'verbose') {
  225. $this->_request->setVerbose(true);
  226. } else {
  227. $property = '_'.$option;
  228. $this->{$property} = true;
  229. }
  230. }
  231. $this->_argumentsWorking = $getoptParser->getRemainingArgs();
  232. return;
  233. }
  234. /**
  235. * Internal routine for parsing the action name from the arguments
  236. *
  237. * @return null
  238. */
  239. protected function _parseActionPart()
  240. {
  241. // the next "word" should be the action name
  242. $consoleActionName = array_shift($this->_argumentsWorking);
  243. if ($consoleActionName == '?') {
  244. $this->_help = true;
  245. return;
  246. }
  247. $actionSearchCriteria = array(
  248. 'type' => 'Tool',
  249. 'name' => 'actionName',
  250. 'value' => $consoleActionName,
  251. 'clientName' => 'console'
  252. );
  253. // is the action name valid?
  254. $actionMetadata = $this->_manifestRepository->getMetadata($actionSearchCriteria);
  255. // check for normalized names as well (all lower, no separators)
  256. if (!$actionMetadata) {
  257. $actionSearchCriteria['name'] = 'normalizedActionName';
  258. $actionSearchCriteria['value'] = strtolower(str_replace(array('-', '_'), '', $consoleActionName));
  259. $actionSearchCriteria['clientName'] = 'all';
  260. $actionMetadata = $this->_manifestRepository->getMetadata($actionSearchCriteria);
  261. }
  262. // if no action, handle error
  263. if (!$actionMetadata) {
  264. throw new Exception\RuntimeException(sprintf(
  265. 'Action "%s" is not a valid action.',
  266. $consoleActionName
  267. ));
  268. }
  269. // prepare action request name
  270. $this->_helpKnownAction = true;
  271. $this->_request->setActionName($actionMetadata->getActionName());
  272. return;
  273. }
  274. /**
  275. * Internal routine for parsing the provider part of the command line arguments
  276. *
  277. * @return null
  278. */
  279. protected function _parseProviderPart()
  280. {
  281. // get the cli "word" as the provider name from command line
  282. $consoleProviderFull = array_shift($this->_argumentsWorking);
  283. $consoleSpecialtyName = '_global';
  284. // if there is notation for specialties? If so, break them up
  285. if (strstr($consoleProviderFull, '.')) {
  286. list($consoleProviderName, $consoleSpecialtyName) = explode('.', $consoleProviderFull);
  287. } else {
  288. $consoleProviderName = $consoleProviderFull;
  289. }
  290. if ($consoleProviderName == '?') {
  291. $this->_help = true;
  292. return;
  293. }
  294. $providerSearchCriteria = array(
  295. 'type' => 'Tool',
  296. 'name' => 'providerName',
  297. 'value' => $consoleProviderName,
  298. 'clientName' => 'console'
  299. );
  300. // get the cli provider names from the manifest
  301. $providerMetadata = $this->_manifestRepository->getMetadata($providerSearchCriteria);
  302. // check for normalized names as well (all lower, no separators)
  303. if (!$providerMetadata) {
  304. $providerSearchCriteria['name'] = 'normalizedProviderName';
  305. $providerSearchCriteria['value'] = strtolower(str_replace(array('-', '_'), '', $consoleProviderName));
  306. $providerSearchCriteria['clientName'] = 'all';
  307. $providerMetadata = $this->_manifestRepository->getMetadata($providerSearchCriteria);
  308. }
  309. if (!$providerMetadata) {
  310. throw new Exception\RuntimeException(sprintf(
  311. 'Provider "%s" is not a valid provider.',
  312. $consoleProviderFull
  313. ));
  314. }
  315. $this->_helpKnownProvider = true;
  316. $this->_request->setProviderName($providerMetadata->getProviderName());
  317. if ($consoleSpecialtyName == '?') {
  318. $this->_help = true;
  319. return;
  320. }
  321. $providerSpecialtySearchCriteria = array(
  322. 'type' => 'Tool',
  323. 'name' => 'specialtyName',
  324. 'value' => $consoleSpecialtyName,
  325. 'providerName' => $providerMetadata->getProviderName(),
  326. 'clientName' => 'console'
  327. );
  328. $providerSpecialtyMetadata = $this->_manifestRepository->getMetadata($providerSpecialtySearchCriteria);
  329. if (!$providerSpecialtyMetadata) {
  330. $providerSpecialtySearchCriteria['name'] = 'normalizedSpecialtyName';
  331. $providerSpecialtySearchCriteria['value'] = strtolower(str_replace(array('-', '_'), '', $consoleSpecialtyName));
  332. $providerSpecialtySearchCriteria['clientName'] = 'all';
  333. $providerSpecialtyMetadata = $this->_manifestRepository->getMetadata($providerSpecialtySearchCriteria);
  334. }
  335. if (!$providerSpecialtyMetadata) {
  336. throw new Exception\RuntimeException(sprintf(
  337. 'Provider "%s" is not a valid specialty.',
  338. $consoleSpecialtyName
  339. ));
  340. }
  341. $this->_helpKnownSpecialty = true;
  342. $this->_request->setSpecialtyName($providerSpecialtyMetadata->getSpecialtyName());
  343. return;
  344. }
  345. /**
  346. * Internal routine for parsing the provider options from the command line
  347. *
  348. * @return null
  349. */
  350. protected function _parseProviderOptionsPart()
  351. {
  352. if (current($this->_argumentsWorking) == '?') {
  353. $this->_help = true;
  354. return;
  355. }
  356. $searchParams = array(
  357. 'type' => 'Tool',
  358. 'providerName' => $this->_request->getProviderName(),
  359. 'actionName' => $this->_request->getActionName(),
  360. 'specialtyName' => $this->_request->getSpecialtyName(),
  361. 'clientName' => 'console'
  362. );
  363. $actionableMethodLongParamsMetadata = $this->_manifestRepository->getMetadata(
  364. array_merge($searchParams, array('name' => 'actionableMethodLongParams'))
  365. );
  366. $actionableMethodShortParamsMetadata = $this->_manifestRepository->getMetadata(
  367. array_merge($searchParams, array('name' => 'actionableMethodShortParams'))
  368. );
  369. $paramNameShortValues = $actionableMethodShortParamsMetadata->getValue();
  370. $getoptOptions = array();
  371. $wordArguments = array();
  372. $longParamCanonicalNames = array();
  373. $actionableMethodLongParamsMetadataReference = $actionableMethodLongParamsMetadata->getReference();
  374. foreach ($actionableMethodLongParamsMetadata->getValue() as $parameterNameLong => $consoleParameterNameLong) {
  375. $optionConfig = $consoleParameterNameLong . '|';
  376. $parameterInfo = $actionableMethodLongParamsMetadataReference['parameterInfo'][$parameterNameLong];
  377. // process ParameterInfo into array for command line option matching
  378. if ($parameterInfo['type'] == 'string' || $parameterInfo['type'] == 'bool') {
  379. $optionConfig .= $paramNameShortValues[$parameterNameLong]
  380. . (($parameterInfo['optional']) ? '-' : '=') . 's';
  381. } elseif (in_array($parameterInfo['type'], array('int', 'integer', 'float'))) {
  382. $optionConfig .= $paramNameShortValues[$parameterNameLong]
  383. . (($parameterInfo['optional']) ? '-' : '=') . 'i';
  384. } else {
  385. $optionConfig .= $paramNameShortValues[$parameterNameLong] . '-s';
  386. }
  387. $getoptOptions[$optionConfig] = ($parameterInfo['description'] != '') ? $parameterInfo['description'] : 'No description available.';
  388. // process ParameterInfo into array for command line WORD (argument) matching
  389. $wordArguments[$parameterInfo['position']]['parameterName'] = $parameterInfo['name'];
  390. $wordArguments[$parameterInfo['position']]['optional'] = $parameterInfo['optional'];
  391. $wordArguments[$parameterInfo['position']]['type'] = $parameterInfo['type'];
  392. // keep a translation of console to canonical names
  393. $longParamCanonicalNames[$consoleParameterNameLong] = $parameterNameLong;
  394. }
  395. if (!$getoptOptions) {
  396. // no options to parse here, return
  397. return;
  398. }
  399. // if non-option arguments exist, attempt to process them before processing options
  400. $wordStack = array();
  401. while (($wordOnTop = array_shift($this->_argumentsWorking))) {
  402. if (substr($wordOnTop, 0, 1) != '-') {
  403. array_push($wordStack, $wordOnTop);
  404. } else {
  405. // put word back on stack and move on
  406. array_unshift($this->_argumentsWorking, $wordOnTop);
  407. break;
  408. }
  409. if (count($wordStack) == count($wordArguments)) {
  410. // when we get at most the number of arguments we are expecting
  411. // then break out.
  412. break;
  413. }
  414. }
  415. if ($wordStack && $wordArguments) {
  416. for ($wordIndex = 1; $wordIndex <= count($wordArguments); $wordIndex++) {
  417. if (!array_key_exists($wordIndex-1, $wordStack) || !array_key_exists($wordIndex, $wordArguments)) {
  418. break;
  419. }
  420. $this->_request->setProviderParameter($wordArguments[$wordIndex]['parameterName'], $wordStack[$wordIndex-1]);
  421. unset($wordStack[$wordIndex-1]);
  422. }
  423. }
  424. $getoptParser = new \Zend\Console\Getopt($getoptOptions, $this->_argumentsWorking, array('parseAll' => false));
  425. $getoptParser->parse();
  426. foreach ($getoptParser->getOptions() as $option) {
  427. $value = $getoptParser->getOption($option);
  428. $providerParamOption = $longParamCanonicalNames[$option];
  429. $this->_request->setProviderParameter($providerParamOption, $value);
  430. }
  431. $this->_argumentsWorking = $getoptParser->getRemainingArgs();
  432. return;
  433. }
  434. /**
  435. * _createHelpResponse
  436. *
  437. * @param unknown_type $options
  438. */
  439. protected function _createHelpResponse($options = array())
  440. {
  441. $helpSystem = new HelpSystem();
  442. $helpSystem->setRegistry($this->_registry);
  443. if (isset($options['error'])) {
  444. $helpSystem->respondWithErrorMessage($options['error']);
  445. }
  446. if (isset($options['actionName']) && isset($options['providerName'])) {
  447. $helpSystem->respondWithSpecialtyAndParamHelp($options['providerName'], $options['actionName']);
  448. } elseif (isset($options['actionName'])) {
  449. $helpSystem->respondWithActionHelp($options['actionName']);
  450. } elseif (isset($options['providerName'])) {
  451. $helpSystem->respondWithProviderHelp($options['providerName']);
  452. } else {
  453. $helpSystem->respondWithGeneralHelp();
  454. }
  455. }
  456. }