1 | <?php |
---|
2 | /** |
---|
3 | * CakePHP Console Shell |
---|
4 | * |
---|
5 | * PHP versions 4 and 5 |
---|
6 | * |
---|
7 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) |
---|
8 | * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) |
---|
9 | * |
---|
10 | * Licensed under The MIT License |
---|
11 | * Redistributions of files must retain the above copyright notice. |
---|
12 | * |
---|
13 | * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) |
---|
14 | * @link http://cakephp.org CakePHP(tm) Project |
---|
15 | * @package cake |
---|
16 | * @subpackage cake.cake.console.libs |
---|
17 | * @since CakePHP(tm) v 1.2.0.5012 |
---|
18 | * @license MIT License (http://www.opensource.org/licenses/mit-license.php) |
---|
19 | */ |
---|
20 | |
---|
21 | /** |
---|
22 | * @package cake |
---|
23 | * @subpackage cake.cake.console.libs |
---|
24 | */ |
---|
25 | class ConsoleShell extends Shell { |
---|
26 | |
---|
27 | /** |
---|
28 | * Available binding types |
---|
29 | * |
---|
30 | * @var array |
---|
31 | * @access public |
---|
32 | */ |
---|
33 | var $associations = array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany'); |
---|
34 | |
---|
35 | /** |
---|
36 | * Chars that describe invalid commands |
---|
37 | * |
---|
38 | * @var array |
---|
39 | * @access public |
---|
40 | */ |
---|
41 | var $badCommandChars = array('$', ';'); |
---|
42 | |
---|
43 | /** |
---|
44 | * Available models |
---|
45 | * |
---|
46 | * @var array |
---|
47 | * @access public |
---|
48 | */ |
---|
49 | var $models = array(); |
---|
50 | |
---|
51 | /** |
---|
52 | * Override intialize of the Shell |
---|
53 | * |
---|
54 | * @access public |
---|
55 | */ |
---|
56 | function initialize() { |
---|
57 | require_once CAKE . 'dispatcher.php'; |
---|
58 | $this->Dispatcher = new Dispatcher(); |
---|
59 | $this->models = App::objects('model'); |
---|
60 | App::import('Model', $this->models); |
---|
61 | |
---|
62 | foreach ($this->models as $model) { |
---|
63 | $class = Inflector::camelize(str_replace('.php', '', $model)); |
---|
64 | $this->models[$model] = $class; |
---|
65 | $this->{$class} =& new $class(); |
---|
66 | } |
---|
67 | $this->out('Model classes:'); |
---|
68 | $this->out('--------------'); |
---|
69 | |
---|
70 | foreach ($this->models as $model) { |
---|
71 | $this->out(" - {$model}"); |
---|
72 | } |
---|
73 | $this->_loadRoutes(); |
---|
74 | } |
---|
75 | |
---|
76 | /** |
---|
77 | * Prints the help message |
---|
78 | * |
---|
79 | * @access public |
---|
80 | */ |
---|
81 | function help() { |
---|
82 | $out = 'Console help:'; |
---|
83 | $out .= '-------------'; |
---|
84 | $out .= 'The interactive console is a tool for testing parts of your app before you'; |
---|
85 | $out .= 'write code.'; |
---|
86 | $out .= "\n"; |
---|
87 | $out .= 'Model testing:'; |
---|
88 | $out .= 'To test model results, use the name of your model without a leading $'; |
---|
89 | $out .= 'e.g. Foo->find("all")'; |
---|
90 | $out .= "\n"; |
---|
91 | $out .= 'To dynamically set associations, you can do the following:'; |
---|
92 | $out .= "\tModelA bind <association> ModelB"; |
---|
93 | $out .= "where the supported assocations are hasOne, hasMany, belongsTo, hasAndBelongsToMany"; |
---|
94 | $out .= "\n"; |
---|
95 | $out .= 'To dynamically remove associations, you can do the following:'; |
---|
96 | $out .= "\t ModelA unbind <association> ModelB"; |
---|
97 | $out .= "where the supported associations are the same as above"; |
---|
98 | $out .= "\n"; |
---|
99 | $out .= "To save a new field in a model, you can do the following:"; |
---|
100 | $out .= "\tModelA->save(array('foo' => 'bar', 'baz' => 0))"; |
---|
101 | $out .= "where you are passing a hash of data to be saved in the format"; |
---|
102 | $out .= "of field => value pairs"; |
---|
103 | $out .= "\n"; |
---|
104 | $out .= "To get column information for a model, use the following:"; |
---|
105 | $out .= "\tModelA columns"; |
---|
106 | $out .= "which returns a list of columns and their type"; |
---|
107 | $out .= "\n"; |
---|
108 | $out .= "\n"; |
---|
109 | $out .= 'Route testing:'; |
---|
110 | $out .= "\n"; |
---|
111 | $out .= 'To test URLs against your app\'s route configuration, type:'; |
---|
112 | $out .= "\n"; |
---|
113 | $out .= "\tRoute <url>"; |
---|
114 | $out .= "\n"; |
---|
115 | $out .= "where url is the path to your your action plus any query parameters,"; |
---|
116 | $out .= "minus the application's base path. For example:"; |
---|
117 | $out .= "\n"; |
---|
118 | $out .= "\tRoute /posts/view/1"; |
---|
119 | $out .= "\n"; |
---|
120 | $out .= "will return something like the following:"; |
---|
121 | $out .= "\n"; |
---|
122 | $out .= "\tarray ("; |
---|
123 | $out .= "\t [...]"; |
---|
124 | $out .= "\t 'controller' => 'posts',"; |
---|
125 | $out .= "\t 'action' => 'view',"; |
---|
126 | $out .= "\t [...]"; |
---|
127 | $out .= "\t)"; |
---|
128 | $out .= "\n"; |
---|
129 | $out .= 'Alternatively, you can use simple array syntax to test reverse'; |
---|
130 | $out .= 'To reload your routes config (config/routes.php), do the following:'; |
---|
131 | $out .= "\n"; |
---|
132 | $out .= "\tRoutes reload"; |
---|
133 | $out .= "\n"; |
---|
134 | $out .= 'To show all connected routes, do the following:'; |
---|
135 | $out .= "\tRoutes show"; |
---|
136 | $this->out($out); |
---|
137 | } |
---|
138 | |
---|
139 | /** |
---|
140 | * Override main() to handle action |
---|
141 | * |
---|
142 | * @access public |
---|
143 | */ |
---|
144 | function main($command = null) { |
---|
145 | while (true) { |
---|
146 | if (empty($command)) { |
---|
147 | $command = trim($this->in('')); |
---|
148 | } |
---|
149 | |
---|
150 | switch ($command) { |
---|
151 | case 'help': |
---|
152 | $this->help(); |
---|
153 | break; |
---|
154 | case 'quit': |
---|
155 | case 'exit': |
---|
156 | return true; |
---|
157 | break; |
---|
158 | case 'models': |
---|
159 | $this->out('Model classes:'); |
---|
160 | $this->hr(); |
---|
161 | foreach ($this->models as $model) { |
---|
162 | $this->out(" - {$model}"); |
---|
163 | } |
---|
164 | break; |
---|
165 | case (preg_match("/^(\w+) bind (\w+) (\w+)/", $command, $tmp) == true): |
---|
166 | foreach ($tmp as $data) { |
---|
167 | $data = strip_tags($data); |
---|
168 | $data = str_replace($this->badCommandChars, "", $data); |
---|
169 | } |
---|
170 | |
---|
171 | $modelA = $tmp[1]; |
---|
172 | $association = $tmp[2]; |
---|
173 | $modelB = $tmp[3]; |
---|
174 | |
---|
175 | if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) { |
---|
176 | $this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false); |
---|
177 | $this->out("Created $association association between $modelA and $modelB"); |
---|
178 | } else { |
---|
179 | $this->out("Please verify you are using valid models and association types"); |
---|
180 | } |
---|
181 | break; |
---|
182 | case (preg_match("/^(\w+) unbind (\w+) (\w+)/", $command, $tmp) == true): |
---|
183 | foreach ($tmp as $data) { |
---|
184 | $data = strip_tags($data); |
---|
185 | $data = str_replace($this->badCommandChars, "", $data); |
---|
186 | } |
---|
187 | |
---|
188 | $modelA = $tmp[1]; |
---|
189 | $association = $tmp[2]; |
---|
190 | $modelB = $tmp[3]; |
---|
191 | |
---|
192 | // Verify that there is actually an association to unbind |
---|
193 | $currentAssociations = $this->{$modelA}->getAssociated(); |
---|
194 | $validCurrentAssociation = false; |
---|
195 | |
---|
196 | foreach ($currentAssociations as $model => $currentAssociation) { |
---|
197 | if ($model == $modelB && $association == $currentAssociation) { |
---|
198 | $validCurrentAssociation = true; |
---|
199 | } |
---|
200 | } |
---|
201 | |
---|
202 | if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) { |
---|
203 | $this->{$modelA}->unbindModel(array($association => array($modelB))); |
---|
204 | $this->out("Removed $association association between $modelA and $modelB"); |
---|
205 | } else { |
---|
206 | $this->out("Please verify you are using valid models, valid current association, and valid association types"); |
---|
207 | } |
---|
208 | break; |
---|
209 | case (strpos($command, "->find") > 0): |
---|
210 | // Remove any bad info |
---|
211 | $command = strip_tags($command); |
---|
212 | $command = str_replace($this->badCommandChars, "", $command); |
---|
213 | |
---|
214 | // Do we have a valid model? |
---|
215 | list($modelToCheck, $tmp) = explode('->', $command); |
---|
216 | |
---|
217 | if ($this->_isValidModel($modelToCheck)) { |
---|
218 | $findCommand = "\$data = \$this->$command;"; |
---|
219 | @eval($findCommand); |
---|
220 | |
---|
221 | if (is_array($data)) { |
---|
222 | foreach ($data as $idx => $results) { |
---|
223 | if (is_numeric($idx)) { // findAll() output |
---|
224 | foreach ($results as $modelName => $result) { |
---|
225 | $this->out("$modelName"); |
---|
226 | |
---|
227 | foreach ($result as $field => $value) { |
---|
228 | if (is_array($value)) { |
---|
229 | foreach ($value as $field2 => $value2) { |
---|
230 | $this->out("\t$field2: $value2"); |
---|
231 | } |
---|
232 | |
---|
233 | $this->out(); |
---|
234 | } else { |
---|
235 | $this->out("\t$field: $value"); |
---|
236 | } |
---|
237 | } |
---|
238 | } |
---|
239 | } else { // find() output |
---|
240 | $this->out($idx); |
---|
241 | |
---|
242 | foreach ($results as $field => $value) { |
---|
243 | if (is_array($value)) { |
---|
244 | foreach ($value as $field2 => $value2) { |
---|
245 | $this->out("\t$field2: $value2"); |
---|
246 | } |
---|
247 | |
---|
248 | $this->out(); |
---|
249 | } else { |
---|
250 | $this->out("\t$field: $value"); |
---|
251 | } |
---|
252 | } |
---|
253 | } |
---|
254 | } |
---|
255 | } else { |
---|
256 | $this->out("\nNo result set found"); |
---|
257 | } |
---|
258 | } else { |
---|
259 | $this->out("$modelToCheck is not a valid model"); |
---|
260 | } |
---|
261 | |
---|
262 | break; |
---|
263 | case (strpos($command, '->save') > 0): |
---|
264 | // Validate the model we're trying to save here |
---|
265 | $command = strip_tags($command); |
---|
266 | $command = str_replace($this->badCommandChars, "", $command); |
---|
267 | list($modelToSave, $tmp) = explode("->", $command); |
---|
268 | |
---|
269 | if ($this->_isValidModel($modelToSave)) { |
---|
270 | // Extract the array of data we are trying to build |
---|
271 | list($foo, $data) = explode("->save", $command); |
---|
272 | $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data); |
---|
273 | $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));"; |
---|
274 | @eval($saveCommand); |
---|
275 | $this->out('Saved record for ' . $modelToSave); |
---|
276 | } |
---|
277 | break; |
---|
278 | case (preg_match("/^(\w+) columns/", $command, $tmp) == true): |
---|
279 | $modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1])); |
---|
280 | |
---|
281 | if ($this->_isValidModel($modelToCheck)) { |
---|
282 | // Get the column info for this model |
---|
283 | $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();"; |
---|
284 | @eval($fieldsCommand); |
---|
285 | |
---|
286 | if (is_array($data)) { |
---|
287 | foreach ($data as $field => $type) { |
---|
288 | $this->out("\t{$field}: {$type}"); |
---|
289 | } |
---|
290 | } |
---|
291 | } else { |
---|
292 | $this->out("Please verify that you selected a valid model"); |
---|
293 | } |
---|
294 | break; |
---|
295 | case (preg_match("/^routes\s+reload/i", $command, $tmp) == true): |
---|
296 | $router =& Router::getInstance(); |
---|
297 | if (!$this->_loadRoutes()) { |
---|
298 | $this->out("There was an error loading the routes config. Please check that the file"); |
---|
299 | $this->out("exists and is free of parse errors."); |
---|
300 | break; |
---|
301 | } |
---|
302 | $this->out("Routes configuration reloaded, " . count($router->routes) . " routes connected"); |
---|
303 | break; |
---|
304 | case (preg_match("/^routes\s+show/i", $command, $tmp) == true): |
---|
305 | $router =& Router::getInstance(); |
---|
306 | $this->out(implode("\n", Set::extract($router->routes, '{n}.0'))); |
---|
307 | break; |
---|
308 | case (preg_match("/^route\s+(\(.*\))$/i", $command, $tmp) == true): |
---|
309 | if ($url = eval('return array' . $tmp[1] . ';')) { |
---|
310 | $this->out(Router::url($url)); |
---|
311 | } |
---|
312 | break; |
---|
313 | case (preg_match("/^route\s+(.*)/i", $command, $tmp) == true): |
---|
314 | $this->out(var_export(Router::parse($tmp[1]), true)); |
---|
315 | break; |
---|
316 | default: |
---|
317 | $this->out("Invalid command\n"); |
---|
318 | break; |
---|
319 | } |
---|
320 | $command = ''; |
---|
321 | } |
---|
322 | } |
---|
323 | |
---|
324 | /** |
---|
325 | * Tells if the specified model is included in the list of available models |
---|
326 | * |
---|
327 | * @param string $modelToCheck |
---|
328 | * @return boolean true if is an available model, false otherwise |
---|
329 | * @access protected |
---|
330 | */ |
---|
331 | function _isValidModel($modelToCheck) { |
---|
332 | return in_array($modelToCheck, $this->models); |
---|
333 | } |
---|
334 | |
---|
335 | /** |
---|
336 | * Reloads the routes configuration from config/routes.php, and compiles |
---|
337 | * all routes found |
---|
338 | * |
---|
339 | * @return boolean True if config reload was a success, otherwise false |
---|
340 | * @access protected |
---|
341 | */ |
---|
342 | function _loadRoutes() { |
---|
343 | $router =& Router::getInstance(); |
---|
344 | |
---|
345 | $router->reload(); |
---|
346 | extract($router->getNamedExpressions()); |
---|
347 | |
---|
348 | if (!@include(CONFIGS . 'routes.php')) { |
---|
349 | return false; |
---|
350 | } |
---|
351 | $router->parse('/'); |
---|
352 | |
---|
353 | foreach (array_keys($router->getNamedExpressions()) as $var) { |
---|
354 | unset(${$var}); |
---|
355 | } |
---|
356 | for ($i = 0, $len = count($router->routes); $i < $len; $i++) { |
---|
357 | $router->routes[$i]->compile(); |
---|
358 | } |
---|
359 | return true; |
---|
360 | } |
---|
361 | } |
---|