1
|
|
|
<?php namespace nyx\console\input\formats\parsers; |
2
|
|
|
|
3
|
|
|
// External dependencies |
4
|
|
|
use nyx\core; |
5
|
|
|
|
6
|
|
|
// Internal dependencies |
7
|
|
|
use nyx\console\input\formats; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* String Parser |
11
|
|
|
* |
12
|
|
|
* The input string is expected to be formatted according to GNU guidelines (same as PHP's internal argv handling). |
13
|
|
|
* @see https://www.gnu.org/software/guile/manual/html_node/Command-Line-Format.html for more information. |
14
|
|
|
* |
15
|
|
|
* This class uses an algorithm ported from Symfony's Console, written by (c) Fabien Potencier <[email protected]> |
16
|
|
|
* |
17
|
|
|
* @version 0.1.0 |
18
|
|
|
* @author Fabien Potencier <[email protected]> |
19
|
|
|
* @author Michal Chojnacki <[email protected]> |
20
|
|
|
* @copyright 2012-2017 Nyx Dev Team |
21
|
|
|
* @link https://github.com/unyx/nyx |
22
|
|
|
*/ |
23
|
|
|
class Str extends Argv implements formats\interfaces\Tokenizer |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* The token matching expressions. |
27
|
|
|
*/ |
28
|
|
|
const PATTERN_BARE = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)'; |
29
|
|
|
const PATTERN_QUOTED = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')'; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* {@inheritdoc} |
33
|
|
|
* |
34
|
|
|
* @param string $input |
35
|
|
|
* @throws core\exceptions\InvalidArgumentType When the given input is not a string. |
36
|
|
|
* @throws \RuntimeException Upon failing to parse a given substring. |
37
|
|
|
*/ |
38
|
|
|
public function tokenize($input) : iterable |
39
|
|
|
{ |
40
|
|
|
if (!is_string($input)) { |
41
|
|
|
throw new core\exceptions\InvalidArgumentType($input, ['string']); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
// Change line breaks, tabs etc. into whitespaces. |
45
|
|
|
$length = strlen($input); |
46
|
|
|
$cursor = 0; |
47
|
|
|
$i = 0; |
48
|
|
|
$tokens = new formats\tokens\Argv; |
49
|
|
|
|
50
|
|
|
while ($cursor < $length) { |
51
|
|
|
if (preg_match('/\s+/A', $input, $match, null, $cursor)) { |
|
|
|
|
52
|
|
|
} elseif (preg_match('/([^="\'\s]+?)(=?)('.static::PATTERN_QUOTED.'+)/A', $input, $match, null, $cursor)) { |
53
|
|
|
$tokens->set($i++, $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, -1)))); |
54
|
|
|
} elseif (preg_match('/'.static::PATTERN_QUOTED.'/A', $input, $match, null, $cursor)) { |
55
|
|
|
$tokens->set($i++, stripcslashes(substr($match[0], 1, -1))); |
56
|
|
|
} elseif (preg_match('/'.static::PATTERN_BARE.'/A', $input, $match, null, $cursor)) { |
57
|
|
|
$tokens->set($i++, stripcslashes($match[1])); |
58
|
|
|
} else { |
59
|
|
|
throw new \RuntimeException('Failed to parse string near "... '.substr($input, $cursor, 10).' ..."'); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
$cursor += strlen($match[0]); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
return $tokens; |
|
|
|
|
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
|
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.