diff -r 5a6b6e770365 -r 68c69c656a2c web/lib/Zend/Service/Console/Command.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/Zend/Service/Console/Command.php Thu May 07 15:16:02 2015 +0200 @@ -0,0 +1,413 @@ +_handler; + } + + /** + * Sets the handler. + * + * @param array $handler + * @return Zend_Service_Console_Command + */ + public function setHandler($handler) + { + $this->_handler = $handler; + return $this; + } + + /** + * Replaces PHP's error handler + * + * @param mixed $errno + * @param mixed $errstr + * @param mixed $errfile + * @param mixed $errline + */ + public static function phpstderr($errno, $errstr, $errfile, $errline) + { + self::stderr($errno . ': Error in ' . $errfile . ':' . $errline . ' - ' . $errstr); + } + + /** + * Replaces PHP's exception handler + * + * @param Exception $exception + */ + public static function phpstdex($exception) + { + self::stderr('Error: ' . $exception->getMessage()); + } + + /** + * Writes output to STDERR, followed by a newline (optional) + * + * @param string $errorMessage + * @param string $newLine + */ + public static function stderr($errorMessage, $newLine = true) + { + if (error_reporting() === 0) { + return; + } + file_put_contents('php://stderr', $errorMessage . ($newLine ? "\r\n" : '')); + } + + /** + * Bootstrap the shell command. + * + * @param array $argv PHP argument values. + */ + public static function bootstrap($argv) + { + // Abort bootstrapping depending on the MICROSOFT_CONSOLE_COMMAND_HOST constant. + if (defined('MICROSOFT_CONSOLE_COMMAND_HOST') && strtolower(MICROSOFT_CONSOLE_COMMAND_HOST) != 'console') { + return; + } + + // Replace error handler + set_error_handler(array('Zend_Service_Console_Command', 'phpstderr')); + set_exception_handler(array('Zend_Service_Console_Command', 'phpstdex')); + + // Build the application model + $model = self::_buildModel(); + + // Find a class that corresponds to the $argv[0] script name + $requiredHandlerName = str_replace('.bat', '', str_replace('.sh', '', str_replace('.php', '', strtolower(basename($argv[0]))))); + $handler = null; + foreach ($model as $possibleHandler) { + if ($possibleHandler->handler == strtolower($requiredHandlerName)) { + $handler = $possibleHandler; + break; + } + } + if (is_null($handler)) { + self::stderr("No class found that implements handler '" . $requiredHandlerName . "'. Create a class that is named '" . $requiredHandlerName . "' and extends Zend_Service_Console_Command or is decorated with a docblock comment '@command-handler " . $requiredHandlerName . "'. Make sure it is loaded either through an autoloader or explicitly using require_once()."); + die(); + } + + // Find a method that matches the command name + $command = null; + foreach ($handler->commands as $possibleCommand) { + if (in_array(strtolower(isset($argv[1]) ? $argv[1] : ''), $possibleCommand->aliases)) { + $command = $possibleCommand; + break; + } + } + if (is_null($command)) { + $commandName = (isset($argv[1]) ? $argv[1] : ''); + self::stderr("No method found that implements command " . $commandName . ". Create a method in class '" . $handler->class . "' that is named '" . strtolower($commandName) . "Command' or is decorated with a docblock comment '@command-name " . $commandName . "'."); + die(); + } + + // Parse parameter values + $parameterValues = array(); + $missingParameterValues = array(); + $parameterInputs = array_splice($argv, 2); + foreach ($command->parameters as $parameter) { + // Default value: null + $value = null; + + // Consult value providers for value. First one wins. + foreach ($parameter->valueproviders as $valueProviderName) { + if (!class_exists($valueProviderName)) { + $valueProviderName = 'Zend_Service_Console_Command_ParameterSource_' . $valueProviderName; + } + $valueProvider = new $valueProviderName(); + + $value = $valueProvider->getValueForParameter($parameter, $parameterInputs); + if (!is_null($value)) { + break; + } + } + if (is_null($value) && $parameter->required) { + $missingParameterValues[] = $parameter->aliases[0]; + } else if (is_null($value)) { + $value = $parameter->defaultvalue; + } + + // Set value + $parameterValues[] = $value; + $argvValues[$parameter->aliases[0]] = $value; + } + + // Mising parameters? + if (count($missingParameterValues) > 0) { + self::stderr("Some parameters are missing:\r\n" . implode("\r\n", $missingParameterValues)); + die(); + } + + // Supply argv in a nice way + $parameterValues['argv'] = $parameterInputs; + + // Run the command + $className = $handler->class; + $classInstance = new $className(); + $classInstance->setHandler($handler); + call_user_func_array(array($classInstance, $command->method), $parameterValues); + + // Restore error handler + restore_exception_handler(); + restore_error_handler(); + } + + /** + * Builds the handler model. + * + * @return array + */ + protected static function _buildModel() + { + $model = array(); + + $classes = get_declared_classes(); + foreach ($classes as $class) { + $type = new ReflectionClass($class); + + $handlers = self::_findValueForDocComment('@command-handler', $type->getDocComment()); + if (count($handlers) == 0 && $type->isSubclassOf('Zend_Service_Console_Command')) { + // Fallback: if the class extends Zend_Service_Console_Command, register it as + // a command handler. + $handlers[] = $class; + } + $handlerDescriptions = self::_findValueForDocComment('@command-handler-description', $type->getDocComment()); + $handlerHeaders = self::_findValueForDocComment('@command-handler-header', $type->getDocComment()); + $handlerFooters = self::_findValueForDocComment('@command-handler-footer', $type->getDocComment()); + + for ($hi = 0; $hi < count($handlers); $hi++) { + $handler = $handlers[$hi]; + $handlerDescription = isset($handlerDescriptions[$hi]) ? $handlerDescriptions[$hi] : isset($handlerDescriptions[0]) ? $handlerDescriptions[0] : ''; + $handlerDescription = str_replace('\r\n', "\r\n", $handlerDescription); + $handlerDescription = str_replace('\n', "\n", $handlerDescription); + + $handlerModel = (object)array( + 'handler' => strtolower($handler), + 'description' => $handlerDescription, + 'headers' => $handlerHeaders, + 'footers' => $handlerFooters, + 'class' => $class, + 'commands' => array() + ); + + $methods = $type->getMethods(); + foreach ($methods as $method) { + $commands = self::_findValueForDocComment('@command-name', $method->getDocComment()); + if (substr($method->getName(), -7) == 'Command' && !in_array(substr($method->getName(), 0, -7), $commands)) { + // Fallback: if the method is named Command, + // register it as a command. + $commands[] = substr($method->getName(), 0, -7); + } + for ($x = 0; $x < count($commands); $x++) { + $commands[$x] = strtolower($commands[$x]); + } + $commands = array_unique($commands); + $commandDescriptions = self::_findValueForDocComment('@command-description', $method->getDocComment()); + $commandExamples = self::_findValueForDocComment('@command-example', $method->getDocComment()); + + if (count($commands) > 0) { + $command = $commands[0]; + $commandDescription = isset($commandDescriptions[0]) ? $commandDescriptions[0] : ''; + + $commandModel = (object)array( + 'command' => $command, + 'aliases' => $commands, + 'description' => $commandDescription, + 'examples' => $commandExamples, + 'class' => $class, + 'method' => $method->getName(), + 'parameters' => array() + ); + + $parameters = $method->getParameters(); + $parametersFor = self::_findValueForDocComment('@command-parameter-for', $method->getDocComment()); + for ($pi = 0; $pi < count($parameters); $pi++) { + // Initialize + $parameter = $parameters[$pi]; + $parameterFor = null; + $parameterForDefaultValue = null; + + // Is it a "catch-all" parameter? + if ($parameter->getName() == 'argv') { + continue; + } + + // Find the $parametersFor with the same name defined + foreach ($parametersFor as $possibleParameterFor) { + $possibleParameterFor = explode(' ', $possibleParameterFor, 4); + if ($possibleParameterFor[0] == '$' . $parameter->getName()) { + $parameterFor = $possibleParameterFor; + break; + } + } + if (is_null($parameterFor)) { + die('@command-parameter-for missing for parameter $' . $parameter->getName()); + } + + if (is_null($parameterForDefaultValue) && $parameter->isOptional()) { + $parameterForDefaultValue = $parameter->getDefaultValue(); + } + + $parameterModel = (object)array( + 'name' => '$' . $parameter->getName(), + 'defaultvalue' => $parameterForDefaultValue, + 'valueproviders' => explode('|', $parameterFor[1]), + 'aliases' => explode('|', $parameterFor[2]), + 'description' => (isset($parameterFor[3]) ? $parameterFor[3] : ''), + 'required' => (isset($parameterFor[3]) ? strpos(strtolower($parameterFor[3]), 'required') !== false && strpos(strtolower($parameterFor[3]), 'required if') === false : false), + ); + + // Add to model + $commandModel->parameters[] = $parameterModel; + } + + // Add to model + $handlerModel->commands[] = $commandModel; + } + } + + // Add to model + $model[] = $handlerModel; + } + } + + return $model; + } + + /** + * Finds the value for a specific docComment. + * + * @param string $docCommentName Comment name + * @param unknown_type $docComment Comment object + * @return array + */ + protected static function _findValueForDocComment($docCommentName, $docComment) + { + $returnValue = array(); + + $commentLines = explode("\n", $docComment); + foreach ($commentLines as $commentLine) { + if (strpos($commentLine, $docCommentName . ' ') !== false) { + $returnValue[] = trim(substr($commentLine, strpos($commentLine, $docCommentName) + strlen($docCommentName) + 1)); + } + } + + return $returnValue; + } + + /** + * Display information on an object + * + * @param object $object Object + * @param array $propertiesToDump Property names to display + */ + protected function _displayObjectInformation($object, $propertiesToDump = array()) + { + foreach ($propertiesToDump as $property) { + printf('%-16s: %s' . "\r\n", $property, $object->$property); + } + printf("\r\n"); + } + + /** + * Displays the help information. + * + * @command-name + * @command-name -h + * @command-name -help + * @command-description Displays the current help information. + */ + public function helpCommand() { + $handler = $this->getHandler(); + $newline = "\r\n"; + + if (count($handler->headers) > 0) { + foreach ($handler->headers as $header) { + printf('%s%s', $header, $newline); + } + printf($newline); + } + printf('%s%s', $handler->description, $newline); + printf($newline); + printf('Available commands:%s', $newline); + foreach ($handler->commands as $command) { + $description = str_split($command->description, 50); + printf(' %-25s %s%s', implode(', ', $command->aliases), $description[0], $newline); + for ($di = 1; $di < count($description); $di++) { + printf(' %-25s %s%s', '', $description[$di], $newline); + } + printf($newline); + + if (count($command->parameters) > 0) { + foreach ($command->parameters as $parameter) { + $description = str_split($parameter->description, 50); + printf(' %-23s %s%s', implode(', ', $parameter->aliases), $description[0], $newline); + for ($di = 1; $di < count($description); $di++) { + printf(' %-23s %s%s', '', $description[$di], $newline); + } + printf($newline); + } + } + printf($newline); + + if (count($command->examples) > 0) { + printf(' Example usage:%s', $newline); + foreach ($command->examples as $example) { + printf(' %s%s', $example, $newline); + } + printf($newline); + } + } + + if (count($handler->footers) > 0) { + printf($newline); + foreach ($handler->footers as $footer) { + printf('%s%s', $footer, $newline); + } + printf($newline); + } + } +}