Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: sfController.class.php 30912 2010-09-15 11:10:46Z fabien $
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 0 == strncasecmp(PHP_SAPI, 'cli', 3);
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
}