統計
| ブランチ: | リビジョン:

pictcode / lib / Cake / Console / Command / ConsoleShell.php @ 0b1b8047

履歴 | 表示 | アノテート | ダウンロード (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
}