Skip to content

Commit

Permalink
Add debug command (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
maidmaid committed Apr 29, 2017
1 parent 1d99d97 commit aef8036
Show file tree
Hide file tree
Showing 10 changed files with 378 additions and 7 deletions.
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Binarized flags are not intuitive to understand, using concepts like
[bitwise operators](http://php.net/manual/en/language.operators.bitwise.php),
[bitmask](https://en.wikipedia.org/wiki/Mask_(computing)) or [Bit field](https://en.wikipedia.org/wiki/Bit_field).
[bitmask](https://en.wikipedia.org/wiki/Mask_(computing)) or [bit field](https://en.wikipedia.org/wiki/Bit_field).
Moreover, theses flags are not easy to debug; find flags that hide behind integer bitfield is very annoying.
This lib propose a fluent API to handle bitfield and improve developer experience with tools for debugging them.
This library propose a fluent API to handle bitfield and improve developer experience with tools for debugging them.

[![Build Status](https://travis-ci.org/maidmaid/flag.svg?branch=master)](https://travis-ci.org/maidmaid/flag)
[![Latest Stable Version](https://poser.pugx.org/maidmaid/flag/v/stable)](https://packagist.org/packages/maidmaid/flag)
Expand Down Expand Up @@ -98,8 +98,8 @@ $flag = Flag::create(null, 'E_')
As in [``Request::METHOD_*`` case](https://github.com/symfony/symfony/blob/8872833c5d6a46ea27a4483e650617361660d946/src/Symfony/Component/HttpFoundation/Request.php#L49-L58), values flags are not integer but string. For example, ``METHOD_GET`` has ``GET`` string as value. This string values are internally binarized.

```php
use Maidmaid\Flag\Flag;
use Symfony\Component\HttpFoundation\Request;
use Maidmaid\Flag\Flag;

$flag = Flag::create(Request::class, 'METHOD_')
->add(Request::METHOD_GET) // logs '[debug] bitfield changed Request::METHOD_* [bin: 10] [dec: 2] [METHOD_*: GET]'
Expand All @@ -125,3 +125,16 @@ $flag = (new Flag())
->add(32) // logs '[debug] bitfield changed [bin: 101000] [dec: 40] [flags: 8 | 32]'
;
```

## Debug

If you add ``symfony/console`` suggested package, you can debug your flags with ``debug:flag`` command.
Run ``php bin/flag --help`` for details.

<p align="center">
<img src="doc/debug_command.png" width="600">
</p>

## License

Flag is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
15 changes: 15 additions & 0 deletions bin/flag
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env php
<?php

use Maidmaid\Flag\Command\DebugCommand;
use Symfony\Component\Console\Application;

require file_exists(__DIR__.'/../vendor/autoload.php')
? __DIR__.'/../vendor/autoload.php'
: __DIR__.'/../../../autoload.php'
;

$app = new Application();
$app->add(new DebugCommand());
$app->setDefaultCommand('debug:flag', true);
$app->run();
152 changes: 152 additions & 0 deletions bin/flag-example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php

namespace Maidmaid\Flag\Demo;

require __DIR__.'/../vendor/autoload.php';

use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\ConsoleOutput;
use Maidmaid\Flag\Flag;

$logger = new ConsoleLogger(new ConsoleOutput(ConsoleOutput::VERBOSITY_DEBUG));

echo "\nOverview\n";
class Yaml
{
const DUMP_OBJECT = 1;
const PARSE_EXCEPTION_ON_INVALID_TYPE = 2;
const PARSE_OBJECT = 4;
const PARSE_OBJECT_FOR_MAP = 8;
const DUMP_EXCEPTION_ON_INVALID_TYPE = 16;
const PARSE_DATETIME = 32;
const DUMP_OBJECT_AS_MAP = 64;
const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
const PARSE_CONSTANT = 256;
}
$flag = Flag::create(Yaml::class);
$flag->setLogger($logger);
$flag
->add(Yaml::DUMP_OBJECT)
->add(Yaml::PARSE_DATETIME)
->remove(Yaml::DUMP_OBJECT)
;
var_dump(
$flag->has(Yaml::DUMP_OBJECT),
$flag->has(Yaml::PARSE_DATETIME),
$flag->get()
);
$flag->set(100);
foreach ($flag as $k => $v) {
echo "$k => $v ";
}

echo "\n\nExample\n";
class Color
{
const RED = 1;
const GREEN = 2;
const YELLOW = 3;
public $flag;

public function __construct($logger)
{
$this->flag = Flag::create(self::class);
$this->flag->setLogger($logger);
}
}
(new Color($logger))->flag
->add(Color::RED)
->add(Color::GREEN)
->remove(Color::GREEN)
;

echo "\nPrefix\n";
class Caster
{
const EXCLUDE_VERBOSE = 1;
const EXCLUDE_VIRTUAL = 2;
const EXCLUDE_DYNAMIC = 4;
const EXCLUDE_PUBLIC = 8;
const EXCLUDE_PROTECTED = 16;
const EXCLUDE_PRIVATE = 32;
const EXCLUDE_NULL = 64;
const EXCLUDE_EMPTY = 128;
const EXCLUDE_NOT_IMPORTANT = 256;
const EXCLUDE_STRICT = 512;
}
$flag = Flag::create(Caster::class, 'EXCLUDE_');
$flag->setLogger($logger);
$flag
->add(Caster::EXCLUDE_EMPTY)
->add(Caster::EXCLUDE_PRIVATE)
->add(Caster::EXCLUDE_NOT_IMPORTANT)
->remove(Caster::EXCLUDE_NOT_IMPORTANT)
;

echo "\nHierarchical\n";
class Output
{
const VERBOSITY_QUIET = 16;
const VERBOSITY_NORMAL = 32;
const VERBOSITY_VERBOSE = 64;
const VERBOSITY_VERY_VERBOSE = 128;
const VERBOSITY_DEBUG = 256;
}
$flag = Flag::create(Output::class, 'VERBOSITY_', true);
$flag->setLogger($logger);
$flag
->add(Output::VERBOSITY_VERBOSE)
->add(Output::VERBOSITY_DEBUG)
->remove(Output::VERBOSITY_DEBUG)
;

echo "\nGlobal space\n";
$flag = Flag::create(null, 'E_');
$flag->setLogger($logger);
$flag
->add(E_ALL)
->set(0)
->add(E_USER_ERROR)
->add(E_USER_DEPRECATED)
->remove(E_USER_DEPRECATED)
;

echo "\nBinarizedFlag\n";
class Request
{
const METHOD_HEAD = 'HEAD';
const METHOD_GET = 'GET';
const METHOD_POST = 'POST';
const METHOD_PUT = 'PUT';
const METHOD_PATCH = 'PATCH';
const METHOD_DELETE = 'DELETE';
const METHOD_PURGE = 'PURGE';
const METHOD_OPTIONS = 'OPTIONS';
const METHOD_TRACE = 'TRACE';
const METHOD_CONNECT = 'CONNECT';
}
$flag = Flag::create(Request::class, 'METHOD_');
$flag->setLogger($logger);
$flag
->add(Request::METHOD_GET)
->add(Request::METHOD_POST)
->add(Request::METHOD_PUT)
->remove(Request::METHOD_PUT)
;

echo "\nStandalone\n";
$flag = new Flag();
$flag->setLogger($logger);
$flag
->add(8)
->add(32)
->remove(32)
;
echo "\n";
$flag = Flag::create();
$flag->setLogger($logger);
$flag
->add('a')
->add('b')
->remove('b')
;
11 changes: 9 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,18 @@
"psr/log": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
"phpunit/phpunit": "^5.7",
"symfony/console": "^3.1"
},
"suggest": {
"symfony/console": "For debugging flags using the debug:flag command"
},
"bin": [
"bin/flag"
],
"autoload": {
"psr-4": {
"Maidmaid\\Flag\\": "src/Flag"
}
}
}
}
Binary file added doc/debug_command.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Flag/AbstractFlag.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function __construct($from = false, $prefix = '', $bitfield = 0)
* @param string|null|bool $from Class from where the searching flags is made; define to null to
* search flags in global space; define to false for standalone use
* @param string $prefix Prefix flags from the search flags is made
* @param bool $hierarchical Defines hierarchical flags
* @param bool $hierarchical Defines flags as hierarchical
* @param int $bitfield Sets bitfield value
*
* @return AbstractFlag
Expand Down
118 changes: 118 additions & 0 deletions src/Flag/Command/DebugCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

/*
* This file is part of the maidmaid/flag package.
*
* (c) Dany Maillard <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Maidmaid\Flag\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Maidmaid\Flag\Flag;

class DebugCommand extends Command
{
private $legends = array();
private $max;

/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('debug:flag')
->addArgument('bitfield', InputArgument::OPTIONAL, '', 0)
->addOption('from', 'f', InputOption::VALUE_REQUIRED, 'Class where searching flags is made')
->addOption('prefix', 'p', InputOption::VALUE_REQUIRED, 'Prefix flags that filter search result')
->addOption('hierarchical', 'l', InputOption::VALUE_NONE, 'Defines flags as hierarchical')
->setHelp(<<<EOF
Examples of <info>%command.name%</info> command:
<info>php %command.full_name% --prefix E_</info>
<info>php %command.full_name% --prefix E_ 8</info>
<info>php %command.full_name% --from Symfony\\\\Component\\\\Console\\\\Output\\\\Output --prefix VERBOSITY_ --hierarchical 64</info>
EOF
)
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$bitfield = (int) $input->getArgument('bitfield');
$from = $input->getOption('from');
$prefix = $input->getOption('prefix') ? $input->getOption('prefix') : '';
$hierarchical = $input->getOption('hierarchical');

$flag = Flag::create($from, $prefix, $hierarchical, $bitfield);

$flags = array();
$flagged = array();
$iterator = $flag->getIterator(false);
$iterator->ksort();
foreach ($iterator as $v => $f) {
// Check if it's a top flag
if (((int) ($r = log($v, 2))) == $r) {
$flags[$r] = $f;
$flagged[$r] = $flag->has($v) ? '<bg=green;fg=black;options=bold>1</>' : 0;
}
}
$this->max = max(array_keys($flags));

$headers = array();
$bitfield = array();
for ($i = $this->max; $i >= 0; --$i) {
$headers[] = $i;
$bitfield[] = isset($flagged[$i]) ? $flagged[$i] : 0;
}

$i = -1;
foreach ($flags as $x => $flag) {
$this->legend($this->max - $x, ++$i, $flag, $flagged[$x]);
}

$rows = $this->legends;
array_unshift($rows, $bitfield);

$table = new Table($output);
$table->setColumnWidths(array_fill(0, count($headers), 2));
$table->setHeaders($headers);
$table->setRows($rows);
$table->setStyle('compact');

$output->writeln('');
$table->render();
}

private function legend($x, $y, $name, $highlight = false)
{
$format = $highlight ? '<info>%s</info>' : '%s';

while (!isset($this->legends[$y])) {
$this->legends[] = array_fill(0, $this->max + 1, '');
}

$this->legends[$y][$x] = sprintf($format, '└─');
$this->legends[$y][$this->max + 1] = sprintf($format, $name);

// h
for ($i = $x + 1; $i <= $this->max; ++$i) {
$this->legends[$y][$i] = sprintf($format, '──');
}

// v
for ($i = $y - 1; $i >= 0; --$i) {
$this->legends[$i][$x] = sprintf($format, '');
}
}
}
2 changes: 1 addition & 1 deletion src/Flag/FlagInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function get();
/**
* Adds a flag.
*
* @param int $flag Bitfield to add
* @param int|string $flag Flag value to add
*
* @return $this
*/
Expand Down
1 change: 1 addition & 0 deletions tests/Flag/AbstractFlagTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Maidmaid\Flag\Tests;

use PHPUnit\Framework\TestCase;
Expand Down
Loading

0 comments on commit aef8036

Please sign in to comment.