Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
#!/usr/bin/php -q
2
<?php
3
/* SVN FILE: $Id: cake.php 7945 2008-12-19 02:16:01Z gwoo $ */
4
/**
5
 * Command-line code generation utility to automate programmer chores.
6
 *
7
 * Shell dispatcher class
8
 *
9
 * PHP versions 4 and 5
10
 *
11
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
12
 * Copyright 2005-2008,	Cake Software Foundation, Inc.
13
 *
14
 * Licensed under The MIT License
15
 * Redistributions of files must retain the above copyright notice.
16
 *
17
 * @filesource
18
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
19
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
20
 * @package       cake
21
 * @subpackage    cake.cake.console
22
 * @since         CakePHP(tm) v 1.2.0.5012
23
 * @version       $Revision: 7945 $
24
 * @modifiedby    $LastChangedBy: gwoo $
25
 * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
26
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
27
 */
28
/**
29
 * Shell dispatcher
30
 *
31
 * @package       cake
32
 * @subpackage    cake.cake.console
33
 */
34
class ShellDispatcher {
35
/**
36
 * Standard input stream.
37
 *
38
 * @var filehandle
39
 * @access public
40
 */
41
	var $stdin;
42
/**
43
 * Standard output stream.
44
 *
45
 * @var filehandle
46
 * @access public
47
 */
48
	var $stdout;
49
/**
50
 * Standard error stream.
51
 *
52
 * @var filehandle
53
 * @access public
54
 */
55
	var $stderr;
56
/**
57
 * Contains command switches parsed from the command line.
58
 *
59
 * @var array
60
 * @access public
61
 */
62
	var $params = array();
63
/**
64
 * Contains arguments parsed from the command line.
65
 *
66
 * @var array
67
 * @access public
68
 */
69
	var $args = array();
70
/**
71
 * The file name of the shell that was invoked.
72
 *
73
 * @var string
74
 * @access public
75
 */
76
	var $shell = null;
77
/**
78
 * The class name of the shell that was invoked.
79
 *
80
 * @var string
81
 * @access public
82
 */
83
	var $shellClass = null;
84
/**
85
 * The command called if public methods are available.
86
 *
87
 * @var string
88
 * @access public
89
 */
90
	var $shellCommand = null;
91
/**
92
 * The path locations of shells.
93
 *
94
 * @var array
95
 * @access public
96
 */
97
	var $shellPaths = array();
98
/**
99
 * The path to the current shell location.
100
 *
101
 * @var string
102
 * @access public
103
 */
104
	var $shellPath = null;
105
/**
106
 * The name of the shell in camelized.
107
 *
108
 * @var string
109
 * @access public
110
 */
111
	var $shellName = null;
112
/**
113
 * Constructs this ShellDispatcher instance.
114
 *
115
 * @param array $args the argv.
116
 */
117
	function ShellDispatcher($args = array()) {
118
		$this->__construct($args);
119
	}
120
/**
121
 * Constructor
122
 *
123
 * @param array $args the argv.
124
 */
125
	function __construct($args = array()) {
126
		set_time_limit(0);
127
		$this->__initConstants();
128
		$this->parseParams($args);
129
		$this->_initEnvironment();
130
		$this->__buildPaths();
131
		$this->_stop($this->dispatch());
132
	}
133
/**
134
 * Defines core configuration.
135
 *
136
 * @access private
137
 */
138
	function __initConstants() {
139
		if (function_exists('ini_set')) {
140
			ini_set('display_errors', '1');
141
			ini_set('error_reporting', E_ALL);
142
			ini_set('html_errors', false);
143
			ini_set('implicit_flush', true);
144
			ini_set('max_execution_time', 0);
145
		}
146
 
147
		if (!defined('CAKE_CORE_INCLUDE_PATH')) {
148
			define('PHP5', (PHP_VERSION >= 5));
149
			define('DS', DIRECTORY_SEPARATOR);
150
			define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__))));
151
			define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
152
			define('DISABLE_DEFAULT_ERROR_HANDLING', false);
153
			define('CAKEPHP_SHELL', true);
154
		}
155
		require_once(CORE_PATH . 'cake' . DS . 'basics.php');
156
	}
157
/**
158
 * Defines current working environment.
159
 *
160
 * @access protected
161
 */
162
	function _initEnvironment() {
163
		$this->stdin = fopen('php://stdin', 'r');
164
		$this->stdout = fopen('php://stdout', 'w');
165
		$this->stderr = fopen('php://stderr', 'w');
166
 
167
		if (!$this->__bootstrap()) {
168
			$this->stderr("\nCakePHP Console: ");
169
			$this->stderr("\nUnable to load Cake core:");
170
			$this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH);
171
			$this->_stop();
172
		}
173
 
174
		if (!isset($this->args[0]) || !isset($this->params['working'])) {
175
			$this->stderr("\nCakePHP Console: ");
176
			$this->stderr('This file has been loaded incorrectly and cannot continue.');
177
			$this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,');
178
			$this->stderr('and check the manual for the correct usage of this command.');
179
			$this->stderr('(http://manual.cakephp.org/)');
180
			$this->_stop();
181
		}
182
 
183
		if (basename(__FILE__) !=  basename($this->args[0])) {
184
			$this->stderr("\nCakePHP Console: ");
185
			$this->stderr('Warning: the dispatcher may have been loaded incorrectly, which could lead to unexpected results...');
186
			if ($this->getInput('Continue anyway?', array('y', 'n'), 'y') == 'n') {
187
				$this->_stop();
188
			}
189
		}
190
 
191
		$this->shiftArgs();
192
	}
193
/**
194
 * Builds the shell paths.
195
 *
196
 * @access private
197
 * @return void
198
 */
199
	function __buildPaths() {
200
		$paths = array();
201
 
202
		$pluginPaths = Configure::read('pluginPaths');
203
		foreach ($pluginPaths as $pluginPath) {
204
			$plugins = Configure::listObjects('plugin', $pluginPath);
205
			foreach ((array)$plugins as $plugin) {
206
				$path = $pluginPath . Inflector::underscore($plugin) . DS . 'vendors' . DS . 'shells' . DS;
207
				if (file_exists($path)) {
208
					$paths[] = $path;
209
				}
210
			}
211
		}
212
 
213
		$vendorPaths = array_values(Configure::read('vendorPaths'));
214
		foreach ($vendorPaths as $vendorPath) {
215
			$path = rtrim($vendorPath, DS) . DS . 'shells' . DS;
216
			if (file_exists($path)) {
217
				$paths[] = $path;
218
			}
219
		}
220
 
221
		$this->shellPaths = array_values(array_unique(array_merge($paths, Configure::read('shellPaths'))));
222
	}
223
/**
224
 * Initializes the environment and loads the Cake core.
225
 *
226
 * @return boolean Success.
227
 * @access private
228
 */
229
	function __bootstrap() {
230
 
231
		define('ROOT', $this->params['root']);
232
		define('APP_DIR', $this->params['app']);
233
		define('APP_PATH', $this->params['working'] . DS);
234
		define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS);
235
 
236
		$includes = array(
237
			CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php',
238
			CORE_PATH . 'cake' . DS . 'libs' . DS . 'object.php',
239
			CORE_PATH . 'cake' . DS . 'libs' . DS . 'inflector.php',
240
			CORE_PATH . 'cake' . DS . 'libs' . DS . 'configure.php',
241
			CORE_PATH . 'cake' . DS . 'libs' . DS . 'file.php',
242
			CORE_PATH . 'cake' . DS . 'libs' . DS . 'cache.php',
243
			CORE_PATH . 'cake' . DS . 'libs' . DS . 'string.php',
244
			CORE_PATH . 'cake' . DS . 'libs' . DS . 'class_registry.php',
245
			CORE_PATH . 'cake' . DS . 'console' . DS . 'error.php'
246
		);
247
 
248
		foreach ($includes as $inc) {
249
			if (!require($inc)) {
250
				$this->stderr("Failed to load Cake core file {$inc}");
251
				return false;
252
			}
253
		}
254
 
255
		Configure::getInstance(file_exists(CONFIGS . 'bootstrap.php'));
256
 
257
		if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) {
258
			include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php';
259
			Configure::buildPaths(array());
260
		}
261
 
262
		Configure::write('debug', 1);
263
		return true;
264
	}
265
 
266
/**
267
 * Dispatches a CLI request
268
 *
269
 * @access public
270
 */
271
	function dispatch() {
272
		if (isset($this->args[0])) {
273
			$plugin = null;
274
			$shell = $this->args[0];
275
			if (strpos($shell, '.') !== false)  {
276
				list($plugin, $shell) = explode('.', $this->args[0]);
277
			}
278
 
279
			$this->shell = $shell;
280
			$this->shiftArgs();
281
			$this->shellName = Inflector::camelize($this->shell);
282
			$this->shellClass = $this->shellName . 'Shell';
283
 
284
			if ($this->shell === 'help') {
285
				$this->help();
286
			} else {
287
				$loaded = false;
288
				foreach ($this->shellPaths as $path) {
289
					$this->shellPath = $path . $this->shell . '.php';
290
 
291
					$isPlugin = ($plugin && strpos($path, DS . $plugin . DS . 'vendors' . DS . 'shells' . DS) !== false);
292
					if (($isPlugin && file_exists($this->shellPath)) || (!$plugin && file_exists($this->shellPath))) {
293
						$loaded = true;
294
						break;
295
					}
296
				}
297
 
298
				if ($loaded) {
299
					if (!class_exists('Shell')) {
300
						require CONSOLE_LIBS . 'shell.php';
301
					}
302
					require $this->shellPath;
303
					if (class_exists($this->shellClass)) {
304
						$command = null;
305
						if (isset($this->args[0])) {
306
							$command = $this->args[0];
307
						}
308
						$this->shellCommand = Inflector::variable($command);
309
						$shell = new $this->shellClass($this);
310
 
311
						if (strtolower(get_parent_class($shell)) == 'shell') {
312
							$shell->initialize();
313
							$shell->loadTasks();
314
 
315
							foreach ($shell->taskNames as $task) {
316
								if (strtolower(get_parent_class($shell)) == 'shell') {
317
									$shell->{$task}->initialize();
318
									$shell->{$task}->loadTasks();
319
								}
320
							}
321
 
322
							$task = Inflector::camelize($command);
323
							if (in_array($task, $shell->taskNames)) {
324
								$this->shiftArgs();
325
								$shell->{$task}->startup();
326
								if (isset($this->args[0]) && $this->args[0] == 'help') {
327
									if (method_exists($shell->{$task}, 'help')) {
328
										$shell->{$task}->help();
329
										$this->_stop();
330
									} else {
331
										$this->help();
332
									}
333
								}
334
								return $shell->{$task}->execute();
335
							}
336
						}
337
 
338
						$classMethods = get_class_methods($shell);
339
 
340
						$privateMethod = $missingCommand = false;
341
						if ((in_array($command, $classMethods) || in_array(strtolower($command), $classMethods)) && strpos($command, '_', 0) === 0) {
342
							$privateMethod = true;
343
						}
344
 
345
						if (!in_array($command, $classMethods) && !in_array(strtolower($command), $classMethods)) {
346
							$missingCommand = true;
347
						}
348
 
349
						$protectedCommands = array(
350
							'initialize','in','out','err','hr',
351
							'createfile', 'isdir','copydir','object','tostring',
352
							'requestaction','log','cakeerror', 'shelldispatcher',
353
							'__initconstants','__initenvironment','__construct',
354
							'dispatch','__bootstrap','getinput','stdout','stderr','parseparams','shiftargs'
355
						);
356
 
357
						if (in_array(strtolower($command), $protectedCommands)) {
358
							$missingCommand = true;
359
						}
360
 
361
						if ($missingCommand && method_exists($shell, 'main')) {
362
							$shell->startup();
363
							return $shell->main();
364
						} elseif (!$privateMethod && method_exists($shell, $command)) {
365
							$this->shiftArgs();
366
							$shell->startup();
367
							return $shell->{$command}();
368
						} else {
369
							$this->stderr("Unknown {$this->shellName} command '$command'.\nFor usage, try 'cake {$this->shell} help'.\n\n");
370
						}
371
					} else {
372
						$this->stderr('Class '.$this->shellClass.' could not be loaded');
373
					}
374
				} else {
375
					$this->help();
376
				}
377
			}
378
		} else {
379
			$this->help();
380
		}
381
	}
382
/**
383
 * Prompts the user for input, and returns it.
384
 *
385
 * @param string $prompt Prompt text.
386
 * @param mixed $options Array or string of options.
387
 * @param string $default Default input value.
388
 * @return Either the default value, or the user-provided input.
389
 * @access public
390
 */
391
	function getInput($prompt, $options = null, $default = null) {
392
		if (!is_array($options)) {
393
			$printOptions = '';
394
		} else {
395
			$printOptions = '(' . implode('/', $options) . ')';
396
		}
397
 
398
		if ($default == null) {
399
			$this->stdout($prompt . " $printOptions \n" . '> ', false);
400
		} else {
401
			$this->stdout($prompt . " $printOptions \n" . "[$default] > ", false);
402
		}
403
		$result = fgets($this->stdin);
404
 
405
		if ($result === false) {
406
			exit;
407
		}
408
		$result = trim($result);
409
 
410
		if ($default != null && empty($result)) {
411
			return $default;
412
		}
413
		return $result;
414
	}
415
/**
416
 * Outputs to the stdout filehandle.
417
 *
418
 * @param string $string String to output.
419
 * @param boolean $newline If true, the outputs gets an added newline.
420
 * @access public
421
 */
422
	function stdout($string, $newline = true) {
423
		if ($newline) {
424
			fwrite($this->stdout, $string . "\n");
425
		} else {
426
			fwrite($this->stdout, $string);
427
		}
428
	}
429
/**
430
 * Outputs to the stderr filehandle.
431
 *
432
 * @param string $string Error text to output.
433
 * @access public
434
 */
435
	function stderr($string) {
436
		fwrite($this->stderr, 'Error: '. $string);
437
	}
438
/**
439
 * Parses command line options
440
 *
441
 * @param array $params Parameters to parse
442
 * @access public
443
 */
444
	function parseParams($params) {
445
		$this->__parseParams($params);
446
 
447
		$defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot');
448
 
449
		$params = array_merge($defaults, array_intersect_key($this->params, $defaults));
450
 
451
		$isWin = array_filter(array_map('strpos', $params, array('\\')));
452
 
453
		$params = str_replace('\\', '/', $params);
454
 
455
		if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) {
456
			if (empty($this->params['app']) && $params['working'] != $params['root']) {
457
				$params['root'] = dirname($params['working']);
458
				$params['app'] = basename($params['working']);
459
			} else {
460
				$params['root'] = $params['working'];
461
			}
462
		}
463
 
464
		if ($params['app'][0] == '/' || preg_match('/([a-zA-Z])(:)/i', $params['app'], $matches)) {
465
			$params['root'] = dirname($params['app']);
466
		} elseif (strpos($params['app'], '/')) {
467
			$params['root'] .= '/' . dirname($params['app']);
468
		}
469
 
470
		$params['app'] = basename($params['app']);
471
		$params['working'] = rtrim($params['root'], '/') . '/' . $params['app'];
472
 
473
		if (!empty($matches[0]) || !empty($isWin)) {
474
			$params = str_replace('/', '\\', $params);
475
		}
476
 
477
		$this->params = array_merge($this->params, $params);
478
	}
479
/**
480
 * Helper for recursively paraing params
481
 *
482
 * @return array params
483
 * @access private
484
 */
485
	function __parseParams($params) {
486
		$count = count($params);
487
		for ($i = 0; $i < $count; $i++) {
488
			if (isset($params[$i])) {
489
				if ($params[$i]{0} === '-') {
490
					$key = substr($params[$i], 1);
491
					$this->params[$key] = true;
492
					unset($params[$i]);
493
					if (isset($params[++$i])) {
494
						if ($params[$i]{0} !== '-') {
495
							$this->params[$key] = str_replace('"', '', $params[$i]);
496
							unset($params[$i]);
497
						} else {
498
							$i--;
499
							$this->__parseParams($params);
500
						}
501
					}
502
				} else {
503
					$this->args[] = $params[$i];
504
					unset($params[$i]);
505
				}
506
 
507
			}
508
		}
509
	}
510
/**
511
 * Removes first argument and shifts other arguments up
512
 *
513
 * @return boolean False if there are no arguments
514
 * @access public
515
 */
516
	function shiftArgs() {
517
		if (empty($this->args)) {
518
			return false;
519
		}
520
		unset($this->args[0]);
521
		$this->args = array_values($this->args);
522
		return true;
523
	}
524
/**
525
 * Shows console help
526
 *
527
 * @access public
528
 */
529
	function help() {
530
		$this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console");
531
		$this->stdout("---------------------------------------------------------------");
532
		$this->stdout("Current Paths:");
533
		$this->stdout(" -app: ". $this->params['app']);
534
		$this->stdout(" -working: " . rtrim($this->params['working'], DS));
535
		$this->stdout(" -root: " . rtrim($this->params['root'], DS));
536
		$this->stdout(" -core: " . rtrim(CORE_PATH, DS));
537
		$this->stdout("");
538
		$this->stdout("Changing Paths:");
539
		$this->stdout("your working path should be the same as your application path");
540
		$this->stdout("to change your path use the '-app' param.");
541
		$this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp");
542
 
543
		$this->stdout("\nAvailable Shells:");
544
		$_shells = array();
545
		foreach ($this->shellPaths as $path) {
546
			if (is_dir($path)) {
547
				$shells = Configure::listObjects('file', $path);
548
				$path = str_replace(CORE_PATH, 'CORE/', $path);
549
				$path = str_replace(ROOT, 'ROOT', $path);
550
				$path = rtrim($path, DS);
551
				$this->stdout("\n " . $path . ":");
552
				if (empty($shells)) {
553
					$this->stdout("\t - none");
554
				} else {
555
					foreach ($shells as $shell) {
556
						if ($shell !== 'shell.php') {
557
							$this->stdout("\t " . str_replace('.php', '', $shell));
558
						}
559
					}
560
				}
561
			}
562
		}
563
		$this->stdout("\nTo run a command, type 'cake shell_name [args]'");
564
		$this->stdout("To get help on a specific command, type 'cake shell_name help'");
565
		$this->_stop();
566
	}
567
/**
568
 * Stop execution of the current script
569
 *
570
 * @param $status see http://php.net/exit for values
571
 * @return void
572
 * @access protected
573
 */
574
	function _stop($status = 0) {
575
		exit($status);
576
	}
577
}
578
if (!defined('DISABLE_AUTO_DISPATCH')) {
579
	$dispatcher = new ShellDispatcher($argv);
580
}
581
?>