Development

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

You must first sign up to be able to contribute.

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

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