Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge pull request zendframework/zendframework#7091 from Lansoweb/dev…
Browse files Browse the repository at this point in the history
…elop

Added Checkbox Console Prompt and its tests
  • Loading branch information
weierophinney committed Feb 24, 2015
166 parents c26f58a + dfbe5c2 + bcf0eb7 + df35d8a + c0d4707 + fb9dce0 + c45b896 + 739e942 + 29a4517 + 3179585 + ae205d0 + 6d3ab5a + 5b7a64f + 7591112 + 7610b9a + f91c471 + acef74d + d753e94 + 7a829bf + 3b93376 + 6c2eca4 + 48bc01e + 3c88cd5 + fbfc750 + 441b9de + b57e2b9 + 2beef92 + 3ae4add + 994832b + 6db3cde + e0f83ee + 712223d + 3b4ec35 + e53a267 + 51bf3c7 + 10a1e95 + 30ee303 + 96417b9 + 50ea13f + 24d1c0c + b09295e + 13758aa + 99316f8 + 40b8ac3 + e284e0b + 8ff51b4 + 4b5ea1e + 5b24371 + ed932b6 + 357852d + 262e5d6 + 9057a3f + 0e1e846 + 5d11876 + 0ae0984 + 6ff135c + 0b45aba + 1778b0c + a574713 + ba3f132 + 56365a6 + 019f89a + b09b3ba + 2af32d0 + cb39fc7 + 3f8a6fd + 8999753 + a7cd86b + da1c469 + 5371cdb + fd9f625 + 99d3fcd + 1e3ed17 + a360459 + 1a1e9aa + 4c8955c + 94f2feb + 5b450ef + e64e5b0 + e9d19a6 + a5a9b6e + 5e59c3a + a47f2db + 3cf915a + a2a5c91 + 1cd3439 + ef9c643 + 298a619 + 001ae99 + c37ab8e + 017f6f1 + 0711f77 + 38af804 + 23120b4 + 5f96f53 + a257169 + 8085bc4 + 3110cd4 + f270d41 + 9536f09 + 4ec1292 + adcdd78 + e80fff6 + 99fc5ce + 1d23e94 + fa167cb + a93e92f + 13a2df8 + 06689fb + 2d601eb + 72a3295 + a59f42c + 0f4489d + b3f7029 + 3c492ce + 259bcdc + a811ae5 + f66f985 + 173e949 + b93077e + f11a10d + 5ac4be0 + cb31aff + d34bdbb + 725b978 + 27b423e + 624ac5e + a3340ac + 80ab2e3 + 3429053 + 998a6d7 + 8b9ab71 + d4dffb0 + 5456435 + 97180fa + 126be7f + c16eb72 + 8ccb096 + 50481b9 + 411738a + 50b5c0b + 0721376 + ca3399f + 5abf474 + 7783b57 + a53deb5 + c8885be + 1adf3e3 + 5a45e41 + 3370b44 + b4caa32 + 2a7fc79 + 755e064 + fde42e0 + fb9bce8 + 3ff447c + 7cbed25 + 61aff3c + a27b9cd + 7bfc680 + 3a02933 + ca6dcba + d776971 + b515ef9 + c7e321d + 0a3000f commit 17effe7
Show file tree
Hide file tree
Showing 3 changed files with 301 additions and 0 deletions.
183 changes: 183 additions & 0 deletions src/Prompt/Checkbox.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Console\Prompt;

use Zend\Console\Exception;
use Zend\Stdlib\ArrayUtils;

final class Checkbox extends AbstractPrompt
{
/**
* @var string
*/
private $promptText;

/**
* @var bool
*/
private $ignoreCase;

/**
* @var array|Transversable
*/
private $options;

/**
* Checked options
* @var array
*/
private $checkedOptions = array();

/**
* If the response should be echoed to the console or not
* @var bool
*/
private $echo;

/**
* Ask the user to select any number of pre-defined options
*
* @param string $promptText The prompt text to display in console
* @param array|Transversable $options Allowed options
* @param bool $echo True to display selected option?
*/
public function __construct($promptText = 'Please select one option (Enter to finish) ', $options = array(), $ignoreCase = true, $echo = false)
{
$this->promptText = (string) $promptText;

$this->setOptions($options);

$this->echo = (bool) $echo;

$this->ignoreCase = (bool) $ignoreCase;
}

/**
* Show a list of options and prompt the user to select any number of them.
*
* @return array Checked options
*/
public function show()
{
$this->checkedOptions = array();
$mask = $this->prepareMask();

do {
$this->showAvailableOptions();

$response = $this->readOption($mask);

if ($this->echo) {
$this->showResponse($response);
}

$this->checkOrUncheckOption($response);
} while ($response != "\r" && $response != "\n");

$this->lastResponse = $this->checkedOptions;

return $this->checkedOptions;
}

/**
* Shows the selected option to the screen
* @param string $response
*/
private function showResponse($response)
{
$console = $this->getConsole();
if (isset($this->options[$response])) {
$console->writeLine($this->options[$response]);
} else {
$console->writeLine();
}
}

/**
* Check or uncheck an option
*
* @param string $response
*/
private function checkOrUncheckOption($response)
{
if ($response != "\r" && $response != "\n" && isset($this->options[$response])) {
$pos = array_search($this->options[$response], $this->checkedOptions);
if ($pos === false) {
$this->checkedOptions[] = $this->options[$response];
} else {
array_splice($this->checkedOptions, $pos, 1);
}
}
}

/**
* Generates a mask to to be used by the readChar method.
*
* @return string
*/
private function prepareMask()
{
$mask = implode("", array_keys($this->options)) . "\r\n";

/**
* Normalize the mask if case is irrelevant
*/
if (!$this->ignoreCase) {
return $mask;
}

$mask = implode("", array_unique(str_split(strtolower($mask) . strtoupper($mask))));

return $mask;
}

/**
* Reads a char from console.
*
* @param string $mask
* @return string
*/
private function readOption($mask)
{
/**
* Read char from console
*/

return $this->getConsole()->readChar($mask);
}

/**
* Shows the available options with checked and unchecked states
*/
private function showAvailableOptions()
{
$console = $this->getConsole();
$console->writeLine($this->promptText);
foreach ($this->options as $k => $v) {
$console->writeLine(' ' . $k . ') ' . (in_array($v, $this->checkedOptions) ? '[X] ' : '[ ] ') . $v);
}
}

/**
* Set allowed options
*
* @param array|\Traversable $options
* @throws Exception\InvalidArgumentException
*/
private function setOptions($options)
{
$options = ArrayUtils::iteratorToArray($options);

if (empty($options)) {
throw new Exception\InvalidArgumentException('Please, specify at least one option');
}

$this->options = $options;
}
}
108 changes: 108 additions & 0 deletions test/Prompt/CheckboxTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\Console\Prompt;

use Zend\Console\Prompt\Checkbox;
use ZendTest\Console\TestAssets\ConsoleAdapter;

/**
* @group Zend_Console
* @covers \Zend\Console\Prompt\Checkbox
*/
class CheckboxTest extends \PHPUnit_Framework_TestCase
{
/**
* @var ConsoleAdapter
*/
protected $adapter;

public function setUp()
{
$this->adapter = new ConsoleAdapter(false);
$this->adapter->stream = fopen('php://memory', 'w+');
}

public function tearDown()
{
fclose($this->adapter->stream);
}

public function testCanCheckOneOption()
{
fwrite($this->adapter->stream, "0");
fwrite($this->adapter->stream, "\n");
rewind($this->adapter->stream);

$checkbox = new Checkbox('Check an option :', array('foo', 'bar'));
$checkbox->setConsole($this->adapter);
ob_start();
$response = $checkbox->show();
$text = ob_get_clean();
$this->assertSame(1, substr_count($text, '0) [X] foo'));
$this->assertSame(1, substr_count($text, '0) [ ] foo'));
$this->assertSame(2, substr_count($text, '1) [ ] bar'));
$this->assertEquals(['0' => 'foo'], $response);
}

public function testCanUncheckOneOption()
{
fwrite($this->adapter->stream, "0");
fwrite($this->adapter->stream, "0");
fwrite($this->adapter->stream, "\n");
rewind($this->adapter->stream);

$checkbox = new Checkbox('Check an option :', array('foo', 'bar'));
$checkbox->setConsole($this->adapter);
ob_start();
$response = $checkbox->show();
$text = ob_get_clean();
$this->assertSame(1, substr_count($text, '0) [X] foo'));
$this->assertSame(2, substr_count($text, '0) [ ] foo'));
$this->assertSame(3, substr_count($text, '1) [ ] bar'));
$this->assertEquals([], $response);
}

public function testCanCheckTwoOption()
{
fwrite($this->adapter->stream, "0");
fwrite($this->adapter->stream, "1");
fwrite($this->adapter->stream, "\n");
rewind($this->adapter->stream);

$checkbox = new Checkbox('Check an option :', array('foo', 'bar'));
$checkbox->setConsole($this->adapter);
ob_start();
$response = $checkbox->show();
$text = ob_get_clean();
$this->assertSame(2, substr_count($text, '0) [X] foo'));
$this->assertSame(1, substr_count($text, '1) [X] bar'));
$this->assertSame(1, substr_count($text, '0) [ ] foo'));
$this->assertSame(2, substr_count($text, '1) [ ] bar'));
$this->assertEquals(['0' => 'foo', '1' => 'bar'], $response);
}

public function testCanCheckOptionWithCustomIndex()
{
fwrite($this->adapter->stream, "2");
fwrite($this->adapter->stream, "\n");
rewind($this->adapter->stream);

$checkbox = new Checkbox('Check an option :', array('2' => 'foo', '6' => 'bar'));
$checkbox->setConsole($this->adapter);
ob_start();
$response = $checkbox->show();
$text = ob_get_clean();
$this->assertSame(1, substr_count($text, '2) [X] foo'));
$this->assertSame(1, substr_count($text, '2) [ ] foo'));
$this->assertSame(2, substr_count($text, '6) [ ] bar'));
$this->assertEquals(['0' => 'foo'], $response);
}

}
10 changes: 10 additions & 0 deletions test/TestAssets/ConsoleAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ class ConsoleAdapter extends AbstractAdapter

public $writtenData = array();

/**
* Construct.
*
* @param bool $autoRewind If rewinds the stream before read the next char/line
*/
public function __construct($autoRewind = true)
{
$this->autoRewind = (bool) $autoRewind;
}

/**
* Read a single line from the console input
*
Expand Down

0 comments on commit 17effe7

Please sign in to comment.