PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.3.2
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.3.2
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / mrclay / minify / lib / MrClay / Cli.php

Cli.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.3.2, at vendor/mrclay/minify/lib/MrClay/Cli.php

393 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace MrClay;
4
5 use MrClay\Cli\Arg;
6 use InvalidArgumentException;
7
8 /**
9 * Forms a front controller for a console app, handling and validating arguments (options)
10 *
11 * Instantiate, add arguments, then call validate(). Afterwards, the user's valid arguments
12 * and their values will be available in $cli->values.
13 *
14 * You may also specify that some arguments be used to provide input/output. By communicating
15 * solely through the file pointers provided by openInput()/openOutput(), you can make your
16 * app more flexible to end users.
17 *
18 * @author Steve Clay <[email protected]>
19 * @license http://www.opensource.org/licenses/mit-license.php MIT License
20 */
21 class Cli
22 {
23
24 /**
25 * @var array validation errors
26 */
27 public $errors = array();
28
29 /**
30 * @var array option values available after validation.
31 *
32 * E.g. array(
33 * 'a' => false // option was missing
34 * ,'b' => true // option was present
35 * ,'c' => "Hello" // option had value
36 * ,'f' => "/home/user/file" // file path from root
37 * ,'f.raw' => "~/file" // file path as given to option
38 * )
39 */
40 public $values = array();
41
42 /**
43 * @var array
44 */
45 public $moreArgs = array();
46
47 /**
48 * @var array
49 */
50 public $debug = array();
51
52 /**
53 * @var bool The user wants help info
54 */
55 public $isHelpRequest = false;
56
57 /**
58 * @var Arg[]
59 */
60 protected $_args = array();
61
62 /**
63 * @var resource
64 */
65 protected $_stdin = null;
66
67 /**
68 * @var resource
69 */
70 protected $_stdout = null;
71
72 /**
73 * @param bool $exitIfNoStdin (default true) Exit() if STDIN is not defined
74 */
75 public function __construct($exitIfNoStdin = true)
76 {
77 if ($exitIfNoStdin && ! defined('STDIN')) {
78 exit('This script is for command-line use only.');
79 }
80 if (isset($GLOBALS['argv'][1])
81 && ($GLOBALS['argv'][1] === '-?' || $GLOBALS['argv'][1] === '--help')) {
82 $this->isHelpRequest = true;
83 }
84 }
85
86 /**
87 * @param Arg|string $letter
88 * @return Arg
89 */
90 public function addOptionalArg($letter)
91 {
92 return $this->addArgument($letter, false);
93 }
94
95 /**
96 * @param Arg|string $letter
97 * @return Arg
98 */
99 public function addRequiredArg($letter)
100 {
101 return $this->addArgument($letter, true);
102 }
103
104 /**
105 * @param string $letter
106 * @param bool $required
107 * @param Arg|null $arg
108 * @return Arg
109 * @throws InvalidArgumentException
110 */
111 public function addArgument($letter, $required, Arg $arg = null)
112 {
113 if (! preg_match('/^[a-zA-Z]$/', $letter)) {
114 throw new InvalidArgumentException('$letter must be in [a-zA-Z]');
115 }
116 if (! $arg) {
117 $arg = new Arg($required);
118 }
119 $this->_args[$letter] = $arg;
120
121 return $arg;
122 }
123
124 /**
125 * @param string $letter
126 * @return Arg|null
127 */
128 public function getArgument($letter)
129 {
130 return isset($this->_args[$letter]) ? $this->_args[$letter] : null;
131 }
132
133 /*
134 * Read and validate options
135 *
136 * @return bool true if all options are valid
137 */
138 public function validate()
139 {
140 $options = '';
141 $this->errors = array();
142 $this->values = array();
143 $this->_stdin = null;
144
145 if ($this->isHelpRequest) {
146 return false;
147 }
148
149 $lettersUsed = '';
150 foreach ($this->_args as $letter => $arg) {
151 /* @var Arg $arg */
152 $options .= $letter;
153 $lettersUsed .= $letter;
154
155 if ($arg->mayHaveValue || $arg->mustHaveValue) {
156 $options .= ($arg->mustHaveValue ? ':' : '::');
157 }
158 }
159
160 $this->debug['argv'] = $GLOBALS['argv'];
161 $argvCopy = array_slice($GLOBALS['argv'], 1);
162 $o = getopt($options);
163 $this->debug['getopt_options'] = $options;
164 $this->debug['getopt_return'] = $o;
165
166 foreach ($this->_args as $letter => $arg) {
167 /* @var Arg $arg */
168 $this->values[$letter] = false;
169 if (isset($o[$letter])) {
170 if (is_bool($o[$letter])) {
171
172 // remove from argv copy
173 $k = array_search("-$letter", $argvCopy);
174 if ($k !== false) {
175 array_splice($argvCopy, $k, 1);
176 }
177
178 if ($arg->mustHaveValue) {
179 $this->addError($letter, "Missing value");
180 } else {
181 $this->values[$letter] = true;
182 }
183 } else {
184 // string
185 $this->values[$letter] = $o[$letter];
186 $v =& $this->values[$letter];
187
188 // remove from argv copy
189 // first look for -ovalue or -o=value
190 $pattern = "/^-{$letter}=?" . preg_quote($v, '/') . "$/";
191 $foundInArgv = false;
192 foreach ($argvCopy as $k => $argV) {
193 if (preg_match($pattern, $argV)) {
194 array_splice($argvCopy, $k, 1);
195 $foundInArgv = true;
196 break;
197 }
198 }
199 if (! $foundInArgv) {
200 // space separated
201 $k = array_search("-$letter", $argvCopy);
202 if ($k !== false) {
203 array_splice($argvCopy, $k, 2);
204 }
205 }
206
207 // check that value isn't really another option
208 if (strlen($lettersUsed) > 1) {
209 $pattern = "/^-[" . str_replace($letter, '', $lettersUsed) . "]/i";
210 if (preg_match($pattern, $v)) {
211 $this->addError($letter, "Value was read as another option: %s", $v);
212
213 return false;
214 }
215 }
216 if ($arg->assertFile || $arg->assertDir) {
217 if ($v[0] !== '/' && $v[0] !== '~') {
218 $this->values["$letter.raw"] = $v;
219 $v = getcwd() . "/$v";
220 }
221 }
222 if ($arg->assertFile) {
223 if ($arg->useAsInfile) {
224 $this->_stdin = $v;
225 } elseif ($arg->useAsOutfile) {
226 $this->_stdout = $v;
227 }
228 if ($arg->assertReadable && ! is_readable($v)) {
229 $this->addError($letter, "File not readable: %s", $v);
230 continue;
231 }
232 if ($arg->assertWritable) {
233 if (is_file($v)) {
234 if (! is_writable($v)) {
235 $this->addError($letter, "File not writable: %s", $v);
236 }
237 } else {
238 if (! is_writable(dirname($v))) {
239 $this->addError($letter, "Directory not writable: %s", dirname($v));
240 }
241 }
242 }
243 } elseif ($arg->assertDir && $arg->assertWritable && ! is_writable($v)) {
244 $this->addError($letter, "Directory not readable: %s", $v);
245 }
246 }
247 } else {
248 if ($arg->isRequired()) {
249 $this->addError($letter, "Missing");
250 }
251 }
252 }
253 $this->moreArgs = $argvCopy;
254 reset($this->moreArgs);
255
256 return empty($this->errors);
257 }
258
259 /**
260 * Get the full paths of file(s) passed in as unspecified arguments
261 *
262 * @return array
263 */
264 public function getPathArgs()
265 {
266 $r = $this->moreArgs;
267 foreach ($r as $k => $v) {
268 if ($v[0] !== '/' && $v[0] !== '~') {
269 $v = getcwd() . "/$v";
270 $v = str_replace('/./', '/', $v);
271 do {
272 $v = preg_replace('@/[^/]+/\\.\\./@', '/', $v, 1, $changed);
273 } while ($changed);
274 $r[$k] = $v;
275 }
276 }
277
278 return $r;
279 }
280
281 /**
282 * Get a short list of errors with options
283 *
284 * @return string
285 */
286 public function getErrorReport()
287 {
288 if (empty($this->errors)) {
289 return '';
290 }
291 $r = "Some arguments did not pass validation:\n";
292 foreach ($this->errors as $letter => $arr) {
293 $r .= " $letter : " . implode(', ', $arr) . "\n";
294 }
295 $r .= "\n";
296
297 return $r;
298 }
299
300 /**
301 * @return string
302 */
303 public function getArgumentsListing()
304 {
305 $r = "\n";
306 foreach ($this->_args as $letter => $arg) {
307 /* @var Arg $arg */
308 $desc = $arg->getDescription();
309 $flag = " -$letter ";
310 if ($arg->mayHaveValue) {
311 $flag .= "[VAL]";
312 } elseif ($arg->mustHaveValue) {
313 $flag .= "VAL";
314 }
315 if ($arg->assertFile) {
316 $flag = str_replace('VAL', 'FILE', $flag);
317 } elseif ($arg->assertDir) {
318 $flag = str_replace('VAL', 'DIR', $flag);
319 }
320 if ($arg->isRequired()) {
321 $desc = "(required) $desc";
322 }
323 $flag = str_pad($flag, 12, " ", STR_PAD_RIGHT);
324 $desc = wordwrap($desc, 70);
325 $r .= $flag . str_replace("\n", "\n ", $desc) . "\n\n";
326 }
327
328 return $r;
329 }
330
331 /**
332 * Get resource of open input stream. May be STDIN or a file pointer
333 * to the file specified by an option with 'STDIN'.
334 *
335 * @return resource
336 */
337 public function openInput()
338 {
339 if (null === $this->_stdin) {
340 return STDIN;
341 } else {
342 $this->_stdin = fopen($this->_stdin, 'rb');
343
344 return $this->_stdin;
345 }
346 }
347
348 public function closeInput()
349 {
350 if (null !== $this->_stdin) {
351 fclose($this->_stdin);
352 }
353 }
354
355 /**
356 * Get resource of open output stream. May be STDOUT or a file pointer
357 * to the file specified by an option with 'STDOUT'. The file will be
358 * truncated to 0 bytes on opening.
359 *
360 * @return resource
361 */
362 public function openOutput()
363 {
364 if (null === $this->_stdout) {
365 return STDOUT;
366 } else {
367 $this->_stdout = fopen($this->_stdout, 'wb');
368
369 return $this->_stdout;
370 }
371 }
372
373 public function closeOutput()
374 {
375 if (null !== $this->_stdout) {
376 fclose($this->_stdout);
377 }
378 }
379
380 /**
381 * @param string $letter
382 * @param string $msg
383 * @param string $value
384 */
385 protected function addError($letter, $msg, $value = null)
386 {
387 if ($value !== null) {
388 $value = var_export($value, 1);
389 }
390 $this->errors[$letter][] = sprintf($msg, $value);
391 }
392 }
393