1: <?php
2:
3: /**
4: * Jyxo PHP Library
5: *
6: * LICENSE
7: *
8: * This source file is subject to the new BSD license that is bundled
9: * with this package in the file license.txt.
10: * It is also available through the world-wide-web at this URL:
11: * https://github.com/jyxo/php/blob/master/license.txt
12: */
13:
14: namespace Jyxo\Shell;
15:
16: /**
17: * Class for executing external commands.
18: *
19: * @category Jyxo
20: * @package Jyxo\Shell
21: * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
22: * @license https://github.com/jyxo/php/blob/master/license.txt
23: * @author Ondřej Procházka
24: * @author Matěj Humpál
25: */
26: class Client
27: {
28: /**
29: * List of running processes.
30: *
31: * @var array
32: */
33: protected $processList;
34:
35: /**
36: * Actual working directory.
37: *
38: * @var string
39: */
40: protected $cwd;
41:
42: /**
43: * Environment properties.
44: *
45: * @var array
46: */
47: protected $env = array();
48:
49: /**
50: * Stdout output.
51: *
52: * @var string
53: */
54: protected $out;
55:
56: /**
57: * Stderr output.
58: *
59: * @var string
60: */
61: protected $error;
62:
63: /**
64: * Constructor.
65: *
66: * @param string $cwd Working directory
67: * @param array $env Array of environment properties
68: */
69: public function __construct($cwd = '', array $env = array())
70: {
71: $this->setCwd($cwd);
72: $this->env = $_ENV;
73: $this->setEnv($env);
74: }
75:
76: /**
77: * Returns a list of processes.
78: *
79: * Works only on Linux.
80: *
81: * @return \Jyxo\Shell\Client
82: */
83: public function loadProcessList()
84: {
85: $output = shell_exec('ps aux');
86:
87: $data = explode("\n", $output);
88:
89: foreach ($data as $value) {
90: $value = preg_replace('/ +/', ' ', $value);
91: $list = explode(' ', $value);
92: $commands[$list[10]][] = $list[1];
93: }
94:
95: $this->processList = $commands;
96:
97: return $this;
98: }
99:
100: /**
101: * Checks if there is a process of the given name.
102: *
103: * Works only on Linux.
104: *
105: * @param string $name Process name
106: * @return boolean
107: */
108: public function processExists($name)
109: {
110: return array_key_exists((string) $name, $this->processList);
111: }
112:
113: /**
114: * Kills all processes of the given name.
115: *
116: * Works only on Linux.
117: *
118: * @param string $name Process name
119: * @return \Jyxo\Shell\Client
120: */
121: public function killProcess($name)
122: {
123: shell_exec('killall -s KILL ' . (string) $name);
124:
125: return $this;
126: }
127:
128: /**
129: * Sets working directory.
130: *
131: * Defaults to null.
132: *
133: * @param string $cwd Working directory
134: * @return \Jyxo\Shell\Client
135: */
136: public function setCwd($cwd = '')
137: {
138: $this->cwd = (string) $cwd;
139:
140: return $this;
141: }
142:
143: /**
144: * Adds one or more environment properties.
145: *
146: * @param array $env Array of properties
147: * @return \Jyxo\Shell\Client
148: */
149: public function setEnv(array $env)
150: {
151: $this->env = array_merge($this->env, $env);
152:
153: return $this;
154: }
155:
156: /**
157: * Removes environment properties.
158: *
159: * @return \Jyxo\Shell\Client
160: */
161: public function clearEnv()
162: {
163: $this->env = $_ENV;
164:
165: return $this;
166: }
167:
168: /**
169: * Executes an external command.
170: *
171: * Captures stdout and stderr.
172: * Throws an exception on status code != 0.
173: *
174: * @param string $cmd Command to execute
175: * @param integer $status Status code
176: * @return \Jyxo\Shell\Client
177: * @throws \Jyxo\Shell\Exception On execution error
178: */
179: public function exec($cmd, &$status = null)
180: {
181: static $descriptorSpec = array(
182: 0 => array('pipe', 'r'),
183: 1 => array('pipe', 'w'),
184: 2 => array('pipe', 'w')
185: );
186:
187: $env = $this->env;
188: if (ini_get('safe_mode')) {
189: // If the safe_mode is set on, we have to check which properties we are allowed to set.
190:
191: $allowedPrefixes = explode(',', ini_get('safe_mode_allowed_env_vars'));
192: $protectedVars = explode(',', ini_get('safe_mode_protected_env_vars'));
193:
194: // Throw away protected properties.
195: $env = array_diff_key($env, array_fill_keys($protectedVars, true));
196:
197: // Throw away properties that do not have the allowed prefix.
198: foreach ($env as $name => $value) {
199: foreach ($allowedPrefixes as $prefix) {
200: // Empty prefix - allow all properties.
201: if ($prefix === '') {
202: break 2;
203: }
204:
205: if (substr($name, 0, strlen($prefix)) == $prefix) {
206: continue 2;
207: }
208: }
209:
210: unset($env[$name]);
211: }
212: }
213:
214: $cmd = (string) $cmd;
215: $process = proc_open($cmd, $descriptorSpec, $pipes, !empty($this->cwd) ? $this->cwd : null, !empty($env) ? $env : null);
216:
217: if (!is_resource($process)) {
218: throw new Exception('Unable to start shell process.');
219: }
220:
221: $this->out = stream_get_contents($pipes[1]);
222: fclose($pipes[1]);
223:
224: $this->error = stream_get_contents($pipes[2]);
225: fclose($pipes[2]);
226:
227: $status = proc_close($process);
228:
229: if ($status !== 0) {
230: throw new Exception(
231: 'Command ' . $cmd . ' returned code ' . $status
232: . '. Output: ' . $this->error
233: );
234: }
235:
236: return $this;
237: }
238:
239: /**
240: * Returns stdout contents.
241: *
242: * @return string
243: */
244: public function getOut()
245: {
246: return $this->out;
247: }
248:
249: /**
250: * Returns stderr contents.
251: *
252: * @return string
253: */
254: public function getError()
255: {
256: return $this->error;
257: }
258: }
259: