Completed
Push — dev ( 0bf02b...a3b92c )
by James Ekow Abaka
08:14
created

ArgumentParser::getNextValueOrFail()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 6
nc 2
nop 3
crap 3
1
<?php
2
3
namespace clearice\argparser;
4
5
6
/**
7
 * Class ArgumentParser
8
 *
9
 * @package clearice\argparser
10
 */
11
class ArgumentParser
12
{
13
    private $description;
14
    private $footer;
15
    private $options = [];
16
17
    public function setDescription($description)
18
    {
19
        $this->description = $description;
20
    }
21
22
    public function setFooter($footer)
23
    {
24
        $this->footer = $footer;
25
    }
26
27
    /**
28
     * Add a value to the available possible options for later parsing.
29
     *
30
     * @param $key
31
     * @param $value
32
     * @throws OptionExistsException
33
     */
34 5
    private function addToOptionArray($key, $value)
35
    {
36 5
        if(isset($value[$key]) && !isset($this->options[$value[$key]])) {
37 5
            $this->options[$value[$key]] = $value;
38 3
        } else if(isset($value[$key])) {
39 2
            throw new OptionExistsException("An argument option with $key {$value[$key]} already exists.");
40
        }
41 5
    }
42
43
    /**
44
     * Add an option to be parsed.
45
     * Arguments are presented as a structured array with the following possible keys.
46
     *
47
     *  name: The name of the option prefixed with a double dash --
48
     *  short_name: A shorter single character option prefixed with a single dash -
49
     *  type: Required for all options that take values. An option specified without a type is considered to be a
50
     *        boolean flag.
51
     *  help: A help message for the option
52
     *
53
     * @param $option
54
     * @throws OptionExistsException
55
     * @throws InvalidArgumentDescriptionException
56
     */
57 6
    public function addOption($option)
58
    {
59 6
        if(!isset($option['name'])) {
60 1
            throw new InvalidArgumentDescriptionException("Argument must have a name");
61
        }
62 5
        $this->addToOptionArray('name', $option);
63 5
        $this->addToOptionArray('short_name', $option);
64 5
    }
65
66
    /**
67
     * @param $arguments
68
     * @param $argPointer
69
     * @return mixed
70
     * @throws InvalidValueException
71
     */
72 3
    private function getNextValueOrFail($arguments, &$argPointer, $name)
73
    {
74 3
        if (isset($arguments[$argPointer + 1]) && $arguments[$argPointer + 1][0] != '-') {
75 1
            $argPointer++;
76 1
            return $arguments[$argPointer];
77
        } else {
78 2
            throw new InvalidValueException("A value must be passed along with argument $name.");
79
        }
80
    }
81
82
    /**
83
     * Parse a long argument that is prefixed with a double dash "--"
84
     *
85
     * @param $arguments
86
     * @param $argPointer
87
     * @return array
88
     * @throws InvalidValueException
89
     */
90 2
    private function parseLongArgument($arguments, &$argPointer)
91
    {
92 2
        $string = substr($arguments[$argPointer], 2);
93 2
        preg_match("/(?<name>[a-zA-Z_0-9-]+)(?<equal>=?)(?<value>.*)/", $string, $matches);
94 2
        $name = $matches['name'];
95 2
        $option = $this->options[$name];
96 2
        $value = true;
97
98 2
        if(isset($option['type'])) {
99 2
            if($matches['equal'] === '=') {
100 1
                $value = $matches['value'];
101
            } else {
102 2
                $value = $this->getNextValueOrFail($arguments, $argPointer, $name);
103
            }
104
        }
105
106 1
        return [$name => $value];
107
    }
108
109
    /**
110
     * Parse a short argument that is prefixed with a single dash '-'
111
     *
112
     * @param $arguments
113
     * @param $argPointer
114
     * @return array
115
     * @throws InvalidValueException
116
     */
117 2
    public function parseShortArgument($arguments, &$argPointer)
118
    {
119 2
        $argument = $arguments[$argPointer];
120 2
        $key = substr($argument, 1, 1);
121 2
        $option = $this->options[$key];
122 2
        $value = true;
123
124 2
        if(isset($option['type'])) {
125 2
            if(substr($argument, 2) != "") {
126 1
                $value = substr($argument, 2);
127
            } else {
128 2
                $value = $this->getNextValueOrFail($arguments, $argPointer, $option['name']);
129
            }
130
        }
131
132 1
        return [$option['name'] => $value];
133
    }
134
135
    /**
136
     * Parses command line arguments and return a structured array of options and their associated values.
137
     *
138
     * @param array $arguments An optional array of arguments that would be parsed instead of those passed to the CLI.
139
     * @return array
140
     * @throws InvalidValueException
141
     */
142 3
    public function parse($arguments = null)
143
    {
144 3
        global $argv;
145 3
        $arguments = $arguments ?? $argv;
146 3
        $numArguments = count($arguments);
147 3
        $output = [];
148
149 3
        for($argPointer = 1; $argPointer < $numArguments; $argPointer++) {
150 3
            $arg = $arguments[$argPointer];
151 3
            if(substr($arg, 0, 2) == "--") {
152 2
                $output = array_merge($output, $this->parseLongArgument($arguments, $argPointer));
153 2
            } else if ($arg[0] == '-') {
154 2
                $output = array_merge($output, $this->parseShortArgument($arguments, $argPointer));
155
            } else {
156 1
                $output['__args'] = isset($output['__args']) ? array_merge($output['__args'], [$arg]) : [$arg];
157
            }
158
        }
159
160 1
        return $output;
161
    }
162
163
    public function getHelp()
164
    {
165
166
    }
167
}
168