482 lines
15 KiB
PHP
482 lines
15 KiB
PHP
<?php
|
|
/*
|
|
* $Id$
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "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 THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS 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.
|
|
*
|
|
* This software consists of voluntary contributions made by many individuals
|
|
* and is licensed under the LGPL. For more information, see
|
|
* <http://www.doctrine-project.org>.
|
|
*/
|
|
|
|
namespace Doctrine\Common\CLI;
|
|
|
|
use Doctrine\Common\CLI\Printers\AbstractPrinter;
|
|
|
|
/**
|
|
* CLI Option Group definition
|
|
*
|
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
|
* @link www.doctrine-project.org
|
|
* @since 2.0
|
|
* @version $Revision$
|
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
|
* @author Jonathan Wage <jonwage@gmail.com>
|
|
* @author Roman Borschel <roman@code-factory.org>
|
|
*/
|
|
class OptionGroup
|
|
{
|
|
/* CLI Option Group CARDINALITY */
|
|
/**
|
|
* Defines the cardinality 0..N to CLI Option Group.
|
|
* This means options in this group are optional and you can
|
|
* define more than one CLI Option on a single command.
|
|
*/
|
|
const CARDINALITY_0_N = 0; // [...] [...] [...]
|
|
|
|
/**
|
|
* Defines the cardinality 0..1 to CLI Option Group.
|
|
* This means all options in this group are optional and you can
|
|
* define only one CLI Option on a single command.
|
|
*/
|
|
const CARDINALITY_0_1 = 1; // [...|...|...]
|
|
|
|
/**
|
|
* Defines the cardinality 1..1 to CLI Option Group.
|
|
* This means all options in this group are required and you must
|
|
* define only one CLI Option on a single command.
|
|
*/
|
|
const CARDINALITY_1_1 = 2; // (...|...|...)
|
|
|
|
/**
|
|
* Defines the cardinality 1..N to CLI Option Group.
|
|
* This means all options in this group are required and you must
|
|
* define at least one CLI Option on a single command.
|
|
*/
|
|
const CARDINALITY_1_N = 3; // (... ... ...)
|
|
|
|
/**
|
|
* Defines the cardinality N..N to CLI Option Group.
|
|
* This means all options in this group are required and you must
|
|
* define all CLI Options on a single command.
|
|
*/
|
|
const CARDINALITY_N_N = 4; // ... ... ...
|
|
|
|
/**
|
|
* Defines the cardinality M..N to CLI Option Group.
|
|
* This means all options in this group are either required or
|
|
* optional and you can CLI Options on a single command.
|
|
* This is the option to skip CLI Option validation.
|
|
*/
|
|
const CARDINALITY_M_N = 5; // ... ... ...
|
|
|
|
|
|
/** @var integer Option Group cardinality */
|
|
private $_cadinality;
|
|
|
|
/** @var array Option Group list of CLI Options */
|
|
private $_options;
|
|
|
|
|
|
/**
|
|
* Constructs a new CLI Option Group
|
|
*
|
|
* @param integer Option Group cardinality
|
|
* @param array CLI Option Group options
|
|
*/
|
|
public function __construct($cardinality, $options = array())
|
|
{
|
|
$this->_cardinality = $cardinality;
|
|
$this->_options = $options;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the CLI Option Group cardinality
|
|
*
|
|
* @return integer Option Group cardinality
|
|
*/
|
|
public function getCardinality()
|
|
{
|
|
return $this->_cardinality;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the CLI Option Group options
|
|
*
|
|
* @return array Option Group options
|
|
*/
|
|
public function getOptions()
|
|
{
|
|
return $this->_options;
|
|
}
|
|
|
|
/**
|
|
* Cleans the CLI Options inside this CLI Option Group
|
|
*
|
|
*/
|
|
public function clear()
|
|
{
|
|
$this->_options = array();
|
|
}
|
|
|
|
/**
|
|
* Includes a new CLI Option to the Option Group
|
|
*
|
|
* @param Option|OptionGroup CLI Option or CLI Option Group
|
|
* @return OptionGroup This object instance
|
|
*/
|
|
public function addOption($option)
|
|
{
|
|
if ($option instanceof Option || $option instanceof OptionGroup) {
|
|
$this->_options[] = $option;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Formats the CLI Option Group into a single line representation
|
|
*
|
|
* @param AbstractPrinter CLI Printer
|
|
* @return string Single line string representation of CLI Option Group
|
|
*/
|
|
public function formatPlain(AbstractPrinter $printer)
|
|
{
|
|
$numOptions = count($this->_options);
|
|
|
|
if ($numOptions == 0) {
|
|
return '';
|
|
}
|
|
|
|
$style = $this->_getGroupOptionStyle();
|
|
$shouldDisplayExtras = (
|
|
$numOptions > 1 ||
|
|
$this->_cardinality == self::CARDINALITY_0_1 ||
|
|
$this->_cardinality == self::CARDINALITY_0_N
|
|
);
|
|
|
|
$str = ($shouldDisplayExtras) ? $printer->format($this->_startGroupDeclaration(), $style) : '';
|
|
|
|
// Loop through all CLI Options defined in OptionGroup
|
|
for ($i = 0; $i < $numOptions; $i++) {
|
|
$option = $this->_options[$i];
|
|
|
|
// Check for possible recursive OptionGroup
|
|
if ($option instanceof OptionGroup) {
|
|
// Simple increase nesting level by calling format recursively
|
|
$str .= $option->formatPlain($printer);
|
|
} else {
|
|
// Expose the option formatted
|
|
$str .= $printer->format((string) $option, $style);
|
|
}
|
|
|
|
// Possibly append content if needed
|
|
if ($i < $numOptions - 1) {
|
|
$str .= $printer->format($this->_separatorGroupDeclaration(), $style);
|
|
}
|
|
}
|
|
|
|
$str .= ($shouldDisplayExtras) ? $printer->format($this->_endGroupDeclaration(), $style) : '';
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* INTERNAL:
|
|
* Defines the start Option Group declaration string
|
|
*
|
|
* @return string Start Option Group declaration string
|
|
*/
|
|
private function _startGroupDeclaration()
|
|
{
|
|
$str = '';
|
|
|
|
// Inspect cardinality of OptionGroup
|
|
switch ($this->_cardinality) {
|
|
case self::CARDINALITY_0_1:
|
|
case self::CARDINALITY_0_N:
|
|
$str .= '[';
|
|
break;
|
|
|
|
case self::CARDINALITY_1_1:
|
|
case self::CARDINALITY_1_N:
|
|
$str .= '(';
|
|
break;
|
|
|
|
case self::CARDINALITY_N_N:
|
|
case self::CARDINALITY_M_N:
|
|
default:
|
|
// Does nothing
|
|
break;
|
|
}
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* INTERNAL:
|
|
* Defines the separator Option Group declaration string
|
|
*
|
|
* @return string Separator Option Group declaration string
|
|
*/
|
|
private function _separatorGroupDeclaration()
|
|
{
|
|
$str = '';
|
|
|
|
// Inspect cardinality of OptionGroup
|
|
switch ($this->_cardinality) {
|
|
case self::CARDINALITY_0_1:
|
|
case self::CARDINALITY_1_1:
|
|
$str .= ' | ';
|
|
break;
|
|
|
|
case self::CARDINALITY_1_N:
|
|
case self::CARDINALITY_N_N:
|
|
case self::CARDINALITY_M_N:
|
|
$str .= ' ';
|
|
break;
|
|
|
|
case self::CARDINALITY_0_N:
|
|
$str .= '] [';
|
|
break;
|
|
|
|
default:
|
|
// Does nothing
|
|
break;
|
|
}
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* INTERNAL:
|
|
* Defines the end Option Group declaration string
|
|
*
|
|
* @return string End Option Group declaration string
|
|
*/
|
|
private function _endGroupDeclaration()
|
|
{
|
|
$str = '';
|
|
|
|
// Inspect cardinality of OptionGroup
|
|
switch ($this->_cardinality) {
|
|
case self::CARDINALITY_0_1:
|
|
case self::CARDINALITY_0_N:
|
|
$str .= ']';
|
|
break;
|
|
|
|
case self::CARDINALITY_1_1:
|
|
case self::CARDINALITY_1_N:
|
|
$str .= ')';
|
|
break;
|
|
|
|
case self::CARDINALITY_N_N:
|
|
case self::CARDINALITY_M_N:
|
|
default:
|
|
// Does nothing
|
|
break;
|
|
}
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* INTERNAL:
|
|
* Retrieve the Option Group style based on defined cardinality
|
|
*
|
|
* @return string CLI Style string representation
|
|
*/
|
|
private function _getGroupOptionStyle()
|
|
{
|
|
$style = 'NONE';
|
|
|
|
// Inspect cardinality of OptionGroup
|
|
switch ($this->_cardinality) {
|
|
case self::CARDINALITY_0_1:
|
|
case self::CARDINALITY_0_N:
|
|
$style = 'OPT_ARG';
|
|
break;
|
|
|
|
case self::CARDINALITY_1_1:
|
|
case self::CARDINALITY_1_N:
|
|
case self::CARDINALITY_N_N:
|
|
case self::CARDINALITY_M_N:
|
|
$style = 'REQ_ARG';
|
|
break;
|
|
|
|
default:
|
|
// Does nothing
|
|
break;
|
|
}
|
|
|
|
return $style;
|
|
}
|
|
|
|
/**
|
|
* Formats the CLI Option Group into a multi-line list with respective description
|
|
*
|
|
* @param AbstractPrinter CLI Printer
|
|
* @return string Multi-line string representation of CLI Option Group
|
|
*/
|
|
public function formatWithDescription(AbstractPrinter $printer)
|
|
{
|
|
$numOptions = count($this->_options);
|
|
|
|
if ($numOptions == 0) {
|
|
return 'No available options' . PHP_EOL . PHP_EOL;
|
|
}
|
|
|
|
$str = '';
|
|
|
|
// Get list of required and optional and max length options
|
|
list(
|
|
$requiredOptions, $optionalOptions, $maxOptionLength
|
|
) = $this->_getOrganizedOptions(
|
|
$this->_options, $this->_cardinality, 0
|
|
);
|
|
|
|
// Array-unique options
|
|
$requiredOptions = array_unique($requiredOptions);
|
|
$optionalOptions = array_unique($optionalOptions);
|
|
|
|
// TODO Sort options alphabetically
|
|
|
|
// Displaying required options
|
|
for ($i = 0, $l = count($requiredOptions); $i < $l; $i++) {
|
|
$str .= $this->_displayOptionWithDescription(
|
|
$printer, $requiredOptions[$i], 'REQ_ARG', $maxOptionLength
|
|
);
|
|
|
|
// Include extra line breaks between options
|
|
$str .= PHP_EOL . PHP_EOL;
|
|
}
|
|
|
|
// Displaying optional options
|
|
for ($i = 0, $l = count($optionalOptions); $i < $l; $i++) {
|
|
$str .= $this->_displayOptionWithDescription(
|
|
$printer, $optionalOptions[$i], 'OPT_ARG', $maxOptionLength
|
|
);
|
|
|
|
// Include extra line breaks between options
|
|
$str .= PHP_EOL . PHP_EOL;
|
|
}
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* Organize the Options into arrays of required and optional options.
|
|
* Also define the maximum length of CLI Options.
|
|
*
|
|
* @param array Array of CLI Option or CLI Option Group
|
|
* @param integer Current CLI OptionGroup cardinality
|
|
* @param integer Maximum length of CLI Options
|
|
* @return array Array containing 3 indexes: required options, optional
|
|
* options and maximum length of CLI Options
|
|
*/
|
|
private function _getOrganizedOptions($options, $cardinality, $maxColumn)
|
|
{
|
|
// Calculate maximum length and also organize the
|
|
// options into required and optional ones
|
|
$numOptions = count($options);
|
|
$requiredOptions = array();
|
|
$optionalOptions = array();
|
|
|
|
for ($i = 0; $i < $numOptions; $i++) {
|
|
$option = $options[$i];
|
|
|
|
// Check for possible recursive OptionGroup
|
|
if ($option instanceof OptionGroup) {
|
|
// Initialize OptionGroup options
|
|
$groupRequiredOptions = array();
|
|
$groupOptionalOptions = array();
|
|
|
|
// Get nested information
|
|
list(
|
|
$groupRequiredOptions, $groupOptionalOptions, $maxGroupColumn
|
|
) = $this->_getOrganizedOptions(
|
|
$option->getOptions(), $option->getCardinality(), $maxColumn
|
|
);
|
|
|
|
// Merge nested required and optional options
|
|
$requiredOptions = array_merge($requiredOptions, $groupRequiredOptions);
|
|
$optionalOptions = array_merge($optionalOptions, $groupOptionalOptions);
|
|
|
|
// If OptionGroup length is bigger than the current maximum, update
|
|
if ($maxColumn < $maxGroupColumn) {
|
|
$maxColumn = $maxGroupColumn;
|
|
}
|
|
} else {
|
|
// Cardinality defines between optional or required options
|
|
switch ($cardinality) {
|
|
case self::CARDINALITY_0_1:
|
|
case self::CARDINALITY_0_N:
|
|
$optionalOptions[] = $option;
|
|
break;
|
|
|
|
case self::CARDINALITY_1_1:
|
|
case self::CARDINALITY_1_N:
|
|
case self::CARDINALITY_N_N:
|
|
case self::CARDINALITY_M_N:
|
|
$requiredOptions[] = $option;
|
|
break;
|
|
|
|
default:
|
|
// Does nothing
|
|
break;
|
|
}
|
|
|
|
// Build Option string
|
|
$optionStr = (string) $option;
|
|
|
|
// + 2 = aditional spaces after option
|
|
$length = strlen($optionStr) + 2;
|
|
|
|
if ($maxColumn < $length) {
|
|
$maxColumn = $length;
|
|
}
|
|
}
|
|
}
|
|
|
|
return array($requiredOptions, $optionalOptions, $maxColumn);
|
|
}
|
|
|
|
/**
|
|
* INTERNAL:
|
|
* Formats the CLI Option and also include the description
|
|
*
|
|
* @param AbstractPrinter CLI Printer
|
|
* @param Option CLI Option to be formatted
|
|
* @param string CLI Style string representation
|
|
* @param integer Maximum CLI Option length
|
|
* @return string Formats the current CLI Option line(s)
|
|
*/
|
|
private function _displayOptionWithDescription($printer, $option, $style, $maxOptionLength)
|
|
{
|
|
// Expose the option formatted
|
|
$optionStr = (string) $option;
|
|
|
|
// Format Option string
|
|
$str = $printer->format($optionStr, $style);
|
|
|
|
// Include missing spaces
|
|
$str .= str_repeat(' ', $maxOptionLength - strlen($optionStr));
|
|
|
|
// Calculate and display description
|
|
$str .= str_replace(
|
|
PHP_EOL, PHP_EOL . str_repeat(' ', $maxOptionLength), $option->getDescription()
|
|
);
|
|
|
|
return $str;
|
|
}
|
|
} |