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