Development

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

You must first sign up to be able to contribute.

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

Revision 33539, 16.1 kB (checked in by fabien, 2 years ago)

[1.4] fixed exception format when using the PHP 5.4 built-in server (closes #10067, based on a patch from jgskin)

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