1<?php
2/*
3 * This file is part of PHPUnit.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11/**
12 * Command-line options parsing class.
13 */
14class PHPUnit_Util_Getopt
15{
16    public static function getopt(array $args, $short_options, $long_options = null)
17    {
18        if (empty($args)) {
19            return [[], []];
20        }
21
22        $opts     = [];
23        $non_opts = [];
24
25        if ($long_options) {
26            sort($long_options);
27        }
28
29        if (isset($args[0][0]) && $args[0][0] != '-') {
30            array_shift($args);
31        }
32
33        reset($args);
34
35        $args = array_map('trim', $args);
36
37        while (false !== $arg = current($args)) {
38            $i = key($args);
39            next($args);
40            if ($arg == '') {
41                continue;
42            }
43
44            if ($arg == '--') {
45                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
46
47                break;
48            }
49
50            if ($arg[0] != '-' || (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) {
51                $non_opts[] = $args[$i];
52
53                continue;
54            } elseif (strlen($arg) > 1 && $arg[1] == '-') {
55                self::parseLongOption(
56                    substr($arg, 2),
57                    $long_options,
58                    $opts,
59                    $args
60                );
61            } else {
62                self::parseShortOption(
63                    substr($arg, 1),
64                    $short_options,
65                    $opts,
66                    $args
67                );
68            }
69        }
70
71        return [$opts, $non_opts];
72    }
73
74    protected static function parseShortOption($arg, $short_options, &$opts, &$args)
75    {
76        $argLen = strlen($arg);
77
78        for ($i = 0; $i < $argLen; $i++) {
79            $opt     = $arg[$i];
80            $opt_arg = null;
81
82            if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') {
83                throw new PHPUnit_Framework_Exception(
84                    "unrecognized option -- $opt"
85                );
86            }
87
88            if (strlen($spec) > 1 && $spec[1] == ':') {
89                if ($i + 1 < $argLen) {
90                    $opts[] = [$opt, substr($arg, $i + 1)];
91
92                    break;
93                }
94                if (!(strlen($spec) > 2 && $spec[2] == ':')) {
95                    if (false === $opt_arg = current($args)) {
96                        throw new PHPUnit_Framework_Exception(
97                            "option requires an argument -- $opt"
98                        );
99                    }
100                    next($args);
101                }
102            }
103
104            $opts[] = [$opt, $opt_arg];
105        }
106    }
107
108    protected static function parseLongOption($arg, $long_options, &$opts, &$args)
109    {
110        $count   = count($long_options);
111        $list    = explode('=', $arg);
112        $opt     = $list[0];
113        $opt_arg = null;
114
115        if (count($list) > 1) {
116            $opt_arg = $list[1];
117        }
118
119        $opt_len = strlen($opt);
120
121        for ($i = 0; $i < $count; $i++) {
122            $long_opt  = $long_options[$i];
123            $opt_start = substr($long_opt, 0, $opt_len);
124
125            if ($opt_start != $opt) {
126                continue;
127            }
128
129            $opt_rest = substr($long_opt, $opt_len);
130
131            if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < $count &&
132                $opt == substr($long_options[$i + 1], 0, $opt_len)) {
133                throw new PHPUnit_Framework_Exception(
134                    "option --$opt is ambiguous"
135                );
136            }
137
138            if (substr($long_opt, -1) == '=') {
139                if (substr($long_opt, -2) != '==') {
140                    if (!strlen($opt_arg)) {
141                        if (false === $opt_arg = current($args)) {
142                            throw new PHPUnit_Framework_Exception(
143                                "option --$opt requires an argument"
144                            );
145                        }
146                        next($args);
147                    }
148                }
149            } elseif ($opt_arg) {
150                throw new PHPUnit_Framework_Exception(
151                    "option --$opt doesn't allow an argument"
152                );
153            }
154
155            $full_option = '--' . preg_replace('/={1,2}$/', '', $long_opt);
156            $opts[]      = [$full_option, $opt_arg];
157
158            return;
159        }
160
161        throw new PHPUnit_Framework_Exception("unrecognized option --$opt");
162    }
163}
164
165