--- /dev/null
+<?php
+/**
+ * @author Tim Düsterhus
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @category Community Framework
+ */
+// define the wcf-root-dir
+define('WCF_DIR', dirname(__FILE__).'/');
+
+// initiate wcf core
+require_once(WCF_DIR.'lib/system/CLIWCF.class.php');
+new wcf\system\CLIWCF();
--- /dev/null
+<?php
+namespace wcf\system;
+require_once('WCF.class.php');
+use phpline\console\ConsoleReader;
+use phpline\TerminalFactory;
+use wcf\system\cli\DatabaseCommandHistory;
+use wcf\system\exception\UserInputException;
+use wcf\system\user\authentication\UserAuthenticationFactory;
+use wcf\util\StringUtil;
+use Zend\Loader\StandardAutoloader as ZendLoader;
+
+/**
+ * Extends WCF class with functions for CLI.
+ *
+ * @author Tim Düsterhus
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system
+ * @category Community Framework
+ */
+class CLIWCF extends WCF {
+ /**
+ * instance of ConsoleReader
+ * @var phpline\console\ConsoleReader
+ */
+ protected static $consoleReader = null;
+
+ /**
+ * Calls all init functions of the WCF class.
+ */
+ public function __construct() {
+ parent::__construct();
+
+ // register additional autoloaders
+ require_once(WCF_DIR.'lib/system/api/phpline/phpline.phar');
+ require_once(WCF_DIR.'lib/system/api/zend/Loader/StandardAutoloader.php');
+ $zendLoader = new ZendLoader(array(ZendLoader::AUTOREGISTER_ZF => true));
+ $zendLoader->register();
+
+ $this->initPHPLine();
+ $this->initAuth();
+ }
+
+ /**
+ * Initializes PHPLine.
+ */
+ protected function initPHPLine() {
+ $terminal = TerminalFactory::get();
+ self::$consoleReader = new ConsoleReader("WoltLab Community Framework", null, null, $terminal);
+ self::getReader()->setExpandEvents(false);
+
+ $headline = str_pad("WoltLab (r) Community Framework (tm) ".WCF_VERSION, self::getTerminal()->getWidth(), " ", STR_PAD_BOTH);
+ self::getReader()->println($headline);
+ }
+
+ /**
+ * Returns ConsoleReader.
+ *
+ * @return phpline\console\ConsoleReader
+ */
+ public function getReader() {
+ return self::$consoleReader;
+ }
+
+ /**
+ * Returns the terminal that is attached to ConsoleReader
+ *
+ * @return phpline\Terminal
+ */
+ public function getTerminal() {
+ return self::getReader()->getTerminal();
+ }
+
+ /**
+ * Converts certain HTML entities to a proper CLI counterpart.
+ *
+ * @param string $string
+ * @return string
+ */
+ public function convertEntities($string) {
+ return Regex::compile('&[lrb]dquo;')->replace($string, '"');
+ }
+
+ /**
+ * Does the user authentification.
+ */
+ protected function initAuth() {
+ do {
+ $username = StringUtil::trim(self::getReader()->readLine(WCF::getLanguage()->get('wcf.user.username').'> '));
+ }
+ while ($username === '');
+ do {
+ $password = StringUtil::trim(self::getReader()->readLine(WCF::getLanguage()->get('wcf.user.password').'> ', '*'));
+ }
+ while ($password === '');
+
+ try {
+ $user = UserAuthenticationFactory::getUserAuthentication()->loginManually($username, $password);
+ WCF::getSession()->changeUser($user);
+ }
+ catch (UserInputException $e) {
+ $message = WCF::getLanguage()->getDynamicVariable('wcf.user.'.$e->getField().'.error.'.$e->getType(), array('username' => $username));
+ self::getReader()->println(self::convertEntities($message));
+ exit;
+ }
+
+ $history = new DatabaseCommandHistory();
+ $history->load();
+ self::getReader()->setHistory($history);
+
+ while ('exit' !== StringUtil::trim(self::getReader()->readLine('>')));
+ }
+}
--- /dev/null
+Copyright (c) 2002-2012, the original author or authors.
+All rights reserved.
+
+http://www.opensource.org/licenses/bsd-license.php
+
+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 JLine 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 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.
+
--- /dev/null
+Version of PHPLine is f3dce6b625ddd50ef42deec5fd198c8b124fb740
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Adapter;
+
+use Zend\Console\Charset;
+use Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Adapter
+ */
+abstract class AbstractAdapter implements AdapterInterface
+{
+ /**
+ * Whether or not mbstring is enabled
+ *
+ * @var null|bool
+ */
+ protected static $hasMBString;
+
+ /**
+ * @var Charset\CharsetInterface
+ */
+ protected $charset;
+
+ /**
+ * Current cursor X position
+ *
+ * @var int
+ */
+ protected $posX;
+
+ /**
+ * Current cursor Y position
+ *
+ * @var int
+ */
+ protected $posY;
+
+ /**
+ * Write a chunk of text to console.
+ *
+ * @param string $text
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function write($text, $color = null, $bgColor = null)
+ {
+ if ($color !== null || $bgColor !== null) {
+ echo $this->colorize($text, $color, $bgColor);
+ } else {
+ echo $text;
+ }
+ }
+
+ /**
+ * Alias for write()
+ *
+ * @param string $text
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function writeText($text, $color = null, $bgColor = null)
+ {
+ return $this->write($text, $color, $bgColor);
+ }
+
+ /**
+ * Write a single line of text to console and advance cursor to the next line.
+ * If the text is longer than console width it will be truncated.
+ *
+ *
+ * @param string $text
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function writeLine($text = "", $color = null, $bgColor = null)
+ {
+ $width = $this->getStringWidth($text);
+
+ // Remove newline characters from the end of string
+ $text = trim($text, "\r\n");
+
+ // Replace newline characters with spaces
+ $test = str_replace("\n", " ", $text);
+
+ // Trim the line if it's too long and output text
+ $consoleWidth = $this->getWidth();
+ if ($width > $consoleWidth) {
+ $text = $this->stringTrim($text, $consoleWidth);
+ $this->write($text, $color, $bgColor);
+ } elseif ($width == $consoleWidth) {
+ $this->write($text, $color, $bgColor);
+ } else {
+ $this->write($text . "\n", $color, $bgColor);
+ }
+ }
+
+ /**
+ * Write a piece of text at the coordinates of $x and $y
+ *
+ *
+ * @param string $text Text to write
+ * @param int $x Console X coordinate (column)
+ * @param int $y Console Y coordinate (row)
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function writeAt($text, $x, $y, $color = null, $bgColor = null)
+ {
+ $this->setPos( $x, $y );
+ $this->write( $text, $color, $bgColor );
+ }
+
+ /**
+ * Write a box at the specified coordinates.
+ * If X or Y coordinate value is negative, it will be calculated as the distance from far right or bottom edge
+ * of the console (respectively).
+ *
+ * @param int $x1 Top-left corner X coordinate (column)
+ * @param int $y1 Top-left corner Y coordinate (row)
+ * @param int $x2 Bottom-right corner X coordinate (column)
+ * @param int $y2 Bottom-right corner Y coordinate (row)
+ * @param int $lineStyle (optional) Box border style.
+ * @param int $fillStyle (optional) Box fill style or a single character to fill it with.
+ * @param int $color (optional) Foreground color
+ * @param int $bgColor (optional) Background color
+ * @param null|int $fillColor (optional) Foreground color of box fill
+ * @param null|int $fillBgColor (optional) Background color of box fill
+ * @throws Exception\BadMethodCallException if coordinates are invalid
+ */
+ public function writeBox(
+ $x1,
+ $y1,
+ $x2,
+ $y2,
+ $lineStyle = self::LINE_SINGLE,
+ $fillStyle = self::FILL_NONE,
+ $color = null,
+ $bgColor = null,
+ $fillColor = null,
+ $fillBgColor = null
+ ) {
+ // Sanitize coordinates
+ $x1 = (int) $x1;
+ $y1 = (int) $y1;
+ $x2 = (int) $x2;
+ $y2 = (int) $y2;
+
+ // Translate negative coordinates
+ if ($x2 < 0) {
+ $x2 = $this->getWidth() - $x2;
+ }
+
+ if ($y2 < 0) {
+ $y2 = $this->getHeight() - $y2;
+ }
+
+ // Validate coordinates
+ if ($x1 < 0
+ || $y1 < 0
+ || $x2 < $x1
+ || $y2 < $y1
+ ) {
+ throw new Exception\BadMethodCallException('Supplied X,Y coordinates are invalid.');
+ }
+
+ // Determine charset and dimensions
+ $charset = $this->getCharset();
+ $width = $x2 - $x1 + 1;
+ $height = $y2 - $y1 + 1;
+
+ if ($width <= 2) {
+ $lineStyle = static::LINE_NONE;
+ }
+
+ // Activate line drawing
+ $this->write($charset::ACTIVATE);
+
+ // Draw horizontal lines
+ if ($lineStyle !== static::LINE_NONE) {
+ switch ($lineStyle) {
+ case static::LINE_SINGLE:
+ $lineChar = $charset::LINE_SINGLE_EW;
+ break;
+
+ case static::LINE_DOUBLE:
+ $lineChar = $charset::LINE_DOUBLE_EW;
+ break;
+
+ case static::LINE_BLOCK:
+ default:
+ $lineChar = $charset::LINE_BLOCK_EW;
+ break;
+ }
+
+ $this->setPos($x1 + 1, $y1);
+ $this->write(str_repeat($lineChar, $width - 2), $color, $bgColor);
+ $this->setPos($x1 + 1, $y2);
+ $this->write(str_repeat($lineChar, $width - 2), $color, $bgColor);
+ }
+
+ // Draw vertical lines and fill
+ if (is_numeric($fillStyle)
+ && $fillStyle !== static::FILL_NONE) {
+
+ switch ($fillStyle) {
+ case static::FILL_SHADE_LIGHT:
+ $fillChar = $charset::SHADE_LIGHT;
+ break;
+ case static::FILL_SHADE_MEDIUM:
+ $fillChar = $charset::SHADE_MEDIUM;
+ break;
+ case static::FILL_SHADE_DARK:
+ $fillChar = $charset::SHADE_DARK;
+ break;
+ case static::FILL_SHADE_LIGHT:
+ $fillChar = $charset::SHADE_LIGHT;
+ break;
+ case static::FILL_BLOCK:
+ default:
+ $fillChar = $charset::BLOCK;
+ break;
+ }
+
+ } elseif ($fillStyle) {
+ $fillChar = $this->stringTrim($fillStyle, 1);
+ } else {
+ $fillChar = ' ';
+ }
+
+ if ($lineStyle === static::LINE_NONE) {
+ for ($y = $y1; $y <= $y2; $y++) {
+ $this->setPos($x1, $y);
+ $this->write(str_repeat($fillChar, $width), $fillColor, $fillBgColor);
+ }
+ } else {
+ switch ($lineStyle) {
+ case static::LINE_DOUBLE:
+ $lineChar = $charset::LINE_DOUBLE_NS;
+ break;
+ case static::LINE_BLOCK:
+ $lineChar = $charset::LINE_BLOCK_NS;
+ break;
+ case static::LINE_SINGLE:
+ default:
+ $lineChar = $charset::LINE_SINGLE_NS;
+ break;
+ }
+
+ for ($y = $y1 + 1; $y < $y2; $y++) {
+ $this->setPos($x1, $y);
+ $this->write($lineChar, $color, $bgColor);
+ $this->write(str_repeat($fillChar, $width - 2), $fillColor, $fillBgColor);
+ $this->write($lineChar, $color, $bgColor);
+ }
+ }
+
+
+ // Draw corners
+ if ($lineStyle !== static::LINE_NONE) {
+ if ($color !== null) {
+ $this->setColor($color);
+ }
+ if ($bgColor !== null) {
+ $this->setBgColor($bgColor);
+ }
+ if ($lineStyle === static::LINE_SINGLE) {
+ $this->writeAt($charset::LINE_SINGLE_NW, $x1, $y1);
+ $this->writeAt($charset::LINE_SINGLE_NE, $x2, $y1);
+ $this->writeAt($charset::LINE_SINGLE_SE, $x2, $y2);
+ $this->writeAt($charset::LINE_SINGLE_SW, $x1, $y2);
+ } elseif ($lineStyle === static::LINE_DOUBLE) {
+ $this->writeAt($charset::LINE_DOUBLE_NW, $x1, $y1);
+ $this->writeAt($charset::LINE_DOUBLE_NE, $x2, $y1);
+ $this->writeAt($charset::LINE_DOUBLE_SE, $x2, $y2);
+ $this->writeAt($charset::LINE_DOUBLE_SW, $x1, $y2);
+ } elseif ($lineStyle === static::LINE_BLOCK) {
+ $this->writeAt($charset::LINE_BLOCK_NW, $x1, $y1);
+ $this->writeAt($charset::LINE_BLOCK_NE, $x2, $y1);
+ $this->writeAt($charset::LINE_BLOCK_SE, $x2, $y2);
+ $this->writeAt($charset::LINE_BLOCK_SW, $x1, $y2);
+ }
+ }
+
+ // Deactivate line drawing and reset colors
+ $this->write($charset::DEACTIVATE);
+ $this->resetColor();
+ }
+
+ /**
+ * Write a block of text at the given coordinates, matching the supplied width and height.
+ * In case a line of text does not fit desired width, it will be wrapped to the next line.
+ * In case the whole text does not fit in desired height, it will be truncated.
+ *
+ * @param string $text Text to write
+ * @param int $width Maximum block width. Negative value means distance from right edge.
+ * @param int|null $height Maximum block height. Negative value means distance from bottom edge.
+ * @param int $x Block X coordinate (column)
+ * @param int $y Block Y coordinate (row)
+ * @param null|int $color (optional) Text color
+ * @param null|int $bgColor (optional) Text background color
+ */
+ public function writeTextBlock(
+ $text,
+ $width,
+ $height = null,
+ $x = 0,
+ $y = 0,
+ $color = null,
+ $bgColor = null
+ ) {
+ }
+
+ /**
+ * Determine and return current console width.
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ return 80;
+ }
+
+ /**
+ * Determine and return current console height.
+ *
+ * @return int
+ */
+ public function getHeight()
+ {
+ return 25;
+ }
+
+ /**
+ * Determine and return current console width and height.
+ *
+ * @return array array($width, $height)
+ */
+ public function getSize()
+ {
+ return array(
+ $this->getWidth(),
+ $this->getHeight(),
+ );
+ }
+
+ /**
+ * Check if console is UTF-8 compatible
+ *
+ * @return bool
+ */
+ public function isUtf8()
+ {
+ return true;
+ }
+
+ /**
+ * Return current cursor position - array($x, $y)
+ *
+ *
+ * @return array array($x, $y);
+ */
+ public function getPos()
+ {
+ }
+
+// /**
+// * Return current cursor X coordinate (column)
+// *
+// *
+// * @return false|int Integer or false if failed to determine.
+// */
+// public function getX();
+//
+// /**
+// * Return current cursor Y coordinate (row)
+// *
+// *
+// * @return false|int Integer or false if failed to determine.
+// */
+// public function getY();
+
+ /**
+ * Set cursor position
+ *
+ * @param int $x
+ * @param int $y
+ */
+ public function setPos($x, $y)
+ {
+ }
+
+ /**
+ * Show console cursor
+ */
+ public function showCursor()
+ {
+ }
+
+ /**
+ * Hide console cursor
+ */
+ public function hideCursor()
+ {
+ }
+
+ /**
+ * Return current console window title.
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ return '';
+ }
+
+ /**
+ * Set console window title
+ *
+ * @param $title
+ */
+ public function setTitle($title)
+ {
+ }
+
+ /**
+ * Reset console window title to previous value.
+ */
+ public function resetTitle()
+ {
+ }
+
+ /**
+ * Prepare a string that will be rendered in color.
+ *
+ * @param string $string
+ * @param int $color
+ * @param null|int $bgColor
+ * @return string
+ */
+ public function colorize($string, $color = null, $bgColor = null)
+ {
+ return $string;
+ }
+
+ /**
+ * Change current drawing color.
+ *
+ * @param int $color
+ */
+ public function setColor($color)
+ {
+ }
+
+ /**
+ * Change current drawing background color
+ *
+ * @param int $color
+ */
+ public function setBgColor($color)
+ {
+ }
+
+ /**
+ * Reset color to console default.
+ */
+ public function resetColor()
+ {
+ }
+
+ /**
+ * Set Console charset to use.
+ *
+ * @param Charset\CharsetInterface $charset
+ */
+ public function setCharset(Charset\CharsetInterface $charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Get charset currently in use by this adapter.
+ *
+ * @return Charset\CharsetInterface $charset
+ */
+ public function getCharset()
+ {
+ if ($this->charset === null) {
+ $this->charset = $this->getDefaultCharset();
+ }
+
+ return $this->charset;
+ }
+
+ /**
+ * @return Charset\Utf8
+ */
+ public function getDefaultCharset()
+ {
+ return new Charset\Utf8;
+ }
+
+ /**
+ * Clear console screen
+ */
+ public function clear()
+ {
+ echo "\f";
+ }
+
+ /**
+ * Clear line at cursor position
+ */
+ public function clearLine()
+ {
+ echo "\r" . str_repeat( " ", $this->getWidth() ) . "\r";
+ }
+
+ /**
+ * Clear console screen
+ */
+ public function clearScreen()
+ {
+ return $this->clear();
+ }
+
+ /**
+ * Helper function that return string length as rendered in console.
+ *
+ * @static
+ * @param $string
+ * @return int
+ */
+ protected function getStringWidth($string)
+ {
+ $width = strlen($string);
+
+ if (!$this->isUtf8()) {
+ return $width;
+ }
+
+ if (static::$hasMBString === null) {
+ static::$hasMBString = extension_loaded( 'mbstring' );
+ }
+
+ $width = (static::$hasMBString)
+ ? mb_strlen($string, 'UTF-8' )
+ : strlen(utf8_decode($string));
+
+ return $width;
+ }
+
+ /**
+ * Trim a string in an encoding-safe way
+ *
+ * @param mixed $string
+ * @param mixed $length
+ * @return int
+ */
+ protected function stringTrim($string, $length)
+ {
+ if ($this->isUtf8()) {
+ if (static::$hasMBString === null) {
+ static::$hasMBString = extension_loaded('mbstring');
+ }
+
+ if (static::$hasMBString) {
+ return mb_strlen($string, 'UTF-8');
+ }
+
+ return strlen(utf8_decode($string));
+ }
+
+ return strlen($string);
+ }
+
+ /**
+ * Read a single line from the console input
+ *
+ * @param int $maxLength Maximum response length
+ * @return string
+ */
+ public function readLine($maxLength = 2048)
+ {
+ $f = fopen('php://stdin','r');
+ $line = stream_get_line($f, $maxLength, PHP_EOL);
+ fclose($f);
+ return rtrim($line,"\n\r");
+ }
+
+ /**
+ * Read a single character from the console input
+ *
+ * @param string|null $mask A list of allowed chars
+ * @return string
+ */
+ public function readChar($mask = null)
+ {
+ $f = fopen('php://stdin','r');
+ do {
+ $char = fread($f,1);
+ } while ($mask === null || stristr($mask, $char));
+ fclose($f);
+ return $char;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Adapter;
+
+use Zend\Console\Charset\CharsetInterface;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ */
+interface AdapterInterface
+{
+ const LINE_NONE = 1;
+ const LINE_SINGLE = 2;
+ const LINE_DOUBLE = 3;
+ const LINE_BLOCK = 4;
+ const FILL_NONE = 0;
+ const FILL_SHADE_LIGHT = 1;
+ const FILL_SHADE_MEDIUM = 2;
+ const FILL_SHADE_DARK = 3;
+ const FILL_BLOCK = 10;
+
+ /**
+ * Write a chunk of text to console.
+ *
+ * @param string $text
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function write($text, $color = null, $bgColor = null);
+
+ /**
+ * Alias for write()
+ *
+ * @param string $text
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function writeText($text, $color = null, $bgColor = null);
+
+ /**
+ * Write a single line of text to console and advance cursor to the next line.
+ * If the text is longer than console width it will be truncated.
+ *
+ * @param string $text
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function writeLine($text = "", $color = null, $bgColor = null);
+
+ /**
+ * Write a piece of text at the coordinates of $x and $y
+ *
+ * @param string $text Text to write
+ * @param int $x Console X coordinate (column)
+ * @param int $y Console Y coordinate (row)
+ * @param null|int $color
+ * @param null|int $bgColor
+ */
+ public function writeAt($text, $x, $y, $color = null, $bgColor = null);
+
+ /**
+ * Write a box at the specified coordinates.
+ * If X or Y coordinate value is negative, it will be calculated as the distance from far right or bottom edge
+ * of the console (respectively).
+ *
+ * @param int $x1 Top-left corner X coordinate (column)
+ * @param int $y1 Top-left corner Y coordinate (row)
+ * @param int $x2 Bottom-right corner X coordinate (column)
+ * @param int $y2 Bottom-right corner Y coordinate (row)
+ * @param int $lineStyle (optional) Box border style.
+ * @param int $fillStyle (optional) Box fill style or a single character to fill it with.
+ * @param int $color (optional) Foreground color
+ * @param int $bgColor (optional) Background color
+ * @param null|int $fillColor (optional) Foreground color of box fill
+ * @param null|int $fillBgColor (optional) Background color of box fill
+ */
+ public function writeBox(
+ $x1,
+ $y1,
+ $x2,
+ $y2,
+ $lineStyle = self::LINE_SINGLE,
+ $fillStyle = self::FILL_NONE,
+ $color = null,
+ $bgColor = null,
+ $fillColor = null,
+ $fillBgColor = null
+ );
+
+ /**
+ * Write a block of text at the given coordinates, matching the supplied width and height.
+ * In case a line of text does not fit desired width, it will be wrapped to the next line.
+ * In case the whole text does not fit in desired height, it will be truncated.
+ *
+ * @param string $text Text to write
+ * @param int $width Maximum block width. Negative value means distance from right edge.
+ * @param int|null $height Maximum block height. Negative value means distance from bottom edge.
+ * @param int $x Block X coordinate (column)
+ * @param int $y Block Y coordinate (row)
+ * @param null|int $color (optional) Text color
+ * @param null|int $bgColor (optional) Text background color
+ */
+ public function writeTextBlock(
+ $text,
+ $width,
+ $height = null,
+ $x = 0,
+ $y = 0,
+ $color = null,
+ $bgColor = null
+ );
+
+
+ /**
+ * Determine and return current console width.
+ *
+ * @return int
+ */
+ public function getWidth();
+
+ /**
+ * Determine and return current console height.
+ *
+ * @return int
+ */
+ public function getHeight();
+
+ /**
+ * Determine and return current console width and height.
+ *
+ * @return array array($width, $height)
+ */
+ public function getSize();
+
+ /**
+ * Check if console is UTF-8 compatible
+ *
+ * @return bool
+ */
+ public function isUtf8();
+
+
+// /**
+// * Return current cursor position - array($x, $y)
+// *
+// * @return array array($x, $y);
+// */
+// public function getPos();
+//
+// /**
+// * Return current cursor X coordinate (column)
+// *
+// * @return false|int Integer or false if failed to determine.
+// */
+// public function getX();
+//
+// /**
+// * Return current cursor Y coordinate (row)
+// *
+// * @return false|int Integer or false if failed to determine.
+// */
+// public function getY();
+
+ /**
+ * Set cursor position
+ *
+ * @param int $x
+ * @param int $y
+ */
+ public function setPos($x, $y);
+
+ /**
+ * Hide console cursor
+ */
+ public function hideCursor();
+
+ /**
+ * Show console cursor
+ */
+ public function showCursor();
+
+ /**
+ * Return current console window title.
+ *
+ * @return string
+ */
+ public function getTitle();
+
+ /**
+ * Set console window title
+ *
+ * @param $title
+ */
+ public function setTitle($title);
+
+ /**
+ * Reset console window title to previous value.
+ */
+ public function resetTitle();
+
+
+ /**
+ * Prepare a string that will be rendered in color.
+ *
+ * @param string $string
+ * @param null|int $color Foreground color
+ * @param null|int $bgColor Background color
+ */
+ public function colorize($string, $color = null, $bgColor = null);
+
+ /**
+ * Change current drawing color.
+ *
+ * @param int $color
+ */
+ public function setColor($color);
+
+ /**
+ * Change current drawing background color
+ *
+ * @param int $color
+ */
+ public function setBgColor($color);
+
+ /**
+ * Reset color to console default.
+ */
+ public function resetColor();
+
+
+ /**
+ * Set Console charset to use.
+ *
+ * @param CharsetInterface $charset
+ */
+ public function setCharset(CharsetInterface $charset);
+
+ /**
+ * Get charset currently in use by this adapter.
+ *
+ * @return CharsetInterface $charset
+ */
+ public function getCharset();
+
+ /**
+ * @return CharsetInterface
+ */
+ public function getDefaultCharset();
+
+ /**
+ * Clear console screen
+ */
+ public function clear();
+
+ /**
+ * Clear line at cursor position
+ */
+ public function clearLine();
+
+ /**
+ * Clear console screen
+ */
+ public function clearScreen();
+
+ /**
+ * Read a single line from the console input
+ *
+ * @param int $maxLength Maximum response length
+ * @return string
+ */
+ public function readLine($maxLength = 2048);
+
+ /**
+ * Read a single character from the console input
+ *
+ * @param string|null $mask A list of allowed chars
+ * @return string
+ */
+ public function readChar($mask = null);
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Adapter;
+
+use Zend\Console\Charset;
+use Zend\Console\Exception;
+use Zend\Console\ColorInterface as Color;
+
+/**
+ * @todo Add GNU readline support
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Adapter
+ * @link http://en.wikipedia.org/wiki/ANSI_escape_code
+ */
+class Posix extends AbstractAdapter
+{
+ /**
+ * Whether or not mbstring is enabled
+ *
+ * @var null|bool
+ */
+ protected static $hasMBString;
+
+ /**
+ * @var Charset\CharsetInterface
+ */
+ protected $charset;
+
+ /**
+ * Map of colors to ANSI codes
+ *
+ * @todo implement Xterm 256 colors (http://www.frexx.de/xterm-256-notes/)
+ * @var array
+ */
+ protected static $ansiColorMap = array(
+ 'fg' => array(
+ Color::NORMAL => '22;39m',
+ Color::RESET => '22;39m',
+
+ Color::BLACK => '0;30',
+ Color::RED => '0;31',
+ Color::GREEN => '0;32',
+ Color::YELLOW => '0;33',
+ Color::BLUE => '0;34',
+ Color::MAGENTA => '0;35',
+ Color::CYAN => '0;36',
+ Color::WHITE => '0;37',
+
+ Color::GRAY => '1;30',
+ Color::LIGHT_RED => '1;31',
+ Color::LIGHT_GREEN => '1;32',
+ Color::LIGHT_YELLOW => '1;33',
+ Color::LIGHT_BLUE => '1;34',
+ Color::LIGHT_MAGENTA => '1;35',
+ Color::LIGHT_CYAN => '1;36',
+ Color::LIGHT_WHITE => '1;37',
+ ),
+ 'bg' => array(
+ Color::NORMAL => '0;49m',
+ Color::RESET => '0;49m',
+
+ Color::BLACK => '40',
+ Color::RED => '41',
+ Color::GREEN => '42',
+ Color::YELLOW => '43',
+ Color::BLUE => '44',
+ Color::MAGENTA => '45',
+ Color::CYAN => '46',
+ Color::WHITE => '47',
+
+ Color::GRAY => '40',
+ Color::LIGHT_RED => '41',
+ Color::LIGHT_GREEN => '42',
+ Color::LIGHT_YELLOW => '43',
+ Color::LIGHT_BLUE => '44',
+ Color::LIGHT_MAGENTA => '45',
+ Color::LIGHT_CYAN => '46',
+ Color::LIGHT_WHITE => '47',
+ ),
+ );
+
+ /**
+ * Last fetched TTY mode
+ *
+ * @var string|null
+ */
+ protected $lastTTYMode = null;
+
+ /**
+ * Determine and return current console width.
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ static $width;
+ if ($width > 0) {
+ return $width;
+ }
+
+ /**
+ * Try to read env variable
+ */
+ if (($result = getenv('COLUMNS')) !== false) {
+ return $width = (int) $result;
+ }
+
+ /**
+ * Try to read console size from "tput" command
+ */
+ $result = exec('tput cols', $output, $return);
+ if (!$return && is_numeric($result)) {
+ return $width = (int) $result;
+ }
+
+ return $width = parent::getWidth();
+ }
+
+ /**
+ * Determine and return current console height.
+ *
+ * @return false|int
+ */
+ public function getHeight()
+ {
+ static $height;
+ if ($height > 0) {
+ return $height;
+ }
+
+ // Try to read env variable
+ if (($result = getenv('LINES')) !== false) {
+ return $height = (int) $result;
+ }
+
+ // Try to read console size from "tput" command
+ $result = exec('tput lines', $output, $return);
+ if (!$return && is_numeric($result)) {
+ return $height = (int) $result;
+ }
+
+ return $height = parent::getHeight();
+ }
+
+ /**
+ * Run a mode command and store results
+ *
+ * @return void
+ */
+ protected function runModeCommand()
+ {
+ exec('mode', $output, $return);
+ if ($return || !count($output)) {
+ $this->modeResult = '';
+ } else {
+ $this->modeResult = trim(implode('', $output));
+ }
+ }
+
+ /**
+ * Check if console is UTF-8 compatible
+ *
+ * @return bool
+ */
+ public function isUtf8()
+ {
+ // Try to retrieve it from LANG env variable
+ if (($lang = getenv('LANG')) !== false) {
+ return stristr($lang, 'utf-8') || stristr($lang, 'utf8');
+ }
+
+ return false;
+ }
+
+ /**
+ * Show console cursor
+ */
+ public function showCursor()
+ {
+ echo "\x1b[?25h";
+ }
+
+ /**
+ * Hide console cursor
+ */
+ public function hideCursor()
+ {
+ echo "\x1b[?25l";
+ }
+
+ /**
+ * Set cursor position
+ * @param int $x
+ * @param int $y
+ */
+ public function setPos($x, $y)
+ {
+ echo "\x1b[" . $y . ';' . $x . 'f';
+ }
+
+ /**
+ * Prepare a string that will be rendered in color.
+ *
+ * @param string $string
+ * @param int $color
+ * @param null|int $bgColor
+ * @throws Exception\BadMethodCallException
+ * @return string
+ */
+ public function colorize($string, $color = null, $bgColor = null)
+ {
+ // Retrieve ansi color codes
+ if ($color !== null) {
+ if (!isset(static::$ansiColorMap['fg'][$color])) {
+ throw new Exception\BadMethodCallException(sprintf(
+ 'Unknown color "%s". Please use one of the Zend\Console\ColorInterface constants',
+ $color
+ ));
+ }
+ $color = static::$ansiColorMap['fg'][$color];
+ }
+
+ if ($bgColor !== null) {
+ if (!isset(static::$ansiColorMap['bg'][$bgColor])) {
+ throw new Exception\BadMethodCallException(sprintf(
+ 'Unknown color "%s". Please use one of the Zend\Console\ColorInterface constants',
+ $bgColor
+ ));
+ }
+ $bgColor = static::$ansiColorMap['bg'][$bgColor];
+ }
+
+ return ($color !== null ? "\x1b[" . $color . 'm' : '')
+ . ($bgColor !== null ? "\x1b[" . $bgColor . 'm' : '')
+ . $string
+ . "\x1b[22;39m\x1b[0;49m";
+ }
+
+ /**
+ * Change current drawing color.
+ *
+ * @param int $color
+ * @throws Exception\BadMethodCallException
+ */
+ public function setColor($color)
+ {
+ // Retrieve ansi color code
+ if ($color !== null) {
+ if (!isset(static::$ansiColorMap['fg'][$color])) {
+ throw new Exception\BadMethodCallException(sprintf(
+ 'Unknown color "%s". Please use one of the Zend\Console\ColorInterface constants',
+ $color
+ ));
+ }
+ $color = static::$ansiColorMap['fg'][$color];
+ }
+
+ echo "\x1b[" . $color . 'm';
+ }
+
+ /**
+ * Change current drawing background color
+ *
+ * @param int $bgColor
+ * @throws Exception\BadMethodCallException
+ */
+ public function setBgColor($bgColor)
+ {
+ // Retrieve ansi color code
+ if ($bgColor !== null) {
+ if (!isset(static::$ansiColorMap['bg'][$bgColor])) {
+ throw new Exception\BadMethodCallException(sprintf(
+ 'Unknown color "%s". Please use one of the Zend\Console\ColorInterface constants',
+ $bgColor
+ ));
+ }
+
+ $bgColor = static::$ansiColorMap['bg'][$bgColor];
+ }
+
+ echo "\x1b[" . ($bgColor) . 'm';
+ }
+
+ /**
+ * Reset color to console default.
+ */
+ public function resetColor()
+ {
+ echo "\x1b[0;49m"; // reset bg color
+ echo "\x1b[22;39m"; // reset fg bold, bright and faint
+ echo "\x1b[25;39m"; // reset fg blink
+ echo "\x1b[24;39m"; // reset fg underline
+ }
+
+ /**
+ * Return current console window title.
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ }
+
+ /**
+ * Set Console charset to use.
+ *
+ * @param Charset\CharsetInterface $charset
+ */
+ public function setCharset(Charset\CharsetInterface $charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Get charset currently in use by this adapter.
+ *
+ * @return Charset\CharsetInterface $charset
+ */
+ public function getCharset()
+ {
+ if ($this->charset === null) {
+ $this->charset = $this->getDefaultCharset();
+ }
+
+ return $this->charset;
+ }
+
+ /**
+ * @return Charset\CharsetInterface
+ */
+ public function getDefaultCharset()
+ {
+ if ($this->isUtf8()) {
+ return new Charset\Utf8;
+ }
+ return new Charset\DECSG();
+ }
+
+ /**
+ * Read a single character from the console input
+ *
+ * @param string|null $mask A list of allowed chars
+ * @return string
+ */
+ public function readChar($mask = null)
+ {
+ $this->setTTYMode('-icanon -echo');
+
+ $stream = fopen('php://stdin', 'rb');
+ do {
+ $char = fgetc($stream);
+ } while (strlen($char) !== 1 || ($mask !== null && stristr($mask, $char) === false));
+ fclose($stream);
+
+ $this->restoreTTYMode();
+ return $char;
+ }
+
+ /**
+ * Reset color to console default.
+ */
+ public function clear()
+ {
+ echo "\x1b[2J"; // reset bg color
+ $this->setPos(1, 1); // reset cursor position
+ }
+
+ /**
+ * Restore TTY (Console) mode to previous value.
+ *
+ * @return void
+ */
+ protected function restoreTTYMode()
+ {
+ if ($this->lastTTYMode === null) {
+ return;
+ }
+
+ shell_exec('stty ' . escapeshellarg($this->lastTTYMode));
+ }
+
+ /**
+ * Change TTY (Console) mode
+ *
+ * @link http://en.wikipedia.org/wiki/Stty
+ * @param $mode
+ */
+ protected function setTTYMode($mode)
+ {
+ // Store last mode
+ $this->lastTTYMode = trim(`stty -g`);
+
+ // Set new mode
+ shell_exec('stty '.escapeshellcmd($mode));
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Adapter;
+
+use Zend\Console\Charset;
+
+/**
+ * Virtual buffer adapter
+ *
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Adapter
+ */
+class Virtual extends AbstractAdapter
+{
+ /**
+ * Whether or not mbstring is enabled
+ *
+ * @var null|bool
+ */
+ protected static $hasMBString;
+
+ /**
+ * Results of mode system command
+ *
+ * @var mixed
+ */
+ protected $modeResult;
+
+ /**
+ * Determine and return current console width.
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ static $width;
+ if ($width > 0) {
+ return $width;
+ }
+
+ // Try to read console size from "mode" command
+ if ($this->modeResult === null) {
+ $this->runProbeCommand();
+ }
+
+ if (preg_match('/Columns\:\s+(\d+)/', $this->modeResult, $matches)) {
+ $width = $matches[1];
+ } else {
+ $width = parent::getWidth();
+ }
+
+ return $width;
+ }
+
+ /**
+ * Determine and return current console height.
+ *
+ * @return false|int
+ */
+ public function getHeight()
+ {
+ static $height;
+ if ($height > 0) {
+ return $height;
+ }
+
+ // Try to read console size from "mode" command
+ if ($this->modeResult === null) {
+ $this->runProbeCommand();
+ }
+
+ if (preg_match('/Rows\:\s+(\d+)/', $this->modeResult, $matches)) {
+ $height = $matches[1];
+ } else {
+ $height = parent::getHeight();
+ }
+
+ return $height;
+ }
+
+ /**
+ * Run and store the results of mode command
+ *
+ * @return void
+ */
+ protected function runProbeCommand()
+ {
+ exec('mode', $output, $return);
+ if ($return || !count($output)) {
+ $this->modeResult = '';
+ } else {
+ $this->modeResult = trim(implode('', $output));
+ }
+ }
+
+ /**
+ * Check if console is UTF-8 compatible
+ *
+ * @return bool
+ */
+ public function isUtf8()
+ {
+ // Try to read code page info from "mode" command
+ if ($this->modeResult === null) {
+ $this->runProbeCommand();
+ }
+
+ if (preg_match('/Code page\:\s+(\d+)/', $this->modeResult, $matches)) {
+ return (int) $matches[1] == 65001;
+ }
+
+ return false;
+ }
+
+ /**
+ * Set cursor position
+ *
+ * @param int $x
+ * @param int $y
+ */
+ public function setPos($x, $y)
+ {
+ }
+
+ /**
+ * Return current console window title.
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ // Try to use powershell to retrieve console window title
+ exec('powershell -command "write $Host.UI.RawUI.WindowTitle"', $output, $result);
+ if ($result || !$output) {
+ return '';
+ }
+
+ return trim($output, "\r\n");
+ }
+
+ /**
+ * Set Console charset to use.
+ *
+ * @param Charset\CharsetInterface $charset
+ */
+ public function setCharset(Charset\CharsetInterface $charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Get charset currently in use by this adapter.
+ *
+ * @return Charset\CharsetInterface $charset
+ */
+ public function getCharset()
+ {
+ if ($this->charset === null) {
+ $this->charset = $this->getDefaultCharset();
+ }
+
+ return $this->charset;
+ }
+
+ /**
+ * @return Charset\AsciiExtended
+ */
+ public function getDefaultCharset()
+ {
+ return new Charset\AsciiExtended;
+ }
+
+ /**
+ * Switch to UTF mode
+ *
+ * @return void
+ */
+ protected function switchToUtf8()
+ {
+ shell_exec('mode con cp select=65001');
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Adapter;
+
+use Zend\Console\Charset;
+use Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Adapter
+ */
+class Windows extends Virtual
+{
+ /**
+ * Whether or not mbstring is enabled
+ *
+ * @var null|bool
+ */
+ protected static $hasMBString;
+
+ /**
+ * Results of probing system capabilities
+ *
+ * @var mixed
+ */
+ protected $probeResult;
+
+ /**
+ * Results of mode command
+ *
+ * @var mixed
+ */
+ protected $modeResult;
+
+ /**
+ * Determine and return current console width.
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ static $width;
+ if ($width > 0) {
+ return $width;
+ }
+
+ // Try to read console size from "mode" command
+ if ($this->probeResult === null) {
+ $this->runProbeCommand();
+ }
+
+ if (count($this->probeResult) && (int) $this->probeResult[0]) {
+ $width = (int)$this->probeResult[0];
+ } else {
+ $width = parent::getWidth();
+ }
+
+ return $width;
+ }
+
+ /**
+ * Determine and return current console height.
+ *
+ * @return false|int
+ */
+ public function getHeight()
+ {
+ static $height;
+ if ($height > 0) {
+ return $height;
+ }
+
+ // Try to read console size from "mode" command
+ if ($this->probeResult === null) {
+ $this->runProbeCommand();
+ }
+
+ if (count($this->probeResult) && (int) $this->probeResult[1]) {
+ $height = (int) $this->probeResult[1];
+ } else {
+ $height = parent::getheight();
+ }
+
+ return $height;
+ }
+
+ /**
+ * Probe for system capabilities and cache results
+ *
+ * Run a Windows Powershell command that determines parameters of console window. The command is fed through
+ * standard input (with echo) to prevent Powershell from creating a sub-thread and hanging PHP when run through
+ * a debugger/IDE.
+ *
+ * @return void
+ */
+ protected function runProbeCommand()
+ {
+ exec(
+ 'echo $size = $Host.ui.rawui.windowsize; write $($size.width) $($size.height) | powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command -',
+ $output,
+ $return
+ );
+ if ($return || empty($output)) {
+ $this->probeResult = '';
+ } else {
+ $this->probeResult = $output;
+ }
+ }
+
+ /**
+ * Run and cache results of mode command
+ *
+ * @return void
+ */
+ protected function runModeCommand()
+ {
+ exec('mode', $output, $return);
+ if ($return || !count($output)) {
+ $this->modeResult = '';
+ } else {
+ $this->modeResult = trim(implode('', $output));
+ }
+ }
+
+ /**
+ * Check if console is UTF-8 compatible
+ *
+ * @return bool
+ */
+ public function isUtf8()
+ {
+ // Try to read code page info from "mode" command
+ if ($this->modeResult === null) {
+ $this->runModeCommand();
+ }
+
+ if (preg_match('/Code page\:\s+(\d+)/', $this->modeResult, $matches)) {
+ return (int) $matches[1] == 65001;
+ }
+
+ return false;
+ }
+
+ /**
+ * Set cursor position
+ * @param int $x
+ * @param int $y
+ */
+ public function setPos($x, $y)
+ {
+ }
+
+ /**
+ * Return current console window title.
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ // Try to use powershell to retrieve console window title
+ exec('powershell -command "write $Host.UI.RawUI.WindowTitle"', $output, $result);
+ if ($result || !$output) {
+ return '';
+ }
+
+ return trim($output, "\r\n");
+ }
+
+ /**
+ * Set Console charset to use.
+ *
+ * @param Charset\CharsetInterface $charset
+ */
+ public function setCharset(Charset\CharsetInterface $charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Get charset currently in use by this adapter.
+ *
+ * @return Charset\CharsetInterface $charset
+ */
+ public function getCharset()
+ {
+ if ($this->charset === null) {
+ $this->charset = $this->getDefaultCharset();
+ }
+
+ return $this->charset;
+ }
+
+ /**
+ * @return Charset\AsciiExtended
+ */
+ public function getDefaultCharset()
+ {
+ return new Charset\AsciiExtended;
+ }
+
+ /**
+ * Switch to utf-8 encoding
+ *
+ * @return void
+ */
+ protected function switchToUtf8()
+ {
+ shell_exec('mode con cp select=65001');
+ }
+
+ /**
+ * Clear console screen
+ */
+ public function clear()
+ {
+ // Attempt to clear the screen using PowerShell command
+ exec("powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command Clear-Host", $output, $return);
+
+ if ($return) {
+ // Could not run powershell... fall back to filling the buffer with newlines
+ echo str_repeat("\r\n", $this->getHeight());
+ }
+ }
+
+ /**
+ * Clear line at cursor position
+ */
+ public function clearLine()
+ {
+ echo "\r" . str_repeat(' ', $this->getWidth()) . "\r";
+ }
+
+ /**
+ * Read a single character from the console input
+ *
+ * @param string|null $mask A list of allowed chars
+ * @throws Exception\RuntimeException
+ * @return string
+ */
+ public function readChar($mask = null)
+ {
+ // Decide if we can use `choice` tool
+ $useChoice = $mask !== null && preg_match('/^[a-zA-Z0-9]+$/D', $mask);
+
+ if ($useChoice) {
+ // Use Windows 95+ "choice" command, which allows for reading a
+ // single character matching a mask, but is limited to lower ASCII
+ // range.
+ do {
+ exec('choice /n /cs /c:' . $mask, $output, $return);
+ if ($return == 255 || $return < 1 || $return > strlen($mask)) {
+ throw new Exception\RuntimeException('"choice" command failed to run. Are you using Windows XP or newer?');
+ }
+
+ // Fetch the char from mask
+ $char = substr($mask, $return - 1, 1);
+ } while (!$char || ($mask !== null && !stristr($mask, $char)));
+
+ return $char;
+ }
+
+ // Try to use PowerShell, giving it console access. Because PowersShell
+ // interpreter can take a short while to load, we are emptying the
+ // whole keyboard buffer and picking the last key that has been pressed
+ // before or after PowerShell command has started. The ASCII code for
+ // that key is then converted to a character.
+ if ($mask === null) {
+ exec(
+ 'powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command "'
+ . 'while ($Host.UI.RawUI.KeyAvailable) {$key = $Host.UI.RawUI.ReadKey(\'NoEcho,IncludeKeyDown\');}'
+ . 'write $key.VirtualKeyCode;'
+ . '"',
+ $result,
+ $return
+ );
+
+ // Retrieve char from the result.
+ $char = !empty($result) ? implode('', $result) : null;
+
+ if (!empty($char) && !$return) {
+ // We have obtained an ASCII code, convert back to a char ...
+ $char = chr($char);
+
+ // ... and return it...
+ return $char;
+ }
+ } else {
+ // Windows and DOS will return carriage-return char (ASCII 13) when
+ // the user presses [ENTER] key, but Console Adapter user might
+ // have provided a \n Newline (ASCII 10) in the mask, to allow [ENTER].
+ // We are going to replace all CR with NL to conform.
+ $mask = strtr($mask, "\n", "\r");
+
+ // Prepare a list of ASCII codes from mask chars
+ $asciiMask = array_map(function ($char) {
+ return ord($char);
+ }, str_split($mask));
+ $asciiMask = array_unique($asciiMask);
+
+ // Char mask filtering is now handled by the PowerShell itself,
+ // because it's a much faster method than invoking PS interpreter
+ // after each mismatch. The command should return ASCII code of a
+ // matching key.
+ $result = $return = null;
+
+ exec(
+ 'powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command "'
+ . '[int[]] $mask = ' . join(',', $asciiMask) . ';'
+ . 'do {'
+ . '$key = $Host.UI.RawUI.ReadKey(\'NoEcho,IncludeKeyDown\').VirtualKeyCode;'
+ . '} while( !($mask -contains $key) );'
+ . 'write $key;'
+ . '"',
+ $result,
+ $return
+ );
+
+ $char = !empty($result) ? trim(implode('', $result)) : null;
+
+ if (!$return && $char && ($mask === null || in_array($char, $asciiMask))) {
+ // Normalize CR to LF
+ if ($char == 13) {
+ $char = 10;
+ }
+
+ // Convert to a char
+ $char = chr($char);
+
+ // ... and return it...
+ return $char;
+ }
+ }
+
+ // Fall back to standard input, which on Windows does not allow reading
+ // a single character. This is a limitation of Windows streams
+ // implementation (not PHP) and this behavior cannot be changed with a
+ // command like "stty", known to POSIX systems.
+ $stream = fopen('php://stdin', 'rb');
+ do {
+ $char = fgetc($stream);
+ $char = substr(trim($char), 0, 1);
+ } while (!$char || ($mask !== null && !stristr($mask, $char)));
+ fclose($stream);
+
+ return $char;
+ }
+
+ /**
+ * Read a single line from the console input.
+ *
+ * @param int $maxLength Maximum response length
+ * @return string
+ */
+ public function readLine($maxLength = 2048)
+ {
+ $f = fopen('php://stdin','r');
+ $line = rtrim(fread($f, $maxLength),"\r\n");
+ fclose($f);
+
+ return $line;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Adapter;
+
+use Zend\Console\Charset\CharsetInterface;
+use Zend\Console\Exception;
+use Zend\Console\Charset;
+
+/**
+ * MS Windows with ANSICON console adapter
+ *
+ * This adapter requires ANSICON extension to be installed. It can be obtained from:
+ * https://github.com/adoxa/ansicon
+ *
+ * ANSICON has to be loaded and enabled before using this adapter. It's best to install
+ * it using following command:
+ * ansicon -I
+ *
+ * Console should not run in UTF8 code page (65001), because ANSICON does not behave well with it.
+ * It's best to use non-unicode code page 437, 850, 851, 852 or similar. Run "help mode" for more
+ * information on how to change Windows console code page.
+ *
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Adapter
+ */
+class WindowsAnsicon extends Posix
+{
+ /**
+ * Whether or not mbstring is enabled
+ *
+ * @var null|bool
+ */
+ protected static $hasMBString;
+
+ /**
+ * Results of mode command
+ *
+ * @var mixed
+ */
+ protected $modeResult;
+
+ /**
+ * Determine and return current console width.
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ static $width;
+ if ($width > 0) {
+ return $width;
+ }
+
+ // Try to read console size from ANSICON env var
+ if (preg_match('/\((\d+)x/', getenv('ANSICON'), $matches)) {
+ $width = $matches[1];
+ } else {
+ $width = AbstractAdapter::getWidth();
+ }
+
+ return $width;
+ }
+
+ /**
+ * Determine and return current console height.
+ *
+ * @return false|int
+ */
+ public function getHeight()
+ {
+ static $height;
+ if ($height > 0) {
+ return $height;
+ }
+
+ // Try to read console size from ANSICON env var
+ if (preg_match('/\(\d+x(\d+)/', getenv('ANSICON'), $matches)) {
+ $height = $matches[1];
+ } else {
+ $height = AbstractAdapter::getHeight();
+ }
+ return $height;
+ }
+
+ /**
+ * Run and cache results of mode command
+ *
+ * @return void
+ */
+ protected function runModeCommand()
+ {
+ exec('mode', $output, $return);
+ if ($return || !count($output)) {
+ $this->modeResult = '';
+ } else {
+ $this->modeResult = trim(implode('', $output));
+ }
+ }
+
+ /**
+ * Check if console is UTF-8 compatible
+ *
+ * @return bool
+ */
+ public function isUtf8()
+ {
+ // Try to read code page info from "mode" command
+ if ($this->modeResult === null) {
+ $this->runModeCommand();
+ }
+
+ if (preg_match('/Code page\:\s+(\d+)/', $this->modeResult, $matches)) {
+ return (int) $matches[1] == 65001;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return current console window title.
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ // Try to use powershell to retrieve console window title
+ exec('powershell -command "write $Host.UI.RawUI.WindowTitle"', $output, $result);
+ if ($result || !$output) {
+ return '';
+ }
+
+ return trim($output, "\r\n");
+ }
+
+ /**
+ * Clear console screen
+ */
+ public function clear()
+ {
+ echo chr(27) . '[1J' . chr(27) . '[u';
+ }
+
+ /**
+ * Clear line at cursor position
+ */
+ public function clearLine()
+ {
+ echo chr(27) . '[1K';
+ }
+
+ /**
+ * Set Console charset to use.
+ *
+ * @param CharsetInterface $charset
+ */
+ public function setCharset(CharsetInterface $charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Get charset currently in use by this adapter.
+ *
+
+ * @return CharsetInterface $charset
+ */
+ public function getCharset()
+ {
+ if ($this->charset === null) {
+ $this->charset = $this->getDefaultCharset();
+ }
+
+ return $this->charset;
+ }
+
+ /**
+ * @return Charset\AsciiExtended
+ */
+ public function getDefaultCharset()
+ {
+ return new Charset\AsciiExtended();
+ }
+
+ /**
+ * Read a single character from the console input
+ *
+ * @param string|null $mask A list of allowed chars
+ * @return string
+ * @throws Exception\RuntimeException
+ */
+ public function readChar($mask = null)
+ {
+ // Decide if we can use `choice` tool
+ $useChoice = $mask !== null && preg_match('/^[a-zA-Z0-9]+$/D', $mask);
+
+ if ($useChoice) {
+ // Use Windows 98+ "choice" command, which allows for reading a
+ // single character matching a mask, but is limited to lower ASCII
+ // range.
+ do {
+ exec('choice /n /cs /c:' . $mask, $output, $return);
+ if ($return == 255 || $return < 1 || $return > strlen($mask)) {
+ throw new Exception\RuntimeException('"choice" command failed to run. Are you using Windows XP or newer?');
+ }
+
+ // Fetch the char from mask
+ $char = substr($mask, $return - 1, 1);
+ } while (!$char || ($mask !== null && !stristr($mask, $char)));
+
+ return $char;
+ }
+
+ // Try to use PowerShell, giving it console access. Because PowersShell
+ // interpreter can take a short while to load, we are emptying the
+ // whole keyboard buffer and picking the last key that has been pressed
+ // before or after PowerShell command has started. The ASCII code for
+ // that key is then converted to a character.
+ if ($mask === null) {
+ exec(
+ 'powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command "'
+ . 'while ($Host.UI.RawUI.KeyAvailable) {$key = $Host.UI.RawUI.ReadKey(\'NoEcho,IncludeKeyDown\');}'
+ . 'write $key.VirtualKeyCode;'
+ . '"',
+ $result,
+ $return
+ );
+
+ // Retrieve char from the result.
+ $char = !empty($result) ? implode('', $result) : null;
+
+ if (!empty($char) && !$return) {
+ // We have obtained an ASCII code, convert back to a char ...
+ $char = chr($char);
+
+ // ... and return it...
+ return $char;
+ }
+ } else {
+ // Windows and DOS will return carriage-return char (ASCII 13) when
+ // the user presses [ENTER] key, but Console Adapter user might
+ // have provided a \n Newline (ASCII 10) in the mask, to allow
+ // [ENTER]. We are going to replace all CR with NL to conform.
+ $mask = strtr($mask, "\n", "\r");
+
+ // Prepare a list of ASCII codes from mask chars
+ $asciiMask = array_map(function ($char) {
+ return ord($char);
+ }, str_split($mask));
+ $asciiMask = array_unique($asciiMask);
+
+ // Char mask filtering is now handled by the PowerShell itself,
+ // because it's a much faster method than invoking PS interpreter
+ // after each mismatch. The command should return ASCII code of a
+ // matching key.
+ $result = $return = null;
+ exec(
+ 'powershell -NonInteractive -NoProfile -NoLogo -OutputFormat Text -Command "'
+ . '[int[]] $mask = '.join(',', $asciiMask).';'
+ . 'do {'
+ . '$key = $Host.UI.RawUI.ReadKey(\'NoEcho,IncludeKeyDown\').VirtualKeyCode;'
+ . '} while( !($mask -contains $key) );'
+ . 'write $key;'
+ . '"',
+ $result,
+ $return
+ );
+
+ $char = !empty($result) ? trim(implode('', $result)) : null;
+
+ if (!$return && $char && ($mask === null || in_array($char, $asciiMask))) {
+ // We have obtained an ASCII code, check if it is a carriage
+ // return and normalize it as needed
+ if ($char == 13) {
+ $char = 10;
+ }
+
+ // Convert to a character
+ $char = chr($char);
+
+ // ... and return it...
+ return $char;
+ }
+ }
+
+ // Fall back to standard input, which on Windows does not allow reading
+ // a single character. This is a limitation of Windows streams
+ // implementation (not PHP) and this behavior cannot be changed with a
+ // command like "stty", known to POSIX systems.
+ $stream = fopen('php://stdin', 'rb');
+ do {
+ $char = fgetc($stream);
+ $char = substr(trim($char), 0, 1);
+ } while (!$char || ($mask !== null && !stristr($mask, $char)));
+ fclose($stream);
+
+ return $char;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Charset;
+
+/**
+ * Basic (low) ASCII line drawing characters.
+ *
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Charset
+ */
+class Ascii implements CharsetInterface
+{
+
+ const ACTIVATE = "";
+ const DEACTIVATE = "";
+
+ const BLOCK = "#";
+ const SHADE_LIGHT = " ";
+ const SHADE_MEDIUM = "#";
+ const SHADE_DARK = "#";
+
+ const LINE_SINGLE_EW = "-";
+ const LINE_SINGLE_NS = "|";
+ const LINE_SINGLE_NW = "+";
+ const LINE_SINGLE_NE = "+";
+ const LINE_SINGLE_SE = "+";
+ const LINE_SINGLE_SW = "+";
+ const LINE_SINGLE_CROSS = "+";
+
+ const LINE_DOUBLE_EW = "=";
+ const LINE_DOUBLE_NS = "|";
+ const LINE_DOUBLE_NW = "+";
+ const LINE_DOUBLE_NE = "+";
+ const LINE_DOUBLE_SE = "+";
+ const LINE_DOUBLE_SW = "+";
+ const LINE_DOUBLE_CROSS = "+";
+
+ const LINE_BLOCK_EW = "=";
+ const LINE_BLOCK_NS = "#";
+ const LINE_BLOCK_NW = "+";
+ const LINE_BLOCK_NE = "+";
+ const LINE_BLOCK_SE = "+";
+ const LINE_BLOCK_SW = "+";
+ const LINE_BLOCK_CROSS = "+";
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Charset;
+
+/**
+ * Extended ASCII character set (positions 127+, MS DOS & Windows compatible)
+ *
+ * @link http://en.wikipedia.org/wiki/Box-drawing_characters
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Charset
+ */
+class AsciiExtended implements CharsetInterface
+{
+
+ const ACTIVATE = "";
+ const DEACTIVATE = "";
+
+ const BLOCK = "\xdb";
+ const SHADE_LIGHT = "\xb0";
+ const SHADE_MEDIUM = "\xb1";
+ const SHADE_DARK = "\xb2";
+
+ const LINE_SINGLE_EW = "\xc4";
+ const LINE_SINGLE_NS = "\xb3";
+ const LINE_SINGLE_NW = "\xda";
+ const LINE_SINGLE_NE = "\xbf";
+ const LINE_SINGLE_SE = "\xd9";
+ const LINE_SINGLE_SW = "\xc0";
+ const LINE_SINGLE_CROSS = "\xc5";
+
+ const LINE_DOUBLE_EW = "\xcd";
+ const LINE_DOUBLE_NS = "\xba";
+ const LINE_DOUBLE_NW = "\xc9";
+ const LINE_DOUBLE_NE = "\xbb";
+ const LINE_DOUBLE_SE = "\xbc";
+ const LINE_DOUBLE_SW = "\xc8";
+ const LINE_DOUBLE_CROSS = "\xce";
+
+ const LINE_BLOCK_EW = "\xdb";
+ const LINE_BLOCK_NS = "\xdb";
+ const LINE_BLOCK_NW = "\xdb";
+ const LINE_BLOCK_NE = "\xdb";
+ const LINE_BLOCK_SE = "\xdb";
+ const LINE_BLOCK_SW = "\xdb";
+ const LINE_BLOCK_CROSS = "\xdb";
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Charset;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ */
+interface CharsetInterface
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Charset;
+
+/**
+ * DEC Special Graphics (VT100 line drawing) character set
+ *
+ * @link http://vt100.net/docs/vt220-rm/table2-4.html
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Charset
+ */
+class DECSG implements CharsetInterface
+{
+
+ const ACTIVATE = "\x1b(0";
+ const DEACTIVATE = "\x1b(B";
+
+ const BLOCK = "\x61";
+ const SHADE_LIGHT = "\x61";
+ const SHADE_MEDIUM = "\x61";
+ const SHADE_DARK = "\x61";
+
+ const LINE_SINGLE_EW = "\x71";
+ const LINE_SINGLE_NS = "\x78";
+ const LINE_SINGLE_NW = "\x6c";
+ const LINE_SINGLE_NE = "\x6b";
+ const LINE_SINGLE_SE = "\x6a";
+ const LINE_SINGLE_SW = "\x6d";
+ const LINE_SINGLE_CROSS = "\x6e";
+
+ const LINE_DOUBLE_EW = "\x73";
+ const LINE_DOUBLE_NS = "\x78";
+ const LINE_DOUBLE_NW = "\x6c";
+ const LINE_DOUBLE_NE = "\x5b";
+ const LINE_DOUBLE_SE = "\x6a";
+ const LINE_DOUBLE_SW = "\x6d";
+ const LINE_DOUBLE_CROSS = "\x6e";
+
+ const LINE_BLOCK_EW = "\x61";
+ const LINE_BLOCK_NS = "\x61";
+ const LINE_BLOCK_NW = "\x61";
+ const LINE_BLOCK_NE = "\x61";
+ const LINE_BLOCK_SE = "\x61";
+ const LINE_BLOCK_SW = "\x61";
+ const LINE_BLOCK_CROSS = "\x61";
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Charset;
+
+/**
+ * UTF-8 box drawing
+ *
+ * @link http://en.wikipedia.org/wiki/Box-drawing_characters
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Charset
+ */
+class Utf8 implements CharsetInterface
+{
+
+ const ACTIVATE = "";
+ const DEACTIVATE = "";
+
+ const BLOCK = "█";
+ const SHADE_LIGHT = "░";
+ const SHADE_MEDIUM = "▒";
+ const SHADE_DARK = "▓";
+
+ const LINE_SINGLE_EW = "─";
+ const LINE_SINGLE_NS = "│";
+ const LINE_SINGLE_NW = "┌";
+ const LINE_SINGLE_NE = "┐";
+ const LINE_SINGLE_SE = "┘";
+ const LINE_SINGLE_SW = "└";
+ const LINE_SINGLE_CROSS = "┼";
+
+ const LINE_DOUBLE_EW = "═";
+ const LINE_DOUBLE_NS = "║";
+ const LINE_DOUBLE_NW = "╔";
+ const LINE_DOUBLE_NE = "╗";
+ const LINE_DOUBLE_SE = "╝";
+ const LINE_DOUBLE_SW = "╚";
+ const LINE_DOUBLE_CROSS = "╬";
+
+ const LINE_BLOCK_EW = "█";
+ const LINE_BLOCK_NS = "█";
+ const LINE_BLOCK_NW = "█";
+ const LINE_BLOCK_NE = "█";
+ const LINE_BLOCK_SE = "█";
+ const LINE_BLOCK_SW = "█";
+ const LINE_BLOCK_CROSS = "█";
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Charset;
+
+/**
+ * UTF-8 box drawing (modified to use heavy single lines)
+ *
+ * @link http://en.wikipedia.org/wiki/Box-drawing_characters
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Charset
+ */
+class Utf8Heavy extends Utf8
+{
+
+ const LINE_SINGLE_EW = "━";
+ const LINE_SINGLE_NS = "┃";
+ const LINE_SINGLE_NW = "┏";
+ const LINE_SINGLE_NE = "┓";
+ const LINE_SINGLE_SE = "┛";
+ const LINE_SINGLE_SW = "┗";
+ const LINE_SINGLE_CROSS = "╋";
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ */
+interface ColorInterface
+{
+ const NORMAL = 0;
+ const RESET = 0;
+
+ const BLACK = 1;
+ const RED = 2;
+ const GREEN = 3;
+ const YELLOW = 4;
+ const BLUE = 5;
+ const MAGENTA = 6;
+ const CYAN = 7;
+ const WHITE = 8;
+
+ const GRAY = 9;
+ const LIGHT_RED = 10;
+ const LIGHT_GREEN = 11;
+ const LIGHT_YELLOW = 12;
+ const LIGHT_BLUE = 13;
+ const LIGHT_MAGENTA = 14;
+ const LIGHT_CYAN = 15;
+ const LIGHT_WHITE = 16;
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console;
+
+/**
+ * An static, utility class for interacting with Console environment.
+ * Declared abstract to prevent from instantiating.
+ *
+ * @category Zend
+ * @package Zend_Console
+ */
+abstract class Console
+{
+ /**
+ * @var Adapter\AdapterInterface
+ */
+ protected static $instance;
+
+ /**
+ * Allow overriding whether or not we're in a console env. If set, and
+ * boolean, returns that value from isConsole().
+ * @var bool
+ */
+ protected static $isConsole;
+
+ /**
+ * Create and return Adapter\AdapterInterface instance.
+ *
+ * @param null|string $forceAdapter Optional adapter class name. Ccan be absolute namespace or class name
+ * relative to Zend\Console\Adapter\. If not provided, a best matching
+ * adapter will be automatically selected.
+ * @param null|string $forceCharset optional charset name can be absolute namespace or class name relative to
+ * Zend\Console\Charset\. If not provided, charset will be detected
+ * automatically.
+ * @throws Exception\InvalidArgumentException
+ * @throws Exception\RuntimeException
+ * @return Adapter\AdapterInterface
+ */
+ public static function getInstance($forceAdapter = null, $forceCharset = null)
+ {
+ if (static::$instance instanceof Adapter\AdapterInterface) {
+ return static::$instance;
+ }
+
+ // Create instance
+
+ if ($forceAdapter !== null) {
+ // Use the supplied adapter class
+ if (substr($forceAdapter, 0, 1) == '\\') {
+ $className = $forceAdapter;
+ } elseif (stristr($forceAdapter, '\\')) {
+ $className = __NAMESPACE__ . '\\' . ltrim($forceAdapter, '\\');
+ } else {
+ $className = __NAMESPACE__ . '\\Adapter\\' . $forceAdapter;
+ }
+
+ if (!class_exists($className)) {
+ throw new Exception\InvalidArgumentException(sprintf(
+ 'Cannot find Console adapter class "%s"',
+ $className
+ ));
+ }
+ } else {
+ // Try to detect best instance for console
+ $className = static::detectBestAdapter();
+
+ // Check if we were able to detect console adapter
+ if (!$className) {
+ throw new Exception\RuntimeException('Cannot create Console adapter - am I running in a console?');
+ }
+ }
+
+ // Create adapter instance
+ static::$instance = new $className();
+
+ // Try to use the supplied charset class
+ if ($forceCharset !== null) {
+ if (substr($forceCharset, 0, 1) == '\\') {
+ $className = $forceCharset;
+ } elseif (stristr($forceAdapter, '\\')) {
+ $className = __NAMESPACE__ . '\\' . ltrim($forceCharset, '\\');
+ } else {
+ $className = __NAMESPACE__ . '\\Charset\\' . $forceCharset;
+ }
+
+ if (!class_exists($className)) {
+ throw new Exception\InvalidArgumentException(sprintf(
+ 'Cannot find Charset class "%s"',
+ $className
+ ));
+ }
+
+ // Set adapter charset
+ static::$instance->setCharset(new $className());
+ }
+
+ return static::$instance;
+ }
+
+ /**
+ * Check if currently running under MS Windows
+ *
+ * @see http://stackoverflow.com/questions/738823/possible-values-for-php-os
+ * @return bool
+ */
+ public static function isWindows()
+ {
+ return
+ ( defined('PHP_OS') && ( substr_compare(PHP_OS,'win',0,3,true) === 0) ) ||
+ (getenv('OS') != false && substr_compare(getenv('OS'),'windows',0,7,true))
+ ;
+ }
+
+ /**
+ * Check if running under MS Windows Ansicon
+ *
+ * @return bool
+ */
+ public static function isAnsicon()
+ {
+ return getenv('ANSICON') !== false;
+ }
+
+ /**
+ * Check if running in a console environment (CLI)
+ *
+ * By default, returns value of PHP_SAPI global constant. If $isConsole is
+ * set, and a boolean value, that value will be returned.
+ *
+ * @return bool
+ */
+ public static function isConsole()
+ {
+ if (null !== static::$isConsole && is_bool(static::$isConsole)) {
+ return static::$isConsole;
+ }
+ return PHP_SAPI == 'cli';
+ }
+
+ /**
+ * Override the "is console environment" flag
+ *
+ * @param null|bool $flag
+ */
+ public static function overrideIsConsole($flag)
+ {
+ static::$isConsole = $flag;
+ }
+
+ /**
+ * Try to detect best matching adapter
+ * @return string|null
+ */
+ public static function detectBestAdapter()
+ {
+ // Check if we are in a console environment
+ if (!static::isConsole()) {
+ return null;
+ }
+
+ // Check if we're on windows
+ if (static::isWindows()) {
+ if (static::isAnsicon()) {
+ $className = __NAMESPACE__ . '\Adapter\WindowsAnsicon';
+ } else {
+ $className = __NAMESPACE__ . '\Adapter\Windows';
+ }
+
+ return $className;
+ }
+
+ // Default is a Posix console
+ $className = __NAMESPACE__ . '\Adapter\Posix';
+ return $className;
+ }
+
+ /**
+ * Pass-thru static call to current AdapterInterface instance.
+ *
+ * @param $funcName
+ * @param $arguments
+ * @return mixed
+ */
+ public static function __callStatic($funcName, $arguments)
+ {
+ $instance = static::getInstance();
+ return call_user_func_array(array($instance, $funcName), $arguments);
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Exception
+ */
+class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
+{
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Exception
+ */
+interface ExceptionInterface
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Exception
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Exception
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+ /**
+ * Usage
+ *
+ * @var string
+ */
+ protected $usage = '';
+
+ /**
+ * Constructor
+ *
+ * @param string $message
+ * @param string $usage
+ */
+ public function __construct($message, $usage = '')
+ {
+ $this->usage = $usage;
+ parent::__construct($message);
+ }
+
+ /**
+ * Returns the usage
+ *
+ * @return string
+ */
+ public function getUsageMessage()
+ {
+ return $this->usage;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console;
+
+/**
+ * Getopt is a class to parse options for command-line
+ * applications.
+ *
+ * Terminology:
+ * Argument: an element of the argv array. This may be part of an option,
+ * or it may be a non-option command-line argument.
+ * Flag: the letter or word set off by a '-' or '--'. Example: in '--output filename',
+ * '--output' is the flag.
+ * Parameter: the additional argument that is associated with the option.
+ * Example: in '--output filename', the 'filename' is the parameter.
+ * Option: the combination of a flag and its parameter, if any.
+ * Example: in '--output filename', the whole thing is the option.
+ *
+ * The following features are supported:
+ *
+ * - Short flags like '-a'. Short flags are preceded by a single
+ * dash. Short flags may be clustered e.g. '-abc', which is the
+ * same as '-a' '-b' '-c'.
+ * - Long flags like '--verbose'. Long flags are preceded by a
+ * double dash. Long flags may not be clustered.
+ * - Options may have a parameter, e.g. '--output filename'.
+ * - Parameters for long flags may also be set off with an equals sign,
+ * e.g. '--output=filename'.
+ * - Parameters for long flags may be checked as string, word, or integer.
+ * - Automatic generation of a helpful usage message.
+ * - Signal end of options with '--'; subsequent arguments are treated
+ * as non-option arguments, even if they begin with '-'.
+ * - Raise exception Zend_Console_Getopt_Exception in several cases
+ * when invalid flags or parameters are given. Usage message is
+ * returned in the exception object.
+ *
+ * The format for specifying options uses a PHP associative array.
+ * The key is has the format of a list of pipe-separated flag names,
+ * followed by an optional '=' to indicate a required parameter or
+ * '-' to indicate an optional parameter. Following that, the type
+ * of parameter may be specified as 's' for string, 'w' for word,
+ * or 'i' for integer.
+ *
+ * Examples:
+ * - 'user|username|u=s' this means '--user' or '--username' or '-u'
+ * are synonyms, and the option requires a string parameter.
+ * - 'p=i' this means '-p' requires an integer parameter. No synonyms.
+ * - 'verbose|v-i' this means '--verbose' or '-v' are synonyms, and
+ * they take an optional integer parameter.
+ * - 'help|h' this means '--help' or '-h' are synonyms, and
+ * they take no parameter.
+ *
+ * The values in the associative array are strings that are used as
+ * brief descriptions of the options when printing a usage message.
+ *
+ * The simpler format for specifying options used by PHP's getopt()
+ * function is also supported. This is similar to GNU getopt and shell
+ * getopt format.
+ *
+ * Example: 'abc:' means options '-a', '-b', and '-c'
+ * are legal, and the latter requires a string parameter.
+ *
+ * @category Zend
+ * @package Zend_Console_Getopt
+ * @version Release: @package_version@
+ * @since Class available since Release 0.6.0
+ *
+ * @todo Handle flags that implicitly print usage message, e.g. --help
+ *
+ * @todo Enable user to specify header and footer content in the help message.
+ *
+ * @todo Feature request to handle option interdependencies.
+ * e.g. if -b is specified, -a must be specified or else the
+ * usage is invalid.
+ *
+ * @todo Feature request to implement callbacks.
+ * e.g. if -a is specified, run function 'handleOptionA'().
+ */
+class Getopt
+{
+
+ /**
+ * The options for a given application can be in multiple formats.
+ * modeGnu is for traditional 'ab:c:' style getopt format.
+ * modeZend is for a more structured format.
+ */
+ const MODE_ZEND = 'zend';
+ const MODE_GNU = 'gnu';
+
+ /**
+ * Constant tokens for various symbols used in the mode_zend
+ * rule format.
+ */
+ const PARAM_REQUIRED = '=';
+ const PARAM_OPTIONAL = '-';
+ const TYPE_STRING = 's';
+ const TYPE_WORD = 'w';
+ const TYPE_INTEGER = 'i';
+ const TYPE_NUMERIC_FLAG = '#';
+
+ /**
+ * These are constants for optional behavior of this class.
+ * ruleMode is either 'zend' or 'gnu' or a user-defined mode.
+ * dashDash is true if '--' signifies the end of command-line options.
+ * ignoreCase is true if '--opt' and '--OPT' are implicitly synonyms.
+ * parseAll is true if all options on the command line should be parsed, regardless of
+ * whether an argument appears before them.
+ */
+ const CONFIG_RULEMODE = 'ruleMode';
+ const CONFIG_DASHDASH = 'dashDash';
+ const CONFIG_IGNORECASE = 'ignoreCase';
+ const CONFIG_PARSEALL = 'parseAll';
+ const CONFIG_CUMULATIVE_PARAMETERS = 'cumulativeParameters';
+ const CONFIG_CUMULATIVE_FLAGS = 'cumulativeFlags';
+ const CONFIG_PARAMETER_SEPARATOR = 'parameterSeparator';
+ const CONFIG_FREEFORM_FLAGS = 'freeformFlags';
+ const CONFIG_NUMERIC_FLAGS = 'numericFlags';
+
+ /**
+ * Defaults for getopt configuration are:
+ * ruleMode is 'zend' format,
+ * dashDash (--) token is enabled,
+ * ignoreCase is not enabled,
+ * parseAll is enabled,
+ * cumulative parameters are disabled,
+ * this means that subsequent options overwrite the parameter value,
+ * cumulative flags are disable,
+ * freeform flags are disable.
+ */
+ protected $getoptConfig = array(
+ self::CONFIG_RULEMODE => self::MODE_ZEND,
+ self::CONFIG_DASHDASH => true,
+ self::CONFIG_IGNORECASE => false,
+ self::CONFIG_PARSEALL => true,
+ self::CONFIG_CUMULATIVE_PARAMETERS => false,
+ self::CONFIG_CUMULATIVE_FLAGS => false,
+ self::CONFIG_PARAMETER_SEPARATOR => null,
+ self::CONFIG_FREEFORM_FLAGS => false,
+ self::CONFIG_NUMERIC_FLAGS => false
+ );
+
+ /**
+ * Stores the command-line arguments for the calling application.
+ *
+ * @var array
+ */
+ protected $argv = array();
+
+ /**
+ * Stores the name of the calling application.
+ *
+ * @var string
+ */
+ protected $progname = '';
+
+ /**
+ * Stores the list of legal options for this application.
+ *
+ * @var array
+ */
+ protected $rules = array();
+
+ /**
+ * Stores alternate spellings of legal options.
+ *
+ * @var array
+ */
+ protected $ruleMap = array();
+
+ /**
+ * Stores options given by the user in the current invocation
+ * of the application, as well as parameters given in options.
+ *
+ * @var array
+ */
+ protected $options = array();
+
+ /**
+ * Stores the command-line arguments other than options.
+ *
+ * @var array
+ */
+ protected $remainingArgs = array();
+
+ /**
+ * State of the options: parsed or not yet parsed?
+ *
+ * @var boolean
+ */
+ protected $parsed = false;
+
+ /**
+ * The constructor takes one to three parameters.
+ *
+ * The first parameter is $rules, which may be a string for
+ * gnu-style format, or a structured array for Zend-style format.
+ *
+ * The second parameter is $argv, and it is optional. If not
+ * specified, $argv is inferred from the global argv.
+ *
+ * The third parameter is an array of configuration parameters
+ * to control the behavior of this instance of Getopt; it is optional.
+ *
+ * @param array $rules
+ * @param array $argv
+ * @param array $getoptConfig
+ * @throws Exception\InvalidArgumentException
+ */
+ public function __construct($rules, $argv = null, $getoptConfig = array())
+ {
+ if (!isset($_SERVER['argv'])) {
+ $errorDescription = (ini_get('register_argc_argv') == false)
+ ? "argv is not available, because ini option 'register_argc_argv' is set Off"
+ : '$_SERVER["argv"] is not set, but Zend_Console_Getopt cannot work without this information.';
+ throw new Exception\InvalidArgumentException($errorDescription);
+ }
+
+ $this->progname = $_SERVER['argv'][0];
+ $this->setOptions($getoptConfig);
+ $this->addRules($rules);
+ if (!is_array($argv)) {
+ $argv = array_slice($_SERVER['argv'], 1);
+ }
+ if (isset($argv)) {
+ $this->addArguments((array) $argv);
+ }
+ }
+
+ /**
+ * Return the state of the option seen on the command line of the
+ * current application invocation. This function returns true, or the
+ * parameter to the option, if any. If the option was not given,
+ * this function returns null.
+ *
+ * The magic __get method works in the context of naming the option
+ * as a virtual member of this class.
+ *
+ * @param string $key
+ * @return string
+ */
+ public function __get($key)
+ {
+ return $this->getOption($key);
+ }
+
+ /**
+ * Test whether a given option has been seen.
+ *
+ * @param string $key
+ * @return boolean
+ */
+ public function __isset($key)
+ {
+ $this->parse();
+ if (isset($this->ruleMap[$key])) {
+ $key = $this->ruleMap[$key];
+ return isset($this->options[$key]);
+ }
+ return false;
+ }
+
+ /**
+ * Set the value for a given option.
+ *
+ * @param string $key
+ * @param string $value
+ * @return void
+ */
+ public function __set($key, $value)
+ {
+ $this->parse();
+ if (isset($this->ruleMap[$key])) {
+ $key = $this->ruleMap[$key];
+ $this->options[$key] = $value;
+ }
+ }
+
+ /**
+ * Return the current set of options and parameters seen as a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * Unset an option.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function __unset($key)
+ {
+ $this->parse();
+ if (isset($this->ruleMap[$key])) {
+ $key = $this->ruleMap[$key];
+ unset($this->options[$key]);
+ }
+ }
+
+ /**
+ * Define additional command-line arguments.
+ * These are appended to those defined when the constructor was called.
+ *
+ * @param array $argv
+ * @throws \Zend\Console\Exception\InvalidArgumentException When not given an array as parameter
+ * @return \Zend\Console\Getopt Provides a fluent interface
+ */
+ public function addArguments($argv)
+ {
+ if (!is_array($argv)) {
+ throw new Exception\InvalidArgumentException("Parameter #1 to addArguments should be an array");
+ }
+ $this->argv = array_merge($this->argv, $argv);
+ $this->parsed = false;
+ return $this;
+ }
+
+ /**
+ * Define full set of command-line arguments.
+ * These replace any currently defined.
+ *
+ * @param array $argv
+ * @throws \Zend\Console\Exception\InvalidArgumentException When not given an array as parameter
+ * @return \Zend\Console\Getopt Provides a fluent interface
+ */
+ public function setArguments($argv)
+ {
+ if (!is_array($argv)) {
+ throw new Exception\InvalidArgumentException("Parameter #1 to setArguments should be an array");
+ }
+ $this->argv = $argv;
+ $this->parsed = false;
+ return $this;
+ }
+
+ /**
+ * Define multiple configuration options from an associative array.
+ * These are not program options, but properties to configure
+ * the behavior of Zend_Console_Getopt.
+ *
+ * @param array $getoptConfig
+ * @return \Zend\Console\Getopt Provides a fluent interface
+ */
+ public function setOptions($getoptConfig)
+ {
+ if (isset($getoptConfig)) {
+ foreach ($getoptConfig as $key => $value) {
+ $this->setOption($key, $value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Define one configuration option as a key/value pair.
+ * These are not program options, but properties to configure
+ * the behavior of Zend_Console_Getopt.
+ *
+ * @param string $configKey
+ * @param string $configValue
+ * @return \Zend\Console\Getopt Provides a fluent interface
+ */
+ public function setOption($configKey, $configValue)
+ {
+ if ($configKey !== null) {
+ $this->getoptConfig[$configKey] = $configValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Define additional option rules.
+ * These are appended to the rules defined when the constructor was called.
+ *
+ * @param array $rules
+ * @return \Zend\Console\Getopt Provides a fluent interface
+ */
+ public function addRules($rules)
+ {
+ $ruleMode = $this->getoptConfig['ruleMode'];
+ switch ($this->getoptConfig['ruleMode']) {
+ case self::MODE_ZEND:
+ if (is_array($rules)) {
+ $this->_addRulesModeZend($rules);
+ break;
+ }
+ // intentional fallthrough
+ case self::MODE_GNU:
+ $this->_addRulesModeGnu($rules);
+ break;
+ default:
+ /**
+ * Call addRulesModeFoo() for ruleMode 'foo'.
+ * The developer should subclass Getopt and
+ * provide this method.
+ */
+ $method = '_addRulesMode' . ucfirst($ruleMode);
+ $this->$method($rules);
+ }
+ $this->parsed = false;
+ return $this;
+ }
+
+ /**
+ * Return the current set of options and parameters seen as a string.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $this->parse();
+ $s = array();
+ foreach ($this->options as $flag => $value) {
+ $s[] = $flag . '=' . ($value === true ? 'true' : $value);
+ }
+ return implode(' ', $s);
+ }
+
+ /**
+ * Return the current set of options and parameters seen
+ * as an array of canonical options and parameters.
+ *
+ * Clusters have been expanded, and option aliases
+ * have been mapped to their primary option names.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ $this->parse();
+ $s = array();
+ foreach ($this->options as $flag => $value) {
+ $s[] = $flag;
+ if ($value !== true) {
+ $s[] = $value;
+ }
+ }
+ return $s;
+ }
+
+ /**
+ * Return the current set of options and parameters seen in Json format.
+ *
+ * @return string
+ */
+ public function toJson()
+ {
+ $this->parse();
+ $j = array();
+ foreach ($this->options as $flag => $value) {
+ $j['options'][] = array(
+ 'option' => array(
+ 'flag' => $flag,
+ 'parameter' => $value
+ )
+ );
+ }
+
+ $json = \Zend\Json\Json::encode($j);
+ return $json;
+ }
+
+ /**
+ * Return the current set of options and parameters seen in XML format.
+ *
+ * @return string
+ */
+ public function toXml()
+ {
+ $this->parse();
+ $doc = new \DomDocument('1.0', 'utf-8');
+ $optionsNode = $doc->createElement('options');
+ $doc->appendChild($optionsNode);
+ foreach ($this->options as $flag => $value) {
+ $optionNode = $doc->createElement('option');
+ $optionNode->setAttribute('flag', utf8_encode($flag));
+ if ($value !== true) {
+ $optionNode->setAttribute('parameter', utf8_encode($value));
+ }
+ $optionsNode->appendChild($optionNode);
+ }
+ $xml = $doc->saveXML();
+ return $xml;
+ }
+
+ /**
+ * Return a list of options that have been seen in the current argv.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ $this->parse();
+ return array_keys($this->options);
+ }
+
+ /**
+ * Return the state of the option seen on the command line of the
+ * current application invocation.
+ *
+ * This function returns true, or the parameter value to the option, if any.
+ * If the option was not given, this function returns false.
+ *
+ * @param string $flag
+ * @return mixed
+ */
+ public function getOption($flag)
+ {
+ $this->parse();
+ if ($this->getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ }
+ if (isset($this->ruleMap[$flag])) {
+ $flag = $this->ruleMap[$flag];
+ if (isset($this->options[$flag])) {
+ return $this->options[$flag];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the arguments from the command-line following all options found.
+ *
+ * @return array
+ */
+ public function getRemainingArgs()
+ {
+ $this->parse();
+ return $this->remainingArgs;
+ }
+
+ public function getArguments()
+ {
+ $result = $this->getRemainingArgs();
+ foreach ($this->getOptions() as $option) {
+ $result[$option] = $this->getOption($option);
+ }
+ return $result;
+ }
+
+ /**
+ * Return a useful option reference, formatted for display in an
+ * error message.
+ *
+ * Note that this usage information is provided in most Exceptions
+ * generated by this class.
+ *
+ * @return string
+ */
+ public function getUsageMessage()
+ {
+ $usage = "Usage: {$this->progname} [ options ]\n";
+ $maxLen = 20;
+ $lines = array();
+ foreach ($this->rules as $rule) {
+ $flags = array();
+ if (is_array($rule['alias'])) {
+ foreach ($rule['alias'] as $flag) {
+ $flags[] = (strlen($flag) == 1 ? '-' : '--') . $flag;
+ }
+ }
+ $linepart['name'] = implode('|', $flags);
+ if (isset($rule['param']) && $rule['param'] != 'none') {
+ $linepart['name'] .= ' ';
+ switch ($rule['param']) {
+ case 'optional':
+ $linepart['name'] .= "[ <{$rule['paramType']}> ]";
+ break;
+ case 'required':
+ $linepart['name'] .= "<{$rule['paramType']}>";
+ break;
+ }
+ }
+ if (strlen($linepart['name']) > $maxLen) {
+ $maxLen = strlen($linepart['name']);
+ }
+ $linepart['help'] = '';
+ if (isset($rule['help'])) {
+ $linepart['help'] .= $rule['help'];
+ }
+ $lines[] = $linepart;
+ }
+ foreach ($lines as $linepart) {
+ $usage .= sprintf("%s %s\n",
+ str_pad($linepart['name'], $maxLen),
+ $linepart['help']);
+ }
+ return $usage;
+ }
+
+ /**
+ * Define aliases for options.
+ *
+ * The parameter $aliasMap is an associative array
+ * mapping option name (short or long) to an alias.
+ *
+ * @param array $aliasMap
+ * @throws \Zend\Console\Exception\ExceptionInterface
+ * @return \Zend\Console\Getopt Provides a fluent interface
+ */
+ public function setAliases($aliasMap)
+ {
+ foreach ($aliasMap as $flag => $alias) {
+ if ($this->getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ $alias = strtolower($alias);
+ }
+ if (!isset($this->ruleMap[$flag])) {
+ continue;
+ }
+ $flag = $this->ruleMap[$flag];
+ if (isset($this->rules[$alias]) || isset($this->ruleMap[$alias])) {
+ $o = (strlen($alias) == 1 ? '-' : '--') . $alias;
+ throw new Exception\InvalidArgumentException("Option \"$o\" is being defined more than once.");
+ }
+ $this->rules[$flag]['alias'][] = $alias;
+ $this->ruleMap[$alias] = $flag;
+ }
+ return $this;
+ }
+
+ /**
+ * Define help messages for options.
+ *
+ * The parameter $help_map is an associative array
+ * mapping option name (short or long) to the help string.
+ *
+ * @param array $helpMap
+ * @return \Zend\Console\Getopt Provides a fluent interface
+ */
+ public function setHelp($helpMap)
+ {
+ foreach ($helpMap as $flag => $help) {
+ if (!isset($this->ruleMap[$flag])) {
+ continue;
+ }
+ $flag = $this->ruleMap[$flag];
+ $this->rules[$flag]['help'] = $help;
+ }
+ return $this;
+ }
+
+ /**
+ * Parse command-line arguments and find both long and short
+ * options.
+ *
+ * Also find option parameters, and remaining arguments after
+ * all options have been parsed.
+ *
+ * @return \Zend\Console\Getopt|null Provides a fluent interface
+ */
+ public function parse()
+ {
+ if ($this->parsed === true) {
+ return;
+ }
+ $argv = $this->argv;
+ $this->options = array();
+ $this->remainingArgs = array();
+ while (count($argv) > 0) {
+ if ($argv[0] == '--') {
+ array_shift($argv);
+ if ($this->getoptConfig[self::CONFIG_DASHDASH]) {
+ $this->remainingArgs = array_merge($this->remainingArgs, $argv);
+ break;
+ }
+ }
+ if (substr($argv[0], 0, 2) == '--') {
+ $this->_parseLongOption($argv);
+ } elseif (substr($argv[0], 0, 1) == '-' && ('-' != $argv[0] || count($argv) >1)) {
+ $this->_parseShortOptionCluster($argv);
+ } elseif ($this->getoptConfig[self::CONFIG_PARSEALL]) {
+ $this->remainingArgs[] = array_shift($argv);
+ } else {
+ /*
+ * We should put all other arguments in remainingArgs and stop parsing
+ * since CONFIG_PARSEALL is false.
+ */
+ $this->remainingArgs = array_merge($this->remainingArgs, $argv);
+ break;
+ }
+ }
+ $this->parsed = true;
+ return $this;
+ }
+
+ /**
+ * Parse command-line arguments for a single long option.
+ * A long option is preceded by a double '--' character.
+ * Long options may not be clustered.
+ *
+ * @param mixed &$argv
+ * @return void
+ */
+ protected function _parseLongOption(&$argv)
+ {
+ $optionWithParam = ltrim(array_shift($argv), '-');
+ $l = explode('=', $optionWithParam, 2);
+ $flag = array_shift($l);
+ $param = array_shift($l);
+ if (isset($param)) {
+ array_unshift($argv, $param);
+ }
+ $this->_parseSingleOption($flag, $argv);
+ }
+
+ /**
+ * Parse command-line arguments for short options.
+ * Short options are those preceded by a single '-' character.
+ * Short options may be clustered.
+ *
+ * @param mixed &$argv
+ * @return void
+ */
+ protected function _parseShortOptionCluster(&$argv)
+ {
+ $flagCluster = ltrim(array_shift($argv), '-');
+ foreach (str_split($flagCluster) as $flag) {
+ $this->_parseSingleOption($flag, $argv);
+ }
+ }
+
+ /**
+ * Parse command-line arguments for a single option.
+ *
+ * @param string $flag
+ * @param mixed $argv
+ * @throws \Zend\Console\Exception\ExceptionInterface
+ * @return void
+ */
+ protected function _parseSingleOption($flag, &$argv)
+ {
+ if ($this->getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ }
+
+ // Check if this option is numeric one
+ if (preg_match('/^\d+$/', $flag)) {
+ return $this->_setNumericOptionValue($flag);
+ }
+
+ if (!isset($this->ruleMap[$flag])) {
+ // Don't throw Exception for flag-like param in case when freeform flags are allowed
+ if (!$this->getoptConfig[self::CONFIG_FREEFORM_FLAGS]) {
+ throw new Exception\RuntimeException(
+ "Option \"$flag\" is not recognized.",
+ $this->getUsageMessage()
+ );
+ }
+
+ // Magic methods in future will use this mark as real flag value
+ $this->ruleMap[$flag] = $flag;
+ $realFlag = $flag;
+ $this->rules[$realFlag] = array('param' => 'optional');
+ } else {
+ $realFlag = $this->ruleMap[$flag];
+ }
+
+ switch ($this->rules[$realFlag]['param']) {
+ case 'required':
+ if (count($argv) > 0) {
+ $param = array_shift($argv);
+ $this->_checkParameterType($realFlag, $param);
+ } else {
+ throw new Exception\RuntimeException(
+ "Option \"$flag\" requires a parameter.",
+ $this->getUsageMessage()
+ );
+ }
+ break;
+ case 'optional':
+ if (count($argv) > 0 && substr($argv[0], 0, 1) != '-') {
+ $param = array_shift($argv);
+ $this->_checkParameterType($realFlag, $param);
+ } else {
+ $param = true;
+ }
+ break;
+ default:
+ $param = true;
+ }
+
+ $this->_setSingleOptionValue($realFlag, $param);
+ }
+
+
+ /**
+ * Set given value as value of numeric option
+ *
+ * Throw runtime exception if this action is deny by configuration
+ * or no one numeric option handlers is defined
+ *
+ * @param int $value
+ * @throws Exception\RuntimeException
+ * @return void
+ */
+ protected function _setNumericOptionValue($value)
+ {
+ if (!$this->getoptConfig[self::CONFIG_NUMERIC_FLAGS]) {
+ throw new Exception\RuntimeException("Using of numeric flags are deny by configuration");
+ }
+
+ if (empty($this->getoptConfig['numericFlagsOption'])) {
+ throw new Exception\RuntimeException("Any option for handling numeric flags are specified");
+ }
+
+ return $this->_setSingleOptionValue($this->getoptConfig['numericFlagsOption'], $value);
+ }
+
+ /**
+ * Add relative to options' flag value
+ *
+ * If options list already has current flag as key
+ * and parser should follow cumulative params by configuration,
+ * we should to add new param to array, not to overwrite
+ *
+ * @param string $flag
+ * @param string $value
+ * @return null
+ */
+ protected function _setSingleOptionValue($flag, $value)
+ {
+ if (true === $value && $this->getoptConfig[self::CONFIG_CUMULATIVE_FLAGS]) {
+ // For boolean values we have to create new flag, or increase number of flags' usage count
+ return $this->_setBooleanFlagValue($flag);
+ }
+
+ // Split multiple values, if necessary
+ // Filter empty values from splited array
+ $separator = $this->getoptConfig[self::CONFIG_PARAMETER_SEPARATOR];
+ if (is_string($value) && !empty($separator) && is_string($separator) && substr_count($value, $separator)) {
+ $value = array_filter(explode($separator, $value));
+ }
+
+ if (!array_key_exists($flag, $this->options)) {
+ $this->options[$flag] = $value;
+ } elseif ($this->getoptConfig[self::CONFIG_CUMULATIVE_PARAMETERS]) {
+ $this->options[$flag] = (array) $this->options[$flag];
+ array_push($this->options[$flag], $value);
+ } else {
+ $this->options[$flag] = $value;
+ }
+ }
+
+ /**
+ * Set TRUE value to given flag, if this option does not exist yet
+ * In other case increase value to show count of flags' usage
+ *
+ * @param string $flag
+ * @return null
+ */
+ protected function _setBooleanFlagValue($flag)
+ {
+ $this->options[$flag] = array_key_exists($flag, $this->options)
+ ? (int) $this->options[$flag] + 1
+ : true;
+ }
+
+ /**
+ * Return true if the parameter is in a valid format for
+ * the option $flag.
+ * Throw an exception in most other cases.
+ *
+ * @param string $flag
+ * @param string $param
+ * @throws \Zend\Console\Exception\ExceptionInterface
+ * @return bool
+ */
+ protected function _checkParameterType($flag, $param)
+ {
+ $type = 'string';
+ if (isset($this->rules[$flag]['paramType'])) {
+ $type = $this->rules[$flag]['paramType'];
+ }
+ switch ($type) {
+ case 'word':
+ if (preg_match('/\W/', $param)) {
+ throw new Exception\RuntimeException(
+ "Option \"$flag\" requires a single-word parameter, but was given \"$param\".",
+ $this->getUsageMessage());
+ }
+ break;
+ case 'integer':
+ if (preg_match('/\D/', $param)) {
+ throw new Exception\RuntimeException(
+ "Option \"$flag\" requires an integer parameter, but was given \"$param\".",
+ $this->getUsageMessage());
+ }
+ break;
+ case 'string':
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Define legal options using the gnu-style format.
+ *
+ * @param string $rules
+ * @return void
+ */
+ protected function _addRulesModeGnu($rules)
+ {
+ $ruleArray = array();
+
+ /**
+ * Options may be single alphanumeric characters.
+ * Options may have a ':' which indicates a required string parameter.
+ * No long options or option aliases are supported in GNU style.
+ */
+ preg_match_all('/([a-zA-Z0-9]:?)/', $rules, $ruleArray);
+ foreach ($ruleArray[1] as $rule) {
+ $r = array();
+ $flag = substr($rule, 0, 1);
+ if ($this->getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ }
+ $r['alias'][] = $flag;
+ if (substr($rule, 1, 1) == ':') {
+ $r['param'] = 'required';
+ $r['paramType'] = 'string';
+ } else {
+ $r['param'] = 'none';
+ }
+ $this->rules[$flag] = $r;
+ $this->ruleMap[$flag] = $flag;
+ }
+ }
+
+ /**
+ * Define legal options using the Zend-style format.
+ *
+ * @param array $rules
+ * @throws \Zend\Console\Exception\ExceptionInterface
+ * @return void
+ */
+ protected function _addRulesModeZend($rules)
+ {
+ foreach ($rules as $ruleCode => $helpMessage) {
+ // this may have to translate the long parm type if there
+ // are any complaints that =string will not work (even though that use
+ // case is not documented)
+ if (in_array(substr($ruleCode, -2, 1), array('-', '='))) {
+ $flagList = substr($ruleCode, 0, -2);
+ $delimiter = substr($ruleCode, -2, 1);
+ $paramType = substr($ruleCode, -1);
+ } else {
+ $flagList = $ruleCode;
+ $delimiter = $paramType = null;
+ }
+ if ($this->getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flagList = strtolower($flagList);
+ }
+ $flags = explode('|', $flagList);
+ $rule = array();
+ $mainFlag = $flags[0];
+ foreach ($flags as $flag) {
+ if (empty($flag)) {
+ throw new Exception\InvalidArgumentException("Blank flag not allowed in rule \"$ruleCode\".");
+ }
+ if (strlen($flag) == 1) {
+ if (isset($this->ruleMap[$flag])) {
+ throw new Exception\InvalidArgumentException(
+ "Option \"-$flag\" is being defined more than once.");
+ }
+ $this->ruleMap[$flag] = $mainFlag;
+ $rule['alias'][] = $flag;
+ } else {
+ if (isset($this->rules[$flag]) || isset($this->ruleMap[$flag])) {
+ throw new Exception\InvalidArgumentException(
+ "Option \"--$flag\" is being defined more than once.");
+ }
+ $this->ruleMap[$flag] = $mainFlag;
+ $rule['alias'][] = $flag;
+ }
+ }
+ if (isset($delimiter)) {
+ switch ($delimiter) {
+ case self::PARAM_REQUIRED:
+ $rule['param'] = 'required';
+ break;
+ case self::PARAM_OPTIONAL:
+ default:
+ $rule['param'] = 'optional';
+ }
+ switch (substr($paramType, 0, 1)) {
+ case self::TYPE_WORD:
+ $rule['paramType'] = 'word';
+ break;
+ case self::TYPE_INTEGER:
+ $rule['paramType'] = 'integer';
+ break;
+ case self::TYPE_NUMERIC_FLAG:
+ $rule['paramType'] = 'numericFlag';
+ $this->getoptConfig['numericFlagsOption'] = $mainFlag;
+ break;
+ case self::TYPE_STRING:
+ default:
+ $rule['paramType'] = 'string';
+ }
+ } else {
+ $rule['param'] = 'none';
+ }
+ $rule['help'] = $helpMessage;
+ $this->rules[$mainFlag] = $rule;
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Prompt;
+
+use ReflectionClass;
+use Zend\Console\Console;
+use Zend\Console\Adapter\AdapterInterface as ConsoleAdapter;
+use Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Prompt
+ */
+abstract class AbstractPrompt implements PromptInterface
+{
+ /**
+ * @var ConsoleAdapter
+ */
+ protected $console;
+
+ /**
+ * @var mixed
+ */
+ protected $lastResponse;
+
+ /**
+ * Show a prompt
+ *
+ * @return void
+ */
+ abstract public function show();
+
+ /**
+ * Return last answer to this prompt.
+ *
+ * @return mixed
+ */
+ public function getLastResponse()
+ {
+ return $this->lastResponse;
+ }
+
+ /**
+ * Return console adapter to use when showing prompt.
+ *
+ * @return ConsoleAdapter
+ */
+ public function getConsole()
+ {
+ if (!$this->console) {
+ $this->console = Console::getInstance();
+ }
+
+ return $this->console;
+ }
+
+ /**
+ * Set console adapter to use when showing prompt.
+ *
+ * @param ConsoleAdapter $adapter
+ */
+ public function setConsole(ConsoleAdapter $adapter)
+ {
+ $this->console = $adapter;
+ }
+
+ /**
+ * Create an instance of this prompt, show it and return response.
+ *
+ * This is a convenience method for creating statically creating prompts, i.e.:
+ *
+ * $name = Zend\Console\Prompt\Line::prompt("Enter your name: ");
+ *
+ * @return mixed
+ * @throws Exception\BadMethodCallException
+ */
+ public static function prompt()
+ {
+ if (get_called_class() === __CLASS__) {
+ throw new Exception\BadMethodCallException(
+ 'Cannot call prompt() on AbstractPrompt class. Use one of the Zend\Console\Prompt\ subclasses.'
+ );
+ }
+
+ $refl = new ReflectionClass(get_called_class());
+ $instance = $refl->newInstanceArgs(func_get_args());
+ return $instance->show();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Prompt;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Prompt
+ */
+class Char extends AbstractPrompt
+{
+ /**
+ * @var string
+ */
+ protected $promptText = 'Please select one option ';
+
+ /**
+ * @var bool
+ */
+ protected $allowEmpty = false;
+
+ /**
+ * @var string
+ */
+ protected $allowedChars = 'yn';
+
+ /**
+ * @var bool
+ */
+ protected $ignoreCase = true;
+
+ /**
+ * @var bool
+ */
+ protected $echo = true;
+
+ /**
+ * Ask the user for a single key stroke
+ *
+ * @param string $promptText The prompt text to display in console
+ * @param string $allowedChars A list of allowed chars (i.e. "abc12345")
+ * @param bool $ignoreCase If true, case will be ignored and prompt will always return lower-cased response
+ * @param bool $allowEmpty Is empty response allowed?
+ * @param bool $echo Display the selection after user presses key
+ */
+ public function __construct(
+ $promptText = 'Please hit a key',
+ $allowedChars = 'abc',
+ $ignoreCase = true,
+ $allowEmpty = false,
+ $echo = true
+ ) {
+
+ if ($promptText !== null) {
+ $this->setPromptText($promptText);
+ }
+
+ if ($allowEmpty !== null) {
+ $this->setAllowEmpty($allowEmpty);
+ }
+
+ if ($ignoreCase !== null) {
+ $this->setIgnoreCase($ignoreCase);
+ }
+
+ if ($allowedChars !== null) {
+ if ($this->ignoreCase) {
+ $this->setAllowedChars(strtolower($allowedChars));
+ } else {
+ $this->setAllowedChars($allowedChars);
+ }
+ }
+
+ if ($echo !== null) {
+ $this->setEcho($echo);
+ }
+ }
+
+ /**
+ * Show the prompt to user and return a single char.
+ *
+ * @return string
+ */
+ public function show()
+ {
+ $this->getConsole()->write($this->promptText);
+ $mask = $this->getAllowedChars();
+
+ /**
+ * Normalize the mask if case is irrelevant
+ */
+ if ($this->ignoreCase) {
+ $mask = strtolower($mask); // lowercase all
+ $mask .= strtoupper($mask); // uppercase and append
+ $mask = str_split($mask); // convert to array
+ $mask = array_unique($mask); // remove duplicates
+ $mask = implode("", $mask); // convert back to string
+ }
+
+ do {
+ /**
+ * Read char from console
+ */
+ $char = $this->getConsole()->readChar($mask);
+
+ /**
+ * Lowercase the response if case is irrelevant
+ */
+ if ($this->ignoreCase) {
+ $char = strtolower($char);
+ }
+
+ /**
+ * Check if it is an allowed char
+ */
+ if (stristr($this->allowedChars, $char) !== false) {
+ if ($this->echo) {
+ echo trim($char)."\n";
+ } else {
+ if ($this->promptText) {
+ echo "\n"; // skip to next line but only if we had any prompt text
+ }
+ }
+ break;
+ }
+ } while (true);
+
+ return $this->lastResponse = $char;
+ }
+
+ /**
+ * @param boolean $allowEmpty
+ */
+ public function setAllowEmpty($allowEmpty)
+ {
+ $this->allowEmpty = $allowEmpty;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getAllowEmpty()
+ {
+ return $this->allowEmpty;
+ }
+
+ /**
+ * @param string $promptText
+ */
+ public function setPromptText($promptText)
+ {
+ $this->promptText = $promptText;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPromptText()
+ {
+ return $this->promptText;
+ }
+
+ /**
+ * @param string $allowedChars
+ */
+ public function setAllowedChars($allowedChars)
+ {
+ $this->allowedChars = $allowedChars;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAllowedChars()
+ {
+ return $this->allowedChars;
+ }
+
+ /**
+ * @param boolean $ignoreCase
+ */
+ public function setIgnoreCase($ignoreCase)
+ {
+ $this->ignoreCase = $ignoreCase;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getIgnoreCase()
+ {
+ return $this->ignoreCase;
+ }
+
+ /**
+ * @param boolean $echo
+ */
+ public function setEcho($echo)
+ {
+ $this->echo = $echo;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getEcho()
+ {
+ return $this->echo;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Prompt;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Prompt
+ */
+class Confirm extends Char
+{
+ /**
+ * @var string
+ */
+ protected $promptText = 'Are you sure?';
+
+ /**
+ * @var string
+ */
+ protected $allowedChars = 'yn';
+
+ /**
+ * @var string
+ */
+ protected $yesChar = 'y';
+
+ /**
+ * @var string
+ */
+ protected $noChar = 'n';
+
+ /**
+ * @var bool
+ */
+ protected $ignoreCase = true;
+
+ /**
+ * Ask the user for a single key stroke
+ *
+ * @param string $promptText The prompt text to display in console
+ * @param string $yesChar The "yes" key (defaults to Y)
+ * @param string $noChar The "no" key (defaults to N)
+ */
+ public function __construct(
+ $promptText = 'Are you sure?',
+ $yesChar = 'y',
+ $noChar = 'n'
+ ) {
+ if ($promptText !== null) {
+ $this->setPromptText($promptText);
+ }
+
+ if ($yesChar !== null) {
+ $this->setYesChar($yesChar);
+ }
+
+ if ($noChar !== null) {
+ $this->setNoChar($noChar);
+ }
+ }
+
+ /**
+ * Show the confirmation message and return result.
+ *
+ * @return bool
+ */
+ public function show()
+ {
+ $response = parent::show() === $this->yesChar;
+ return $this->lastResponse = $response;
+ }
+
+
+ /**
+ * @param string $noChar
+ */
+ public function setNoChar($noChar)
+ {
+ $this->noChar = $noChar;
+ $this->setAllowedChars($this->yesChar . $this->noChar);
+ }
+
+ /**
+ * @return string
+ */
+ public function getNoChar()
+ {
+ return $this->noChar;
+ }
+
+ /**
+ * @param string $yesChar
+ */
+ public function setYesChar($yesChar)
+ {
+ $this->yesChar = $yesChar;
+ $this->setAllowedChars($this->yesChar . $this->noChar);
+ }
+
+ /**
+ * @return string
+ */
+ public function getYesChar()
+ {
+ return $this->yesChar;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Prompt;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Prompt
+ */
+class Line extends AbstractPrompt
+{
+ /**
+ * @var string
+ */
+ protected $promptText = 'Please enter value: ';
+
+ /**
+ * @var bool
+ */
+ protected $allowEmpty = false;
+
+ /**
+ * @var int
+ */
+ protected $maxLength = 2048;
+
+ /**
+ * Ask the user for an answer (a line of text)
+ *
+ * @param string $promptText The prompt text to display in console
+ * @param bool $allowEmpty Is empty response allowed?
+ * @param int $maxLength Maximum response length
+ */
+ public function __construct($promptText = 'Please enter value: ', $allowEmpty = false, $maxLength = 2048)
+ {
+ if ($promptText !== null) {
+ $this->setPromptText($promptText);
+ }
+
+ if ($allowEmpty !== null) {
+ $this->setAllowEmpty($allowEmpty);
+ }
+
+ if ($maxLength !== null) {
+ $this->setMaxLength($maxLength);
+ }
+ }
+
+ /**
+ * Show the prompt to user and return the answer.
+ *
+ * @return mixed
+ */
+ public function show()
+ {
+ do {
+ $this->getConsole()->write($this->promptText);
+ $line = $this->getConsole()->readLine($this->maxLength);
+ } while (!$this->allowEmpty && !$line);
+
+ return $this->lastResponse = $line;
+ }
+
+ /**
+ * @param boolean $allowEmpty
+ */
+ public function setAllowEmpty($allowEmpty)
+ {
+ $this->allowEmpty = $allowEmpty;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getAllowEmpty()
+ {
+ return $this->allowEmpty;
+ }
+
+ /**
+ * @param int $maxLength
+ */
+ public function setMaxLength($maxLength)
+ {
+ $this->maxLength = $maxLength;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMaxLength()
+ {
+ return $this->maxLength;
+ }
+
+ /**
+ * @param string $promptText
+ */
+ public function setPromptText($promptText)
+ {
+ $this->promptText = $promptText;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPromptText()
+ {
+ return $this->promptText;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Prompt;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Prompt
+ */
+class Number extends Line
+{
+ /**
+ * @var string
+ */
+ protected $promptText = 'Please enter a number: ';
+
+ /**
+ * @var bool
+ */
+ protected $allowFloat = false;
+
+ /**
+ * @var int
+ */
+ protected $min;
+
+ /**
+ * @var int
+ */
+ protected $max;
+
+ /**
+ * Ask the user for a number.
+ *
+ * @param string $promptText The prompt text to display in console
+ * @param bool $allowEmpty Is empty response allowed?
+ * @param bool $allowFloat Are floating (non-decimal) numbers allowed?
+ * @param integer $min Minimum value (inclusive)
+ * @param integer $max Maximum value (inclusive)
+ */
+ public function __construct(
+ $promptText = 'Please enter a number: ',
+ $allowEmpty = false,
+ $allowFloat = false,
+ $min = null,
+ $max = null
+ ) {
+ if ($promptText !== null) {
+ $this->setPromptText($promptText);
+ }
+
+ if ($allowEmpty !== null) {
+ $this->setAllowEmpty($allowEmpty);
+ }
+
+ if ($min !== null) {
+ $this->setMin($min);
+ }
+
+ if ($max !== null) {
+ $this->setMax($max);
+ }
+
+ if ($allowFloat !== null) {
+ $this->setAllowFloat($allowFloat);
+ }
+ }
+
+ /**
+ * Show the prompt to user and return the answer.
+ *
+ * @return mixed
+ */
+ public function show()
+ {
+ /**
+ * Ask for a number and validate it.
+ */
+ do {
+ $valid = true;
+ $number = parent::show();
+ if ($number === "" && !$this->allowEmpty) {
+ $valid = false;
+ } elseif ($number === "") {
+ $number = null;
+ } elseif (!is_numeric($number)) {
+ $this->getConsole()->writeLine("$number is not a number\n");
+ $valid = false;
+ } elseif (!$this->allowFloat && (round($number) != $number) ) {
+ $this->getConsole()->writeLine("Please enter a non-floating number, i.e. " . round($number) . "\n");
+ $valid = false;
+ } elseif ($this->max !== null && $number > $this->max) {
+ $this->getConsole()->writeLine("Please enter a number not greater than " . $this->max . "\n");
+ $valid = false;
+ } elseif ($this->min !== null && $number < $this->min) {
+ $this->getConsole()->writeLine("Please enter a number not smaller than " . $this->min . "\n");
+ $valid = false;
+ }
+ } while (!$valid);
+
+ /**
+ * Cast proper type
+ */
+ if ($number !== null) {
+ $number = $this->allowFloat ? (double) $number : (int) $number;
+ }
+
+ return $this->lastResponse = $number;
+ }
+
+ /**
+ * @param boolean $allowEmpty
+ */
+ public function setAllowEmpty($allowEmpty)
+ {
+ $this->allowEmpty = $allowEmpty;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getAllowEmpty()
+ {
+ return $this->allowEmpty;
+ }
+
+ /**
+ * @param int $maxLength
+ */
+ public function setMaxLength($maxLength)
+ {
+ $this->maxLength = $maxLength;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMaxLength()
+ {
+ return $this->maxLength;
+ }
+
+ /**
+ * @param string $promptText
+ */
+ public function setPromptText($promptText)
+ {
+ $this->promptText = $promptText;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPromptText()
+ {
+ return $this->promptText;
+ }
+
+ /**
+ * @param int $max
+ */
+ public function setMax($max)
+ {
+ $this->max = $max;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMax()
+ {
+ return $this->max;
+ }
+
+ /**
+ * @param int $min
+ */
+ public function setMin($min)
+ {
+ $this->min = $min;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMin()
+ {
+ return $this->min;
+ }
+
+ /**
+ * @param boolean $allowFloat
+ */
+ public function setAllowFloat($allowFloat)
+ {
+ $this->allowFloat = $allowFloat;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getAllowFloat()
+ {
+ return $this->allowFloat;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Prompt;
+
+use Zend\Console\Adapter\AdapterInterface as ConsoleAdapter;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ */
+interface PromptInterface
+{
+ /**
+ * Show the prompt to user and return the answer.
+ *
+ * @return mixed
+ */
+ public function show();
+
+ /**
+ * Return last answer to this prompt.
+ *
+ * @return mixed
+ */
+ public function getLastResponse();
+
+ /**
+ * Return console adapter to use when showing prompt.
+ *
+ * @return ConsoleAdapter
+ */
+ public function getConsole();
+
+ /**
+ * Set console adapter to use when showing prompt.
+ *
+ * @param ConsoleAdapter $adapter
+ */
+ public function setConsole(ConsoleAdapter $adapter);
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console\Prompt;
+
+use Zend\Console\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ * @subpackage Prompt
+ */
+class Select extends Char
+{
+ /**
+ * @var string
+ */
+ protected $promptText = 'Please select an option';
+
+ /**
+ * @var bool
+ */
+ protected $ignoreCase = true;
+
+ /**
+ * @var array
+ */
+ protected $options = array();
+
+ /**
+ * Ask the user to select one of pre-defined options
+ *
+ * @param string $promptText The prompt text to display in console
+ * @param array $options Allowed options
+ * @param bool $allowEmpty Allow empty (no) selection?
+ * @param bool $echo True to display selected option?
+ * @throws Exception\BadMethodCallException if no options available
+ */
+ public function __construct(
+ $promptText = 'Please select one option',
+ $options = array(),
+ $allowEmpty = false,
+ $echo = false
+ ) {
+ if ($promptText !== null) {
+ $this->setPromptText($promptText);
+ }
+
+ if (!count($options)) {
+ throw new Exception\BadMethodCallException(
+ 'Cannot construct a "select" prompt without any options'
+ );
+ }
+
+ $this->setOptions($options);
+
+ if ($allowEmpty !== null) {
+ $this->setAllowEmpty($allowEmpty);
+ }
+
+ if ($echo !== null) {
+ $this->setEcho($echo);
+ }
+
+ }
+
+ /**
+ * Show a list of options and prompt the user to select one of them.
+ *
+ * @return string Selected option
+ */
+ public function show()
+ {
+ // Show prompt text and available options
+ $console = $this->getConsole();
+ $console->writeLine($this->promptText);
+ foreach ($this->options as $k => $v) {
+ $console->writeLine(' ' . $k . ') ' . $v);
+ }
+
+ // Prepare mask
+ $mask = implode("", array_keys($this->options));
+ if ($this->allowEmpty) {
+ $mask .= "\r\n";
+ }
+
+ // Prepare other params for parent class
+ $this->setAllowedChars($mask);
+ $oldPrompt = $this->promptText;
+ $oldEcho = $this->echo;
+ $this->echo = false;
+ $this->promptText = null;
+
+ // Retrieve a single character
+ $response = parent::show();
+
+ // Restore old params
+ $this->promptText = $oldPrompt;
+ $this->echo = $oldEcho;
+
+ // Display selected option if echo is enabled
+ if ($this->echo) {
+ if (isset($this->options[$response])) {
+ $console->writeLine($this->options[$response]);
+ } else {
+ $console->writeLine();
+ }
+ }
+
+ $this->lastResponse = $response;
+ return $response;
+ }
+
+ /**
+ * Set allowed options
+ *
+ * @param array|\Traversable $options
+ * @throws Exception\BadMethodCallException
+ */
+ public function setOptions($options)
+ {
+ if (!is_array($options) && !$options instanceof \Traversable) {
+ throw new Exception\BadMethodCallException(
+ 'Please specify an array or Traversable object as options'
+ );
+ }
+
+ if (!is_array($options)) {
+ $this->options = array();
+ foreach ($options as $k => $v) {
+ $this->options[$k] = $v;
+ }
+ } else {
+ $this->options = $options;
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console;
+
+use Zend\Stdlib\Message;
+use Zend\Stdlib\Parameters;
+use Zend\Stdlib\RequestInterface;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ */
+class Request extends Message implements RequestInterface
+{
+ /**
+ * @var \Zend\Stdlib\Parameters
+ */
+ protected $params = null;
+
+ /**
+ * @var \Zend\Stdlib\Parameters
+ */
+ protected $envParams = null;
+
+ /**
+ * @var string
+ */
+ protected $scriptName = null;
+
+ /**
+ * Create a new CLI request
+ *
+ * @param array|null $args Console arguments. If not supplied, $_SERVER['argv'] will be used
+ * @param array|null $env Environment data. If not supplied, $_ENV will be used
+ * @throws Exception\RuntimeException
+ */
+ public function __construct(array $args = null, array $env = null)
+ {
+ if ($args === null) {
+ if (!isset($_SERVER['argv'])) {
+ $errorDescription = (ini_get('register_argc_argv') == false)
+ ? "Cannot create Console\\Request because PHP ini option 'register_argc_argv' is set Off"
+ : 'Cannot create Console\\Request because $_SERVER["argv"] is not set for unknown reason.';
+ throw new Exception\RuntimeException($errorDescription);
+ }
+ $args = $_SERVER['argv'];
+ }
+
+ if ($env === null) {
+ $env = $_ENV;
+ }
+
+ /**
+ * Extract first param assuming it is the script name
+ */
+ if (count($args) > 0) {
+ $this->setScriptName(array_shift($args));
+ }
+
+ /**
+ * Store runtime params
+ */
+ $this->params()->fromArray($args);
+ $this->setContent($args);
+
+ /**
+ * Store environment data
+ */
+ $this->env()->fromArray($env);
+ }
+
+ /**
+ * Exchange parameters object
+ *
+ * @param \Zend\Stdlib\Parameters $params
+ * @return Request
+ */
+ public function setParams(Parameters $params)
+ {
+ $this->params = $params;
+ $this->setContent($params);
+ return $this;
+ }
+
+ /**
+ * Return the container responsible for parameters
+ *
+ * @return \Zend\Stdlib\Parameters
+ */
+ public function getParams()
+ {
+ if ($this->params === null) {
+ $this->params = new Parameters();
+ }
+
+ return $this->params;
+ }
+
+ /**
+ * Return a single parameter.
+ * Shortcut for $request->params()->get()
+ *
+ * @param string $name Parameter name
+ * @param string $default (optional) default value in case the parameter does not exist
+ * @return mixed
+ */
+ public function getParam($name, $default = null)
+ {
+ return $this->params()->get($name, $default);
+ }
+
+ /**
+ * Return the container responsible for parameters
+ *
+ * @return \Zend\Stdlib\Parameters
+ */
+ public function params()
+ {
+ if ($this->params === null) {
+ $this->params = new Parameters();
+ }
+
+ return $this->params;
+ }
+
+ /**
+ * Provide an alternate Parameter Container implementation for env parameters in this object, (this is NOT the
+ * primary API for value setting, for that see env())
+ *
+ * @param \Zend\Stdlib\Parameters $env
+ * @return \Zend\Console\Request
+ */
+ public function setEnv(Parameters $env)
+ {
+ $this->envParams = $env;
+ return $this;
+ }
+
+ /**
+ * Return the parameter container responsible for env parameters
+ *
+ * @return \Zend\Stdlib\Parameters
+ */
+ public function env()
+ {
+ if ($this->envParams === null) {
+ $this->envParams = new Parameters();
+ }
+
+ return $this->envParams;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return trim(implode(' ', $this->params()->toArray()));
+ }
+
+ /**
+ * Allow PHP casting of this object
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * @param string $scriptName
+ */
+ public function setScriptName($scriptName)
+ {
+ $this->scriptName = $scriptName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getScriptName()
+ {
+ return $this->scriptName;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Console
+ */
+
+namespace Zend\Console;
+
+use Zend\Stdlib\Message;
+use Zend\Stdlib\ResponseInterface;
+
+/**
+ * @category Zend
+ * @package Zend_Console
+ */
+class Response extends Message implements ResponseInterface
+{
+ protected $contentSent = false;
+
+ public function contentSent()
+ {
+ return $this->contentSent;
+ }
+
+ /**
+ * Set the error level that will be returned to shell.
+ *
+ * @param integer $errorLevel
+ * @return Response
+ */
+ public function setErrorLevel($errorLevel)
+ {
+ $this->setMetadata('errorLevel', $errorLevel);
+ return $this;
+ }
+
+ /**
+ * Get response error level that will be returned to shell.
+ *
+ * @return integer|0
+ */
+ public function getErrorLevel()
+ {
+ return $this->getMetadata('errorLevel', 0);
+ }
+
+ public function sendContent()
+ {
+ if ($this->contentSent()) {
+ return $this;
+ }
+ echo $this->getContent();
+ $this->contentSent = true;
+ return $this;
+ }
+
+ public function send()
+ {
+ $this->sendContent();
+ $errorLevel = (int)$this->getMetadata('errorLevel',0);
+ exit($errorLevel);
+ }
+}
--- /dev/null
+{
+ "name": "zendframework/zend-console",
+ "description": " ",
+ "license": "BSD-3-Clause",
+ "keywords": [
+ "zf2",
+ "console"
+ ],
+ "autoload": {
+ "psr-0": {
+ "Zend\\Console\\": ""
+ }
+ },
+ "target-dir": "Zend/Console",
+ "require": {
+ "php": ">=5.3.3"
+ }
+}
\ No newline at end of file
--- /dev/null
+Copyright (c) 2005-2012, Zend Technologies USA, Inc.
+All rights reserved.
+
+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 Zend Technologies USA, Inc. 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 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.
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+use ReflectionClass;
+use Traversable;
+
+require_once __DIR__ . '/SplAutoloader.php';
+
+if (class_exists('Zend\Loader\AutoloaderFactory')) {
+ return;
+}
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ */
+abstract class AutoloaderFactory
+{
+ const STANDARD_AUTOLOADER = 'Zend\Loader\StandardAutoloader';
+
+ /**
+ * @var array All autoloaders registered using the factory
+ */
+ protected static $loaders = array();
+
+ /**
+ * @var StandardAutoloader StandardAutoloader instance for resolving
+ * autoloader classes via the include_path
+ */
+ protected static $standardAutoloader;
+
+ /**
+ * Factory for autoloaders
+ *
+ * Options should be an array or Traversable object of the following structure:
+ * <code>
+ * array(
+ * '<autoloader class name>' => $autoloaderOptions,
+ * )
+ * </code>
+ *
+ * The factory will then loop through and instantiate each autoloader with
+ * the specified options, and register each with the spl_autoloader.
+ *
+ * You may retrieve the concrete autoloader instances later using
+ * {@link getRegisteredAutoloaders()}.
+ *
+ * Note that the class names must be resolvable on the include_path or via
+ * the Zend library, using PSR-0 rules (unless the class has already been
+ * loaded).
+ *
+ * @param array|Traversable $options (optional) options to use. Defaults to Zend\Loader\StandardAutoloader
+ * @return void
+ * @throws Exception\InvalidArgumentException for invalid options
+ * @throws Exception\InvalidArgumentException for unloadable autoloader classes
+ * @throws Exception\DomainException for autoloader classes not implementing SplAutoloader
+ */
+ public static function factory($options = null)
+ {
+ if (null === $options) {
+ if (!isset(static::$loaders[static::STANDARD_AUTOLOADER])) {
+ $autoloader = static::getStandardAutoloader();
+ $autoloader->register();
+ static::$loaders[static::STANDARD_AUTOLOADER] = $autoloader;
+ }
+
+ // Return so we don't hit the next check's exception (we're done here anyway)
+ return;
+ }
+
+ if (!is_array($options) && !($options instanceof Traversable)) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException(
+ 'Options provided must be an array or Traversable'
+ );
+ }
+
+ foreach ($options as $class => $options) {
+ if (!isset(static::$loaders[$class])) {
+ $autoloader = static::getStandardAutoloader();
+ if (!class_exists($class) && !$autoloader->autoload($class)) {
+ require_once 'Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException(
+ sprintf('Autoloader class "%s" not loaded', $class)
+ );
+ }
+
+ if (!self::isSubclassOf($class, 'Zend\Loader\SplAutoloader')) {
+ require_once 'Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException(
+ sprintf('Autoloader class %s must implement Zend\\Loader\\SplAutoloader', $class)
+ );
+ }
+
+ if ($class === static::STANDARD_AUTOLOADER) {
+ $autoloader->setOptions($options);
+ } else {
+ $autoloader = new $class($options);
+ }
+ $autoloader->register();
+ static::$loaders[$class] = $autoloader;
+ } else {
+ static::$loaders[$class]->setOptions($options);
+ }
+ }
+ }
+
+ /**
+ * Get an list of all autoloaders registered with the factory
+ *
+ * Returns an array of autoloader instances.
+ *
+ * @return array
+ */
+ public static function getRegisteredAutoloaders()
+ {
+ return static::$loaders;
+ }
+
+ /**
+ * Retrieves an autoloader by class name
+ *
+ * @param string $class
+ * @return SplAutoloader
+ * @throws Exception\InvalidArgumentException for non-registered class
+ */
+ public static function getRegisteredAutoloader($class)
+ {
+ if (!isset(static::$loaders[$class])) {
+ require_once 'Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException(sprintf('Autoloader class "%s" not loaded', $class));
+ }
+ return static::$loaders[$class];
+ }
+
+ /**
+ * Unregisters all autoloaders that have been registered via the factory.
+ * This will NOT unregister autoloaders registered outside of the fctory.
+ *
+ * @return void
+ */
+ public static function unregisterAutoloaders()
+ {
+ foreach (static::getRegisteredAutoloaders() as $class => $autoloader) {
+ spl_autoload_unregister(array($autoloader, 'autoload'));
+ unset(static::$loaders[$class]);
+ }
+ }
+
+ /**
+ * Unregister a single autoloader by class name
+ *
+ * @param string $autoloaderClass
+ * @return bool
+ */
+ public static function unregisterAutoloader($autoloaderClass)
+ {
+ if (!isset(static::$loaders[$autoloaderClass])) {
+ return false;
+ }
+
+ $autoloader = static::$loaders[$autoloaderClass];
+ spl_autoload_unregister(array($autoloader, 'autoload'));
+ unset(static::$loaders[$autoloaderClass]);
+ return true;
+ }
+
+ /**
+ * Get an instance of the standard autoloader
+ *
+ * Used to attempt to resolve autoloader classes, using the
+ * StandardAutoloader. The instance is marked as a fallback autoloader, to
+ * allow resolving autoloaders not under the "Zend" namespace.
+ *
+ * @return SplAutoloader
+ */
+ protected static function getStandardAutoloader()
+ {
+ if (null !== static::$standardAutoloader) {
+ return static::$standardAutoloader;
+ }
+
+
+ if (!class_exists(static::STANDARD_AUTOLOADER)) {
+ // Extract the filename from the classname
+ $stdAutoloader = substr(strrchr(static::STANDARD_AUTOLOADER, '\\'), 1);
+ require_once __DIR__ . "/$stdAutoloader.php";
+ }
+ $loader = new StandardAutoloader();
+ static::$standardAutoloader = $loader;
+ return static::$standardAutoloader;
+ }
+
+ /**
+ * Checks if the object has this class as one of its parents
+ *
+ * @see https://bugs.php.net/bug.php?id=53727
+ * @see https://github.com/zendframework/zf2/pull/1807
+ *
+ * @param string $className
+ * @param string $type
+ * @return bool
+ */
+ protected static function isSubclassOf($className, $type)
+ {
+ if (is_subclass_of($className, $type)) {
+ return true;
+ }
+ if (version_compare(PHP_VERSION, '5.3.7', '>=')) {
+ return false;
+ }
+ if (!interface_exists($type)) {
+ return false;
+ }
+ $r = new ReflectionClass($className);
+ return $r->implementsInterface($type);
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+use Traversable;
+
+// Grab SplAutoloader interface
+require_once __DIR__ . '/SplAutoloader.php';
+
+/**
+ * Class-map autoloader
+ *
+ * Utilizes class-map files to lookup classfile locations.
+ *
+ * @category Zend
+ * @package Zend_Loader
+ */
+class ClassMapAutoloader implements SplAutoloader
+{
+ /**
+ * Registry of map files that have already been loaded
+ * @var array
+ */
+ protected $mapsLoaded = array();
+
+ /**
+ * Class name/filename map
+ * @var array
+ */
+ protected $map = array();
+
+ /**
+ * Constructor
+ *
+ * Create a new instance, and optionally configure the autoloader.
+ *
+ * @param null|array|Traversable $options
+ */
+ public function __construct($options = null)
+ {
+ if (null !== $options) {
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Configure the autoloader
+ *
+ * Proxies to {@link registerAutoloadMaps()}.
+ *
+ * @param array|Traversable $options
+ * @return ClassMapAutoloader
+ */
+ public function setOptions($options)
+ {
+ $this->registerAutoloadMaps($options);
+ return $this;
+ }
+
+ /**
+ * Register an autoload map
+ *
+ * An autoload map may be either an associative array, or a file returning
+ * an associative array.
+ *
+ * An autoload map should be an associative array containing
+ * classname/file pairs.
+ *
+ * @param string|array $map
+ * @throws Exception\InvalidArgumentException
+ * @return ClassMapAutoloader
+ */
+ public function registerAutoloadMap($map)
+ {
+ if (is_string($map)) {
+ $location = $map;
+ if ($this === ($map = $this->loadMapFromFile($location))) {
+ return $this;
+ }
+ }
+
+ if (!is_array($map)) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException(sprintf(
+ 'Map file provided does not return a map. Map file: "%s"',
+ (isset($location) && is_string($location) ? $location : 'unexpected type: ' . gettype($map))
+ ));
+ }
+
+ $this->map = array_merge($this->map, $map);
+
+ if (isset($location)) {
+ $this->mapsLoaded[] = $location;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Register many autoload maps at once
+ *
+ * @param array $locations
+ * @throws Exception\InvalidArgumentException
+ * @return ClassMapAutoloader
+ */
+ public function registerAutoloadMaps($locations)
+ {
+ if (!is_array($locations) && !($locations instanceof Traversable)) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException('Map list must be an array or implement Traversable');
+ }
+ foreach ($locations as $location) {
+ $this->registerAutoloadMap($location);
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve current autoload map
+ *
+ * @return array
+ */
+ public function getAutoloadMap()
+ {
+ return $this->map;
+ }
+
+ /**
+ * Defined by Autoloadable
+ *
+ * @param string $class
+ * @return void
+ */
+ public function autoload($class)
+ {
+ if (isset($this->map[$class])) {
+ require_once $this->map[$class];
+ }
+ }
+
+ /**
+ * Register the autoloader with spl_autoload registry
+ *
+ * @return void
+ */
+ public function register()
+ {
+ spl_autoload_register(array($this, 'autoload'), true, true);
+ }
+
+ /**
+ * Load a map from a file
+ *
+ * If the map has been previously loaded, returns the current instance;
+ * otherwise, returns whatever was returned by calling include() on the
+ * location.
+ *
+ * @param string $location
+ * @return ClassMapAutoloader|mixed
+ * @throws Exception\InvalidArgumentException for nonexistent locations
+ */
+ protected function loadMapFromFile($location)
+ {
+ if (!file_exists($location)) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException(sprintf(
+ 'Map file provided does not exist. Map file: "%s"',
+ (is_string($location) ? $location : 'unexpected type: ' . gettype($location))
+ ));
+ }
+
+ if (!$path = static::realPharPath($location)) {
+ $path = realpath($location);
+ }
+
+ if (in_array($path, $this->mapsLoaded)) {
+ // Already loaded this map
+ return $this;
+ }
+
+ $map = include $path;
+
+ return $map;
+ }
+
+ /**
+ * Resolve the real_path() to a file within a phar.
+ *
+ * @see https://bugs.php.net/bug.php?id=52769
+ * @param string $path
+ * @return string
+ */
+ public static function realPharPath($path)
+ {
+ if (strpos($path, 'phar:///') !== 0) {
+ return;
+ }
+
+ $parts = explode('/', str_replace(array('/','\\'), '/', substr($path, 8)));
+ $parts = array_values(array_filter($parts, function($p) { return ($p !== '' && $p !== '.'); }));
+
+ array_walk($parts, function ($value, $key) use(&$parts) {
+ if ($value === '..') {
+ unset($parts[$key], $parts[$key-1]);
+ $parts = array_values($parts);
+ }
+ });
+
+ if (file_exists($realPath = 'phar:///' . implode('/', $parts))) {
+ return $realPath;
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/ExceptionInterface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class BadMethodCallException extends \BadMethodCallException implements
+ ExceptionInterface
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/ExceptionInterface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class DomainException extends \DomainException implements ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ */
+interface ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/ExceptionInterface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements
+ ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/ExceptionInterface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class InvalidPathException extends \Exception implements ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/ExceptionInterface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class MissingResourceNamespaceException extends \Exception implements
+ ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/DomainException.php';
+
+/**
+ * Plugin class loader exceptions
+ *
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class PluginLoaderException extends DomainException
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/ExceptionInterface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader\Exception;
+
+require_once __DIR__ . '/DomainException.php';
+
+/**
+ * @category Zend
+ * @package Zend_Loader
+ * @subpackage Exception
+ */
+class SecurityException extends DomainException
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+// Grab SplAutoloader interface
+require_once __DIR__ . '/SplAutoloader.php';
+
+use GlobIterator;
+use SplFileInfo;
+use Traversable;
+
+class ModuleAutoloader implements SplAutoloader
+{
+ /**
+ * @var array An array of module paths to scan
+ */
+ protected $paths = array();
+
+ /**
+ * @var array An array of modulename => path
+ */
+ protected $explicitPaths = array();
+
+ /**
+ * @var array An array of namespaceName => namespacePath
+ */
+ protected $namespacedPaths = array();
+
+ /**
+ * @var array An array of supported phar extensions (filled on constructor)
+ */
+ protected $pharExtensions = array();
+
+ /**
+ * @var array An array of module classes to their containing files
+ */
+ protected $moduleClassMap = array();
+
+ /**
+ * Constructor
+ *
+ * Allow configuration of the autoloader via the constructor.
+ *
+ * @param null|array|Traversable $options
+ */
+ public function __construct($options = null)
+ {
+ if (extension_loaded('phar')) {
+ $this->pharExtensions = array(
+ 'phar',
+ 'phar.tar',
+ 'tar',
+ );
+
+ // ext/zlib enabled -> phar can read gzip & zip compressed files
+ if (extension_loaded('zlib')) {
+ $this->pharExtensions[] = 'phar.gz';
+ $this->pharExtensions[] = 'phar.tar.gz';
+ $this->pharExtensions[] = 'tar.gz';
+
+ $this->pharExtensions[] = 'phar.zip';
+ $this->pharExtensions[] = 'zip';
+ }
+
+ // ext/bzip2 enabled -> phar can read bz2 compressed files
+ if (extension_loaded('bzip2')) {
+ $this->pharExtensions[] = 'phar.bz2';
+ $this->pharExtensions[] = 'phar.tar.bz2';
+ $this->pharExtensions[] = 'tar.bz2';
+ }
+ }
+
+ if (null !== $options) {
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Configure the autoloader
+ *
+ * In most cases, $options should be either an associative array or
+ * Traversable object.
+ *
+ * @param array|Traversable $options
+ * @return ModuleAutoloader
+ */
+ public function setOptions($options)
+ {
+ $this->registerPaths($options);
+ return $this;
+ }
+
+ /**
+ * Autoload a class
+ *
+ * @param $class
+ * @return mixed
+ * False [if unable to load $class]
+ * get_class($class) [if $class is successfully loaded]
+ */
+ public function autoload($class)
+ {
+ // Limit scope of this autoloader
+ if (substr($class, -7) !== '\Module') {
+ return false;
+ }
+
+ $moduleName = substr($class, 0, -7);
+ if (isset($this->explicitPaths[$moduleName])) {
+ $classLoaded = $this->loadModuleFromDir($this->explicitPaths[$moduleName], $class);
+ if ($classLoaded) {
+ return $classLoaded;
+ }
+
+ $classLoaded = $this->loadModuleFromPhar($this->explicitPaths[$moduleName], $class);
+ if ($classLoaded) {
+ return $classLoaded;
+ }
+ }
+
+ if (count($this->namespacedPaths) >= 1 ) {
+ foreach ( $this->namespacedPaths as $namespace=>$path ) {
+ if ( false === strpos($moduleName,$namespace) ) {
+ continue;
+ }
+
+ $moduleName_buffer = str_replace($namespace . "\\", "", $moduleName );
+ $path .= DIRECTORY_SEPARATOR . $moduleName_buffer . DIRECTORY_SEPARATOR;
+
+ $classLoaded = $this->loadModuleFromDir($path, $class);
+ if ($classLoaded) {
+ return $classLoaded;
+ }
+
+ $classLoaded = $this->loadModuleFromPhar($path, $class);
+ if ($classLoaded) {
+ return $classLoaded;
+ }
+ }
+ }
+
+
+ $moduleClassPath = str_replace('\\', DIRECTORY_SEPARATOR, $moduleName);
+
+ $pharSuffixPattern = null;
+ if ($this->pharExtensions) {
+ $pharSuffixPattern = '(' . implode('|', array_map('preg_quote', $this->pharExtensions)) . ')';
+ }
+
+ foreach ($this->paths as $path) {
+ $path = $path . $moduleClassPath;
+
+ $classLoaded = $this->loadModuleFromDir($path, $class);
+ if ($classLoaded) {
+ return $classLoaded;
+ }
+
+ // No directory with Module.php, searching for phars
+ if ($pharSuffixPattern) {
+ foreach (new GlobIterator($path . '.*') as $entry) {
+ if ($entry->isDir()) {
+ continue;
+ }
+
+ if (!preg_match('#.+\.' . $pharSuffixPattern . '$#', $entry->getPathname())) {
+ continue;
+ }
+
+ $classLoaded = $this->loadModuleFromPhar($entry->getPathname(), $class);
+ if ($classLoaded) {
+ return $classLoaded;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * loadModuleFromDir
+ *
+ * @param string $dirPath
+ * @param string $class
+ * @return mixed
+ * False [if unable to load $class]
+ * get_class($class) [if $class is successfully loaded]
+ */
+ protected function loadModuleFromDir($dirPath, $class)
+ {
+ $file = new SplFileInfo($dirPath . '/Module.php');
+ if ($file->isReadable() && $file->isFile()) {
+ // Found directory with Module.php in it
+ require_once $file->getRealPath();
+ if (class_exists($class)) {
+ $this->moduleClassMap[$class] = $file->getRealPath();
+ return $class;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * loadModuleFromPhar
+ *
+ * @param string $pharPath
+ * @param string $class
+ * @return mixed
+ * False [if unable to load $class]
+ * get_class($class) [if $class is successfully loaded]
+ */
+ protected function loadModuleFromPhar($pharPath, $class)
+ {
+ $pharPath = static::normalizePath($pharPath, false);
+ $file = new SplFileInfo($pharPath);
+ if (!$file->isReadable() || !$file->isFile()) {
+ return false;
+ }
+
+ $fileRealPath = $file->getRealPath();
+
+ // Phase 0: Check for executable phar with Module class in stub
+ if (strpos($fileRealPath, '.phar') !== false) {
+ // First see if the stub makes the Module class available
+ require_once $fileRealPath;
+ if (class_exists($class)) {
+ $this->moduleClassMap[$class] = $fileRealPath;
+ return $class;
+ }
+ }
+
+ // Phase 1: Not executable phar, no stub, or stub did not provide Module class; try Module.php directly
+ $moduleClassFile = 'phar://' . $fileRealPath . '/Module.php';
+ $moduleFile = new SplFileInfo($moduleClassFile);
+ if ($moduleFile->isReadable() && $moduleFile->isFile()) {
+ require_once $moduleClassFile;
+ if (class_exists($class)) {
+ $this->moduleClassMap[$class] = $moduleClassFile;
+ return $class;
+ }
+ }
+
+ // Phase 2: Check for nested module directory within archive
+ // Checks for /path/to/MyModule.tar/MyModule/Module.php
+ // (shell-integrated zip/tar utilities wrap directories like this)
+ $pharBaseName = $this->pharFileToModuleName($fileRealPath);
+ $moduleClassFile = 'phar://' . $fileRealPath . '/' . $pharBaseName . '/Module.php';
+ $moduleFile = new SplFileInfo($moduleClassFile);
+ if ($moduleFile->isReadable() && $moduleFile->isFile()) {
+ require_once $moduleClassFile;
+ if (class_exists($class)) {
+ $this->moduleClassMap[$class] = $moduleClassFile;
+ return $class;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Register the autoloader with spl_autoload registry
+ *
+ * @return void
+ */
+ public function register()
+ {
+ spl_autoload_register(array($this, 'autoload'));
+ }
+
+ /**
+ * Unregister the autoloader with spl_autoload registry
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'autoload'));
+ }
+
+ /**
+ * registerPaths
+ *
+ * @param array|Traversable $paths
+ * @throws \InvalidArgumentException
+ * @return ModuleAutoloader
+ */
+ public function registerPaths($paths)
+ {
+ if (!is_array($paths) && !$paths instanceof Traversable) {
+ throw new \InvalidArgumentException(
+ 'Parameter to \\Zend\\Loader\\ModuleAutoloader\'s '
+ . 'registerPaths method must be an array or '
+ . 'implement the \\Traversable interface'
+ );
+ }
+
+ foreach ($paths as $module => $path) {
+ if (is_string($module)) {
+ $this->registerPath($path, $module);
+ } else {
+ $this->registerPath($path);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * registerPath
+ *
+ * @param string $path
+ * @param bool|string $moduleName
+ * @throws \InvalidArgumentException
+ * @return ModuleAutoloader
+ */
+ public function registerPath($path, $moduleName = false)
+ {
+ if (!is_string($path)) {
+ throw new \InvalidArgumentException(sprintf(
+ 'Invalid path provided; must be a string, received %s',
+ gettype($path)
+ ));
+ }
+ if ($moduleName) {
+ if (in_array( substr($moduleName, -2 ), array('\\*','\\%') ) ) {
+ $this->namespacedPaths[ substr($moduleName, 0, -2 ) ] = static::normalizePath($path);
+ } else {
+ $this->explicitPaths[$moduleName] = static::normalizePath($path);
+ }
+ } else {
+ $this->paths[] = static::normalizePath($path);
+ }
+ return $this;
+ }
+
+ /**
+ * getPaths
+ *
+ * This is primarily for unit testing, but could have other uses.
+ *
+ * @return array
+ */
+ public function getPaths()
+ {
+ return $this->paths;
+ }
+
+ /**
+ * Returns the base module name from the path to a phar
+ *
+ * @param string $pharPath
+ * @return string
+ */
+ protected function pharFileToModuleName($pharPath)
+ {
+ do {
+ $pathinfo = pathinfo($pharPath);
+ $pharPath = $pathinfo['filename'];
+ } while (isset($pathinfo['extension']));
+ return $pathinfo['filename'];
+ }
+
+ /**
+ * Normalize a path for insertion in the stack
+ *
+ * @param string $path
+ * @param bool $trailingSlash Whether trailing slash should be included
+ * @return string
+ */
+ public static function normalizePath($path, $trailingSlash = true)
+ {
+ $path = rtrim($path, '/');
+ $path = rtrim($path, '\\');
+ if ($trailingSlash) {
+ $path .= DIRECTORY_SEPARATOR;
+ }
+ return $path;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+use ArrayIterator;
+use IteratorAggregate;
+use Traversable;
+
+/**
+ * Plugin class locator interface
+ *
+ * @category Zend
+ * @package Zend_Loader
+ */
+class PluginClassLoader implements PluginClassLocator
+{
+ /**
+ * List of plugin name => class name pairs
+ * @var array
+ */
+ protected $plugins = array();
+
+ /**
+ * Static map allow global seeding of plugin loader
+ * @var array
+ */
+ protected static $staticMap = array();
+
+ /**
+ * Constructor
+ *
+ * @param null|array|Traversable $map If provided, seeds the loader with a map
+ */
+ public function __construct($map = null)
+ {
+ // Merge in static overrides
+ if (!empty(static::$staticMap)) {
+ $this->registerPlugins(static::$staticMap);
+ }
+
+ // Merge in constructor arguments
+ if ($map !== null) {
+ $this->registerPlugins($map);
+ }
+ }
+
+ /**
+ * Add a static map of plugins
+ *
+ * A null value will clear the static map.
+ *
+ * @param null|array|Traversable $map
+ * @throws Exception\InvalidArgumentException
+ * @return void
+ */
+ public static function addStaticMap($map)
+ {
+ if (null === $map) {
+ static::$staticMap = array();
+ return;
+ }
+
+ if (!is_array($map) && !$map instanceof \Traversable) {
+ throw new Exception\InvalidArgumentException('Expects an array or Traversable object');
+ }
+ foreach ($map as $key => $value) {
+ static::$staticMap[$key] = $value;
+ }
+ }
+
+ /**
+ * Register a class to a given short name
+ *
+ * @param string $shortName
+ * @param string $className
+ * @return PluginClassLoader
+ */
+ public function registerPlugin($shortName, $className)
+ {
+ $this->plugins[strtolower($shortName)] = $className;
+ return $this;
+ }
+
+ /**
+ * Register many plugins at once
+ *
+ * If $map is a string, assumes that the map is the class name of a
+ * Traversable object (likely a ShortNameLocator); it will then instantiate
+ * this class and use it to register plugins.
+ *
+ * If $map is an array or Traversable object, it will iterate it to
+ * register plugin names/classes.
+ *
+ * For all other arguments, or if the string $map is not a class or not a
+ * Traversable class, an exception will be raised.
+ *
+ * @param string|array|Traversable $map
+ * @return PluginClassLoader
+ * @throws Exception\InvalidArgumentException
+ */
+ public function registerPlugins($map)
+ {
+ if (is_string($map)) {
+ if (!class_exists($map)) {
+ throw new Exception\InvalidArgumentException('Map class provided is invalid');
+ }
+ $map = new $map;
+ }
+ if (is_array($map)) {
+ $map = new ArrayIterator($map);
+ }
+ if (!$map instanceof Traversable) {
+ throw new Exception\InvalidArgumentException('Map provided is invalid; must be traversable');
+ }
+
+ // iterator_apply doesn't work as expected with IteratorAggregate
+ if ($map instanceof IteratorAggregate) {
+ $map = $map->getIterator();
+ }
+
+ foreach ($map as $name => $class) {
+ if (is_int($name) || is_numeric($name)) {
+ if (!is_object($class) && class_exists($class)) {
+ $class = new $class();
+ }
+
+ if ($class instanceof Traversable) {
+ $this->registerPlugins($class);
+ continue;
+ }
+ }
+
+ $this->registerPlugin($name, $class);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Unregister a short name lookup
+ *
+ * @param mixed $shortName
+ * @return PluginClassLoader
+ */
+ public function unregisterPlugin($shortName)
+ {
+ $lookup = strtolower($shortName);
+ if (array_key_exists($lookup, $this->plugins)) {
+ unset($this->plugins[$lookup]);
+ }
+ return $this;
+ }
+
+ /**
+ * Get a list of all registered plugins
+ *
+ * @return array|Traversable
+ */
+ public function getRegisteredPlugins()
+ {
+ return $this->plugins;
+ }
+
+ /**
+ * Whether or not a plugin by a specific name has been registered
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function isLoaded($name)
+ {
+ $lookup = strtolower($name);
+ return isset($this->plugins[$lookup]);
+ }
+
+ /**
+ * Return full class name for a named helper
+ *
+ * @param string $name
+ * @return string|false
+ */
+ public function getClassName($name)
+ {
+ return $this->load($name);
+ }
+
+ /**
+ * Load a helper via the name provided
+ *
+ * @param string $name
+ * @return string|false
+ */
+ public function load($name)
+ {
+ if (!$this->isLoaded($name)) {
+ return false;
+ }
+ return $this->plugins[strtolower($name)];
+ }
+
+ /**
+ * Defined by IteratorAggregate
+ *
+ * Returns an instance of ArrayIterator, containing a map of
+ * all plugins
+ *
+ * @return ArrayIterator
+ */
+ public function getIterator()
+ {
+ return new ArrayIterator($this->plugins);
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+use Traversable;
+
+/**
+ * Plugin class locator interface
+ *
+ * @category Zend
+ * @package Zend_Loader
+ */
+interface PluginClassLocator extends ShortNameLocator, \IteratorAggregate
+{
+ /**
+ * Register a class to a given short name
+ *
+ * @param string $shortName
+ * @param string $className
+ * @return PluginClassLocator
+ */
+ public function registerPlugin($shortName, $className);
+
+ /**
+ * Unregister a short name lookup
+ *
+ * @param mixed $shortName
+ * @return void
+ */
+ public function unregisterPlugin($shortName);
+
+ /**
+ * Get a list of all registered plugins
+ *
+ * @return array|Traversable
+ */
+ public function getRegisteredPlugins();
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+/**
+ * Short name locator interface
+ *
+ * @category Zend
+ * @package Zend_Loader
+ */
+interface ShortNameLocator
+{
+ /**
+ * Whether or not a Helper by a specific name
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function isLoaded($name);
+
+ /**
+ * Return full class name for a named helper
+ *
+ * @param string $name
+ * @return string
+ */
+ public function getClassName($name);
+
+ /**
+ * Load a helper via the name provided
+ *
+ * @param string $name
+ * @return string
+ */
+ public function load($name);
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+use Traversable;
+
+if (interface_exists('Zend\Loader\SplAutoloader')) return;
+
+/**
+ * Defines an interface for classes that may register with the spl_autoload
+ * registry
+ *
+ * @package Zend_Loader
+ */
+interface SplAutoloader
+{
+ /**
+ * Constructor
+ *
+ * Allow configuration of the autoloader via the constructor.
+ *
+ * @param null|array|Traversable $options
+ */
+ public function __construct($options = null);
+
+ /**
+ * Configure the autoloader
+ *
+ * In most cases, $options should be either an associative array or
+ * Traversable object.
+ *
+ * @param array|Traversable $options
+ * @return SplAutoloader
+ */
+ public function setOptions($options);
+
+ /**
+ * Autoload a class
+ *
+ * @param $class
+ * @return mixed
+ * False [if unable to load $class]
+ * get_class($class) [if $class is successfully loaded]
+ */
+ public function autoload($class);
+
+ /**
+ * Register the autoloader with spl_autoload registry
+ *
+ * Typically, the body of this will simply be:
+ * <code>
+ * spl_autoload_register(array($this, 'autoload'));
+ * </code>
+ *
+ * @return void
+ */
+ public function register();
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Loader
+ */
+
+namespace Zend\Loader;
+
+// Grab SplAutoloader interface
+require_once __DIR__ . '/SplAutoloader.php';
+
+/**
+ * PSR-0 compliant autoloader
+ *
+ * Allows autoloading both namespaced and vendor-prefixed classes. Class
+ * lookups are performed on the filesystem. If a class file for the referenced
+ * class is not found, a PHP warning will be raised by include().
+ *
+ * @package Zend_Loader
+ */
+class StandardAutoloader implements SplAutoloader
+{
+ const NS_SEPARATOR = '\\';
+ const PREFIX_SEPARATOR = '_';
+ const LOAD_NS = 'namespaces';
+ const LOAD_PREFIX = 'prefixes';
+ const ACT_AS_FALLBACK = 'fallback_autoloader';
+ const AUTOREGISTER_ZF = 'autoregister_zf';
+
+ /**
+ * @var array Namespace/directory pairs to search; ZF library added by default
+ */
+ protected $namespaces = array();
+
+ /**
+ * @var array Prefix/directory pairs to search
+ */
+ protected $prefixes = array();
+
+ /**
+ * @var bool Whether or not the autoloader should also act as a fallback autoloader
+ */
+ protected $fallbackAutoloaderFlag = false;
+
+ /**
+ * Constructor
+ *
+ * @param null|array|\Traversable $options
+ */
+ public function __construct($options = null)
+ {
+ if (null !== $options) {
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Configure autoloader
+ *
+ * Allows specifying both "namespace" and "prefix" pairs, using the
+ * following structure:
+ * <code>
+ * array(
+ * 'namespaces' => array(
+ * 'Zend' => '/path/to/Zend/library',
+ * 'Doctrine' => '/path/to/Doctrine/library',
+ * ),
+ * 'prefixes' => array(
+ * 'Phly_' => '/path/to/Phly/library',
+ * ),
+ * 'fallback_autoloader' => true,
+ * )
+ * </code>
+ *
+ * @param array|\Traversable $options
+ * @throws Exception\InvalidArgumentException
+ * @return StandardAutoloader
+ */
+ public function setOptions($options)
+ {
+ if (!is_array($options) && !($options instanceof \Traversable)) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException('Options must be either an array or Traversable');
+ }
+
+ foreach ($options as $type => $pairs) {
+ switch ($type) {
+ case self::AUTOREGISTER_ZF:
+ if ($pairs) {
+ $this->registerNamespace('Zend', dirname(__DIR__));
+ }
+ break;
+ case self::LOAD_NS:
+ if (is_array($pairs) || $pairs instanceof \Traversable) {
+ $this->registerNamespaces($pairs);
+ }
+ break;
+ case self::LOAD_PREFIX:
+ if (is_array($pairs) || $pairs instanceof \Traversable) {
+ $this->registerPrefixes($pairs);
+ }
+ break;
+ case self::ACT_AS_FALLBACK:
+ $this->setFallbackAutoloader($pairs);
+ break;
+ default:
+ // ignore
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Set flag indicating fallback autoloader status
+ *
+ * @param bool $flag
+ * @return StandardAutoloader
+ */
+ public function setFallbackAutoloader($flag)
+ {
+ $this->fallbackAutoloaderFlag = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Is this autoloader acting as a fallback autoloader?
+ *
+ * @return bool
+ */
+ public function isFallbackAutoloader()
+ {
+ return $this->fallbackAutoloaderFlag;
+ }
+
+ /**
+ * Register a namespace/directory pair
+ *
+ * @param string $namespace
+ * @param string $directory
+ * @return StandardAutoloader
+ */
+ public function registerNamespace($namespace, $directory)
+ {
+ $namespace = rtrim($namespace, self::NS_SEPARATOR) . self::NS_SEPARATOR;
+ $this->namespaces[$namespace] = $this->normalizeDirectory($directory);
+ return $this;
+ }
+
+ /**
+ * Register many namespace/directory pairs at once
+ *
+ * @param array $namespaces
+ * @throws Exception\InvalidArgumentException
+ * @return StandardAutoloader
+ */
+ public function registerNamespaces($namespaces)
+ {
+ if (!is_array($namespaces) && !$namespaces instanceof \Traversable) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException('Namespace pairs must be either an array or Traversable');
+ }
+
+ foreach ($namespaces as $namespace => $directory) {
+ $this->registerNamespace($namespace, $directory);
+ }
+ return $this;
+ }
+
+ /**
+ * Register a prefix/directory pair
+ *
+ * @param string $prefix
+ * @param string $directory
+ * @return StandardAutoloader
+ */
+ public function registerPrefix($prefix, $directory)
+ {
+ $prefix = rtrim($prefix, self::PREFIX_SEPARATOR). self::PREFIX_SEPARATOR;
+ $this->prefixes[$prefix] = $this->normalizeDirectory($directory);
+ return $this;
+ }
+
+ /**
+ * Register many namespace/directory pairs at once
+ *
+ * @param array $prefixes
+ * @throws Exception\InvalidArgumentException
+ * @return StandardAutoloader
+ */
+ public function registerPrefixes($prefixes)
+ {
+ if (!is_array($prefixes) && !$prefixes instanceof \Traversable) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException('Prefix pairs must be either an array or Traversable');
+ }
+
+ foreach ($prefixes as $prefix => $directory) {
+ $this->registerPrefix($prefix, $directory);
+ }
+ return $this;
+ }
+
+ /**
+ * Defined by Autoloadable; autoload a class
+ *
+ * @param string $class
+ * @return false|string
+ */
+ public function autoload($class)
+ {
+ $isFallback = $this->isFallbackAutoloader();
+ if (false !== strpos($class, self::NS_SEPARATOR)) {
+ if ($this->loadClass($class, self::LOAD_NS)) {
+ return $class;
+ } elseif ($isFallback) {
+ return $this->loadClass($class, self::ACT_AS_FALLBACK);
+ }
+ return false;
+ }
+ if (false !== strpos($class, self::PREFIX_SEPARATOR)) {
+ if ($this->loadClass($class, self::LOAD_PREFIX)) {
+ return $class;
+ } elseif ($isFallback) {
+ return $this->loadClass($class, self::ACT_AS_FALLBACK);
+ }
+ return false;
+ }
+ if ($isFallback) {
+ return $this->loadClass($class, self::ACT_AS_FALLBACK);
+ }
+ return false;
+ }
+
+ /**
+ * Register the autoloader with spl_autoload
+ *
+ * @return void
+ */
+ public function register()
+ {
+ spl_autoload_register(array($this, 'autoload'));
+ }
+
+ /**
+ * Transform the class name to a filename
+ *
+ * @param string $class
+ * @param string $directory
+ * @return string
+ */
+ protected function transformClassNameToFilename($class, $directory)
+ {
+ // $class may contain a namespace portion, in which case we need
+ // to preserve any underscores in that portion.
+ $matches = array();
+ preg_match('/(?P<namespace>.+\\\)?(?P<class>[^\\\]+$)/', $class, $matches);
+
+ $class = (isset($matches['class'])) ? $matches['class'] : '';
+ $namespace = (isset($matches['namespace'])) ? $matches['namespace'] : '';
+
+ return $directory
+ . str_replace(self::NS_SEPARATOR, '/', $namespace)
+ . str_replace(self::PREFIX_SEPARATOR, '/', $class)
+ . '.php';
+ }
+
+ /**
+ * Load a class, based on its type (namespaced or prefixed)
+ *
+ * @param string $class
+ * @param string $type
+ * @return bool|string
+ * @throws Exception\InvalidArgumentException
+ */
+ protected function loadClass($class, $type)
+ {
+ if (!in_array($type, array(self::LOAD_NS, self::LOAD_PREFIX, self::ACT_AS_FALLBACK))) {
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php';
+ throw new Exception\InvalidArgumentException();
+ }
+
+ // Fallback autoloading
+ if ($type === self::ACT_AS_FALLBACK) {
+ // create filename
+ $filename = $this->transformClassNameToFilename($class, '');
+ $resolvedName = stream_resolve_include_path($filename);
+ if ($resolvedName !== false) {
+ return include $resolvedName;
+ }
+ return false;
+ }
+
+ // Namespace and/or prefix autoloading
+ foreach ($this->$type as $leader => $path) {
+ if (0 === strpos($class, $leader)) {
+ // Trim off leader (namespace or prefix)
+ $trimmedClass = substr($class, strlen($leader));
+
+ // create filename
+ $filename = $this->transformClassNameToFilename($trimmedClass, $path);
+ if (file_exists($filename)) {
+ return include $filename;
+ }
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Normalize the directory to include a trailing directory separator
+ *
+ * @param string $directory
+ * @return string
+ */
+ protected function normalizeDirectory($directory)
+ {
+ $last = $directory[strlen($directory) - 1];
+ if (in_array($last, array('/', '\\'))) {
+ $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR;
+ return $directory;
+ }
+ $directory .= DIRECTORY_SEPARATOR;
+ return $directory;
+ }
+
+}
--- /dev/null
+{
+ "name": "zendframework/zend-loader",
+ "description": " ",
+ "license": "BSD-3-Clause",
+ "keywords": [
+ "zf2",
+ "loader"
+ ],
+ "autoload": {
+ "psr-0": {
+ "Zend\\Loader\\": ""
+ }
+ },
+ "target-dir": "Zend/Loader",
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "zendframework/zend-stdlib": "Zend\\Stdlib component"
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Adapter;
+
+use Traversable;
+use Zend\Stdlib\ArrayUtils;
+
+/**
+ * Abstract class for Zend_ProgressBar_Adapters
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+abstract class AbstractAdapter
+{
+ /**
+ * Option keys to skip when calling setOptions()
+ *
+ * @var array
+ */
+ protected $skipOptions = array(
+ 'options',
+ 'config',
+ );
+
+ /**
+ * Create a new adapter
+ *
+ * $options may be either be an array or a Zend_Config object which
+ * specifies adapter related options.
+ *
+ * @param array|Traversable $options
+ */
+ public function __construct($options = null)
+ {
+ if ($options instanceof Traversable) {
+ $options = ArrayUtils::iteratorToArray($options);
+ }
+ if (is_array($options)) {
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Set options via an array
+ *
+ * @param array $options
+ * @return AbstractAdapter
+ */
+ public function setOptions(array $options)
+ {
+ foreach ($options as $key => $value) {
+ if (in_array(strtolower($key), $this->skipOptions)) {
+ continue;
+ }
+
+ $method = 'set' . ucfirst($key);
+ if (method_exists($this, $method)) {
+ $this->$method($value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Notify the adapter about an update
+ *
+ * @param float $current Current progress value
+ * @param float $max Max progress value
+ * @param float $percent Current percent value
+ * @param integer $timeTaken Taken time in seconds
+ * @param integer $timeRemaining Remaining time in seconds
+ * @param string $text Status text
+ * @return void
+ */
+ abstract public function notify($current, $max, $percent, $timeTaken, $timeRemaining, $text);
+
+ /**
+ * Called when the progress is explicitly finished
+ *
+ * @return void
+ */
+ abstract public function finish();
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Adapter;
+
+use Zend\ProgressBar\Adapter\Exception;
+use Zend\Stdlib\ErrorHandler;
+
+/**
+ * Zend_ProgressBar_Adapter_Console offers a text-based progressbar for console
+ * applications
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class Console extends AbstractAdapter
+{
+ /**
+ * Percentage value of the progress
+ */
+ const ELEMENT_PERCENT = 'ELEMENT_PERCENT';
+
+ /**
+ * Visual value of the progress
+ */
+ const ELEMENT_BAR = 'ELEMENT_BAR';
+
+ /**
+ * ETA of the progress
+ */
+ const ELEMENT_ETA = 'ELEMENT_ETA';
+
+ /**
+ * Text part of the progress
+ */
+ const ELEMENT_TEXT = 'ELEMENT_TEXT';
+
+ /**
+ * Finish action: End of Line
+ */
+ const FINISH_ACTION_EOL = 'FINISH_ACTION_EOL';
+
+ /**
+ * Finish action: Clear Line
+ */
+ const FINISH_ACTION_CLEAR_LINE = 'FINISH_ACTION_CLEAR_LINE';
+
+ /**
+ * Finish action: None
+ */
+ const FINISH_ACTION_NONE = 'FINISH_ACTION_NONE';
+
+ /**
+ * Width of the progressbar
+ *
+ * @var integer
+ */
+ protected $width = null;
+
+ /**
+ * Elements to display
+ *
+ * @var array
+ */
+ protected $elements = array(self::ELEMENT_PERCENT,
+ self::ELEMENT_BAR,
+ self::ELEMENT_ETA);
+
+ /**
+ * Which action to do at finish call
+ *
+ * @var string
+ */
+ protected $finishAction = self::FINISH_ACTION_EOL;
+
+ /**
+ * Width of the bar element
+ *
+ * @var integer
+ */
+ protected $barWidth;
+
+ /**
+ * Left character(s) within the bar
+ *
+ * @var string
+ */
+ protected $barLeftChar = '#';
+
+ /**
+ * Indicator character(s) within the bar
+ *
+ * @var string
+ */
+ protected $barIndicatorChar = '';
+
+ /**
+ * Right character(s) within the bar
+ *
+ * @var string
+ */
+ protected $barRightChar = '-';
+
+ /**
+ * Output-stream, when STDOUT is not defined (e.g. in CGI) or set manually
+ *
+ * @var resource
+ */
+ protected $outputStream = null;
+
+ /**
+ * Width of the text element
+ *
+ * @var string
+ */
+ protected $textWidth = 20;
+
+ /**
+ * Whether the output started yet or not
+ *
+ * @var boolean
+ */
+ protected $outputStarted = false;
+
+ /**
+ * Charset of text element
+ *
+ * @var string
+ */
+ protected $charset = 'utf-8';
+
+ /**
+ * Defined by Zend_ProgressBar_Adapter
+ *
+ * @param array|\Traversable $options
+ */
+ public function __construct($options = null)
+ {
+ // Call parent constructor with options
+ parent::__construct($options);
+
+ // Check if a width was set, else use auto width
+ if ($this->width === null) {
+ $this->setWidth();
+ }
+ }
+
+ /**
+ * Close local stdout, when open
+ */
+ public function __destruct()
+ {
+ if ($this->outputStream !== null) {
+ fclose($this->outputStream);
+ }
+ }
+
+ /**
+ * Set a different output-stream
+ *
+ * @param string $resource
+ * @throws Exception\RuntimeException
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setOutputStream($resource)
+ {
+ ErrorHandler::start();
+ $stream = fopen($resource, 'w');
+ $error = ErrorHandler::stop();
+
+ if ($stream === false) {
+ throw new Exception\RuntimeException('Unable to open stream', 0, $error);
+ }
+
+ if ($this->outputStream !== null) {
+ fclose($this->outputStream);
+ }
+
+ $this->outputStream = $stream;
+ }
+
+ /**
+ * Get the current output stream
+ *
+ * @return resource
+ */
+ public function getOutputStream()
+ {
+ if ($this->outputStream === null) {
+ if (!defined('STDOUT')) {
+ $this->outputStream = fopen('php://stdout', 'w');
+ } else {
+ return STDOUT;
+ }
+ }
+
+ return $this->outputStream;
+ }
+
+ /**
+ * Set the width of the progressbar
+ *
+ * @param integer $width
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setWidth($width = null)
+ {
+ if ($width === null || !is_integer($width)) {
+ if (substr(PHP_OS, 0, 3) === 'WIN') {
+ // We have to default to 79 on windows, because the windows
+ // terminal always has a fixed width of 80 characters and the
+ // cursor is counted to the line, else windows would line break
+ // after every update.
+ $this->width = 79;
+ } else {
+ // Set the default width of 80
+ $this->width = 80;
+
+ // Try to determine the width through stty
+ ErrorHandler::start();
+ if (preg_match('#\d+ (\d+)#', shell_exec('stty size'), $match) === 1) {
+ $this->width = (int) $match[1];
+ } elseif (preg_match('#columns = (\d+);#', shell_exec('stty'), $match) === 1) {
+ $this->width = (int) $match[1];
+ }
+ ErrorHandler::stop();
+ }
+ } else {
+ $this->width = (int) $width;
+ }
+
+ $this->_calculateBarWidth();
+
+ return $this;
+ }
+
+ /**
+ * Set the elements to display with the progressbar
+ *
+ * @param array $elements
+ * @throws \Zend\ProgressBar\Adapter\Exception\InvalidArgumentException When an invalid element is found in the array
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setElements(array $elements)
+ {
+ $allowedElements = array(self::ELEMENT_PERCENT,
+ self::ELEMENT_BAR,
+ self::ELEMENT_ETA,
+ self::ELEMENT_TEXT);
+
+ if (count(array_diff($elements, $allowedElements)) > 0) {
+ throw new Exception\InvalidArgumentException('Invalid element found in $elements array');
+ }
+
+ $this->elements = $elements;
+
+ $this->_calculateBarWidth();
+
+ return $this;
+ }
+
+ /**
+ * Set the left-hand character for the bar
+ *
+ * @param string $char
+ * @throws \Zend\ProgressBar\Adapter\Exception\InvalidArgumentException When character is empty
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setBarLeftChar($char)
+ {
+ if (empty($char)) {
+ throw new Exception\InvalidArgumentException('Character may not be empty');
+ }
+
+ $this->barLeftChar = (string) $char;
+
+ return $this;
+ }
+
+ /**
+ * Set the right-hand character for the bar
+ *
+ * @param string $char
+ * @throws \Zend\ProgressBar\Adapter\Exception\InvalidArgumentException When character is empty
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setBarRightChar($char)
+ {
+ if (empty($char)) {
+ throw new Exception\InvalidArgumentException('Character may not be empty');
+ }
+
+ $this->barRightChar = (string) $char;
+
+ return $this;
+ }
+
+ /**
+ * Set the indicator character for the bar
+ *
+ * @param string $char
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setBarIndicatorChar($char)
+ {
+ $this->barIndicatorChar = (string) $char;
+
+ return $this;
+ }
+
+ /**
+ * Set the width of the text element
+ *
+ * @param integer $width
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setTextWidth($width)
+ {
+ $this->textWidth = (int) $width;
+
+ $this->_calculateBarWidth();
+
+ return $this;
+ }
+
+ /**
+ * Set the charset of the text element
+ *
+ * @param string $charset
+ */
+ public function setCharset($charset)
+ {
+ $this->charset = $charset;
+ }
+
+ /**
+ * Set the finish action
+ *
+ * @param string $action
+ * @throws \Zend\ProgressBar\Adapter\Exception\InvalidArgumentException When an invalid action is specified
+ * @return \Zend\ProgressBar\Adapter\Console
+ */
+ public function setFinishAction($action)
+ {
+ $allowedActions = array(self::FINISH_ACTION_CLEAR_LINE,
+ self::FINISH_ACTION_EOL,
+ self::FINISH_ACTION_NONE);
+
+ if (!in_array($action, $allowedActions)) {
+ throw new Exception\InvalidArgumentException('Invalid finish action specified');
+ }
+
+ $this->finishAction = $action;
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend\ProgressBar\Adapter\AbstractAdapter
+ *
+ * @param float $current Current progress value
+ * @param float $max Max progress value
+ * @param float $percent Current percent value
+ * @param integer $timeTaken Taken time in seconds
+ * @param integer $timeRemaining Remaining time in seconds
+ * @param string $text Status text
+ * @return void
+ */
+ public function notify($current, $max, $percent, $timeTaken, $timeRemaining, $text)
+ {
+ // See if we must clear the line
+ if ($this->outputStarted) {
+ $data = str_repeat("\x08", $this->width);
+ } else {
+ $data = '';
+ $this->outputStarted = true;
+ }
+
+ // Build all elements
+ $renderedElements = array();
+
+ foreach ($this->elements as $element) {
+ switch ($element) {
+ case self::ELEMENT_BAR:
+ $visualWidth = $this->barWidth - 2;
+ $bar = '[';
+
+ $indicatorWidth = strlen($this->barIndicatorChar);
+
+ $doneWidth = min($visualWidth - $indicatorWidth, round($visualWidth * $percent));
+ if ($doneWidth > 0) {
+ $bar .= substr(str_repeat($this->barLeftChar, ceil($doneWidth / strlen($this->barLeftChar))), 0, $doneWidth);
+ }
+
+ $bar .= $this->barIndicatorChar;
+
+ $leftWidth = $visualWidth - $doneWidth - $indicatorWidth;
+ if ($leftWidth > 0) {
+ $bar .= substr(str_repeat($this->barRightChar, ceil($leftWidth / strlen($this->barRightChar))), 0, $leftWidth);
+ }
+
+ $bar .= ']';
+
+ $renderedElements[] = $bar;
+ break;
+
+ case self::ELEMENT_PERCENT:
+ $renderedElements[] = str_pad(round($percent * 100), 3, ' ', STR_PAD_LEFT) . '%';
+ break;
+
+ case self::ELEMENT_ETA:
+ // In the first 5 seconds we don't get accurate results,
+ // this skipping technique is found in many progressbar
+ // implementations.
+ if ($timeTaken < 5) {
+ $renderedElements[] = str_repeat(' ', 12);
+ break;
+ }
+
+ if ($timeRemaining === null || $timeRemaining > 86400) {
+ $etaFormatted = '??:??:??';
+ } else {
+ $hours = floor($timeRemaining / 3600);
+ $minutes = floor(($timeRemaining % 3600) / 60);
+ $seconds = ($timeRemaining % 3600 % 60);
+
+ $etaFormatted = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
+ }
+
+ $renderedElements[] = 'ETA ' . $etaFormatted;
+ break;
+
+ case self::ELEMENT_TEXT:
+ $renderedElements[] = \Zend\Text\MultiByte::strPad(substr($text, 0, $this->textWidth), $this->textWidth, ' ', STR_PAD_RIGHT, $this->charset);
+ break;
+ }
+ }
+
+ $data .= implode(' ', $renderedElements);
+
+ // Output line data
+ $this->_outputData($data);
+ }
+
+ /**
+ * Defined by Zend\ProgressBar\Adapter\AbstractAdapter
+ *
+ * @return void
+ */
+ public function finish()
+ {
+ switch ($this->finishAction) {
+ case self::FINISH_ACTION_EOL:
+ $this->_outputData(PHP_EOL);
+ break;
+
+ case self::FINISH_ACTION_CLEAR_LINE:
+ if ($this->outputStarted) {
+ $data = str_repeat("\x08", $this->width)
+ . str_repeat(' ', $this->width)
+ . str_repeat("\x08", $this->width);
+
+ $this->_outputData($data);
+ }
+ break;
+
+ case self::FINISH_ACTION_NONE:
+ break;
+ }
+ }
+
+ /**
+ * Calculate the bar width when other elements changed
+ *
+ * @return void
+ */
+ protected function _calculateBarWidth()
+ {
+ if (in_array(self::ELEMENT_BAR, $this->elements)) {
+ $barWidth = $this->width;
+
+ if (in_array(self::ELEMENT_PERCENT, $this->elements)) {
+ $barWidth -= 4;
+ }
+
+ if (in_array(self::ELEMENT_ETA, $this->elements)) {
+ $barWidth -= 12;
+ }
+
+ if (in_array(self::ELEMENT_TEXT, $this->elements)) {
+ $barWidth -= $this->textWidth;
+ }
+
+ $this->barWidth = $barWidth - (count($this->elements) - 1);
+ }
+ }
+
+ /**
+ * Outputs given data to STDOUT.
+ *
+ * This split-off is required for unit-testing.
+ *
+ * @param string $data
+ * @return void
+ */
+ protected function _outputData($data)
+ {
+ fwrite($this->getOutputStream(), $data);
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Adapter\Exception;
+
+use Zend\ProgressBar\Exception\ExceptionInterface as ProgressBarException;
+
+/**
+ * Exception class for Zend_ProgressBar_Adapter
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+interface ExceptionInterface extends ProgressBarException
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Adapter\Exception;
+
+use Zend\ProgressBar\Exception;
+
+/**
+ * Exception for Zend_Progressbar component.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class InvalidArgumentException extends Exception\InvalidArgumentException implements
+ ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Adapter\Exception;
+
+use Zend\ProgressBar\Exception;
+
+/**
+ * Exception for Zend_Progressbar component.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class RuntimeException extends Exception\RuntimeException implements
+ ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Adapter;
+
+use Zend\Json\Json;
+
+/**
+ * Zend_ProgressBar_Adapter_JsPull offers a simple method for updating a
+ * progressbar in a browser.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class JsPull extends AbstractAdapter
+{
+ /**
+ * Whether to exit after json data send or not
+ *
+ * @var boolean
+ */
+ protected $exitAfterSend = true;
+
+ /**
+ * Set whether to exit after json data send or not
+ *
+ * @param boolean $exitAfterSend
+ * @return \Zend\ProgressBar\Adapter\JsPull
+ */
+ public function setExitAfterSend($exitAfterSend)
+ {
+ $this->exitAfterSend = $exitAfterSend;
+ }
+
+ /**
+ * Defined by Zend\ProgressBar\Adapter\AbstractAdapter
+ *
+ * @param float $current Current progress value
+ * @param float $max Max progress value
+ * @param float $percent Current percent value
+ * @param integer $timeTaken Taken time in seconds
+ * @param integer $timeRemaining Remaining time in seconds
+ * @param string $text Status text
+ * @return void
+ */
+ public function notify($current, $max, $percent, $timeTaken, $timeRemaining, $text)
+ {
+ $arguments = array(
+ 'current' => $current,
+ 'max' => $max,
+ 'percent' => ($percent * 100),
+ 'timeTaken' => $timeTaken,
+ 'timeRemaining' => $timeRemaining,
+ 'text' => $text,
+ 'finished' => false
+ );
+
+ $data = Json::encode($arguments);
+
+ // Output the data
+ $this->_outputData($data);
+ }
+
+ /**
+ * Defined by Zend\ProgressBar\Adapter\AbstractAdapter
+ *
+ * @return void
+ */
+ public function finish()
+ {
+ $data = Json::encode(array('finished' => true));
+
+ $this->_outputData($data);
+ }
+
+ /**
+ * Outputs given data the user agent.
+ *
+ * This split-off is required for unit-testing.
+ *
+ * @param string $data
+ * @return void
+ */
+ protected function _outputData($data)
+ {
+ echo $data;
+
+ if ($this->exitAfterSend) {
+ exit;
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Adapter;
+
+use Zend\Json\Json;
+
+/**
+ * Zend_ProgressBar_Adapter_JsPush offers a simple method for updating a
+ * progressbar in a browser.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class JsPush extends AbstractAdapter
+{
+ /**
+ * Name of the JavaScript method to call on update
+ *
+ * @var string
+ */
+ protected $updateMethodName = 'Zend\ProgressBar\ProgressBar\Update';
+
+ /**
+ * Name of the JavaScript method to call on finish
+ *
+ * @var string
+ */
+ protected $finishMethodName;
+
+ /**
+ * Set the update method name
+ *
+ * @param string $methodName
+ * @return \Zend\ProgressBar\Adapter\JsPush
+ */
+ public function setUpdateMethodName($methodName)
+ {
+ $this->updateMethodName = $methodName;
+
+ return $this;
+ }
+
+ /**
+ * Set the finish method name
+ *
+ * @param string $methodName
+ * @return \Zend\ProgressBar\Adapter\JsPush
+ */
+ public function setFinishMethodName($methodName)
+ {
+ $this->finishMethodName = $methodName;
+
+ return $this;
+ }
+
+ /**
+ * Defined by Zend\ProgressBar\Adapter\AbstractAdapter
+ *
+ * @param float $current Current progress value
+ * @param float $max Max progress value
+ * @param float $percent Current percent value
+ * @param integer $timeTaken Taken time in seconds
+ * @param integer $timeRemaining Remaining time in seconds
+ * @param string $text Status text
+ * @return void
+ */
+ public function notify($current, $max, $percent, $timeTaken, $timeRemaining, $text)
+ {
+ $arguments = array(
+ 'current' => $current,
+ 'max' => $max,
+ 'percent' => ($percent * 100),
+ 'timeTaken' => $timeTaken,
+ 'timeRemaining' => $timeRemaining,
+ 'text' => $text
+ );
+
+ $data = '<script type="text/javascript">'
+ . 'parent.' . $this->updateMethodName . '(' . Json::encode($arguments) . ');'
+ . '</script>';
+
+ // Output the data
+ $this->_outputData($data);
+ }
+
+ /**
+ * Defined by Zend\ProgressBar\Adapter\AbstractAdapter
+ *
+ * @return void
+ */
+ public function finish()
+ {
+ if ($this->finishMethodName === null) {
+ return;
+ }
+
+ $data = '<script type="text/javascript">'
+ . 'parent.' . $this->finishMethodName . '();'
+ . '</script>';
+
+ $this->_outputData($data);
+ }
+
+ /**
+ * Outputs given data the user agent.
+ *
+ * This split-off is required for unit-testing.
+ *
+ * @param string $data
+ * @return void
+ */
+ protected function _outputData($data)
+ {
+ // 1024 padding is required for Safari, while 256 padding is required
+ // for Internet Explorer. The <br /> is required so Safari actually
+ // executes the <script />
+ echo str_pad($data . '<br />', 1024, ' ', STR_PAD_RIGHT) . "\n";
+
+ flush();
+ ob_flush();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Exception;
+
+/**
+ * Exception class for Zend_ProgressBar
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+interface ExceptionInterface
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Exception;
+
+/**
+ * Exception for Zend_Progressbar component.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements
+ ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Exception;
+
+/**
+ * Exception for Zend_Progressbar component.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class OutOfRangeException extends \OutOfRangeException implements ExceptionInterface
+{
+}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar\Exception;
+
+/**
+ * Exception for Zend_Progressbar component.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{}
--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_ProgressBar
+ */
+
+namespace Zend\ProgressBar;
+
+use Zend\ProgressBar\Exception;
+use Zend\Session;
+
+/**
+ * Zend_ProgressBar offers an interface for multiple environments.
+ *
+ * @category Zend
+ * @package Zend_ProgressBar
+ */
+class ProgressBar
+{
+ /**
+ * Min value
+ *
+ * @var float
+ */
+ protected $min;
+
+ /**
+ * Max value
+ *
+ * @var float
+ */
+ protected $max;
+
+ /**
+ * Current value
+ *
+ * @var float
+ */
+ protected $current;
+
+ /**
+ * Start time of the progressbar, required for ETA
+ *
+ * @var integer
+ */
+ protected $startTime;
+
+ /**
+ * Current status text
+ *
+ * @var string
+ */
+ protected $statusText = null;
+
+ /**
+ * Adapter for the output
+ *
+ * @var \Zend\ProgressBar\Adapter\AbstractAdapter
+ */
+ protected $adapter;
+
+ /**
+ * Namespace for keeping the progressbar persistent
+ *
+ * @var string
+ */
+ protected $persistenceNamespace = null;
+
+ /**
+ * Create a new progressbar backend.
+ *
+ * @param Adapter\AbstractAdapter $adapter
+ * @param float|int $min
+ * @param float|int $max
+ * @param string|null $persistenceNamespace
+ * @throws Exception\OutOfRangeException When $min is greater than $max
+ */
+ public function __construct(Adapter\AbstractAdapter $adapter, $min = 0, $max = 100, $persistenceNamespace = null)
+ {
+ // Check min/max values and set them
+ if ($min > $max) {
+ throw new Exception\OutOfRangeException('$max must be greater than $min');
+ }
+
+ $this->min = (float) $min;
+ $this->max = (float) $max;
+ $this->current = (float) $min;
+
+ // See if we have to open a session namespace
+ if ($persistenceNamespace !== null) {
+ $this->persistenceNamespace = new Session\Container($persistenceNamespace);
+ }
+
+ // Set adapter
+ $this->adapter = $adapter;
+
+ // Track the start time
+ $this->startTime = time();
+
+ // See If a persistenceNamespace exists and handle accordingly
+ if ($this->persistenceNamespace !== null) {
+ if (isset($this->persistenceNamespace->isSet)) {
+ $this->startTime = $this->persistenceNamespace->startTime;
+ $this->current = $this->persistenceNamespace->current;
+ $this->statusText = $this->persistenceNamespace->statusText;
+ } else {
+ $this->persistenceNamespace->isSet = true;
+ $this->persistenceNamespace->startTime = $this->startTime;
+ $this->persistenceNamespace->current = $this->current;
+ $this->persistenceNamespace->statusText = $this->statusText;
+ }
+ } else {
+ $this->update();
+ }
+ }
+
+ /**
+ * Get the current adapter
+ *
+ * @return Adapter\AbstractAdapter
+ */
+ public function getAdapter()
+ {
+ return $this->adapter;
+ }
+
+ /**
+ * Update the progressbar
+ *
+ * @param float $value
+ * @param string $text
+ * @return void
+ */
+ public function update($value = null, $text = null)
+ {
+ // Update value if given
+ if ($value !== null) {
+ $this->current = min($this->max, max($this->min, $value));
+ }
+
+ // Update text if given
+ if ($text !== null) {
+ $this->statusText = $text;
+ }
+
+ // See if we have to update a namespace
+ if ($this->persistenceNamespace !== null) {
+ $this->persistenceNamespace->current = $this->current;
+ $this->persistenceNamespace->statusText = $this->statusText;
+ }
+
+ // Calculate percent
+ if ($this->min === $this->max) {
+ $percent = false;
+ } else {
+ $percent = (float) ($this->current - $this->min) / ($this->max - $this->min);
+ }
+
+ // Calculate ETA
+ $timeTaken = time() - $this->startTime;
+
+ if ($percent === .0 || $percent === false) {
+ $timeRemaining = null;
+ } else {
+ $timeRemaining = round(((1 / $percent) * $timeTaken) - $timeTaken);
+ }
+
+ // Poll the adapter
+ $this->adapter->notify($this->current, $this->max, $percent, $timeTaken, $timeRemaining, $this->statusText);
+ }
+
+ /**
+ * Update the progressbar to the next value
+ *
+ * @param int $diff
+ * @param string $text
+ * @return void
+ */
+ public function next($diff = 1, $text = null)
+ {
+ $this->update(max($this->min, min($this->max, $this->current + $diff)), $text);
+ }
+
+ /**
+ * Call the adapters finish() behaviour
+ *
+ * @return void
+ */
+ public function finish()
+ {
+ if ($this->persistenceNamespace !== null) {
+ unset($this->persistenceNamespace->isSet);
+ }
+
+ $this->adapter->finish();
+ }
+}
--- /dev/null
+{
+ "name": "zendframework/zend-progressbar",
+ "description": "component to create and update progressbars in different environments",
+ "license": "BSD-3-Clause",
+ "keywords": [
+ "zf2",
+ "progressbar"
+ ],
+ "autoload": {
+ "psr-0": {
+ "Zend\\ProgressBar\\": ""
+ }
+ },
+ "target-dir": "Zend/ProgressBar",
+ "require": {
+ "php": ">=5.3.3",
+ "zendframework/zend-stdlib": "self.version"
+ }
+}
--- /dev/null
+# USING THE GIT REPOSITORY
+
+## Setup your own public repository
+
+Your first step is to establish a public repository from which we can
+pull your work into the master repository. You have two options: use
+GitHub or other public site, or setup/use your own repository.
+
+While you can use a private repository and utilize ``git format-patch`` to
+submit patches, this is discouraged as it does not facilitate public peer
+review.
+
+### Option 1: GitHub
+
+ 1. Setup a GitHub account (http://github.com/), if you haven't yet
+ 2. Fork the ZF2 repository (http://github.com/zendframework/zf2)
+ 3. Clone your fork locally and enter it (use your own GitHub username
+ in the statement below)
+
+ ```sh
+ % git clone git@github.com:<username>/zf2.git
+ % cd zf2
+ ```
+
+ 4. Add a remote to the canonical ZF repository, so you can keep your fork
+ up-to-date:
+
+ ```sh
+ % git remote add zf2 https://github.com/zendframework/zf2.git
+ % git fetch zf2
+ ```
+
+### Option 2: Personal Repository
+
+We assume you will use gitosis (http://git-scm.com/book/en/Git-on-the-Server-Gitosis)
+or gitolite (http://git-scm.com/book/en/Git-on-the-Server-Gitolite) to host your
+own repository. If you go this route, we will assume you have the knowledge to
+do so, or know where to obtain it. We will not assist you in setting up such a
+repository.
+
+ 1. Create a new repository
+
+ ```sh
+ % git init
+ ```
+
+ 2. Add an "origin" remote pointing to your gitosis/gitolite repo:
+
+ ```sh
+ % git remote add origin git://yourdomain/yourrepo.git
+ ```
+
+ 3. Add a remote for the ZF repository and fetch it
+
+ ```sh
+ % git remote add zf2 https://github.com/zendframework/zf2.git
+ % git fetch zf2
+ ```
+
+ 4. Create a new branch for the ZF repository (named "zf/master" here)
+
+ ```sh
+ % git checkout -b zf/master zf2/master
+ ```
+
+ 5. Create your master branch off the ZF branch, and push to your
+ repository
+
+ ```sh
+ % git checkout -b master
+ % git push origin HEAD:master
+ ```
+
+### Pre-Commit Hook (Optional)
+
+The ZF2 Travis-CI will confirm that code style standards are met
+by using ```php-cs-fixer``` (https://github.com/fabpot/PHP-CS-Fixer) during it's build runs.
+
+To reduce the number of red Travis-CI builds, the following Git pre-commit hook
+can help catch code style issues before committing. Save it as
+```.git/hooks/pre-commit```, and make sure it is executable.
+
+```php
+#!/usr/bin/env php
+<?php
+/**
+ * .git/hooks/pre-commit
+ *
+ * This pre-commit hooks will check for PHP errors (lint), and make sure the
+ * code is PSR-2 compliant.
+ *
+ * Dependecy: PHP-CS-Fixer (https://github.com/fabpot/PHP-CS-Fixer)
+ *
+ * @author Mardix http://github.com/mardix
+ * @author Matthew Weier O'Phinney http://mwop.net/
+ * @since 4 Sept 2012
+ */
+
+$exit = 0;
+
+/*
+ * collect all files which have been added, copied or
+ * modified and store them in an array called output
+ */
+$output = array();
+exec('git diff --cached --name-status --diff-filter=ACM', $output);
+
+foreach ($output as $file) {
+ if ('D' === substr($file, 0, 1)) {
+ // deleted file; do nothing
+ continue;
+ }
+
+ $fileName = trim(substr($file, 1));
+
+ /*
+ * Only PHP files
+ */
+ $extension = pathinfo($fileName, PATHINFO_EXTENSION);
+ if (!preg_match('/^ph(p|tml)$/', $extension)) {
+ continue;
+ }
+
+ /*
+ * Check for parse errors
+ */
+ $output = array();
+ $return = 0;
+ exec("php -l " . escapeshellarg($fileName), $output, $return);
+
+ if ($return != 0) {
+ echo "PHP file fails to parse: " . $fileName . ":" . PHP_EOL;
+ echo implode(PHP_EOL, $lintOutput) . PHP_EOL;
+ $exit = 1;
+ continue;
+ }
+
+ /*
+ * PHP-CS-Fixer
+ */
+ $output = array();
+ $return = null;
+ exec("php-cs-fixer --dry-run --level=psr2 " . escapeshellarg($fileName), $output, $return);
+ if ($return != 0 || !empty($output)) {
+ echo "PHP file fails contains CS issues: " . $fileName . ":" . PHP_EOL;
+ echo implode(PHP_EOL, $output) . PHP_EOL;
+ $exit = 1;
+ continue;
+ }
+}
+
+exit($exit);
+```
+
+## Keeping Up-to-Date
+
+Periodically, you should update your fork or personal repository to
+match the canonical ZF repository. In each of the above setups, we have
+added a remote to the Zend Framework repository, which allows you to do
+the following:
+
+
+```sh
+% git checkout master
+% git pull zf2 master
+- OPTIONALLY, to keep your remote up-to-date -
+% git push origin
+```
+
+If you're tracking other branches -- for example, the "develop" branch, where
+new feature development occurs -- you'll want to do the same operations for that
+branch; simply substibute "develop" for "master".
+
+## Working on Zend Framework
+
+When working on Zend Framework, we recommend you do each new feature or
+bugfix in a new branch. This simplifies the task of code review as well
+as of merging your changes into the canonical repository.
+
+A typical work flow will then consist of the following:
+
+ 1. Create a new local branch based off your master branch.
+ 2. Switch to your new local branch. (This step can be combined with the
+ previous step with the use of `git checkout -b`.)
+ 3. Do some work, commit, repeat as necessary.
+ 4. Push the local branch to your remote repository.
+ 5. Send a pull request.
+
+The mechanics of this process are actually quite trivial. Below, we will
+create a branch for fixing an issue in the tracker.
+
+```sh
+% git checkout -b zf9295
+Switched to a new branch 'zf9295'
+```
+... do some work ...
+
+```sh
+% git commit
+```
+... write your log message ...
+
+```sh
+% git push origin HEAD:zf9295
+Counting objects: 38, done.
+Delta compression using up to 2 threads.
+Compression objects: 100% (18/18), done.
+Writing objects: 100% (20/20), 8.19KiB, done.
+Total 20 (delta 12), reused 0 (delta 0)
+To ssh://git@github.com/weierophinney/zf2.git
+ b5583aa..4f51698 HEAD -> master
+```
+
+
+To send a pull request, you have two options.
+
+If using GitHub, you can do the pull request from there. Navigate to
+your repository, select the branch you just created, and then select the
+"Pull Request" button in the upper right. Select the user
+"zendframework" as the recipient.
+
+If using your own repository - or even if using GitHub - you can send an
+email indicating you have changes to pull:
+
+ - Send to <zf-devteam@zend.com>
+
+ - In your message, specify:
+ - The URL to your repository (e.g., `git://mwop.net/zf2.git`)
+ - The branch containing the changes you want pulled (e.g., `zf9295`)
+ - The nature of the changes (e.g., `implements
+ Zend_Service_Twitter`, `fixes ZF-9295`, etc.)
+
+### What branch to issue the pull request against?
+
+Which branch should you issue a pull request against?
+
+- For fixes against the stable release, issue the pull request against the
+ "master" branch.
+- For new features, or fixes that introduce new elements to the public API (such
+ as new public methods or properties), issue the pull request against the
+ "develop" branch.
+
+## Branch Cleanup
+
+As you might imagine, if you are a frequent contributor, you'll start to
+get a ton of branches both locally and on your remote.
+
+Once you know that your changes have been accepted to the master
+repository, we suggest doing some cleanup of these branches.
+
+ - Local branch cleanup
+
+ ```sh
+ % git branch -d <branchname>
+ ```
+
+ - Remote branch removal
+
+ ```sh
+ % git push origin :<branchname>
+ ```
+
+
+## FEEDS AND EMAILS
+
+RSS feeds may be found at:
+
+`https://github.com/zendframework/zf2/commits/<branch>.atom`
+
+where <branch> is a branch in the repository.
+
+To subscribe to git email notifications, simply watch or fork the zf2 repository
+on GitHub.
+
+## CONTRIBUTORS AND COMMITTERS
+
+Both Zend's internal Zend Framework team and the members of the Community Review
+team have push privileges to the ZF2 repository.
--- /dev/null
+# USING THE GIT REPOSITORY
+
+## Setup your own public repository
+
+Your first step is to establish a public repository from which we can
+pull your work into the master repository. You have two options: use
+GitHub or other public site, or setup/use your own repository.
+
+While you can use a private repository and utilize ``git format-patch`` to
+submit patches, this is discouraged as it does not facilitate public peer
+review.
+
+### Option 1: GitHub
+
+ 1. Setup a GitHub account (http://github.com/), if you haven't yet
+ 2. Fork the ZF2 repository (http://github.com/zendframework/zf2)
+ 3. Clone your fork locally and enter it (use your own GitHub username
+ in the statement below)
+
+ ```sh
+ % git clone git@github.com:<username>/zf2.git
+ % cd zf2
+ ```
+
+ 4. Add a remote to the canonical ZF repository, so you can keep your fork
+ up-to-date:
+
+ ```sh
+ % git remote add zf2 https://github.com/zendframework/zf2.git
+ % git fetch zf2
+ ```
+
+### Option 2: Personal Repository
+
+We assume you will use gitosis (http://git-scm.com/book/en/Git-on-the-Server-Gitosis)
+or gitolite (http://git-scm.com/book/en/Git-on-the-Server-Gitolite) to host your
+own repository. If you go this route, we will assume you have the knowledge to
+do so, or know where to obtain it. We will not assist you in setting up such a
+repository.
+
+ 1. Create a new repository
+
+ ```sh
+ % git init
+ ```
+
+ 2. Add an "origin" remote pointing to your gitosis/gitolite repo:
+
+ ```sh
+ % git remote add origin git://yourdomain/yourrepo.git
+ ```
+
+ 3. Add a remote for the ZF repository and fetch it
+
+ ```sh
+ % git remote add zf2 https://github.com/zendframework/zf2.git
+ % git fetch zf2
+ ```
+
+ 4. Create a new branch for the ZF repository (named "zf/master" here)
+
+ ```sh
+ % git checkout -b zf/master zf2/master
+ ```
+
+ 5. Create your master branch off the ZF branch, and push to your
+ repository
+
+ ```sh
+ % git checkout -b master
+ % git push origin HEAD:master
+ ```
+
+### Pre-Commit Hook (Optional)
+
+The ZF2 Travis-CI will confirm that code style standards are met
+by using ```php-cs-fixer``` (https://github.com/fabpot/PHP-CS-Fixer) during it's build runs.
+
+To reduce the number of red Travis-CI builds, the following Git pre-commit hook
+can help catch code style issues before committing. Save it as
+```.git/hooks/pre-commit```, and make sure it is executable.
+
+```php
+#!/usr/bin/env php
+<?php
+/**
+ * .git/hooks/pre-commit
+ *
+ * This pre-commit hooks will check for PHP errors (lint), and make sure the
+ * code is PSR-2 compliant.
+ *
+ * Dependecy: PHP-CS-Fixer (https://github.com/fabpot/PHP-CS-Fixer)
+ *
+ * @author Mardix http://github.com/mardix
+ * @author Matthew Weier O'Phinney http://mwop.net/
+ * @since 4 Sept 2012
+ */
+
+$exit = 0;
+
+/*
+ * collect all files which have been added, copied or
+ * modified and store them in an array called output
+ */
+$output = array();
+exec('git diff --cached --name-status --diff-filter=ACM', $output);
+
+foreach ($output as $file) {
+ if ('D' === substr($file, 0, 1)) {
+ // deleted file; do nothing
+ continue;
+ }
+
+ $fileName = trim(substr($file, 1));
+
+ /*
+ * Only PHP files
+ */
+ $extension = pathinfo($fileName, PATHINFO_EXTENSION);
+ if (!preg_match('/^ph(p|tml)$/', $extension)) {
+ continue;
+ }
+
+ /*
+ * Check for parse errors
+ */
+ $output = array();
+ $return = 0;
+ exec("php -l " . escapeshellarg($fileName), $output, $return);
+
+ if ($return != 0) {
+ echo "PHP file fails to parse: " . $fileName . ":" . PHP_EOL;
+ echo implode(PHP_EOL, $lintOutput) . PHP_EOL;
+ $exit = 1;
+ continue;
+ }
+
+ /*
+ * PHP-CS-Fixer
+ */
+ $output = array();
+ $return = null;
+ exec("php-cs-fixer --dry-run --level=psr2 " . escapeshellarg($fileName), $output, $return);
+ if ($return != 0 || !empty($output)) {
+ echo "PHP file fails contains CS issues: " . $fileName . ":" . PHP_EOL;
+ echo implode(PHP_EOL, $output) . PHP_EOL;
+ $exit = 1;
+ continue;
+ }
+}
+
+exit($exit);
+```
+
+## Keeping Up-to-Date
+
+Periodically, you should update your fork or personal repository to
+match the canonical ZF repository. In each of the above setups, we have
+added a remote to the Zend Framework repository, which allows you to do
+the following:
+
+
+```sh
+% git checkout master
+% git pull zf2 master
+- OPTIONALLY, to keep your remote up-to-date -
+% git push origin
+```
+
+If you're tracking other branches -- for example, the "develop" branch, where
+new feature development occurs -- you'll want to do the same operations for that
+branch; simply substibute "develop" for "master".
+
+## Working on Zend Framework
+
+When working on Zend Framework, we recommend you do each new feature or
+bugfix in a new branch. This simplifies the task of code review as well
+as of merging your changes into the canonical repository.
+
+A typical work flow will then consist of the following:
+
+ 1. Create a new local branch based off your master branch.
+ 2. Switch to your new local branch. (This step can be combined with the
+ previous step with the use of `git checkout -b`.)
+ 3. Do some work, commit, repeat as necessary.
+ 4. Push the local branch to your remote repository.
+ 5. Send a pull request.
+
+The mechanics of this process are actually quite trivial. Below, we will
+create a branch for fixing an issue in the tracker.
+
+```sh
+% git checkout -b zf9295
+Switched to a new branch 'zf9295'
+```
+... do some work ...
+
+```sh
+% git commit
+```
+... write your log message ...
+
+```sh
+% git push origin HEAD:zf9295
+Counting objects: 38, done.
+Delta compression using up to 2 threads.
+Compression objects: 100% (18/18), done.
+Writing objects: 100% (20/20), 8.19KiB, done.
+Total 20 (delta 12), reused 0 (delta 0)
+To ssh://git@github.com/weierophinney/zf2.git
+ b5583aa..4f51698 HEAD -> master
+```
+
+
+To send a pull request, you have two options.
+
+If using GitHub, you can do the pull request from there. Navigate to
+your repository, select the branch you just created, and then select the
+"Pull Request" button in the upper right. Select the user
+"zendframework" as the recipient.
+
+If using your own repository - or even if using GitHub - you can send an
+email indicating you have changes to pull:
+
+ - Send to <zf-devteam@zend.com>
+
+ - In your message, specify:
+ - The URL to your repository (e.g., `git://mwop.net/zf2.git`)
+ - The branch containing the changes you want pulled (e.g., `zf9295`)
+ - The nature of the changes (e.g., `implements
+ Zend_Service_Twitter`, `fixes ZF-9295`, etc.)
+
+### What branch to issue the pull request against?
+
+Which branch should you issue a pull request against?
+
+- For fixes against the stable release, issue the pull request against the
+ "master" branch.
+- For new features, or fixes that introduce new elements to the public API (such
+ as new public methods or properties), issue the pull request against the
+ "develop" branch.
+
+## Branch Cleanup
+
+As you might imagine, if you are a frequent contributor, you'll start to
+get a ton of branches both locally and on your remote.
+
+Once you know that your changes have been accepted to the master
+repository, we suggest doing some cleanup of these branches.
+
+ - Local branch cleanup
+
+ ```sh
+ % git branch -d <branchname>
+ ```
+
+ - Remote branch removal
+
+ ```sh
+ % git push origin :<branchname>
+ ```
+
+
+## FEEDS AND EMAILS
+
+RSS feeds may be found at:
+
+`https://github.com/zendframework/zf2/commits/<branch>.atom`
+
+where <branch> is a branch in the repository.
+
+To subscribe to git email notifications, simply watch or fork the zf2 repository
+on GitHub.
+
+## CONTRIBUTORS AND COMMITTERS
+
+Both Zend's internal Zend Framework team and the members of the Community Review
+team have push privileges to the ZF2 repository.
--- /dev/null
+Version of Zend Framework is release-2.0.3
\ No newline at end of file
--- /dev/null
+<?php
+namespace wcf\system\cli;
+use phpline\console\history\MemoryHistory;
+use wcf\system\WCF;
+
+/**
+ * A phpline history that saves the items in database.
+ *
+ * @author Tim Düsterhus
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system
+ * @category Community Framework
+ */
+class DatabaseCommandHistory extends MemoryHistory {
+ /**
+ * should the history automatically be saved
+ * @var boolean
+ */
+ public $autoSave = true;
+
+ /**
+ * Saves the history.
+ *
+ * @param boolean $append
+ */
+ public function save($append = false) {
+ if (!$append) {
+ $sql = "DELETE FROM wcf".WCF_N."_cli_history
+ WHERE userID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(WCF::getUser()->userID));
+ }
+
+ $sql = "INSERT INTO wcf".WCF_N."_cli_history (userID, command)
+ VALUES (?, ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ WCF::getDB()->beginTransaction();
+ foreach ($this as $item) {
+ $statement->execute(array(WCF::getUser()->userID, $item));
+ }
+ WCF::getDB()->commitTransaction();
+ }
+
+ /**
+ * Loads the history.
+ */
+ public function load() {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_cli_history
+ WHERE userID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(WCF::getUser()->userID));
+
+ while ($row = $statement->fetchArray()) {
+ $this->add($row['command']);
+ }
+
+ $this->moveToEnd();
+ }
+
+ /**
+ * Automatically saves the history if $autoSave is set to true.
+ */
+ public function __destruct() {
+ if ($this->autoSave) {
+ $this->save();
+ }
+ }
+}
KEY objectType (objectType)
);
+DROP TABLE IF EXISTS wcf1_cli_history;
+CREATE TABLE wcf1_cli_history (
+ historyItem INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ userID INT(10) NOT NULL,
+ command VARCHAR(255) NOT NULL,
+ KEY (userID)
+);
+
DROP TABLE IF EXISTS wcf1_clipboard_action;
CREATE TABLE wcf1_clipboard_action (
actionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
ALTER TABLE wcf1_cleanup_log ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_cli_history ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
+
ALTER TABLE wcf1_clipboard_action ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
ALTER TABLE wcf1_clipboard_item ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;