1: <?php
2:
3: /*
4: * This file is part of the Symfony package.
5: *
6: * (c) Fabien Potencier <fabien@symfony.com>
7: *
8: * For the full copyright and license information, please view the LICENSE
9: * file that was distributed with this source code.
10: */
11:
12: namespace Symfony\Component\Finder\Shell;
13:
14: /**
15: * @author Jean-François Simon <contact@jfsimon.fr>
16: */
17: class Command
18: {
19: /**
20: * @var Command|null
21: */
22: private $parent;
23:
24: /**
25: * @var array
26: */
27: private $bits;
28:
29: /**
30: * @var array
31: */
32: private $labels;
33:
34: /**
35: * Constructor.
36: *
37: * @param Command $parent Parent command
38: */
39: public function __construct(Command $parent = null)
40: {
41: $this->parent = $parent;
42: $this->bits = array();
43: $this->labels = array();
44: }
45:
46: /**
47: * Returns command as string.
48: *
49: * @return string
50: */
51: public function __toString()
52: {
53: return $this->join();
54: }
55:
56: /**
57: * Creates a new Command instance.
58: *
59: * @param Command $parent Parent command
60: *
61: * @return Command New Command instance
62: */
63: public static function create(Command $parent = null)
64: {
65: return new self($parent);
66: }
67:
68: /**
69: * Escapes special chars from input.
70: *
71: * @param string $input A string to escape
72: *
73: * @return string The escaped string
74: */
75: public static function escape($input)
76: {
77: return escapeshellcmd($input);
78: }
79:
80: /**
81: * Quotes input.
82: *
83: * @param string $input An argument string
84: *
85: * @return string The quoted string
86: */
87: public static function quote($input)
88: {
89: return escapeshellarg($input);
90: }
91:
92: /**
93: * Appends a string or a Command instance.
94: *
95: * @param string|Command $bit
96: *
97: * @return Command The current Command instance
98: */
99: public function add($bit)
100: {
101: $this->bits[] = $bit;
102:
103: return $this;
104: }
105:
106: /**
107: * Prepends a string or a command instance.
108: *
109: * @param string|Command $bit
110: *
111: * @return Command The current Command instance
112: */
113: public function top($bit)
114: {
115: array_unshift($this->bits, $bit);
116:
117: foreach ($this->labels as $label => $index) {
118: $this->labels[$label] += 1;
119: }
120:
121: return $this;
122: }
123:
124: /**
125: * Appends an argument, will be quoted.
126: *
127: * @param string $arg
128: *
129: * @return Command The current Command instance
130: */
131: public function arg($arg)
132: {
133: $this->bits[] = self::quote($arg);
134:
135: return $this;
136: }
137:
138: /**
139: * Appends escaped special command chars.
140: *
141: * @param string $esc
142: *
143: * @return Command The current Command instance
144: */
145: public function cmd($esc)
146: {
147: $this->bits[] = self::escape($esc);
148:
149: return $this;
150: }
151:
152: /**
153: * Inserts a labeled command to feed later.
154: *
155: * @param string $label The unique label
156: *
157: * @return Command The current Command instance
158: *
159: * @throws \RuntimeException If label already exists
160: */
161: public function ins($label)
162: {
163: if (isset($this->labels[$label])) {
164: throw new \RuntimeException('Label "'.$label.'" already exists.');
165: }
166:
167: $this->bits[] = self::create($this);
168: $this->labels[$label] = count($this->bits)-1;
169:
170: return $this->bits[$this->labels[$label]];
171: }
172:
173: /**
174: * Retrieves a previously labeled command.
175: *
176: * @param string $label
177: *
178: * @return Command The labeled command
179: *
180: * @throws \RuntimeException
181: */
182: public function get($label)
183: {
184: if (!isset($this->labels[$label])) {
185: throw new \RuntimeException('Label "'.$label.'" does not exists.');
186: }
187:
188: return $this->bits[$this->labels[$label]];
189: }
190:
191: /**
192: * Returns parent command (if any).
193: *
194: * @return Command Parent command
195: *
196: * @throws \RuntimeException If command has no parent
197: */
198: public function end()
199: {
200: if (null === $this->parent) {
201: throw new \RuntimeException('Calling end on root command doesn\'t make sense.');
202: }
203:
204: return $this->parent;
205: }
206:
207: /**
208: * Counts bits stored in command.
209: *
210: * @return int The bits count
211: */
212: public function length()
213: {
214: return count($this->bits);
215: }
216:
217: /**
218: * Executes current command.
219: *
220: * @return array The command result
221: *
222: * @throws \RuntimeException
223: */
224: public function execute()
225: {
226: exec($this->join(), $output, $code);
227:
228: if (0 !== $code) {
229: throw new \RuntimeException('Execution failed with return code: '.$code.'.');
230: }
231:
232: return $output ?: array();
233: }
234:
235: /**
236: * Joins bits.
237: *
238: * @return string
239: */
240: public function join()
241: {
242: return implode(' ', array_filter(
243: array_map(function($bit) {
244: return $bit instanceof Command ? $bit->join() : ($bit ?: null);
245: }, $this->bits),
246: function($bit) { return null !== $bit; }
247: ));
248: }
249:
250: /**
251: * Insert a string or a Command instance before the bit at given position $index (index starts from 0).
252: *
253: * @param string|Command $bit
254: * @param integer $index
255: *
256: * @return Command The current Command instance
257: */
258: public function addAtIndex($bit, $index)
259: {
260: array_splice($this->bits, $index, 0, $bit);
261:
262: return $this;
263: }
264: }
265: