Source for file Command.php
Documentation is available at Command.php
* Copyright (c) 2009 - 2011, RealDolmen
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of RealDolmen nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY RealDolmen ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL RealDolmen BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @package Microsoft_Console
* @version $Id: Exception.php 55733 2011-01-03 09:17:16Z unknown $
* @copyright Copyright (c) 2009 - 2011, RealDolmen (http://www.realdolmen.com)
* @license http://phpazure.codeplex.com/license
* @see Microsoft_AutoLoader
require_once dirname(__FILE__ ) . '/../AutoLoader.php';
* @package Microsoft_Console
* @copyright Copyright (c) 2009 - 2011, RealDolmen (http://www.realdolmen.com)
* @license http://phpazure.codeplex.com/license
* @return Microsoft_Console_Command
* Replaces PHP's error handler
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
public static function stderr($errorMessage, $newLine = true)
* Bootstrap the shell command.
* @param array $argv PHP argument values.
// Abort bootstrapping depending on the MICROSOFT_CONSOLE_COMMAND_HOST constant.
if (defined('MICROSOFT_CONSOLE_COMMAND_HOST') && strtolower(MICROSOFT_CONSOLE_COMMAND_HOST) != 'console') {
// Build the application model
$model = self::_buildModel();
// Find a class that corresponds to the $argv[0] script name
foreach ($model as $possibleHandler) {
if ($possibleHandler->handler == strtolower($requiredHandlerName)) {
$handler = $possibleHandler;
self::stderr("No class found that implements handler '" . $requiredHandlerName . "'. Create a class that is named '" . $requiredHandlerName . "' and extends Microsoft_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().");
// Find a method that matches the command name
foreach ($handler->commands as $possibleCommand) {
if (in_array(strtolower(isset ($argv[1]) ? $argv[1] : '<default>'), $possibleCommand->aliases)) {
$command = $possibleCommand;
$commandName = (isset ($argv[1]) ? $argv[1] : '<default>');
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 . "'.");
// Parse parameter values
$parameterValues = array();
$missingParameterValues = array();
foreach ($command->parameters as $parameter) {
// Consult value providers for value. First one wins.
foreach ($parameter->valueproviders as $valueProviderName) {
$valueProviderName = 'Microsoft_Console_Command_ParameterSource_' . $valueProviderName;
$valueProvider = new $valueProviderName();
$value = $valueProvider->getValueForParameter($parameter, $parameterInputs);
if (is_null($value) && $parameter->required) {
$missingParameterValues[] = $parameter->aliases[0];
$value = $parameter->defaultvalue;
$parameterValues[] = $value;
$argvValues[$parameter->aliases[0]] = $value;
if (count($missingParameterValues) > 0) {
self::stderr("Some parameters are missing:\r\n" . implode("\r\n", $missingParameterValues));
// Supply argv in a nice way
$parameterValues['argv'] = $parameterInputs;
$className = $handler->class;
$classInstance = new $className();
$classInstance->setHandler($handler);
* Builds the handler model.
foreach ($classes as $class) {
$type = new ReflectionClass($class);
$handlers = self::_findValueForDocComment('@command-handler', $type->getDocComment());
if (count($handlers) == 0 && $type->isSubclassOf('Microsoft_Console_Command')) {
// Fallback: if the class extends Microsoft_Console_Command, register it as
$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(
'description' => $handlerDescription,
'headers' => $handlerHeaders,
'footers' => $handlerFooters,
$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 <commandname>Command,
// register it as a command.
$commands[] = substr($method->getName(), 0, - 7);
for ($x = 0; $x < count($commands); $x++ ) {
$commandDescriptions = self::_findValueForDocComment('@command-description', $method->getDocComment());
$commandExamples = self::_findValueForDocComment('@command-example', $method->getDocComment());
if (count($commands) > 0) {
$commandDescription = isset ($commandDescriptions[0]) ? $commandDescriptions[0] : '';
$commandModel = (object) array(
'description' => $commandDescription,
'examples' => $commandExamples,
'method' => $method->getName(),
$parameters = $method->getParameters();
$parametersFor = self::_findValueForDocComment('@command-parameter-for', $method->getDocComment());
for ($pi = 0; $pi < count($parameters); $pi++ ) {
$parameter = $parameters[$pi];
$parameterForDefaultValue = null;
// Is it a "catch-all" parameter?
if ($parameter->getName() == 'argv') {
// Find the $parametersFor with the same name defined
foreach ($parametersFor as $possibleParameterFor) {
$possibleParameterFor = explode(' ', $possibleParameterFor, 4);
if ($possibleParameterFor[0] == '$' . $parameter->getName()) {
$parameterFor = $possibleParameterFor;
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),
$commandModel->parameters[] = $parameterModel;
$handlerModel->commands[] = $commandModel;
$model[] = $handlerModel;
* Finds the value for a specific docComment.
* @param string $docCommentName Comment name
* @param unknown_type $docComment Comment object
$commentLines = explode("\n", $docComment);
foreach ($commentLines as $commentLine) {
if (strpos($commentLine, $docCommentName . ' ') !== false) {
$returnValue[] = trim(substr($commentLine, strpos($commentLine, $docCommentName) + strlen($docCommentName) + 1));
* Display information on an object
* @param object $object Object
* @param array $propertiesToDump Property names to display
foreach ($propertiesToDump as $property) {
printf('%-16s: %s' . "\r\n", $property, $object->$property);
* Displays the help information.
* @command-name <default>
* @command-description Displays the current help information.
if (count($handler->headers) > 0) {
foreach ($handler->headers as $header) {
printf('%s%s', $header, $newline);
printf('%s%s', $handler->description, $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);
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);
if (count($command->examples) > 0) {
printf(' Example usage:%s', $newline);
foreach ($command->examples as $example) {
printf(' %s%s', $example, $newline);
if (count($handler->footers) > 0) {
foreach ($handler->footers as $footer) {
printf('%s%s', $footer, $newline);
|