Development

/branches/1.1/lib/controller/sfController.class.php

You must first sign up to be able to contribute.

root/branches/1.1/lib/controller/sfController.class.php

Revision 19442, 17.1 kB (checked in by fabien, 5 years ago)

[1.1, 1.2] fixed default value for max forwards in sfController (closes #6675)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Rev Date
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  * (c) 2004-2006 Sean Kerr <sean@code-box.org>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 /**
13  * sfController directs application flow.
14  *
15  * @package    symfony
16  * @subpackage controller
17  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
18  * @author     Sean Kerr <sean@code-box.org>
19  * @version    SVN: $Id$
20  */
21 abstract class sfController
22 {
23   protected
24     $context           = null,
25     $dispatcher        = null,
26     $controllerClasses = array(),
27     $maxForwards       = 5,
28     $renderMode        = sfView::RENDER_CLIENT;
29
30   /**
31    * Class constructor.
32    *
33    * @see initialize()
34    */
35   public function __construct($context)
36   {
37     $this->initialize($context);
38   }
39
40   /**
41    * Initializes this controller.
42    *
43    * @param sfContext $context A sfContext implementation instance
44    */
45   public function initialize($context)
46   {
47     $this->context    = $context;
48     $this->dispatcher = $context->getEventDispatcher();
49
50     if (sfConfig::get('sf_logging_enabled'))
51     {
52       $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Initialization')));
53     }
54
55     // set max forwards
56     $this->maxForwards = sfConfig::get('sf_max_forwards', $this->maxForwards);
57   }
58
59   /**
60    * Indicates whether or not a module has a specific component.
61    *
62    * @param string $moduleName    A module name
63    * @param string $componentName An component name
64    *
65    * @return bool true, if the component exists, otherwise false
66    */
67   public function componentExists($moduleName, $componentName)
68   {
69     return $this->controllerExists($moduleName, $componentName, 'component', false);
70   }
71
72   /**
73    * Indicates whether or not a module has a specific action.
74    *
75    * @param string $moduleName  A module name
76    * @param string $actionName  An action name
77    *
78    * @return bool true, if the action exists, otherwise false
79    */
80   public function actionExists($moduleName, $actionName)
81   {
82     return $this->controllerExists($moduleName, $actionName, 'action', false);
83   }
84
85   /**
86    * Looks for a controller and optionally throw exceptions if existence is required (i.e.
87    * in the case of {@link getController()}).
88    *
89    * @param string  $moduleName       The name of the module
90    * @param string  $controllerName   The name of the controller within the module
91    * @param string  $extension        Either 'action' or 'component' depending on the type of controller to look for
92    * @param boolean $throwExceptions  Whether to throw exceptions if the controller doesn't exist
93    *
94    * @throws sfConfigurationException thrown if the module is not enabled
95    * @throws sfControllerException thrown if the controller doesn't exist and the $throwExceptions parameter is set to true
96    *
97    * @return boolean true if the controller exists, false otherwise
98    */
99   protected function controllerExists($moduleName, $controllerName, $extension, $throwExceptions)
100   {
101     $dirs = $this->context->getConfiguration()->getControllerDirs($moduleName);
102     foreach ($dirs as $dir => $checkEnabled)
103     {
104       // plugin module enabled?
105       if ($checkEnabled && !in_array($moduleName, sfConfig::get('sf_enabled_modules')) && is_readable($dir))
106       {
107         throw new sfConfigurationException(sprintf('The module "%s" is not enabled.', $moduleName));
108       }
109
110       // one action per file or one file for all actions
111       $classFile   = strtolower($extension);
112       $classSuffix = ucfirst(strtolower($extension));
113       $file        = $dir.'/'.$controllerName.$classSuffix.'.class.php';
114       if (is_readable($file))
115       {
116         // action class exists
117         require_once($file);
118
119         $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix] = $controllerName.$classSuffix;
120
121         return true;
122       }
123
124       $module_file = $dir.'/'.$classFile.'s.class.php';
125       if (is_readable($module_file))
126       {
127         // module class exists
128         require_once($module_file);
129
130         if (!class_exists($moduleName.$classSuffix.'s', false))
131         {
132           if ($throwExceptions)
133           {
134             throw new sfControllerException(sprintf('There is no "%s" class in your action file "%s".', $moduleName.$classSuffix.'s', $module_file));
135           }
136
137           return false;
138         }
139
140         // action is defined in this class?
141         if (!in_array('execute'.ucfirst($controllerName), get_class_methods($moduleName.$classSuffix.'s')))
142         {
143           if ($throwExceptions)
144           {
145             throw new sfControllerException(sprintf('There is no "%s" method in your action class "%s".', 'execute'.ucfirst($controllerName), $moduleName.$classSuffix.'s'));
146           }
147
148           return false;
149         }
150
151         $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix] = $moduleName.$classSuffix.'s';
152         return true;
153       }
154     }
155
156     // send an exception if debug
157     if ($throwExceptions && sfConfig::get('sf_debug'))
158     {
159       $dirs = array_keys($dirs);
160
161       // remove sf_root_dir from dirs
162       foreach ($dirs as &$dir)
163       {
164         $dir = str_replace(sfConfig::get('sf_root_dir'), '%SF_ROOT_DIR%', $dir);
165       }
166
167       throw new sfControllerException(sprintf('Controller "%s/%s" does not exist in: %s.', $moduleName, $controllerName, implode(', ', $dirs)));
168     }
169
170     return false;
171   }
172
173   /**
174    * Forwards the request to another action.
175    *
176    * @param string  $moduleName  A module name
177    * @param string  $actionName  An action name
178    *
179    * @throws <b>sfConfigurationException</b> If an invalid configuration setting has been found
180    * @throws <b>sfForwardException</b> If an error occurs while forwarding the request
181    * @throws <b>sfInitializationException</b> If the action could not be initialized
182    * @throws <b>sfSecurityException</b> If the action requires security but the user implementation is not of type sfSecurityUser
183    */
184   public function forward($moduleName, $actionName)
185   {
186     // replace unwanted characters
187     $moduleName = preg_replace('/[^a-z0-9_]+/i', '', $moduleName);
188     $actionName = preg_replace('/[^a-z0-9_]+/i', '', $actionName);
189
190     if ($this->getActionStack()->getSize() >= $this->maxForwards)
191     {
192       // let's kill this party before it turns into cpu cycle hell
193       throw new sfForwardException(sprintf('Too many forwards have been detected for this request (> %d).', $this->maxForwards));
194     }
195
196     // check for a module generator config file
197     $this->context->getConfigCache()->import('modules/'.$moduleName.'/config/generator.yml', false, true);
198
199     if (!$this->actionExists($moduleName, $actionName))
200     {
201       // the requested action doesn't exist
202       if (sfConfig::get('sf_logging_enabled'))
203       {
204         $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Action "%s/%s" does not exist', $moduleName, $actionName))));
205       }
206
207       throw new sfError404Exception(sprintf('Action "%s/%s" does not exist.', $moduleName, $actionName));
208     }
209
210     // create an instance of the action
211     $actionInstance = $this->getAction($moduleName, $actionName);
212
213     // add a new action stack entry
214     $this->getActionStack()->addEntry($moduleName, $actionName, $actionInstance);
215
216     // include module configuration
217     require($this->context->getConfigCache()->checkConfig('modules/'.$moduleName.'/config/module.yml'));
218
219     // check if this module is internal
220     if ($this->getActionStack()->getSize() == 1 && sfConfig::get('mod_'.strtolower($moduleName).'_is_internal') && !sfConfig::get('sf_test'))
221     {
222       throw new sfConfigurationException(sprintf('Action "%s" from module "%s" cannot be called directly.', $actionName, $moduleName));
223     }
224
225     // module enabled?
226     if (sfConfig::get('mod_'.strtolower($moduleName).'_enabled'))
227     {
228       // check for a module config.php
229       $moduleConfig = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/config/config.php';
230       if (is_readable($moduleConfig))
231       {
232         require_once($moduleConfig);
233       }
234
235       // create a new filter chain
236       $filterChain = new sfFilterChain();
237       $filterChain->loadConfiguration($actionInstance);
238
239       $this->context->getEventDispatcher()->notify(new sfEvent($this, 'controller.change_action', array('module' => $moduleName, 'action' => $actionName)));
240
241       if ($moduleName == sfConfig::get('sf_error_404_module') && $actionName == sfConfig::get('sf_error_404_action'))
242       {
243         $this->context->getResponse()->setStatusCode(404);
244         $this->context->getResponse()->setHttpHeader('Status', '404 Not Found');
245
246         $this->dispatcher->notify(new sfEvent($this, 'controller.page_not_found', array('module' => $moduleName, 'action' => $actionName)));
247       }
248
249       // process the filter chain
250       $filterChain->execute();
251     }
252     else
253     {
254       $moduleName = sfConfig::get('sf_module_disabled_module');
255       $actionName = sfConfig::get('sf_module_disabled_action');
256
257       if (!$this->actionExists($moduleName, $actionName))
258       {
259         // cannot find mod disabled module/action
260         throw new sfConfigurationException(sprintf('Invalid configuration settings: [sf_module_disabled_module] "%s", [sf_module_disabled_action] "%s".', $moduleName, $actionName));
261       }
262
263       $this->forward($moduleName, $actionName);
264     }
265   }
266
267   /**
268    * Retrieves an sfAction implementation instance.
269    *
270    * @param  string  $moduleName  A module name
271    * @param  string  $actionname  An action name
272    *
273    * @return sfAction An sfAction implementation instance, if the action exists, otherwise null
274    */
275   public function getAction($moduleName, $actionName)
276   {
277     return $this->getController($moduleName, $actionName, 'action');
278   }
279
280   /**
281    * Retrieves a sfComponent implementation instance.
282    *
283    * @param  string  $moduleName    A module name
284    * @param  string  $componentName A component name
285    *
286    * @return sfComponent A sfComponent implementation instance, if the component exists, otherwise null
287    */
288   public function getComponent($moduleName, $componentName)
289   {
290     return $this->getController($moduleName, $componentName, 'component');
291   }
292
293   /**
294    * Retrieves a controller implementation instance.
295    *
296    * @param  string $moduleName     A module name
297    * @param  string $controllerName A component name
298    * @param  string $extension      Either 'action' or 'component' depending on the type of controller to look for
299    *
300    * @return object A controller implementation instance, if the controller exists, otherwise null
301    *
302    * @see getComponent(), getAction()
303    */
304   protected function getController($moduleName, $controllerName, $extension)
305   {
306     $classSuffix = ucfirst(strtolower($extension));
307     if (!isset($this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix]))
308     {
309       $this->controllerExists($moduleName, $controllerName, $extension, true);
310     }
311
312     $class = $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix];
313
314     // fix for same name classes
315     $moduleClass = $moduleName.'_'.$class;
316
317     if (class_exists($moduleClass, false))
318     {
319       $class = $moduleClass;
320     }
321
322     return new $class($this->context, $moduleName, $controllerName);
323   }
324
325   /**
326    * Retrieves the action stack.
327    *
328    * @return sfActionStack An sfActionStack instance, if the action stack is enabled, otherwise null
329    */
330   public function getActionStack()
331   {
332     return $this->context->getActionStack();
333   }
334
335   /**
336    * Retrieves the presentation rendering mode.
337    *
338    * @return int One of the following:
339    *             - sfView::RENDER_CLIENT
340    *             - sfView::RENDER_VAR
341    */
342   public function getRenderMode()
343   {
344     return $this->renderMode;
345   }
346
347   /**
348    * Retrieves a sfView implementation instance.
349    *
350    * @param string $moduleName  A module name
351    * @param string $actionName  An action name
352    * @param string $viewName    A view name
353    *
354    * @return sfView A sfView implementation instance, if the view exists, otherwise null
355    */
356   public function getView($moduleName, $actionName, $viewName)
357   {
358     // user view exists?
359     $file = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/view/'.$actionName.$viewName.'View.class.php';
360
361     if (is_readable($file))
362     {
363       require_once($file);
364
365       $class = $actionName.$viewName.'View';
366
367       // fix for same name classes
368       $moduleClass = $moduleName.'_'.$class;
369
370       if (class_exists($moduleClass, false))
371       {
372         $class = $moduleClass;
373       }
374     }
375     else
376     {
377       // view class (as configured in module.yml or defined in action)
378       $class = sfConfig::get('mod_'.strtolower($moduleName).'_view_class', 'sfPHP').'View';
379     }
380
381     return new $class($this->context, $moduleName, $actionName, $viewName);
382   }
383
384   /**
385    * [DEPRECATED] Sends and email.
386    *
387    * This methods calls a module/action with the sfMailView class.
388    *
389    * @param  string  $module  A module name
390    * @param  string  $action  An action name
391    *
392    * @return string The generated mail content
393    *
394    * @see sfMailView, getPresentationFor(), sfController
395    * @deprecated 1.1
396    */
397   public function sendEmail($module, $action)
398   {
399     if (sfConfig::get('sf_logging_enabled'))
400     {
401       $this->dispatcher->notify(new sfEvent($this, 'application.log', array('sendEmail method is deprecated', 'priority' => sfLogger::ERR)));
402     }
403
404     return $this->getPresentationFor($module, $action, 'sfMail');
405   }
406
407   /**
408    * Returns the rendered view presentation of a given module/action.
409    *
410    * @param  string  $module    A module name
411    * @param  string  $action    An action name
412    * @param  string  $viewName  A View class name
413    *
414    * @return string The generated content
415    */
416   public function getPresentationFor($module, $action, $viewName = null)
417   {
418     if (sfConfig::get('sf_logging_enabled'))
419     {
420       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Get presentation for action "%s/%s" (view class: "%s")', $module, $action, $viewName))));
421     }
422
423     // get original render mode
424     $renderMode = $this->getRenderMode();
425
426     // set render mode to var
427     $this->setRenderMode(sfView::RENDER_VAR);
428
429     // grab the action stack
430     $actionStack = $this->getActionStack();
431
432     // grab this next forward's action stack index
433     $index = $actionStack->getSize();
434
435     // set viewName if needed
436     if ($viewName)
437     {
438       $currentViewName = sfConfig::get('mod_'.strtolower($module).'_view_class');
439       sfConfig::set('mod_'.strtolower($module).'_view_class', $viewName);
440     }
441
442     try
443     {
444       // forward to the mail action
445       $this->forward($module, $action);
446     }
447     catch (Exception $e)
448     {
449       // put render mode back
450       $this->setRenderMode($renderMode);
451
452       // remove viewName
453       if ($viewName)
454       {
455         sfConfig::set('mod_'.strtolower($module).'_view_class', $currentViewName);
456       }
457
458       throw $e;
459     }
460
461     // grab the action entry from this forward
462     $actionEntry = $actionStack->getEntry($index);
463
464     // get raw email content
465     $presentation =& $actionEntry->getPresentation();
466
467     // put render mode back
468     $this->setRenderMode($renderMode);
469
470     // remove the action entry
471     $nb = $actionStack->getSize() - $index;
472     while ($nb-- > 0)
473     {
474       $actionEntry = $actionStack->popEntry();
475
476       if ($actionEntry->getModuleName() == sfConfig::get('sf_login_module') && $actionEntry->getActionName() == sfConfig::get('sf_login_action'))
477       {
478         throw new sfException('Your action is secured, but the user is not authenticated.');
479       }
480       else if ($actionEntry->getModuleName() == sfConfig::get('sf_secure_module') && $actionEntry->getActionName() == sfConfig::get('sf_secure_action'))
481       {
482         throw new sfException('Your action is secured, but the user does not have access.');
483       }
484     }
485
486     // remove viewName
487     if ($viewName)
488     {
489       sfConfig::set('mod_'.strtolower($module).'_view_class', $currentViewName);
490     }
491
492     return $presentation;
493   }
494
495   /**
496    * Sets the presentation rendering mode.
497    *
498    * @param int $mode A rendering mode
499    *
500    * @throws sfRenderException If an invalid render mode has been set
501    */
502   public function setRenderMode($mode)
503   {
504     if ($mode == sfView::RENDER_CLIENT || $mode == sfView::RENDER_VAR || $mode == sfView::RENDER_NONE)
505     {
506       $this->renderMode = $mode;
507
508       return;
509     }
510
511     // invalid rendering mode type
512     throw new sfRenderException(sprintf('Invalid rendering mode: %s.', $mode));
513   }
514
515   /**
516    * Indicates whether or not we were called using the CLI version of PHP.
517    *
518    * @return bool true, if using cli, otherwise false.
519    */
520   public function inCLI()
521   {
522     return 0 == strncasecmp(PHP_SAPI, 'cli', 3);
523   }
524
525   /**
526    * Calls methods defined via sfEventDispatcher.
527    *
528    * @param string $method    The method name
529    * @param array  $arguments The method arguments
530    *
531    * @return mixed The returned value of the called method
532    */
533   public function __call($method, $arguments)
534   {
535     $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'controller.method_not_found', array('method' => $method, 'arguments' => $arguments)));
536     if (!$event->isProcessed())
537     {
538       throw new sfException(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
539     }
540
541     return $event->getReturnValue();
542   }
543 }
544
Note: See TracBrowser for help on using the browser.