pictcode / lib / Cake / Console / Command / ConsoleShell.php @ 635eef61
履歴 | 表示 | アノテート | ダウンロード (12.997 KB)
1 | 635eef61 | spyder1211 | <?php
|
---|---|---|---|
2 | /**
|
||
3 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||
4 | * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
5 | *
|
||
6 | * Licensed under The MIT License
|
||
7 | * For full copyright and license information, please see the LICENSE.txt
|
||
8 | * Redistributions of files must retain the above copyright notice.
|
||
9 | *
|
||
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
11 | * @link http://cakephp.org CakePHP(tm) Project
|
||
12 | * @since CakePHP(tm) v 1.2.0.5012
|
||
13 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||
14 | */
|
||
15 | |||
16 | App::uses('AppShell', 'Console/Command'); |
||
17 | |||
18 | /**
|
||
19 | * Provides a very basic 'interactive' console for CakePHP apps.
|
||
20 | *
|
||
21 | * @package Cake.Console.Command
|
||
22 | * @deprecated 3.0.0 Deprecated since version 2.4, will be removed in 3.0
|
||
23 | */
|
||
24 | class ConsoleShell extends AppShell { |
||
25 | |||
26 | /**
|
||
27 | * Available binding types
|
||
28 | *
|
||
29 | * @var array
|
||
30 | */
|
||
31 | public $associations = array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany'); |
||
32 | |||
33 | /**
|
||
34 | * Chars that describe invalid commands
|
||
35 | *
|
||
36 | * @var array
|
||
37 | */
|
||
38 | public $badCommandChars = array('$', ';'); |
||
39 | |||
40 | /**
|
||
41 | * Available models
|
||
42 | *
|
||
43 | * @var array
|
||
44 | */
|
||
45 | public $models = array(); |
||
46 | |||
47 | /**
|
||
48 | * _finished
|
||
49 | *
|
||
50 | * This shell is perpetual, setting this property to true exits the process
|
||
51 | *
|
||
52 | * @var mixed
|
||
53 | */
|
||
54 | protected $_finished = false; |
||
55 | |||
56 | /**
|
||
57 | * _methodPatterns
|
||
58 | *
|
||
59 | * @var array
|
||
60 | */
|
||
61 | protected $_methodPatterns = array( |
||
62 | 'help' => '/^(help|\?)/', |
||
63 | '_exit' => '/^(quit|exit)/', |
||
64 | '_models' => '/^models/i', |
||
65 | '_bind' => '/^(\w+) bind (\w+) (\w+)/', |
||
66 | '_unbind' => '/^(\w+) unbind (\w+) (\w+)/', |
||
67 | '_find' => '/.+->find/', |
||
68 | '_save' => '/.+->save/', |
||
69 | '_columns' => '/^(\w+) columns/', |
||
70 | '_routesReload' => '/^routes\s+reload/i', |
||
71 | '_routesShow' => '/^routes\s+show/i', |
||
72 | '_routeToString' => '/^route\s+(\(.*\))$/i', |
||
73 | '_routeToArray' => '/^route\s+(.*)$/i', |
||
74 | ); |
||
75 | |||
76 | /**
|
||
77 | * Override startup of the Shell
|
||
78 | *
|
||
79 | * @return void
|
||
80 | */
|
||
81 | public function startup() { |
||
82 | App::uses('Dispatcher', 'Routing'); |
||
83 | $this->Dispatcher = new Dispatcher(); |
||
84 | $this->models = App::objects('Model'); |
||
85 | |||
86 | foreach ($this->models as $model) { |
||
87 | $class = $model; |
||
88 | App::uses($class, 'Model'); |
||
89 | $this->{$class} = new $class(); |
||
90 | } |
||
91 | $this->out(__d('cake_console', 'Model classes:')); |
||
92 | $this->hr();
|
||
93 | |||
94 | foreach ($this->models as $model) { |
||
95 | $this->out(" - {$model}"); |
||
96 | } |
||
97 | |||
98 | if (!$this->_loadRoutes()) { |
||
99 | $message = __d(
|
||
100 | 'cake_console',
|
||
101 | 'There was an error loading the routes config. Please check that the file exists and contains no errors.'
|
||
102 | ); |
||
103 | $this->err($message); |
||
104 | } |
||
105 | } |
||
106 | |||
107 | /**
|
||
108 | * Gets the option parser instance and configures it.
|
||
109 | *
|
||
110 | * @return ConsoleOptionParser
|
||
111 | */
|
||
112 | public function getOptionParser() { |
||
113 | $parser = parent::getOptionParser(); |
||
114 | |||
115 | $parser->description(array( |
||
116 | 'The interactive console is a tool for testing parts of your',
|
||
117 | 'app before you write code.',
|
||
118 | '',
|
||
119 | 'See below for a list of supported commands.'
|
||
120 | ))->epilog(array(
|
||
121 | '<info>Model testing</info>',
|
||
122 | '',
|
||
123 | 'To test model results, use the name of your model without a leading $',
|
||
124 | 'e.g. Foo->find("all")',
|
||
125 | "",
|
||
126 | 'To dynamically set associations, you can do the following:',
|
||
127 | '',
|
||
128 | "\tModelA bind <association> ModelB",
|
||
129 | '',
|
||
130 | "where the supported associations are hasOne, hasMany, belongsTo, hasAndBelongsToMany",
|
||
131 | "",
|
||
132 | 'To dynamically remove associations, you can do the following:',
|
||
133 | '',
|
||
134 | "\t ModelA unbind <association> ModelB",
|
||
135 | '',
|
||
136 | "where the supported associations are the same as above",
|
||
137 | "",
|
||
138 | "To save a new field in a model, you can do the following:",
|
||
139 | '',
|
||
140 | "\tModelA->save(array('foo' => 'bar', 'baz' => 0))",
|
||
141 | '',
|
||
142 | "where you are passing a hash of data to be saved in the format",
|
||
143 | "of field => value pairs",
|
||
144 | "",
|
||
145 | "To get column information for a model, use the following:",
|
||
146 | '',
|
||
147 | "\tModelA columns",
|
||
148 | '',
|
||
149 | "which returns a list of columns and their type",
|
||
150 | "",
|
||
151 | '<info>Route testing</info>',
|
||
152 | "",
|
||
153 | 'To test URLs against your app\'s route configuration, type:',
|
||
154 | "",
|
||
155 | "\tRoute <url>",
|
||
156 | "",
|
||
157 | "where url is the path to your your action plus any query parameters,",
|
||
158 | "minus the application's base path. For example:",
|
||
159 | "",
|
||
160 | "\tRoute /posts/view/1",
|
||
161 | "",
|
||
162 | "will return something like the following:",
|
||
163 | "",
|
||
164 | "\tarray(",
|
||
165 | "\t [...]",
|
||
166 | "\t 'controller' => 'posts',",
|
||
167 | "\t 'action' => 'view',",
|
||
168 | "\t [...]",
|
||
169 | "\t)",
|
||
170 | "",
|
||
171 | 'Alternatively, you can use simple array syntax to test reverse',
|
||
172 | 'To reload your routes config (Config/routes.php), do the following:',
|
||
173 | "",
|
||
174 | "\tRoutes reload",
|
||
175 | "",
|
||
176 | 'To show all connected routes, do the following:',
|
||
177 | '',
|
||
178 | "\tRoutes show",
|
||
179 | )); |
||
180 | |||
181 | return $parser; |
||
182 | } |
||
183 | /**
|
||
184 | * Prints the help message
|
||
185 | *
|
||
186 | * @return void
|
||
187 | */
|
||
188 | public function help() { |
||
189 | $optionParser = $this->getOptionParser(); |
||
190 | $this->out($optionParser->epilog()); |
||
191 | } |
||
192 | |||
193 | /**
|
||
194 | * Override main() to handle action
|
||
195 | *
|
||
196 | * @param string $command The command to run.
|
||
197 | * @return void
|
||
198 | */
|
||
199 | public function main($command = null) { |
||
200 | $this->_finished = false; |
||
201 | while (!$this->_finished) { |
||
202 | if (empty($command)) { |
||
203 | $command = trim($this->in('')); |
||
204 | } |
||
205 | |||
206 | $method = $this->_method($command); |
||
207 | |||
208 | if ($method) { |
||
209 | $this->$method($command); |
||
210 | } else {
|
||
211 | $this->out(__d('cake_console', "Invalid command")); |
||
212 | $this->out();
|
||
213 | } |
||
214 | $command = ''; |
||
215 | } |
||
216 | } |
||
217 | |||
218 | /**
|
||
219 | * Determine the method to process the current command
|
||
220 | *
|
||
221 | * @param string $command The command to run.
|
||
222 | * @return string or false
|
||
223 | */
|
||
224 | protected function _method($command) { |
||
225 | foreach ($this->_methodPatterns as $method => $pattern) { |
||
226 | if (preg_match($pattern, $command)) { |
||
227 | return $method; |
||
228 | } |
||
229 | } |
||
230 | |||
231 | return false; |
||
232 | } |
||
233 | |||
234 | /**
|
||
235 | * Set the finiished property so that the loop in main method ends
|
||
236 | *
|
||
237 | * @return void
|
||
238 | */
|
||
239 | protected function _exit() { |
||
240 | $this->_finished = true; |
||
241 | } |
||
242 | |||
243 | /**
|
||
244 | * List all models
|
||
245 | *
|
||
246 | * @return void
|
||
247 | */
|
||
248 | protected function _models() { |
||
249 | $this->out(__d('cake_console', 'Model classes:')); |
||
250 | $this->hr();
|
||
251 | foreach ($this->models as $model) { |
||
252 | $this->out(" - {$model}"); |
||
253 | } |
||
254 | } |
||
255 | |||
256 | /**
|
||
257 | * Bind an association
|
||
258 | *
|
||
259 | * @param mixed $command The command to run.
|
||
260 | * @return void
|
||
261 | */
|
||
262 | protected function _bind($command) { |
||
263 | preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); |
||
264 | |||
265 | foreach ($tmp as $data) { |
||
266 | $data = strip_tags($data); |
||
267 | $data = str_replace($this->badCommandChars, "", $data); |
||
268 | } |
||
269 | |||
270 | $modelA = $tmp[1]; |
||
271 | $association = $tmp[2]; |
||
272 | $modelB = $tmp[3]; |
||
273 | |||
274 | if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) { |
||
275 | $this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false); |
||
276 | $this->out(__d('cake_console', "Created %s association between %s and %s", |
||
277 | $association, $modelA, $modelB)); |
||
278 | } else {
|
||
279 | $this->out(__d('cake_console', "Please verify you are using valid models and association types")); |
||
280 | } |
||
281 | } |
||
282 | |||
283 | /**
|
||
284 | * Unbind an association
|
||
285 | *
|
||
286 | * @param mixed $command The command to run.
|
||
287 | * @return void
|
||
288 | */
|
||
289 | protected function _unbind($command) { |
||
290 | preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); |
||
291 | |||
292 | foreach ($tmp as $data) { |
||
293 | $data = strip_tags($data); |
||
294 | $data = str_replace($this->badCommandChars, "", $data); |
||
295 | } |
||
296 | |||
297 | $modelA = $tmp[1]; |
||
298 | $association = $tmp[2]; |
||
299 | $modelB = $tmp[3]; |
||
300 | |||
301 | // Verify that there is actually an association to unbind
|
||
302 | $currentAssociations = $this->{$modelA}->getAssociated(); |
||
303 | $validCurrentAssociation = false; |
||
304 | |||
305 | foreach ($currentAssociations as $model => $currentAssociation) { |
||
306 | if ($model === $modelB && $association === $currentAssociation) { |
||
307 | $validCurrentAssociation = true; |
||
308 | } |
||
309 | } |
||
310 | |||
311 | if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) { |
||
312 | $this->{$modelA}->unbindModel(array($association => array($modelB))); |
||
313 | $this->out(__d('cake_console', "Removed %s association between %s and %s", |
||
314 | $association, $modelA, $modelB)); |
||
315 | } else {
|
||
316 | $this->out(__d('cake_console', "Please verify you are using valid models, valid current association, and valid association types")); |
||
317 | } |
||
318 | } |
||
319 | |||
320 | /**
|
||
321 | * Perform a find
|
||
322 | *
|
||
323 | * @param mixed $command The command to run.
|
||
324 | * @return void
|
||
325 | */
|
||
326 | protected function _find($command) { |
||
327 | $command = strip_tags($command); |
||
328 | $command = str_replace($this->badCommandChars, "", $command); |
||
329 | |||
330 | // Do we have a valid model?
|
||
331 | list($modelToCheck) = explode('->', $command); |
||
332 | |||
333 | if ($this->_isValidModel($modelToCheck)) { |
||
334 | $findCommand = "\$data = \$this->$command;"; |
||
335 | //@codingStandardsIgnoreStart
|
||
336 | @eval($findCommand); |
||
337 | //@codingStandardsIgnoreEnd
|
||
338 | |||
339 | if (is_array($data)) { |
||
340 | foreach ($data as $idx => $results) { |
||
341 | if (is_numeric($idx)) { // findAll() output |
||
342 | foreach ($results as $modelName => $result) { |
||
343 | $this->out("$modelName"); |
||
344 | |||
345 | foreach ($result as $field => $value) { |
||
346 | if (is_array($value)) { |
||
347 | foreach ($value as $field2 => $value2) { |
||
348 | $this->out("\t$field2: $value2"); |
||
349 | } |
||
350 | |||
351 | $this->out();
|
||
352 | } else {
|
||
353 | $this->out("\t$field: $value"); |
||
354 | } |
||
355 | } |
||
356 | } |
||
357 | } else { // find() output |
||
358 | $this->out($idx); |
||
359 | |||
360 | foreach ($results as $field => $value) { |
||
361 | if (is_array($value)) { |
||
362 | foreach ($value as $field2 => $value2) { |
||
363 | $this->out("\t$field2: $value2"); |
||
364 | } |
||
365 | |||
366 | $this->out();
|
||
367 | } else {
|
||
368 | $this->out("\t$field: $value"); |
||
369 | } |
||
370 | } |
||
371 | } |
||
372 | } |
||
373 | } else {
|
||
374 | $this->out();
|
||
375 | $this->out(__d('cake_console', "No result set found")); |
||
376 | } |
||
377 | } else {
|
||
378 | $this->out(__d('cake_console', "%s is not a valid model", $modelToCheck)); |
||
379 | } |
||
380 | } |
||
381 | |||
382 | /**
|
||
383 | * Save a record
|
||
384 | *
|
||
385 | * @param mixed $command The command to run.
|
||
386 | * @return void
|
||
387 | */
|
||
388 | protected function _save($command) { |
||
389 | // Validate the model we're trying to save here
|
||
390 | $command = strip_tags($command); |
||
391 | $command = str_replace($this->badCommandChars, "", $command); |
||
392 | list($modelToSave) = explode("->", $command); |
||
393 | |||
394 | if ($this->_isValidModel($modelToSave)) { |
||
395 | // Extract the array of data we are trying to build
|
||
396 | list(, $data) = explode("->save", $command); |
||
397 | $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data); |
||
398 | $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));"; |
||
399 | //@codingStandardsIgnoreStart
|
||
400 | @eval($saveCommand); |
||
401 | //@codingStandardsIgnoreEnd
|
||
402 | $this->out(__d('cake_console', 'Saved record for %s', $modelToSave)); |
||
403 | } |
||
404 | } |
||
405 | |||
406 | /**
|
||
407 | * Show the columns for a model
|
||
408 | *
|
||
409 | * @param mixed $command The command to run.
|
||
410 | * @return void
|
||
411 | */
|
||
412 | protected function _columns($command) { |
||
413 | preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); |
||
414 | |||
415 | $modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1])); |
||
416 | |||
417 | if ($this->_isValidModel($modelToCheck)) { |
||
418 | // Get the column info for this model
|
||
419 | $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();"; |
||
420 | //@codingStandardsIgnoreStart
|
||
421 | @eval($fieldsCommand); |
||
422 | //@codingStandardsIgnoreEnd
|
||
423 | |||
424 | if (is_array($data)) { |
||
425 | foreach ($data as $field => $type) { |
||
426 | $this->out("\t{$field}: {$type}"); |
||
427 | } |
||
428 | } |
||
429 | } else {
|
||
430 | $this->out(__d('cake_console', "Please verify that you selected a valid model")); |
||
431 | } |
||
432 | } |
||
433 | |||
434 | /**
|
||
435 | * Reload route definitions
|
||
436 | *
|
||
437 | * @return void
|
||
438 | */
|
||
439 | protected function _routesReload() { |
||
440 | if (!$this->_loadRoutes()) { |
||
441 | return $this->err(__d('cake_console', "There was an error loading the routes config. Please check that the file exists and is free of parse errors.")); |
||
442 | } |
||
443 | $this->out(__d('cake_console', "Routes configuration reloaded, %d routes connected", count(Router::$routes))); |
||
444 | } |
||
445 | |||
446 | /**
|
||
447 | * Show all routes
|
||
448 | *
|
||
449 | * @return void
|
||
450 | */
|
||
451 | protected function _routesShow() { |
||
452 | $this->out(print_r(Hash::combine(Router::$routes, '{n}.template', '{n}.defaults'), true)); |
||
453 | } |
||
454 | |||
455 | /**
|
||
456 | * Parse an array URL and show the equivalent URL as a string
|
||
457 | *
|
||
458 | * @param mixed $command The command to run.
|
||
459 | * @return void
|
||
460 | */
|
||
461 | protected function _routeToString($command) { |
||
462 | preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); |
||
463 | |||
464 | //@codingStandardsIgnoreStart
|
||
465 | if ($url = eval('return array' . $tmp[1] . ';')) { |
||
466 | //@codingStandardsIgnoreEnd
|
||
467 | $this->out(Router::url($url)); |
||
468 | } |
||
469 | } |
||
470 | |||
471 | /**
|
||
472 | * Parse a string URL and show as an array
|
||
473 | *
|
||
474 | * @param mixed $command The command to run.
|
||
475 | * @return void
|
||
476 | */
|
||
477 | protected function _routeToArray($command) { |
||
478 | preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp); |
||
479 | |||
480 | $this->out(var_export(Router::parse($tmp[1]), true)); |
||
481 | } |
||
482 | |||
483 | /**
|
||
484 | * Tells if the specified model is included in the list of available models
|
||
485 | *
|
||
486 | * @param string $modelToCheck The model to check.
|
||
487 | * @return bool true if is an available model, false otherwise
|
||
488 | */
|
||
489 | protected function _isValidModel($modelToCheck) { |
||
490 | return in_array($modelToCheck, $this->models); |
||
491 | } |
||
492 | |||
493 | /**
|
||
494 | * Reloads the routes configuration from app/Config/routes.php, and compiles
|
||
495 | * all routes found
|
||
496 | *
|
||
497 | * @return bool True if config reload was a success, otherwise false
|
||
498 | */
|
||
499 | protected function _loadRoutes() { |
||
500 | Router::reload();
|
||
501 | extract(Router::getNamedExpressions()); |
||
502 | |||
503 | //@codingStandardsIgnoreStart
|
||
504 | if (!@include APP . 'Config' . DS . 'routes.php') { |
||
505 | //@codingStandardsIgnoreEnd
|
||
506 | return false; |
||
507 | } |
||
508 | CakePlugin::routes();
|
||
509 | |||
510 | Router::parse('/'); |
||
511 | return true; |
||
512 | } |
||
513 | |||
514 | } |