xref: /PHP-7.0/ext/phar/phar/clicommand.inc (revision 1b9e0de2)
1<?php
2
3/** @file clicommand.inc
4 * @ingroup Phar
5 * @brief class CLICommand
6 * @author  Marcus Boerger
7 * @date    2007 - 2008
8 *
9 * Phar Command
10 */
11
12/** @ingroup Phar
13 * @brief   Abstract base console command implementation
14 * @author  Marcus Boerger
15 * @version 1.0
16 */
17abstract class CLICommand
18{
19	protected $argc;
20	protected $argv;
21	protected $cmds = array();
22	protected $args = array();
23	protected $typs = array();
24
25	function __construct($argc, array $argv)
26	{
27		$this->argc = $argc;
28		$this->argv = $argv;
29		$this->cmds = self::getCommands($this);
30		$this->typs = self::getArgTyps($this);
31
32		if ($argc < 2) {
33			self::error("No command given, check ${argv[0]} help\n");
34		} elseif (!isset($this->cmds[$argv[1]]['run'])) {
35			self::error("Unknown command '${argv[1]}', check ${argv[0]} help\n");
36		} else {
37			$command = $argv[1];
38		}
39
40		if (isset($this->cmds[$command]['arg'])) {
41			$this->args = call_user_func(array($this, $this->cmds[$command]['arg']));
42			$i = 1;
43			$missing = false;
44			while (++$i < $argc) {
45				if ($argv[$i][0] == '-') {
46					if (strlen($argv[$i]) == 2 && isset($this->args[$argv[$i][1]])) {
47						$arg = $argv[$i][1];
48						if (++$i >= $argc) {
49							self::error("Missing argument to parameter '$arg' of command '$command', check ${argv[0]} help\n");
50						} else {
51							$this->args[$arg]['val'] = $this->checkArgTyp($arg, $i, $argc, $argv);
52						}
53					}  else {
54						self::error("Unknown parameter '${argv[$i]}' to command $command, check ${argv[0]} help\n");
55					}
56				} else {
57					break;
58				}
59			}
60
61			if (isset($this->args[''])) {
62				if ($i >= $argc) {
63					if (isset($this->args['']['require']) && $this->args['']['require']) {
64						self::error("Missing default trailing arguments to command $command, check ${argv[0]} help\n");
65					}
66				} else {
67					$this->args['']['val'] = array();
68					while($i < $argc) {
69						$this->args['']['val'][] = $argv[$i++];
70					}
71				}
72			} else if ($i < $argc) {
73				self::error("Unexpected default arguments to command $command, check ${argv[0]} help\n");
74			}
75
76			foreach($this->args as $arg => $inf) {
77				if (strlen($arg) && !isset($inf['val']) && isset($inf['required']) && $inf['required']) {
78					$missing .=  "Missing parameter '-$arg' to command $command, check ${argv[0]} help\n";
79				}
80			}
81
82			if (strlen($missing)) {
83				self::error($missing);
84			}
85		}
86
87		call_user_func(array($this, $this->cmds[$command]['run']), $this->args);
88	}
89
90	static function notice ($msg)
91	{
92		fprintf(STDERR, $msg);
93	}
94
95	static function error ($msg, $exit_code = 1)
96	{
97		self::notice($msg);
98		exit($exit_code);
99	}
100
101	function checkArgTyp($arg, $i, $argc, $argv)
102	{
103		$typ = $this->args[$arg]['typ'];
104
105		if (isset($this->typs[$typ]['typ'])) {
106			return call_user_func(array($this, $this->typs[$typ]['typ']), $argv[$i], $this->args[$arg], $arg);
107		} else {
108			return $argv[$i];
109		}
110	}
111
112	static function getSubFuncs(CLICommand $cmdclass, $prefix, array $subs)
113	{
114		$a = array();
115		$r = new ReflectionClass($cmdclass);
116		$l = strlen($prefix);
117
118		foreach($r->getMethods() as $m) {
119			if (substr($m->name, 0, $l) == $prefix) {
120				foreach($subs as $sub) {
121					$what = substr($m->name, $l+strlen($sub)+1);
122					$func = $prefix . $sub . '_' . $what;
123					$what = str_replace('_', '-', $what);
124					if ($r->hasMethod($func)) {
125						if (!isset($a[$what])) {
126							$a[$what] = array();
127						}
128						$a[$what][$sub] = /*$m->class . '::' .*/ $func;
129					}
130				}
131			}
132		}
133		return $a;
134	}
135
136	static function getCommands(CLICommand $cmdclass)
137	{
138		return self::getSubFuncs($cmdclass, 'cli_cmd_', array('arg','inf','run'));
139	}
140
141	static function getArgTyps(CLICommand $cmdclass)
142	{
143		return self::getSubFuncs($cmdclass, 'cli_arg_', array('typ'));
144	}
145
146	static function cli_arg_typ_bool($arg, $cfg, $key)
147	{
148		return (bool)$arg;
149	}
150
151	static function cli_arg_typ_int($arg, $cfg, $key)
152	{
153		if ((int)$arg != $arg) {
154			self::error("Argument to -$key must be an integer.\n");
155		}
156
157		return (int)$arg;
158	}
159
160	static function cli_arg_typ_regex($arg, $cfg, $key)
161	{
162		if (strlen($arg)) {
163			if (strlen($arg) > 1 && $arg[0] == $arg[strlen($arg)-1] && strpos('/,', $arg) !== false) {
164				return $arg;
165			} else {
166				return '/' . $arg . '/';
167			}
168		} else {
169			return NULL;
170		}
171	}
172
173	static function cli_arg_typ_select($arg, $cfg, $key)
174	{
175		if (!in_array($arg, array_keys($cfg['select']))) {
176			self::error("Parameter value '$arg' not one of '" . join("', '", array_keys($cfg['select'])) . "'.\n");
177		}
178		return $arg;
179	}
180
181	static function cli_arg_typ_dir($arg, $cfg, $key)
182	{
183		$f = realpath($arg);
184
185		if ($f===false || !file_exists($f) || !is_dir($f)) {
186			self::error("Requested path '$arg' does not exist.\n");
187		}
188		return $f;
189	}
190
191	static function cli_arg_typ_file($arg)
192	{
193		$f = new SplFileInfo($arg);
194		$f = $f->getRealPath();
195		if ($f===false || !file_exists($f)) {
196			echo "Requested file '$arg' does not exist.\n";
197			exit(1);
198		}
199		return $f;
200	}
201
202	static function cli_arg_typ_filenew($arg, $cfg, $key)
203	{
204		$d = dirname($arg);
205		$f = realpath($d);
206
207		if ($f === false) {
208			self::error("Path for file '$arg' does not exist.\n");
209		}
210		return $f . '/' . basename($arg);
211	}
212
213	static function cli_arg_typ_filecont($arg, $cfg, $key)
214	{
215		return file_get_contents(self::cli_arg_typ_file($arg, $cfg, $key));
216	}
217
218	function cli_get_SP2($l1, $arg_inf)
219	{
220		return str_repeat(' ', $l1 + 2 + 4 + 8);
221	}
222
223	function cli_get_SP3($l1, $l2, $arg_inf)
224	{
225		return str_repeat(' ', $l1 + 2 + 4 + 8 + 2 + $l2 + 2);
226	}
227
228	static function cli_cmd_inf_help()
229	{
230		return "This help or help for a selected command.";
231	}
232
233	private function cli_wordwrap($what, $l, $sp)
234	{
235		$p = max(79 - $l, 40);     // minimum length for paragraph
236		$b = substr($what, 0, $l); // strip out initial $l
237		$r = substr($what, $l);    // remainder
238		$r = str_replace("\n", "\n".$sp, $r); // in remainder replace \n's
239		return $b . wordwrap($r, $p, "\n".$sp);
240	}
241
242	private function cli_help_get_args($func, $l, $sp, $required)
243	{
244		$inf = "";
245		foreach(call_user_func($func, $l, $sp) as $arg => $conf) {
246			if ((isset($conf['required']) && $conf['required']) != $required) {
247				continue;
248			}
249
250			if (strlen($arg)) {
251				$arg = "-$arg  ";
252			} else {
253				$arg = "... ";
254			}
255
256			$sp2 = $this->cli_get_SP2($l, $inf);
257			$l2  = strlen($sp2);
258			$inf .= $this->cli_wordwrap($sp . $arg . $conf['inf'], $l2, $sp2) . "\n";
259
260			if (isset($conf['select']) && count($conf['select'])) {
261				$ls = 0;
262				foreach($conf['select'] as $opt => $what) {
263					$ls = max($ls, strlen($opt));
264				}
265				$sp3 = $this->cli_get_SP3($l, $ls, $inf);
266				$l3  = strlen($sp3);
267				foreach($conf['select'] as $opt => $what) {
268					$inf .= $this->cli_wordwrap($sp2 . "  " . sprintf("%-${ls}s  ", $opt) . $what, $l3, $sp3) . "\n";
269				}
270			}
271		}
272		if (strlen($inf)) {
273			if ($required) {
274				return $sp . "Required arguments:\n\n" . $inf;
275			} else {
276				return $sp . "Optional arguments:\n\n". $inf;
277			}
278		}
279	}
280
281	function cli_cmd_arg_help()
282	{
283		return array('' => array('typ'=>'any','val'=>NULL,'inf'=>'Optional command to retrieve help for.'));
284	}
285
286	function cli_cmd_run_help()
287	{
288		$argv  = $this->argv;
289		$which = $this->args['']['val'];
290		if (isset($which)) {
291			if (count($which) != 1) {
292				self::error("More than one command given.\n");
293			}
294
295			$which = $which[0];
296			if (!array_key_exists($which, $this->cmds)) {
297				if (strtolower($which) == 'commands') {
298					self::cli_cmd_run_help_list();
299					exit(0);
300				}
301				self::error("Unknown command, cannot retrieve help.\n");
302			}
303
304			$l = strlen($which);
305			$cmds = array($which => $this->cmds[$which]);
306		} else {
307			echo "\n$argv[0] <command> [options]\n\n";
308			$l = 0;
309			ksort($this->cmds);
310			foreach($this->cmds as $name => $funcs) {
311				$l = max($l, strlen($name));
312			}
313			$inf = "Commands:";
314			$lst = "";
315			$ind = strlen($inf) + 1;
316			foreach($this->cmds as $name => $funcs) {
317				$lst .= ' ' . $name;
318			}
319			echo $this->cli_wordwrap($inf.$lst, $ind, str_repeat(' ', $ind)) . "\n\n";
320			$cmds = $this->cmds;
321		}
322		$sp = str_repeat(' ', $l + 2);
323		foreach($cmds as $name => $funcs) {
324			$inf = $name . substr($sp, strlen($name));
325			if (isset($funcs['inf'])) {
326				$inf .= $this->cli_wordwrap(call_user_func(array($this, $funcs['inf'])), $l, $sp) . "\n";
327				if (isset($funcs['arg'])) {
328					$inf .= "\n";
329					$inf .= $this->cli_help_get_args(array($this, $funcs['arg']), $l, $sp, true);
330					$inf .= "\n";
331					$inf .= $this->cli_help_get_args(array($this, $funcs['arg']), $l, $sp, false);
332				}
333			}
334			echo "$inf\n\n";
335		}
336		exit(0);
337	}
338
339	static function cli_cmd_inf_help_list()
340	{
341		return "Lists available commands.";
342	}
343
344	function cli_cmd_run_help_list()
345	{
346		ksort($this->cmds);
347		echo join(' ', array_keys($this->cmds)) . "\n";
348	}
349}
350
351?>
352