1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
18:
19: 20: 21: 22: 23:
24: class ShellDispatcher {
25:
26: 27: 28: 29: 30:
31: public $params = array();
32:
33: 34: 35: 36: 37:
38: public $args = array();
39:
40: 41: 42: 43: 44: 45: 46: 47: 48:
49: public function __construct($args = array(), $bootstrap = true) {
50: set_time_limit(0);
51:
52: if ($bootstrap) {
53: $this->_initConstants();
54: }
55: $this->parseParams($args);
56: if ($bootstrap) {
57: $this->_initEnvironment();
58: }
59: }
60:
61: 62: 63: 64: 65: 66:
67: public static function run($argv) {
68: $dispatcher = new ShellDispatcher($argv);
69: $dispatcher->_stop($dispatcher->dispatch() === false ? 1 : 0);
70: }
71:
72: 73: 74: 75: 76:
77: protected function _initConstants() {
78: if (function_exists('ini_set')) {
79: ini_set('html_errors', false);
80: ini_set('implicit_flush', true);
81: ini_set('max_execution_time', 0);
82: }
83:
84: if (!defined('CAKE_CORE_INCLUDE_PATH')) {
85: define('DS', DIRECTORY_SEPARATOR);
86: define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__))));
87: define('CAKEPHP_SHELL', true);
88: if (!defined('CORE_PATH')) {
89: define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
90: }
91: }
92: }
93:
94: 95: 96: 97: 98: 99:
100: protected function _initEnvironment() {
101: if (!$this->_bootstrap()) {
102: $message = "Unable to load CakePHP core.\nMake sure " . DS . 'lib' . DS . 'Cake exists in ' . CAKE_CORE_INCLUDE_PATH;
103: throw new CakeException($message);
104: }
105:
106: if (!isset($this->args[0]) || !isset($this->params['working'])) {
107: $message = "This file has been loaded incorrectly and cannot continue.\n" .
108: "Please make sure that " . DS . 'lib' . DS . 'Cake' . DS . "Console is in your system path,\n" .
109: "and check the cookbook for the correct usage of this command.\n" .
110: "(http://book.cakephp.org/)";
111: throw new CakeException($message);
112: }
113:
114: $this->shiftArgs();
115: }
116:
117: 118: 119: 120: 121:
122: protected function _bootstrap() {
123: define('ROOT', $this->params['root']);
124: define('APP_DIR', $this->params['app']);
125: define('APP', $this->params['working'] . DS);
126: define('WWW_ROOT', APP . $this->params['webroot'] . DS);
127: if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) {
128: define('TMP', CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'tmp' . DS);
129: }
130: $boot = file_exists(ROOT . DS . APP_DIR . DS . 'Config' . DS . 'bootstrap.php');
131: require CORE_PATH . 'Cake' . DS . 'bootstrap.php';
132:
133: if (!file_exists(APP . 'Config' . DS . 'core.php')) {
134: include_once CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'Config' . DS . 'core.php';
135: App::build();
136: }
137:
138: $this->setErrorHandlers();
139:
140: if (!defined('FULL_BASE_URL')) {
141: define('FULL_BASE_URL', 'http://localhost');
142: }
143:
144: return true;
145: }
146:
147: 148: 149: 150: 151: 152: 153: 154:
155: public function setErrorHandlers() {
156: App::uses('ConsoleErrorHandler', 'Console');
157: $error = Configure::read('Error');
158: $exception = Configure::read('Exception');
159:
160: $errorHandler = new ConsoleErrorHandler();
161: if (empty($error['consoleHandler'])) {
162: $error['consoleHandler'] = array($errorHandler, 'handleError');
163: Configure::write('Error', $error);
164: }
165: if (empty($exception['consoleHandler'])) {
166: $exception['consoleHandler'] = array($errorHandler, 'handleException');
167: Configure::write('Exception', $exception);
168: }
169: set_exception_handler($exception['consoleHandler']);
170: set_error_handler($error['consoleHandler'], Configure::read('Error.level'));
171: }
172:
173: 174: 175: 176: 177: 178:
179: public function dispatch() {
180: $shell = $this->shiftArgs();
181:
182: if (!$shell) {
183: $this->help();
184: return false;
185: }
186: if (in_array($shell, array('help', '--help', '-h'))) {
187: $this->help();
188: return true;
189: }
190:
191: $Shell = $this->_getShell($shell);
192:
193: $command = null;
194: if (isset($this->args[0])) {
195: $command = $this->args[0];
196: }
197:
198: if ($Shell instanceof Shell) {
199: $Shell->initialize();
200: $Shell->loadTasks();
201: return $Shell->runCommand($command, $this->args);
202: }
203: $methods = array_diff(get_class_methods($Shell), get_class_methods('Shell'));
204: $added = in_array($command, $methods);
205: $private = $command[0] == '_' && method_exists($Shell, $command);
206:
207: if (!$private) {
208: if ($added) {
209: $this->shiftArgs();
210: $Shell->startup();
211: return $Shell->{$command}();
212: }
213: if (method_exists($Shell, 'main')) {
214: $Shell->startup();
215: return $Shell->main();
216: }
217: }
218:
219: throw new MissingShellMethodException(array('shell' => $shell, 'method' => $command));
220: }
221:
222: 223: 224: 225: 226: 227: 228: 229: 230:
231: protected function _getShell($shell) {
232: list($plugin, $shell) = pluginSplit($shell, true);
233:
234: $plugin = Inflector::camelize($plugin);
235: $class = Inflector::camelize($shell) . 'Shell';
236:
237: App::uses('Shell', 'Console');
238: App::uses('AppShell', 'Console/Command');
239: App::uses($class, $plugin . 'Console/Command');
240:
241: if (!class_exists($class)) {
242: throw new MissingShellException(array(
243: 'class' => $class
244: ));
245: }
246: $Shell = new $class();
247: $Shell->plugin = trim($plugin, '.');
248: return $Shell;
249: }
250:
251: 252: 253: 254: 255: 256:
257: public function parseParams($args) {
258: $this->_parsePaths($args);
259:
260: $defaults = array(
261: 'app' => 'app',
262: 'root' => dirname(dirname(dirname(dirname(__FILE__)))),
263: 'working' => null,
264: 'webroot' => 'webroot'
265: );
266: $params = array_merge($defaults, array_intersect_key($this->params, $defaults));
267: $isWin = false;
268: foreach ($defaults as $default => $value) {
269: if (strpos($params[$default], '\\') !== false) {
270: $isWin = true;
271: break;
272: }
273: }
274: $params = str_replace('\\', '/', $params);
275:
276: if (isset($params['working'])) {
277: $params['working'] = trim($params['working']);
278: }
279: if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) {
280: if (empty($this->params['app']) && $params['working'] != $params['root']) {
281: $params['root'] = dirname($params['working']);
282: $params['app'] = basename($params['working']);
283: } else {
284: $params['root'] = $params['working'];
285: }
286: }
287:
288: if ($params['app'][0] == '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) {
289: $params['root'] = dirname($params['app']);
290: } elseif (strpos($params['app'], '/')) {
291: $params['root'] .= '/' . dirname($params['app']);
292: }
293:
294: $params['app'] = basename($params['app']);
295: $params['working'] = rtrim($params['root'], '/');
296: if (!$isWin || !preg_match('/^[A-Z]:$/i', $params['app'])) {
297: $params['working'] .= '/' . $params['app'];
298: }
299:
300: if (!empty($matches[0]) || !empty($isWin)) {
301: $params = str_replace('/', '\\', $params);
302: }
303:
304: $this->params = array_merge($this->params, $params);
305: }
306:
307: 308: 309: 310: 311: 312:
313: protected function _parsePaths($args) {
314: $parsed = array();
315: $keys = array('-working', '--working', '-app', '--app', '-root', '--root');
316: foreach ($keys as $key) {
317: while (($index = array_search($key, $args)) !== false) {
318: $keyname = str_replace('-', '', $key);
319: $valueIndex = $index + 1;
320: $parsed[$keyname] = $args[$valueIndex];
321: array_splice($args, $index, 2);
322: }
323: }
324: $this->args = $args;
325: $this->params = $parsed;
326: }
327:
328: 329: 330: 331: 332:
333: public function shiftArgs() {
334: return array_shift($this->args);
335: }
336:
337: 338: 339: 340: 341:
342: public function help() {
343: $this->args = array_merge(array('command_list'), $this->args);
344: $this->dispatch();
345: }
346:
347: 348: 349: 350: 351: 352:
353: protected function _stop($status = 0) {
354: exit($status);
355: }
356:
357: }
358: