Development

Documentation/ru_RU/06-Inside-the-Controller-Layer.sv (diff)

You must first sign up to be able to contribute.

Changes from Version 1 of Documentation/ru_RU/06-Inside-the-Controller-Layer.sv

Show
Ignore:
Author:
Sergei.Vasilyev (IP: 217.77.208.227)
Timestamp:
06/18/07 18:05:26 (11 years ago)
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Documentation/ru_RU/06-Inside-the-Controller-Layer.sv

    v0 v1  
     1Chapter 6 - Inside The Controller Layer 
     2======================================= 
     3 
     4In symfony, the controller layer, which contains the code linking the business logic and the presentation, is split into several components that you use for different purposes: 
     5 
     6  * The front controller is the unique entry point to the application. It loads the configuration and determines the action to execute. 
     7  * Actions contain the applicative logic. They check the integrity of the request and prepare the data needed by the presentation layer. 
     8  * The request, response, and session objects give access to the request parameters, the response headers, and the persistent user data. They are used very often in the controller layer. 
     9  * Filters are portions of code executed for every request, before or after the action. For example, the security and validation filters are commonly used in web applications. You can extend the framework by creating your own filters. 
     10 
     11This chapter describes all these components, but don't be intimidated by their number. For a basic page, you will probably need to write only a few lines in the action class, and that's all. The other controller components will be of use only in specific situations. 
     12 
     13The Front Controller 
     14-------------------- 
     15 
     16All web requests are handled by a single front controller, which is the unique entry point to the whole application in a given environment. 
     17 
     18When the front controller receives a request, it uses the routing system to match an action name and a module name with the URL typed (or clicked) by the user. For instance, the following request URL calls the `index.php` script (that's the front controller) and will be understood as a call to the action `myAction` of the module `mymodule`: 
     19 
     20    http://localhost/index.php/mymodule/myAction 
     21 
     22If you are not interested in symfony's internals, that's all that you need to know about the front controller. It is an indispensable component of the symfony MVC architecture, but you will seldom need to change it. So you can jump to the next section unless you really want to know about the guts of the front controller. 
     23 
     24### The Front Controller's Job in Detail 
     25 
     26The front controller does the dispatching of the request, but that means a little more than just determining the action to execute. In fact, it executes the code that is common to all actions, including the following: 
     27 
     28  1. Define the core constants. 
     29  2. Locate the symfony libraries. 
     30  3. Load and initiate the core framework classes. 
     31  4. Load the configuration. 
     32  5. Decode the request URL to determine the action to execute and the request parameters. 
     33  6. If the action does not exist, redirect to the 404 error action. 
     34  7. Activate filters (for instance, if the request needs authentication). 
     35  8. Execute the filters, first pass. 
     36  9. Execute the action and render the view. 
     37  10. Execute the filters, second pass. 
     38  11. Output the response. 
     39 
     40### The Default Front Controller 
     41 
     42The default front controller, called `index.php` and located in the `web/` directory of the project, is a simple PHP file, as shown in Listing 6-1. 
     43 
     44Listing 6-1 - The Default Production Front Controller 
     45 
     46    [php] 
     47    <?php 
     48 
     49    define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..')); 
     50    define('SF_APP',         'myapp'); 
     51    define('SF_ENVIRONMENT', 'prod'); 
     52    define('SF_DEBUG',       false); 
     53 
     54    require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php'); 
     55 
     56    sfContext::getInstance()->getController()->dispatch(); 
     57 
     58The constants definition corresponds to the first step described in the previous section. Then the front controller includes the application config.php, which takes care of steps 2 through 4. The call to the `dispatch()` method of the `sfController` object (which is the core controller object of the symfony MVC architecture) dispatches the request, taking care of steps 5 through 7. The last steps are handled by the filter chain, as explained later in this chapter. 
     59 
     60### Calling Another Front Controller to Switch the Environment 
     61 
     62One front controller exists per environment. As a matter of fact, it is the very existence of a front controller that defines an environment. The environment is defined in the `SF_ENVIRONMENT` constant. 
     63 
     64To change the environment in which you're browsing your application, just choose another front controller. The default front controllers available when you create a new application with the `symfony init-app` task are `index.php` for the production environment and `myapp_dev.php` for the development environment (provided that your application is called `myapp`). The default `mod_rewrite` configuration will use `index.php` when the URL doesn't contain a front controller script name. So both of these URLs display the same page (`mymodule/index`) in the production environment: 
     65 
     66    http://localhost/index.php/mymodule/index 
     67    http://localhost/mymodule/index 
     68 
     69and this URL displays that same page in the development environment: 
     70 
     71    http://localhost/myapp_dev.php/mymodule/index 
     72 
     73Creating a new environment is as easy as creating a new front controller. For instance, you may need a staging environment to allow your customers to test the application before going to production. To create this staging environment, just copy `web/myapp_dev.php` into `web/myapp_staging.php`, and change the value of the `SF_ENVIRONMENT` constant to `staging`. Now, in all the configuration files, you can add a new `staging:` section to set specific values for this environment, as shown in Listing 6-2. 
     74 
     75Listing 6-2 - Sample `app.yml` with Specific Settings for the Staging Environment 
     76 
     77    staging: 
     78      mail: 
     79        webmaster:    dummy@mysite.com 
     80        contact:      dummy@mysite.com 
     81    all: 
     82      mail: 
     83        webmaster:    webmaster@mysite.com 
     84        contact:      contact@mysite.com 
     85 
     86If you want to see how the application reacts in this new environment, call the related front controller: 
     87 
     88    http://localhost/myapp_staging.php/mymodule/index 
     89 
     90### Batch Files 
     91 
     92You may want to execute a script from the command line (or via a cron table) with access to all the symfony classes and features, for instance to launch batch e-mail jobs or to periodically update your model through a process-intensive calculation. For such a script, you need to include the same lines as in a front controller at the beginning. Listing 6-3 shows an example of the beginning of a batch script. 
     93 
     94Listing 6-3 - Sample Batch Script 
     95 
     96    [php] 
     97    <?php 
     98 
     99    define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..')); 
     100    define('SF_APP',         'myapp'); 
     101    define('SF_ENVIRONMENT', 'prod'); 
     102    define('SF_DEBUG',       false); 
     103 
     104    require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php'); 
     105 
     106    // add code here 
     107 
     108You can see that the only missing line is the call to the `dispatch()` method of the sfController object, which can be used only with a web server, not in a batch process. Defining an application and an environment gives you access to a specific configuration. Including the application `config.php` initiates the context and the autoloading. 
     109 
     110>**TIP** 
     111>The symfony CLI offers an `init-batch` task, which automatically creates a skeleton similar to the one in Listing 6-3 in the `batch/` directory. Just pass it an application name, an environment name, and a batch name as arguments. 
     112 
     113Actions 
     114------- 
     115 
     116The actions are the heart of an application, because they contain all the application's logic. They use the model and define variables for the view. When you make a web request in a symfony application, the URL defines an action and the request parameters. 
     117 
     118### The Action Class 
     119 
     120Actions are methods named `executeActionName` of a class named `moduleNameActions` inheriting from the `sfActions` class, and grouped by modules. The action class of a module is stored in an `actions.class.php` file, in the module's `actions/` directory. 
     121 
     122Listing 6-4 shows an example of an `actions.class.php` file with only an `index` action for the whole `mymodule` module. 
     123 
     124Listing 6-4 - Sample Action Class, in `apps/myapp/modules/mymodule/actions/actions.class.php` 
     125 
     126    [php] 
     127    class mymoduleActions extends sfActions 
     128    { 
     129      public function executeIndex() 
     130      { 
     131 
     132      } 
     133    } 
     134 
     135>**CAUTION** 
     136>Even if method names are not case-sensitive in PHP, they are in symfony. So don't forget that the action methods must start with a lowercase `execute`, followed by the exact action name with the first letter capitalized. 
     137 
     138In order to request an action, you need to call the front controller script with the module name and action name as parameters. By default, this is done by appending the couple `module_name`/`action_name` to the script. This means that the action defined in Listing 6-4 can be called by this URL: 
     139 
     140    http://localhost/index.php/mymodule/index 
     141 
     142Adding more actions just means adding more `execute` methods to the `sfActions` object, as shown in Listing 6-5. 
     143 
     144Listing 6-5 - Action Class with Two Actions, in `myapp/modules/mymodule/actions/actions.class.php` 
     145 
     146    [php] 
     147    class mymoduleActions extends sfActions 
     148    { 
     149      public function executeIndex() 
     150      { 
     151        ... 
     152      } 
     153 
     154      public function executeList() 
     155      { 
     156        ... 
     157      } 
     158    } 
     159 
     160If the size of an action class grows too much, you probably need to do some refactoring and move some code to the model layer. Actions should often be kept short (not more than a few lines), and all the business logic should usually be in the model. 
     161 
     162Still, the number of actions in a module can be important enough to lead you to split it in two modules. 
     163 
     164>**SIDEBAR** 
     165>Symfony coding standards 
     166> 
     167>In the code examples given in this book, you probably noticed that the opening and closing curly braces (`{` and `}`) occupy one line each. This standard makes the code easier to read. 
     168> 
     169>Among the other coding standards of the framework, indentation is always done by two blank spaces; tabs are not used. This is because tabs have a different space value according to the text editor you use, and because code with mixed tab and blank indentation is impossible to read. 
     170> 
     171>Core and generated symfony PHP files do not end with the usual `?>` closing tag. This is because it is not really needed, and because it can create problems in the output if you ever have blanks after this tag. 
     172> 
     173>And if you really pay attention, you will see that a line never ends with a blank space in symfony. The reason, this time, is more prosaic: lines ending with blanks look ugly in Fabien's text editor. 
     174 
     175### Alternative Action Class Syntax 
     176 
     177An alternative action syntax is available to dispatch the actions in separate files, one file per action. In this case, each action class extends `sfAction` (instead of `sfActions`) and is named `actionNameAction`. The actual action method is simply named `execute`. The file name is the same as the class name. This means that the equivalent of Listing 6-5 can be written with the two files shown in Listings 6-6 and 6-7. 
     178 
     179Listing 6-6 - Single Action File, in `myapp/modules/mymodule/actions/indexAction.class.php` 
     180 
     181    [php] 
     182    class indexAction extends sfAction 
     183    { 
     184      public function execute() 
     185      { 
     186        ... 
     187      } 
     188    } 
     189 
     190Listing 6-7 - Single Action File, in `myapp/modules/mymodule/actions/listAction.class.php` 
     191 
     192    [php] 
     193    class listAction extends sfAction 
     194    { 
     195      public function execute() 
     196      { 
     197        ... 
     198      } 
     199    } 
     200 
     201### Retrieving Information in the Action 
     202 
     203The action class offers a way to access controller-related information and the core symfony objects. Listing 6-8 demonstrates how to use them. 
     204 
     205Listing 6-8 - `sfActions` Common Methods 
     206 
     207    [php] 
     208    class mymoduleActions extends sfActions 
     209    { 
     210      public function executeIndex() 
     211      { 
     212        // Retrieving request parameters 
     213        $password    = $this->getRequestParameter('password'); 
     214 
     215        // Retrieving controller information 
     216        $moduleName  = $this->getModuleName(); 
     217        $actionName  = $this->getActionName(); 
     218 
     219        // Retrieving framework core objects 
     220        $request     = $this->getRequest(); 
     221        $userSession = $this->getUser(); 
     222        $response    = $this->getResponse(); 
     223        $controller  = $this->getController(); 
     224        $context     = $this->getContext(); 
     225 
     226        // Setting action variables to pass information to the template 
     227        $this->setVar('foo', 'bar'); 
     228        $this->foo = 'bar';            // Shorter version 
     229 
     230      } 
     231    } 
     232 
     233>**SIDEBAR** 
     234>The context singleton 
     235> 
     236>You already saw, in the front controller, a call to sfContext::getInstance(). In an action, the getContext() method returns the same singleton. It is a very useful object that stores a reference to all the symfony core objects related to a given request, and offers an accessor for each of them: 
     237> 
     238>`sfController`: The controller object (`->getController()`) `sfRequest`: The request object (`->getRequest()`) `sfResponse`: The response object (`->getResponse()`) `sfUser`: The user session object (`->getUser()`) `sfDatabaseConnection`: The database connection (`->getDatabaseConnection()`) `sfLogger`: The logger object (`->getLogger()`) `sfI18N`: The internationalization object (`->getI18N()`) 
     239> 
     240>You can call the `sfContext::getInstance()` singleton from any part of the code. 
     241 
     242### Action Termination 
     243 
     244Various behaviors are possible at the conclusion of an action's execution. The value returned by the action method determines how the view will be rendered. Constants of the `sfView` class are used to specify which template is to be used to display the result of the action. 
     245 
     246If there is a default view to call (this is the most common case), the action should end as follows: 
     247 
     248    [php] 
     249    return sfView::SUCCESS; 
     250 
     251Symfony will then look for a template called `actionNameSuccess.php`. This is defined as the default action behavior, so if you omit the `return` statement in an action method, symfony will also look for an `actionNameSuccess.php` template. Empty actions will also trigger that behavior. See Listing 6-9 for examples of successful action termination. 
     252 
     253Listing 6-9 - Actions That Will Call the `indexSuccess.php` and `listSuccess.php` Templates 
     254 
     255    [php] 
     256    public function executeIndex() 
     257    { 
     258      return sfView::SUCCESS; 
     259    } 
     260 
     261    public function executeList() 
     262    { 
     263    } 
     264 
     265If there is an error view to call, the action should end like this: 
     266 
     267    [php] 
     268    return sfView::ERROR; 
     269 
     270Symfony will then look for a template called `actionNameError.php`. 
     271 
     272To call a custom view, use this ending: 
     273 
     274    [php] 
     275    return 'MyResult'; 
     276 
     277Symfony will then look for a template called `actionNameMyResult.php`. 
     278 
     279If there is no view to call--for instance, in the case of an action executed in a batch process--the action should end as follows: 
     280 
     281    [php] 
     282    return sfView::NONE; 
     283 
     284No template will be executed in that case. It means that you can bypass completely the view layer and output HTML code directly from an action. As shown in Listing 6-10, symfony provides a specific `renderText()` method for this case. This can be useful when you need extreme responsiveness of the action, such as for Ajax interactions, which will be discussed in Chapter 11. 
     285 
     286Listing 6-10 - Bypassing the View by Echoing the Response and Returning `sfView::NONE` 
     287 
     288    [php] 
     289    public function executeIndex() 
     290    { 
     291      echo "<html><body>Hello, World!</body></html>"; 
     292 
     293      return sfView::NONE; 
     294    } 
     295 
     296    // Is equivalent to 
     297    public function executeIndex() 
     298    { 
     299      return $this->renderText("<html><body>Hello, World!</body></html>"); 
     300    } 
     301 
     302In some cases, you need to send an empty response but with some headers defined in it (especially the `X-JSON` header). Define the headers via the `sfResponse` object, discussed in the next chapter, and return the `sfView::HEADER_ONLY` constant, as shown in Listing 6-11. 
     303 
     304Listing 6-11 - Escaping View Rendering and Sending Only Headers 
     305 
     306    [php] 
     307    public function executeRefresh() 
     308    { 
     309      $output = '<"title","My basic letter"],["name","Mr Brown">'; 
     310      $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')'); 
     311 
     312      return sfView::HEADER_ONLY; 
     313    } 
     314 
     315If the action must be rendered by a specific template, ignore the `return` statement and use the `setTemplate()` method instead. 
     316 
     317    [php] 
     318    $this->setTemplate('myCustomTemplate'); 
     319 
     320### Skipping to Another Action 
     321 
     322In some cases, the action execution ends by requesting a new action execution. For instance, an action handling a form submission in a POST request usually redirects to another action after updating the database. Another example is an action alias: the `index` action is often a way to display a list, and actually forwards to a `list` action. 
     323 
     324The action class provides two methods to execute another action: 
     325 
     326  * If the action forwards the call to another action: 
     327 
     328        [php] 
     329        $this->forward('otherModule', 'index'); 
     330 
     331  * If the action results in a web redirection: 
     332 
     333        [php] 
     334        $this->redirect('otherModule/index'); 
     335        $this->redirect('http://www.google.com/'); 
     336 
     337>**NOTE** 
     338>The code located after a forward or a redirect in an action is never executed. You can consider that these calls are equivalent to a `return` statement. They throw an `sfStopException` to stop the execution of the action; this exception is later caught by symfony and simply ignored. 
     339 
     340The choice between a redirect or a forward is sometimes tricky. To choose the best solution, keep in mind that a forward is internal to the application and transparent to the user. As far as the user is concerned, the displayed URL is the same as the one requested. In contrast, a redirect is a message to the user's browser, involving a new request from it and a change in the final resulting URL. 
     341 
     342If the action is called from a submitted form with `method="post"`, you should always do a redirect. The main advantage is that if the user refreshes the resulting page, the form will not be submitted again; in addition, the back button works as expected by displaying the form and not an alert asking the user if he wants to resubmit a POST request. 
     343 
     344There is a special kind of forward that is used very commonly. The `forward404()` method forwards to a "page not found" action. This method is often called when a parameter necessary to the action execution is not present in the request (thus detecting a wrongly typed URL). Listing 6-12 shows an example of a `show` action expecting an `id` parameter. 
     345 
     346Listing 6-12 - Use of the `forward404()` Method 
     347 
     348    [php] 
     349    public function executeShow() 
     350    { 
     351      $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id')); 
     352      if (!$article) 
     353      { 
     354        $this->forward404(); 
     355      } 
     356    } 
     357 
     358>**TIP** 
     359>If you are looking for the error 404 action and template, you will find them in the `$sf_symfony_ data_dir/modules/default/` directory. You can customize this page by adding a new `default` module to your application, overriding the one located in the framework, and by defining an `error404` action and an error404Success template inside. Alternatively, you can set the error_404_module and error_404_ action constants in the `settings.yml` file to use an existing action. 
     360 
     361Experience shows that, most of the time, an action makes a redirect or a forward after testing something, such as in Listing 6-12. That's why the sfActions class has a few more methods, named `forwardIf()`, `forwardUnless()`, `forward404If()`, `forward404Unless()`, `redirectIf()`, and `redirectUnless()`. These methods simply take an additional parameter representing a condition that triggers the execution if tested true (for the `xxxIf()` methods) or false (for the `xxxUnless()` methods), as illustrated in Listing 6-13. 
     362 
     363Listing 6-13 - Use of the `forward404If()` Method 
     364 
     365    [php] 
     366    // This action is equivalent to the one shown in Listing 6-12 
     367    public function executeShow() 
     368    { 
     369      $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id')); 
     370      $this->forward404If(!$article); 
     371    } 
     372 
     373    // So is this one 
     374    public function executeShow() 
     375    { 
     376      $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id')); 
     377      $this->forward404Unless($article); 
     378    } 
     379 
     380Using these methods will not only keep your code short, but it will also make it more readable. 
     381 
     382>**TIP** 
     383>When the action calls forward404() or its fellow methods, symfony throws an sfError404Exception that manages the 404 response. This means that if you need to display a 404 message from somewhere where you don't want to access the controller, you can just throw a similar exception. 
     384 
     385### Repeating Code for Several Actions of a Module 
     386 
     387The convention to name actions `executeActionName()` (in the case of an `sfActions` class) or execute() (in the case of an sfAction class) guarantees that symfony will find the action method. It gives you the ability to add other methods of your own that will not be considered as actions, as long as they don't start with `execute`. 
     388 
     389There is another useful convention for when you need to repeat several statements in each action before the actual action execution. You can then extract them into the `preExecute()` method of your action class. You can probably guess how to repeat statements after every action is executed: wrap them in a `postExecute()` method. The syntax of these methods is shown in Listing 6-14. 
     390 
     391Listing 6-14 - Using `preExecute`, `postExecute`, and Custom Methods in an Action Class 
     392 
     393    [php] 
     394    class mymoduleActions extends sfActions 
     395    { 
     396      public function preExecute() 
     397      { 
     398        // The code inserted here is executed at the beginning of each action call 
     399        ... 
     400      } 
     401 
     402      public function executeIndex() 
     403      { 
     404        ... 
     405      } 
     406 
     407      public function executeList() 
     408      { 
     409        ... 
     410        $this->myCustomMethod();  // Methods of the action class are accessible 
     411      } 
     412 
     413      public function postExecute() 
     414      { 
     415        // The code inserted here is executed at the end of each action call 
     416        ... 
     417      } 
     418 
     419      protected function myCustomMethod() 
     420      { 
     421        // You can also add your own methods, as long as they don't start with "execute" 
     422        // In that case, it's better to declare them as protected or private 
     423        ... 
     424      } 
     425    } 
     426 
     427Accessing the Request 
     428--------------------- 
     429 
     430You're familiar with the `getRequestParameter('myparam')` method, used to retrieve the value of a request parameter by its name. As a matter of fact, this method is a proxy for a chain of calls to the request's parameter holder `getRequest()->getParameter('myparam')`. The action class has access to the request object, called `sfWebRequest` in symfony, and to all its methods, via the `getRequest()` method. Table 6-1 lists the most useful `sfWebRequest` methods. 
     431 
     432Table 6-1 - Methods of the `sfWebRequest` Object 
     433 
     434Name                             | Function                               |  Sample Output 
     435-------------------------------- | -------------------------------------- | ----------------------------------------------------------------------- 
     436**Request Information**          |                                        | 
     437`getMethod()`                    | Request method                         | Returns `sfRequest::GET` or `sfRequest::POST` constants 
     438`getMethodName()`                | Request method name                    | `'POST'` 
     439`getHttpHeader('Server')`        | Value of a given HTTP header           | `'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'` 
     440`getCookie('foo')`               | Value of a named cookie                | `'bar'` 
     441`isXmlHttpRequest()`*            | Is it an Ajax request?                 | `true` 
     442`isSecure()`                     | Is it an SSL request?                  | `true` 
     443**Request Parameters**           |                                        | 
     444`hasParameter('foo')`            | Is a parameter present in the request? | `true` 
     445`getParameter('foo')`            | Value of a named parameter             | `'bar'` 
     446`getParameterHolder()->getAll()` | Array of all request parameters        | 
     447**URI-Related Information**      |                                        | 
     448`getUri()`                       | Full URI                               | `'http://localhost/myapp_dev.php/mymodule/myaction'` 
     449`getPathInfo()`                  | Path info                              | `'/mymodule/myaction'` 
     450`getReferer()`**                 | Referrer                               | `'http://localhost/myapp_dev.php/'` 
     451`getHost()`                      | Host name                              | `'localhost'` 
     452`getScriptName()`                | Front controller path and name         | `'myapp_dev.php'` 
     453**Client Browser Information**   |                                        | 
     454`getLanguages()`                 | Array of accepted languages            | `Array( ` ` [0] => fr ` ` [1] => fr_FR ` ` [2] => en_US ` ` [3] => en )` 
     455`getCharsets()`                  | Array of accepted charsets             | `Array( ` ` [0] => ISO-8859-1 ` ` [1] => UTF-8 ` ` [2] => * )` 
     456getAcceptableContentTypes()      | Array of accepted content types        | `Array( [0] => text/xml [1] => text/html` 
     457 
     458* *Works only with prototype* 
     459 
     460** *Sometimes blocked by proxies* 
     461 
     462The `sfActions` class offers a few proxies to access the request methods more quickly, as shown in Listing 6-15. 
     463 
     464Listing 6-15 - Accessing the `sfRequest` Object Methods from an Action 
     465 
     466    [php] 
     467    class mymoduleActions extends sfActions 
     468    { 
     469      public function executeIndex() 
     470      { 
     471        $hasFoo = $this->getRequest()->hasParameter('foo'); 
     472        $hasFoo = $this->hasRequestParameter('foo');  // Shorter version 
     473        $foo    = $this->getRequest()->getParameter('foo'); 
     474        $foo    = $this->getRequestParameter('foo');  // Shorter version 
     475      } 
     476    } 
     477 
     478For multipart requests to which users attach files, the `sfWebRequest` object provides a means to access and move these files, as shown in Listing 6-16. 
     479 
     480Listing 6-16 - The `sfWebRequest` Object Knows How to Handle Attached Files 
     481 
     482    [php] 
     483    class mymoduleActions extends sfActions 
     484    { 
     485      public function executeUpload() 
     486      { 
     487        if ($this->getRequest()->hasFiles()) 
     488        { 
     489          foreach ($this->getRequest()->getFileNames() as $fileName) 
     490          { 
     491            $fileSize  = $this->getRequest()->getFileSize($fileName); 
     492            $fileType  = $this->getRequest()->getFileType($fileName); 
     493            $fileError = $this->getRequest()->hasFileError($fileName); 
     494            $uploadDir = sfConfig::get('sf_upload_dir'); 
     495            $this->getRequest()->moveFile('file', $uploadDir.'/'.$fileName); 
     496          } 
     497        } 
     498      } 
     499    } 
     500 
     501You don't have to worry about whether your server supports the `$_SERVER` or the `$_ENV` variables, or about default values or server-compatibility issues--the `sfWebRequest` methods do it all for you. Besides, their names are so evident that you will no longer need to browse the PHP documentation to find out how to get information from the request. 
     502 
     503Сессия пользователя (User Session) 
     504------------ 
     505 
     506Симфони автоматически управляет сессиями пользователей и она способна сохранять данные сессий между запросами. Для этого она использует встроенные в PHP механизмы поддержки сессий и расширяет их, для того, что бы сделать их более настраиваемыми и легкими в использовании. 
     507 
     508Symfony automatically manages user sessions and is able to keep persistent data between requests for users. It uses the built-in PHP session-handling mechanisms and enhances them to make them more configurable and easier to use. 
     509 
     510### Доступ к сессии пользователя - Accessing the User Session 
     511 
     512Доступ к объекту сессии текущего пользователя можно получить при помощи метода `getUser()`, которая возвратит экземпляр класса `sfUser`. Этот класс позволяет сохранять любые атрибуты пользователя. Они будут доступны для других запросов до конца сессии, как показано в Примере 6-17. Атрибуты пользователя могут содержать данные любого типа, представленные в виде срок, массивов и ассоциативных массивов. Они могут быть установлены для каждого индивидуального пользователя, даже если пользователь не регистрировался специальным образом на вашем сайте. 
     513 
     514Пример 6-17 - Объект `sfUser` может содержать индивидуальные атрибуты пользователя, сохраняемые между запросами Object Can Hold Custom User Attributes Existing Across Requests 
     515 
     516    [php] 
     517    class mymoduleActions extends sfActions 
     518    { 
     519      public function executeFirstPage() 
     520      { 
     521        $nickname = $this->getRequestParameter('nickname'); 
     522 
     523        // Store data in the user session 
     524        $this->getUser()->setAttribute('nickname', $nickname); 
     525      } 
     526 
     527      public function executeSecondPage() 
     528      { 
     529        // Retrieve data from the user session with a default value 
     530        $nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward'); 
     531      } 
     532    } 
     533 
     534>**CAUTION** 
     535> Вы можете сохранять объекты в сессии пользователя, но это крайне не рекомендуется. Это потому, что объект сессии сериализуется между запросами и сохраняется в файле. В тот момент, когда сессия десериализуется, класс сохраненного объекта должен быть уже загружен, а это не всегда так. Кроме этого, there can be "stalled" objects if you store Propel objects. 
     536 
     537Как и многие геттеры в symfony, метод `getAttribute()` может принимать второй аргумент, определяющий типовое значение, которое следует использовать, если атрибут не определен. Что бы проверить, определён ли атрибут для текущего пользователя,  используйте метод `hasAttribute()`. The attributes are stored in a parameter holder that can be accessed by the `getAttributeHolder()` method. It allows for easy cleanup of the user attributes with the usual parameter holder methods, as shown in Listing 6-18. 
     538 
     539Пример 6-18 - Удаление данных из сессии пользователя  
     540 
     541    [php] 
     542    class mymoduleActions extends sfActions 
     543    { 
     544      public function executeRemoveNickname() 
     545      { 
     546        $this->getUser()->getAttributeHolder()->remove('nickname'); 
     547      } 
     548 
     549      public function executeCleanup() 
     550      { 
     551        $this->getUser()->getAttributeHolder()->clear(); 
     552      } 
     553    } 
     554 
     555Атрибуты сессии пользователя так же доступны в шаблонах через пременную `$sf_user`, which stores the current `sfUser` object, as shown in Listing 6-19. 
     556 
     557Listing 6-19 - Шаблон так же имеет доступ к атрибутам сессии пользователя - Templates Also Have Access to the User Session Attributes 
     558 
     559    [php] 
     560    <p> 
     561      Hello, <?php echo $sf_user->getAttribute('nickname') ?> 
     562    </p> 
     563 
     564>**NOTE** 
     565>Если вам необходимо сохранить информацию только в течение текущего запроса -- например, что бы передать информацию по цепочке вызовов действий, возможно, вы предпочтёте использовать класс `sfRequest`, у которого так же имеются методы `getAttribute()` и `setAttribute()`. Только атрибуты объекта `sfUser` постоянны в промежутке *между* запросами. 
     566 
     567### Очистка атрибутов - Flash Attributes 
     568 
     569Постоянно повторяющаяся проблема с атрибутами пользователя состоит в том, что необходимо очищать сессию пользователя, когда атрибут больше не нужен. Например, вы можете захотеть запросить подтверждение после обновления данных в форме. Так как действие, обслуживающее форму, выполняет перенаправление на другую страницу, единственный сопособ для передачи информации от действия к действию - сохранить информацию в пользовательской сессии. Но после того, как страница с подтверждением уже показана, вам необходимо очистить атрибут; в противном случае, он будет оставаться в сессии до тех пор, пока она не устареет.  
     570 
     571Флеш-атрибут - это эфемерный атрибут, который вы можете определить и забыть о нём, зная, что он исчезнет при следующем запросе. В вашем действии, определите флеш-атрибут как: 
     572 
     573    [php] 
     574    $this->setFlash('attrib', $value); 
     575 
     576Шаблон будет отрисован и отправлен пользователю, который затем выполнит запрос к другому действию. В этом втором действии, получите значение флеш-атрибута следующим образом: 
     577 
     578    [php] 
     579    $value = $this->getFlash('attrib'); 
     580 
     581Теперь забудьте о нем. После доставки второй странице, флеш-атрибут будет очищен. И даже если вы не обратитесь к нему во время этой второй обработки запроса, флеш-атрибут исчезнет из сессии в любом случае. 
     582 
     583If you need to access a flash attribute from a template, use the `$sf_flash` object: 
     584 
     585    [php] 
     586    <?php if ($sf_flash->has('attrib')): ?> 
     587      <?php echo $sf_flash->get('attrib') ?> 
     588    <?php endif; ?> 
     589 
     590or just: 
     591 
     592    [php] 
     593    <?php echo $sf_flash->get('attrib') ?> 
     594 
     595Flash attributes are a clean way of passing information to the very next request. 
     596 
     597### Session Management 
     598 
     599Symfony's session-handling feature completely masks the client and server storage of the session IDs to the developer. However, if you want to modify the default behaviors of the session-management mechanisms, it is still possible. This is mostly for advanced users. 
     600 
     601On the client side, sessions are handled by cookies. The symfony session cookie is called `symfony`, but you can change its name by editing the `factories.yml` configuration file, as shown in Listing 6-20. 
     602 
     603Listing 6-20 - Changing the Session Cookie Name, in `apps/myapp/config/factories.yml` 
     604 
     605    all: 
     606      storage: 
     607        class: sfSessionStorage 
     608        param: 
     609          session_name: my_cookie_name 
     610 
     611>**TIP** 
     612>The session is started (with the PHP function `session_start()`) only if the `auto_start` parameter is set to true in factories.yml (which is the case by default). If you want to start the user session manually, disable this setting of the storage factory. 
     613 
     614Symfony's session handling is based on PHP sessions. This means that if you want the client-side management of sessions to be handled by URL parameters instead of cookies, you just need to change the use_trans_sid setting in your php.ini. Be aware that this is not recommended. 
     615 
     616    session.use_trans_sid = 1 
     617 
     618On the server side, symfony stores user sessions in files by default. You can store them in your database by changing the value of the `class` parameter in `factories.yml`, as shown in Listing 6-21. 
     619 
     620Listing 6-21 - Changing the Server Session Storage, in `apps/myapp/config/factories.yml` 
     621 
     622    all: 
     623      storage: 
     624        class: sfMySQLSessionStorage 
     625        param: 
     626          db_table: SESSION_TABLE_NAME      # Name of the table storing the sessions 
     627          database: DATABASE_CONNECTION     # Name of the database connection to use 
     628 
     629The available session storage classes are `sfMySQLSessionStorage`, `sfPostgreSQLSessionStorage`, and `sfPDOSessionStorage`; the latter is preferred. The optional `database` setting defines the database connection to be used; symfony will then use `databases.yml` (see Chapter 8) to determine the connection settings (host, database name, user, and password) for this connection. 
     630 
     631Session expiration occurs automatically after sf_timeout seconds. This constant is 30 minutes by default and can be modified for each environment in the `settings.yml` configuration file, as shown in Listing 6-22. 
     632 
     633Listing 6-22 - Changing Session Lifetime, in `apps/myapp/config/settings.yml` 
     634 
     635    default: 
     636      .settings: 
     637        timeout:     1800           # Session lifetime in seconds 
     638 
     639Action Security 
     640--------------- 
     641 
     642The ability to execute an action can be restricted to users with certain privileges. The tools provided by symfony for this purpose allow the creation of secure applications, where users need to be authenticated before accessing some features or parts of the application. Securing an application requires two steps: declaring the security requirements for each action and logging in users with privileges so that they can access these secure actions. 
     643 
     644### Access Restriction 
     645 
     646Before being executed, every action passes by a special filter that checks if the current user has the privileges to access the requested action. In symfony, privileges are composed of two parts: 
     647 
     648  * Secure actions require users to be authenticated. 
     649  * Credentials are named security privileges that allow organizing security by group. 
     650 
     651Restricting access to an action is simply made by creating and editing a YAML configuration file called `security.yml` in the module `config/` directory. In this file, you can specify the security requirements that users must fulfill for each action or for `all` actions. Listing 6-23 shows a sample `security.yml`. 
     652 
     653Listing 6-23 - Setting Access Restrictions, in `apps/myapp/modules/mymodule/config/security.yml` 
     654 
     655    read: 
     656      is_secure:   off       # All users can request the read action 
     657 
     658    update: 
     659      is_secure:   on        # The update action is only for authenticated users 
     660 
     661    delete: 
     662      is_secure:   on        # Only for authenticated users 
     663      credentials: admin     # With the admin credential 
     664 
     665    all: 
     666      is_secure:  off        # off is the default value anyway 
     667 
     668Actions are not secure by default, so when there is no `security.yml` or no mention of an action in it, actions are accessible by everyone. If there is a `security.yml`, symfony looks for the name of the requested action and, if it exists, checks the fulfillment of the security requirements. What happens when a user tries to access a restricted action depends on his credentials: 
     669 
     670  * If the user is authenticated and has the proper credentials, the action is executed. 
     671  * If the user is not identified, he will be redirected to the default login action. 
     672  * If the user is identified but doesn't have the proper credentials, he will be redirected to the default secure action, shown in Figure 6-1. 
     673 
     674The default login and secure pages are pretty simple, and you will probably want to customize them. You can configure which actions are to be called in case of insufficient privileges in the application `settings.yml` by changing the value of the properties shown in Listing 6-24. 
     675 
     676Figure 6-1 - The default secure action page 
     677 
     678![The default secure action page](/images/book/F0601.jpg "The default secure action page") 
     679 
     680Listing 6-24 - Default Security Actions Are Defined in `apps/myapp/config/settings.yml` 
     681 
     682    all: 
     683      .actions: 
     684        login_module:           default 
     685        login_action:           login 
     686 
     687        secure_module:          default 
     688        secure_action:          secure 
     689 
     690### Granting Access 
     691 
     692To get access to restricted actions, users need to be authenticated and/or to have certain credentials. You can extend a user's privileges by calling methods of the `sfUser` object. The authenticated status of the user is set by the `setAuthenticated()` method. Listing 6-25 shows a simple example of user authentication. 
     693 
     694Listing 6-25 - Setting the Authenticated Status of a User 
     695 
     696    [php] 
     697    class myAccountActions extends sfActions 
     698    { 
     699      public function executeLogin() 
     700      { 
     701        if ($this->getRequestParameter('login') == 'foobar') 
     702        { 
     703          $this->getUser()->setAuthenticated(true); 
     704        } 
     705      } 
     706 
     707      public function executeLogout() 
     708      { 
     709        $this->getUser()->setAuthenticated(false); 
     710      } 
     711    } 
     712 
     713Credentials are a bit more complex to deal with, since you can check, add, remove, and clear credentials. Listing 6-26 describes the credential methods of the `sfUser` class. 
     714 
     715Listing 6-26 - Dealing with User Credentials in an Action 
     716 
     717    [php] 
     718    class myAccountActions extends sfActions 
     719    { 
     720      public function executeDoThingsWithCredentials() 
     721      { 
     722        $user = $this->getUser(); 
     723 
     724        // Add one or more credentials 
     725        $user->addCredential('foo'); 
     726        $user->addCredentials('foo', 'bar'); 
     727 
     728        // Check if the user has a credential 
     729        echo $user->hasCredential('foo');                      =>   true 
     730 
     731        // Check if the user has both credentials 
     732        echo $user->hasCredential(array('foo', 'bar'));        =>   true 
     733 
     734        // Check if the user has one of the credentials 
     735        echo $user->hasCredential(array('foo', 'bar'), false); =>   true 
     736 
     737        // Remove a credential 
     738        $user->removeCredential('foo'); 
     739        echo $user->hasCredential('foo');                      =>   false 
     740 
     741        // Remove all credentials (useful in the logout process) 
     742        $user->clearCredentials(); 
     743        echo $user->hasCredential('bar');                      =>   false 
     744      } 
     745    } 
     746 
     747If a user has the `'foo'` credential, that user will be able to access the actions for which the `security.yml` requires that credential. Credentials can also be used to display only authorized content in a template, as shown in Listing 6-27. 
     748 
     749Listing 6-27 - Dealing with User Credentials in a Template 
     750 
     751    [php] 
     752    <ul> 
     753      <li><?php echo link_to('section1', 'content/section1') ?></li> 
     754      <li><?php echo link_to('section2', 'content/section2') ?></li> 
     755      <?php if ($sf_user->hasCredential('section3')): ?> 
     756      <li><?php echo link_to('section3', 'content/section3') ?></li> 
     757      <?php endif; ?> 
     758    </ul> 
     759 
     760As for the authenticated status, credentials are often given to users during the login process. This is why the `sfUser` object is often extended to add login and logout methods, in order to set the security status of users in a central place. 
     761 
     762>**TIP** 
     763>Among the symfony plug-ins, the `sfGuardPlugin` extends the session class to make login and logout easy. Refer to Chapter 17 for more information. 
     764 
     765### Complex Credentials 
     766 
     767The YAML syntax used in the security.yml file allows you to restrict access to users having a combination of credentials, using either AND-type or OR-type associations. With such a combination, you can build a complex workflow and user privilege management system--for instance, a content management system (CMS) back-office accessible only to users with the admin credential, where articles can be edited only by users with the `editor` credential and published only by the ones with the `publisher` credential. Listing 6-28 shows this example. 
     768 
     769Listing 6-28 - Credentials Combination Syntax 
     770 
     771    editArticle: 
     772      credentials: [ admin, editor ]              # admin AND editor 
     773 
     774    publishArticle: 
     775      credentials: [ admin, publisher ]           # admin AND publisher 
     776 
     777    userManagement: 
     778      credentials: [[ admin, superuser ]]         # admin OR superuser 
     779 
     780Each time you add a new level of square brackets, the logic swaps between AND and OR. So you can create very complex credential combinations, such as this: 
     781 
     782    credentials: [[root, [supplier, [owner, quasiowner]], accounts]] 
     783                 # root OR (supplier AND (owner OR quasiowner)) OR accounts 
     784 
     785Validation and Error-Handling Methods 
     786------------------------------------- 
     787 
     788Validating the action input--mostly request parameters--is a repetitive and tedious task. Symfony offers a built-in request validation system, using methods of the action class. 
     789 
     790Let's start with an example. When a user makes a request for `myAction`, symfony always looks for a method called `validateMyAction()` first. If it is found, then symfony executes it. The return value of this validation method determines the next method to be executed: if it returns `true`, then `executeMyAction()` is executed; otherwise, `handleErrorMyAction()` is executed. And, if in the latter case, handleErrorMyAction() doesn't exist, symfony looks for a generic handleError() method. If that doesn't exist either, it simply returns `sfView::ERROR` to render the `myActionError. php` template. Figure 6-2 depicts this process. 
     791 
     792Figure 6-2 - The validation process 
     793 
     794![The validation process](/images/book/F0602.png "The validation process") 
     795 
     796So the key to validation is to respect the naming conventions for the action methods: 
     797 
     798  * `validateActionName` is the validation method, returning `true` or `false`. It is the first method looked for when the action `ActionName` is requested. If it doesn't exist, the action method is executed directly. 
     799  * `handleErrorActionName` is the method called when the validation method fails. If it doesn't exist, the `Error` template is displayed. 
     800  * `executeActionName` is the action method. It must exist for all actions. 
     801 
     802Listing 6-29 shows an example of an action class with validation methods. Whether the validation passes or fails in this example, the `myActionSuccess.php` template will be executed, but not with the same parameters. 
     803 
     804Listing 6-29 - Sample Validation Methods 
     805 
     806    [php] 
     807    class mymoduleActions extends sfActions 
     808    { 
     809      public function validateMyAction() 
     810      { 
     811        return ($this->getRequestParameter('id') > 0); 
     812      } 
     813 
     814      public function handleErrorMyAction() 
     815      { 
     816        $this->message = "Invalid parameters"; 
     817 
     818        return sfView::SUCCESS; 
     819      } 
     820 
     821      public function executeMyAction() 
     822      { 
     823        $this->message = "The parameters are correct"; 
     824      } 
     825    } 
     826 
     827You can put any code you want in the `validate()` methods. Just make sure they return either `true` or `false`. As it is a method of the `sfActions` class, it has access to the `sfRequest` and `sfUser` objects as well, which can be really useful for input and context validation. 
     828 
     829You could use this mechanism to implement form validation (that is, control the values entered by the user in a form before processing it), but this is the type of repetitive task for which symfony provides automated tools, as described in Chapter 10. 
     830 
     831Filters 
     832------- 
     833 
     834The security process can be understood as a filter by which all requests must pass before executing the action. According to some tests executed in the filter, the processing of the request is modified--for instance, by changing the action executed (default/secure instead of the requested action in the case of the security filter). Symfony extends this idea to filter classes. You can specify any number of filter classes to be executed before the action execution or before the response rendering, and do this for every request. You can see filters as a way to package some code, similar to `preExecute()` and `postExecute()`, but at a higher level (for a whole application instead of for a whole module). 
     835 
     836### The Filter Chain 
     837 
     838Symfony actually sees the processing of a request as a chain of filters. When a request is received by the framework, the first filter (which is always the `sfRenderingFilter`) is executed. At some point, it calls the next filter in the chain, then the next, and so on. When the last filter (which is always `sfExecutionFilter`) is executed, the previous filter can finish, and so on back to the rendering filter. Figure 6-3 illustrates this idea with a sequence diagram, using an artificially small filter chain (the real one contains more filters). 
     839 
     840Figure 6-3 - Sample filter chain 
     841 
     842![Sample filter chain](/images/book/F0603.png "Sample filter chain") 
     843 
     844This process justifies the structure of the filter classes. They all extend the `sfFilter` class, and contain one `execute()` method, expecting a `$filterChain` object as parameter. Somewhere in this method, the filter passes to the next filter in the chain by calling `$filterChain->execute()`. See Listing 6-30 for an example. So basically, filters are divided into two parts: 
     845 
     846  * The code before the call to `$filterChain->execute()` executes before the action execution. 
     847  * The code after the call to `$filterChain->execute()` executes after the action execution and before the rendering. 
     848 
     849Listing 6-30 - Filter Class Struture 
     850 
     851    [php] 
     852    class myFilter extends sfFilter 
     853    { 
     854      public function execute ($filterChain) 
     855      { 
     856        // Code to execute before the action execution 
     857        ... 
     858 
     859        // Execute next filter in the chain 
     860        $filterChain->execute(); 
     861 
     862        // Code to execute after the action execution, before the rendering 
     863        ... 
     864      } 
     865    } 
     866 
     867The default filter chain is defined in an application configuration file called `filters.yml`, and is shown in Listing 6-31. This file lists the filters that are to be executed for every request. 
     868 
     869Listing 6-31 - Default Filter Chain, in `myapp/config/filters.yml` 
     870 
     871    rendering: ~ 
     872    web_debug: ~ 
     873    security:  ~ 
     874 
     875    # Generally, you will want to insert your own filters here 
     876 
     877    cache:     ~ 
     878    common:    ~ 
     879    flash:     ~ 
     880    execution: ~ 
     881 
     882These declarations have no parameter (the tilde character, `~`, means "null" in YAML), because they inherit the parameters defined in the symfony core. In the core, symfony defines `class` and `param` settings for each of these filters. For instance, Listing 6-32 shows the default parameters for the `rendering` filter. 
     883 
     884Listing 6-32 - Default Parameters of the rendering Filter, in $sf_symfony_data_dir/config/filters.yml 
     885 
     886    rendering: 
     887      class: sfRenderingFilter   # Filter class 
     888      param:                     # Filter parameters 
     889        type: rendering 
     890 
     891By leaving the empty value (`~`) in the application `filters.yml`, you tell symfony to apply the filter with the default settings defined in the core. 
     892 
     893You can customize the filter chain in various ways: 
     894 
     895  * Disable some filters from the chain by adding an `enabled: off` parameter. For instance, to disable the web debug filter, write: 
     896 
     897        web_debug: 
     898          enabled: off 
     899 
     900  * Do not remove an entry from the `filters.yml` to disable a filter; symfony would throw an exception in this case. 
     901  * Add your own declarations somewhere in the chain (usually after the `security` filter) to add a custom filter (as discussed in the next section). Be aware that the `rendering` filter must be the first entry, and the `execution` filter must be the last entry of the filter chain. 
     902  * Override the default class and parameters of the default filters (notably to modify the security system and use your own security filter). 
     903 
     904>**TIP** 
     905>The `enabled: off` parameter works well to disable your own filters, but you can deactivate the default filters via the `settings.yml` file, by modifying the values of the `web_debug`, `use_security`, `cache`, and `use_flash` settings. This is because each of the default filters has a `condition` parameter that tests the value of these settings. 
     906 
     907### Building Your Own Filter 
     908 
     909It is pretty simple to build a filter. Create a class definition similar to the one shown in Listing 6-30, and place it in one of the project's lib/ folders to take advantage of the autoloading feature. 
     910 
     911As an action can forward or redirect to another action and consequently relaunch the full chain of filters, you might want to restrict the execution of your own filters to the first action call of the request. The `isFirstCall()` method of the `sfFilter` class returns a Boolean for this purpose. This call only makes sense before the action execution. 
     912 
     913These concepts are clearer with an example. Listing 6-33 shows a filter used to auto-log users with a specific `MyWebSite` cookie, which is supposedly created by the login action. It is a rudimentary but working way to implement the "remember me" feature offered in login forms. 
     914 
     915Listing 6-33 - Sample Filter Class File, Saved in `apps/myapp/lib/rememberFilter.class.php` 
     916 
     917    [php] 
     918    class rememberFilter extends sfFilter 
     919    { 
     920      public function execute($filterChain) 
     921      { 
     922        // Execute this filter only once 
     923        if ($this->isFirstCall()) 
     924        { 
     925          // Filters don't have direct access to the request and user objects. 
     926          // You will need to use the context object to get them 
     927          $request = $this->getContext()->getRequest(); 
     928          $user    = $this->getContext()->getUser(); 
     929 
     930          if ($request->getCookie('MyWebSite')) 
     931          { 
     932            // sign in 
     933            $user->setAuthenticated(true); 
     934          } 
     935        } 
     936 
     937        // Execute next filter 
     938        $filterChain->execute(); 
     939      } 
     940    } 
     941 
     942In some cases, instead of continuing the filter chain execution, you will need to forward to a specific action at the end of a filter. `sfFilter` doesn't have a `forward()` method, but `sfController` does, so you can simply do that by calling the following: 
     943 
     944    [php] 
     945    return $this->getContext()->getController()->forward('mymodule', 'myAction'); 
     946 
     947>**NOTE** 
     948>The `sfFilter` class has an `initialize()` method, executed when the filter object is created. You can override it in your custom filter if you need to deal with filter parameters (defined in `filters.yml`, as described next) in your own way. 
     949 
     950### Filter Activation and Parameters 
     951 
     952Creating a filter file is not enough to activate it. You need to add your filter to the filter chain, and for that, you must declare the filter class in the `filters.yml`, located in the application or in the module `config/` directory, as shown in Listing 6-34. 
     953 
     954Listing 6-34 - Sample Filter Activation File, Saved in `apps/myapp/config/filters.yml` 
     955 
     956    rendering: ~ 
     957    web_debug: ~ 
     958    security:  ~ 
     959 
     960    remember:                 # Filters need a unique name 
     961      class: rememberFilter 
     962      param: 
     963        cookie_name: MyWebSite 
     964        condition:   %APP_ENABLE_REMEMBER_ME% 
     965 
     966    cache:     ~ 
     967    common:    ~ 
     968    flash:     ~ 
     969    execution: ~ 
     970 
     971When activated, the filter is executed for each request. The filter configuration file can contain one or more parameter definitions under the `param` key. The filter class has the ability to get the value of these parameters with the getParameter() method. Listing 6-35 demonstrates how to get a filter parameter value. 
     972 
     973Listing 6-35 - Getting the Parameter Value, in `apps/myapp/lib/rememberFilter.class.php` 
     974 
     975    [php] 
     976    class rememberFilter extends sfFilter 
     977    { 
     978      public function execute ($filterChain) 
     979      { 
     980          ... 
     981          if ($request->getCookie($this->getParameter('cookie_name'))) 
     982          ... 
     983      } 
     984    } 
     985 
     986The `condition` parameter is tested by the filter chain to see if the filter must be executed. So your filter declarations can rely on an application configuration, just like the one in Listing 6-34. The remember filter will be executed only if your application `app.yml` shows this: 
     987 
     988    all: 
     989      enable_remember_me: on 
     990 
     991### Sample Filters 
     992 
     993The filter feature is useful to repeat code for every action. For instance, if you use a distant analytics system, you probably need to put a code snippet calling a distant tracker script in every page. You could put this code in the global layout, but then it would be active for all of the application. Alternatively, you could place it in a filter, such as the one shown in Listing 6-36, and activate it on a per-module basis. 
     994 
     995Listing 6-36 - Google Analytics Filter 
     996 
     997    [php] 
     998    class sfGoogleAnalyticsFilter extends sfFilter 
     999    { 
     1000      public function execute($filterChain) 
     1001      { 
     1002        // Nothing to do before the action 
     1003        $filterChain->execute(); 
     1004 
     1005        // Decorate the response with the tracker code 
     1006        $googleCode = ' 
     1007    <script src="http://www.google-analytics.com/urchin.js"  type="text/javascript"> 
     1008    </script> 
     1009    <script type="text/javascript"> 
     1010      _uacct="UA-'.$this->getParameter('google_id').'";urchinTracker(); 
     1011    </script>'; 
     1012        $response = $this->getContext()->getResponse(); 
     1013        $response->setContent(str_ireplace('</body>', $googleCode.'</body>',$response->getContent())); 
     1014       } 
     1015    } 
     1016 
     1017Be aware that this filter is not perfect, as it should not add the tracker on responses that are not HTML. 
     1018 
     1019Another example would be a filter that switches the request to SSL if it is not already, to secure the communication, as shown in Listing 6-37. 
     1020 
     1021Listing 6-37 - Secure Communication Filter 
     1022 
     1023    [php] 
     1024    class sfSecureFilter extends sfFilter 
     1025    { 
     1026      public function execute($filterChain) 
     1027      { 
     1028        $context = $this->getContext(); 
     1029        $request = $context->getRequest(); 
     1030 
     1031        if (!$request->isSecure()) 
     1032        { 
     1033          $secure_url = str_replace('http', 'https', $request->getUri()); 
     1034 
     1035          return $context->getController()->redirect($secure_url); 
     1036          // We don't continue the filter chain 
     1037        } 
     1038        else 
     1039        { 
     1040          // The request is already secure, so we can continue 
     1041          $filterChain->execute(); 
     1042        } 
     1043      } 
     1044    } 
     1045 
     1046Filters are used extensively in plug-ins, as they allow you to extend the features of an application globally. Refer to Chapter 17 to learn more about plug-ins, and see the online wiki ([http://www.symfony-project.com/trac/wiki](http://www.symfony-project.com/trac/wiki)) for more filter examples. 
     1047 
     1048Module Configuration 
     1049-------------------- 
     1050 
     1051A few module behaviors rely on configuration. To modify them, you must create a `module.yml` file in the module's `config/` directory and define settings on a per-environment basis (or under the `all:` header for all environments). Listing 6-38 shows an example of a `module.yml` file for the `mymodule` module. 
     1052 
     1053Listing 6-38 - Module Configuration, in `apps/myapp/modules/mymodule/config/module.yml` 
     1054 
     1055    all:                 # For all environments 
     1056      enabled:     true 
     1057      is_internal: false 
     1058      view_name:   sfPHP 
     1059 
     1060The enabled parameter allows you to disable all actions of a module. All actions are redirected to the `module_disabled_module`/`module_disabled_action` action (as defined in `settings.yml`). 
     1061 
     1062The is_internal parameter allows you to restrict the execution of all actions of a module to internal calls. For example, this is useful for mail actions that you must be able to call from another action, to send an e-mail message, but not from the outside. 
     1063 
     1064The view_name parameter defines the view class. It must inherit from `sfView`. Overriding this value allows you to use other view systems, with other templating engines, such as Smarty. 
     1065 
     1066Summary 
     1067------- 
     1068 
     1069In symfony, the controller layer is split into two parts: the front controller, which is the unique entry point to the application for a given environment, and the actions, which contain the page logic. An action has the ability to determine how its view will be executed, by returning one of the `sfView` constants. Inside an action, you can manipulate the different elements of the context, including the request object (`sfRequest`) and the current user session object (`sfUser`). 
     1070 
     1071Combining the power of the session object, the action object, and the security configuration provides a complete security system, with access restriction and credentials. Special `validate()` and `handleError()` methods in actions allow handling of request validation. And if the `preExecute()` and `postExecute()` methods are made for reusability of code inside a module, the filters authorize the same reusability for all the applications by making controller code executed for every request.