From 372c099ab809d51f32217d3a3be20fb2a11d0f17 Mon Sep 17 00:00:00 2001 From: Daniel Londero Date: Fri, 9 Apr 2010 14:42:59 -0700 Subject: [PATCH 1/8] [doc][1.4] Fixed test link as vhost configuration seen in day 1 --- jobeet/en/03.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jobeet/en/03.markdown b/jobeet/en/03.markdown index 556e5c4..918b27f 100644 --- a/jobeet/en/03.markdown +++ b/jobeet/en/03.markdown @@ -664,7 +664,7 @@ The `actions/actions.class.php` file defines all the available You can now test the job module in a browser: - http://jobeet.localhost/frontend_dev.php/job + http://www.jobeet.com.localhost/frontend_dev.php/job ![Job module](http://www.symfony-project.org/images/jobeet/1_4/03/job.png) From adcf9cb326d059b0cf868dff9e2b42c4181c618e Mon Sep 17 00:00:00 2001 From: Daniel Londero Date: Fri, 18 Jun 2010 22:52:06 +0200 Subject: [PATCH 2/8] [doc-it][1.4] Initial translation for chapter 6 --- .../06-Inside-the-Controller-Layer.markdown | 990 ++++++++++++++++++ 1 file changed, 990 insertions(+) create mode 100644 gentle-introduction/it/06-Inside-the-Controller-Layer.markdown diff --git a/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown b/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown new file mode 100644 index 0000000..608b1a7 --- /dev/null +++ b/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown @@ -0,0 +1,990 @@ +Capitolo 6 - All'interno dello strato controllore +================================================= + +Il controllore in symfony è lo strato che contiene il codice che collega la business logic alla presentazione, esso è diviso in diversi componenti che si usano per diversi scopi: + + * Il fronto controller è il punto d'accesso univoco all'applicazione. Si occupa di caricare la configurazione e determina l'azione da eseguire. + * Le azioni contengono la logica dell'applicazione. Verificano l'integrità della richiesta e preparano i dati necessati allo strato di presentazione. + * Richiesta, risposta e oggetti di sessione permettono l'accesso ai parametri della richiesta, alle intestazioni della risposta ed ai dati persistenti dell'utente. Vengono usati molto spesso nello strato controllore. + * I filtri sono porzioni di codice eseguito ad ogni richiesta, prima o dopo l'azione. Per esempio i filtri di sicurezza e validazione sono usati comunemente nelle applicazioni web. Si può facilmente estendere il framework creando i propri filtri. + +Questo capitolo descrive tutti questi componenti, non fatevi intimidire dal loro numero. Per una pagina semplice molto probabilmente basterà scrivere poche righe di codice nella classe dell'azione, tutto qui. Gli altri componenti del controllore vengono utilizzati solo in situazioni particolari. + +Il Front Controller +------------------- + +Tutte le richieste web vengono gestite da un singolo fron controller, che rappresenta l'unico punto d'accesso per l'intera applicazione in un ambiente. + +Quando il front controller riceve una richiesta utilizza il sistema delle rotte per identificare il nome di un'azione ed il nome di un modulo partendo dall'URL inserito (o cliccato) dall'utente. Per esempio, l'URL della richiesta seguente richiama lo script `index.php` (il front controller) e verrà interpretato come una chiamata all'azione `myAction` del modulo `mymodule`: + + http://localhost/index.php/mymodule/myAction + +Se non si è interessati a conoscere gli internals di symfony, questo è tutto ciò che si deve sapere a proposito del front controller. Si tratta di un componente indispensabile nell'architettura MVC di symfony, raramente sarà necessario modificarlo. Detto questo è possibile passare alla prossima sezione a meno di non essere realmente interessati a sviscerare l'argomento front controller. + +### Il lavoro del front controller in dettaglio + +Il front controller si occupa di distribuire le richieste, questo però significa qualcosa di più della semplice determinazione dell'azione da eseguire. Infatti esegue il codice comune a tutte le azioni, incluso il seguente: + + 1. Carica la classe con la configurazione del progetto e le librerie di symfony. + 2. Crea la configurazione dell'applicazione ed il contesto di symfony. + 3. Carica ed inizializza le classi del core del framework. + 4. Carica la configurazione. + 5. Interpreta l'URL della richiesta per determinare l'azione da eseguire ed i parametri della richiesta. + 6. Se l'azione non esiste redirige all'azione d'errore 404. + 7. Attiva i filtri (per esempio se la richiesta richiede autenticazione). + 8. Esegue i filtri, primo passaggio. + 9. Esegue l'azione e rende la vista. + 10. Esegue i filtri, secondo passaggio. + 11. Ritorna la risposta. + +### Il front controller di default + +Il front controller di default, chiamato `index.php` e posizionato nella directory `web/` del progetto, è un semplice file PHP come si può vedere nel Listing 6-1. + +Listing 6-1 - Il front controller di default per l'ambiente di produzione + + [php] + dispatch(); + +Il front controller crea un'istanza della configurazione dell'applicazione che si occupa dei passaggi dal 2 al 4. La chiamata al metodo `dispatch()` dell'oggetto `sfController` (che è l'oggetto controllore cardine dell'architettura MVC di symfony) smista le richieste, facendosi carico dei punti da 5 a 7. Gli ultimi compiti sono gestiti dalla catena dei filtri, come verrà spiegato più avanti in questo capitolo. + +### Chiamare un diverso front controller per cambiare ambiente + +Esiste un front controller per ogni ambiente. Infatti la reale esistenza di un front controller definisce un ambiente. L'ambiente è definito dal secondo argomento passato alla chiamata del metodo `ProjectConfiguration::getApplicationConfiguration()`. + +Per cambiare l'ambiente in cui si sta visualizzando l'applicazione e sufficiente selezionare un altro front controller. I front controller di default quando si crea una nuova applicazione con il task `generate:app` sono `index.php` per l'ambiente di produzione e `frontend_dev.php` per l'ambiente di sviluppo (ammesso che la vostra applicazione si chiami `frontend`). La configurazione predefinita del `mod_rewrite` userà `index.php` quanto l'URL non conterrà il nome di un front controller. Quindi i seguenti URL visualizzeranno la stessa pagina (`mymodule/index`) nell'ambiente di produzione: + + http://localhost/index.php/mymodule/index + http://localhost/mymodule/index + +e questo URL visualizzerà la stessa pagina nell'ambiente di sviluppo: + + http://localhost/frontend_dev.php/mymodule/index + +Creare un nuovo ambientr è semplice quanto creare un nuovo front controller. Per esempio, potrebbe essere necessario avere un ambiente di staging per permettere ai clienti di testare l'applicazione prima di andare in produzione. Per creare questo ambiente di staging basta copiare `web/frontend_dev.php` in `web/frontend_staging.php` e cambiare il valore del secondo argomento della chiamata a `ProjectConfiguration::getApplicationConfiguration()` in `staging`. Ora in tutti i file di configurazione è possibile aggiungere una nuova sezione `staging:` per impostare valori specifici per questo ambiente, come mostrato nel Listing 6-2. + +Listing 6-2 - Sample `app.yml` with Specific Settings for the Staging Environment + + staging: + mail: + webmaster: dummy@mysite.com + contact: dummy@mysite.com + all: + mail: + webmaster: webmaster@mysite.com + contact: contact@mysite.com + +If you want to see how the application reacts in this new environment, call the related front controller: + + http://localhost/frontend_staging.php/mymodule/index + +Actions +------- + +The actions are the heart of an application, because they contain all the application's logic. They call 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. + +### The Action Class + +Actions 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. + +Listing 6-3 shows an example of an `actions.class.php` file with only an `index` action for the whole `mymodule` module. + +Listing 6-3 - Sample Action Class, in `apps/frontend/modules/mymodule/actions/actions.class.php` + + [php] + class mymoduleActions extends sfActions + { + public function executeIndex($request) + { + // ... + } + } + +>**CAUTION** +>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. + +In 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: + + http://localhost/index.php/mymodule/index + +Adding more actions just means adding more `execute` methods to the `sfActions` object, as shown in Listing 6-4. + +Listing 6-4 - Action Class with Two Actions, in `frontend/modules/mymodule/actions/actions.class.php` + + [php] + class mymoduleActions extends sfActions + { + public function executeIndex($request) + { + // ... + } + + public function executeList($request) + { + // ... + } + } + +If 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. + +Still, the number of actions in a module can be important enough to lead you to split it in two modules. + +>**SIDEBAR** +>Symfony coding standards +> +>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. +> +>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. +> +>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. +> +>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. + +### Alternative Action Class Syntax + +An 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-4 can be written with the two files shown in Listings 6-5 and 6-6. + +Listing 6-5 - Single Action File, in `frontend/modules/mymodule/actions/indexAction.class.php` + + [php] + class indexAction extends sfAction + { + public function execute($request) + { + // ... + } + } + +Listing 6-6 - Single Action File, in `frontend/modules/mymodule/actions/listAction.class.php` + + [php] + class listAction extends sfAction + { + public function execute($request) + { + // ... + } + } + +### Retrieving Information in the Action + +The action class offers a way to access controller-related information and the core symfony objects. Listing 6-7 demonstrates how to use them. + +Listing 6-7 - `sfActions` Common Methods + + [php] + class mymoduleActions extends sfActions + { + public function executeIndex(sfWebRequest $request) + { + // Retrieving request parameters + $password = $request->getParameter('password'); + + // Retrieving controller information + $moduleName = $this->getModuleName(); + $actionName = $this->getActionName(); + + // Retrieving framework core objects + $userSession = $this->getUser(); + $response = $this->getResponse(); + $controller = $this->getController(); + $context = $this->getContext(); + + // Setting action variables to pass information to the template + $this->setVar('foo', 'bar'); + $this->foo = 'bar'; // Shorter version + } + } + +>**SIDEBAR** +>The context singleton +> +>You already saw, in the front controller, a call to `sfContext::createInstance()`. 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: +> +>`sfController`: The controller object (`->getController()`) +> +>`sfRequest`: The request object (`->getRequest()`) +> +>`sfResponse`: The response object (`->getResponse()`) +> +>`sfUser`: The user session object (`->getUser()`) +> +>`sfRouting`: The routing object (`->getRouting()`) +> +>`sfMailer`: The mailer object (`->getMailer()`) +> +>`sfI18N`: The internationalization object (`->getI18N()`) +> +>`sfLogger`: The logger object (`->getLogger()`) +> +>`sfDatabaseConnection`: The database connection (`->getDatabaseConnection()`) +> +>All these core objects are availables through the `sfContext::getInstance()` singleton from any part of the code. However, it's a really bad practice because this will create some hard dependencies making your code really hard to test, reuse and maintain. You will learn in this book how to avoid the usage of `sfContext::getInstance()`. + +### Action Termination + +Various 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. + +If there is a default view to call (this is the most common case), the action should end as follows: + + [php] + return sfView::SUCCESS; + +Symfony 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-8 for examples of successful action termination. + +Listing 6-8 - Actions That Will Call the `indexSuccess.php` and `listSuccess.php` Templates + + [php] + public function executeIndex() + { + return sfView::SUCCESS; + } + + public function executeList() + { + } + +If there is an error view to call, the action should end like this: + + [php] + return sfView::ERROR; + +Symfony will then look for a template called `actionNameError.php`. + +To call a custom view, use this ending: + + [php] + return 'MyResult'; + +Symfony will then look for a template called `actionNameMyResult.php`. + +If 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: + + [php] + return sfView::NONE; + +No template will be executed in that case. It means that you can bypass completely the view layer and set the response HTML code directly from an action. As shown in Listing 6-9, 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. + +Listing 6-9 - Bypassing the View by Echoing the Response and Returning `sfView::NONE` + + [php] + public function executeIndex() + { + $this->getResponse()->setContent("Hello, World!"); + + return sfView::NONE; + } + + // Is equivalent to + public function executeIndex() + { + return $this->renderText("Hello, World!"); + } + +In 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-10. + +Listing 6-10 - Escaping View Rendering and Sending Only Headers + + [php] + public function executeRefresh() + { + $output = '<"title","My basic letter"],["name","Mr Brown">'; + $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')'); + + return sfView::HEADER_ONLY; + } + +If the action must be rendered by a specific template, ignore the `return` statement and use the `setTemplate()` method instead. + + [php] + public function executeIndex() + { + $this->setTemplate('myCustomTemplate'); + } + +With this code, symfony will look for a `myCustomTemplateSuccess.php` file, instead of `indexSuccess.php`. + +### Skipping to Another Action + +In 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. + +The action class provides two methods to execute another action: + + * If the action forwards the call to another action: + + [php] + $this->forward('otherModule', 'index'); + + * If the action results in a web redirection: + + [php] + $this->redirect('otherModule/index'); + $this->redirect('http://www.google.com/'); + + +>**NOTE** +>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. + +The 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. + +If 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. + +There 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-11 shows an example of a `show` action expecting an `id` parameter. + +Listing 6-11 - Use of the `forward404()` Method + + [php] + public function executeShow(sfWebRequest $request) + { + // Doctrine + $article = Doctrine::getTable('Article')->find($request->getParameter('id')); + + // Propel + $article = ArticlePeer::retrieveByPK($request->getParameter('id')); + + if (!$article) + { + $this->forward404(); + } + } + +>**TIP** +>If you are looking for the error 404 action and template, you will find them in the `$sf_symfony_ lib_dir/controller/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. + +Experience 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-12. + +Listing 6-12 - Use of the `forward404If()` Method + + [php] + // This action is equivalent to the one shown in Listing 6-11 + public function executeShow(sfWebRequest $request) + { + $article = Doctrine::getTable('Article')->find($request->getParameter('id')); + $this->forward404If(!$article); + } + + // So is this one + public function executeShow(sfWebRequest $request) + { + $article = Doctrine::getTable('Article')->find($request->getParameter('id')); + $this->forward404Unless($article); + } + +Using these methods will not only keep your code short, but it will also make it more readable. + +>**TIP** +>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. + +### Repeating Code for Several Actions of a Module + +The 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`. + +There 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-13. + +Listing 6-13 - Using `preExecute()`, `postExecute()`, and Custom Methods in an Action Class + + [php] + class mymoduleActions extends sfActions + { + public function preExecute() + { + // The code inserted here is executed at the beginning of each action call + ... + } + + public function executeIndex($request) + { + ... + } + + public function executeList($request) + { + ... + $this->myCustomMethod(); // Methods of the action class are accessible + } + + public function postExecute() + { + // The code inserted here is executed at the end of each action call + ... + } + + protected function myCustomMethod() + { + // You can also add your own methods, as long as they don't start with "execute" + // In that case, it's better to declare them as protected or private + ... + } + } + +>**TIP** +>As the pre/post execute methods are called for **each** actions of the current module, be sure you really need the execute this code for **all** your actions, to avoid unwanted side-effects. + +Accessing the Request +--------------------- + +The first argument passed to any action method is the request object, called `sfWebRequest` in symfony. You're already familiar with the `getParameter('myparam')` method, used to retrieve the value of a request parameter by its name. Table 6-1 lists the most useful `sfWebRequest` methods. + +Table 6-1 - Methods of the `sfWebRequest` Object + +Name | Function | Sample Output +-------------------------------- | -------------------------------------- | ----------------------------------------------------------------------- +**Request Information** | | +`isMethod($method)` | Is it a post or a get? | true or false +`getMethod()` | Request method name | `'POST'` +`getHttpHeader('Server')` | Value of a given HTTP header | `'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'` +`getCookie('foo')` | Value of a named cookie | `'bar'` +`isXmlHttpRequest()`* | Is it an Ajax request? | `true` +`isSecure()` | Is it an SSL request? | `true` +**Request Parameters** | | +`hasParameter('foo')` | Is a parameter present in the request? | `true` +`getParameter('foo')` | Value of a named parameter | `'bar'` +`getParameterHolder()->getAll()` | Array of all request parameters | +**URI-Related Information** | | +`getUri()` | Full URI | `'http://localhost/frontend_dev.php/mymodule/myaction'` +`getPathInfo()` | Path info | `'/mymodule/myaction'` +`getReferer()`** | Referrer | `'http://localhost/frontend_dev.php/'` +`getHost()` | Host name | `'localhost'` +`getScriptName()` | Front controller path and name | `'frontend_dev.php'` +**Client Browser Information** | | +`getLanguages()` | Array of accepted languages | `Array( ` ` [0] => fr ` ` [1] => fr_FR ` ` [2] => en_US ` ` [3] => en )` +`getCharsets()` | Array of accepted charsets | `Array( ` ` [0] => ISO-8859-1 ` ` [1] => UTF-8 ` ` [2] => * )` +getAcceptableContentTypes() | Array of accepted content types | `Array( [0] => text/xml [1] => text/html` + +`*` *Works with prototype, Prototype, Mootools, and jQuery* + +`**` *Sometimes blocked by proxies* + +You 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. + +User Session +------------ + +Symfony 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. + +### Accessing the User Session + +The session object for the current user is accessed in the action with the `getUser()` method and is an instance of the `sfUser` class. This class contains a parameter holder that allows you to store any user attribute in it. This data will be available to other requests until the end of the user session, as shown in Listing 6-14. User attributes can store any type of data (strings, arrays, and associative arrays). They can be set for every individual user, even if that user is not identified. + +Listing 6-14 - The `sfUser` Object Can Hold Custom User Attributes Existing Across Requests + + [php] + class mymoduleActions extends sfActions + { + public function executeFirstPage($request) + { + $nickname = $request->getParameter('nickname'); + + // Store data in the user session + $this->getUser()->setAttribute('nickname', $nickname); + } + + public function executeSecondPage() + { + // Retrieve data from the user session with a default value + $nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward'); + } + } + +>**CAUTION** +>You can store objects in the user session, but it is strongly discouraged. This is because the session object is serialized between requests. When the session is deserialized, the class of the stored objects must already be loaded, and that's not always the case. In addition, there can be "stalled" objects if you store Propel or Doctrine objects. + +Like many getters in symfony, the `getAttribute()` method accepts a second argument, specifying the default value to be used when the attribute is not defined. To check whether an attribute has been defined for a user, use the `hasAttribute()` method. 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-15. + +Listing 6-15 - Removing Data from the User Session + + [php] + class mymoduleActions extends sfActions + { + public function executeRemoveNickname() + { + $this->getUser()->getAttributeHolder()->remove('nickname'); + } + + public function executeCleanup() + { + $this->getUser()->getAttributeHolder()->clear(); + } + } + +The user session attributes are also available in the templates by default via the `$sf_user` variable, which stores the current `sfUser` object, as shown in Listing 6-16. + +Listing 6-16 - Templates Also Have Access to the User Session Attributes + + [php] +

+ Hello, getAttribute('nickname') ?> +

+ +### Flash Attributes + +A recurrent problem with user attributes is the cleaning of the user session once the attribute is not needed anymore. For instance, you may want to display a confirmation after updating data via a form. As the form-handling action makes a redirect, the only way to pass information from this action to the action it redirects to is to store the information in the user session. But once the confirmation message is displayed, you need to clear the attribute; otherwise, it will remain in the session until it expires. + +The flash attribute is an ephemeral attribute that you can define and forget, knowing that it will disappear after the very next request and leave the user session clean for the future. In your action, define the flash attribute like this: + + [php] + $this->getUser()->setFlash('notice', $value); + +The template will be rendered and delivered to the user, who will then make a new request to another action. In this second action, just get the value of the flash attribute like this: + + [php] + $value = $this->getUser()->getFlash('notice'); + +Then forget about it. After delivering this second page, the `notice` flash attribute will be flushed. And even if you don't require it during this second action, the flash will disappear from the session anyway. + +If you need to access a flash attribute from a template, use the `$sf_user` object: + + [php] + hasFlash('notice')): ?> + getFlash('notice') ?> + + +or just: + + [php] + getFlash('notice') ?> + +Flash attributes are a clean way of passing information to the very next request. + +### Session Management + +Symfony'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. + +On 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-17. + +Listing 6-17 - Changing the Session Cookie Name, in `apps/frontend/config/factories.yml` + + all: + storage: + class: sfSessionStorage + param: + session_name: my_cookie_name + +>**TIP** +>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. + +Symfony'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. + + session.use_trans_sid = 1 + +On 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-18. + +Listing 6-18 - Changing the Server Session Storage, in `apps/frontend/config/factories.yml` + + all: + storage: + class: sfMySQLSessionStorage + param: + db_table: session # Name of the table storing the sessions + database: propel # Name of the database connection to use + # Optional parameters + db_id_col: sess_id # Name of the column storing the session id + db_data_col: sess_data # Name of the column storing the session data + db_time_col: sess_time # Name of the column storing the session timestamp + +The `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. + +The available session storage classes are `sfCacheSessionStorage`, `sfMySQLSessionStorage`, `sfMySQLiSessionStorage`, `sfPostgreSQLSessionStorage`, and `sfPDOSessionStorage`; the latter is preferred. To disable session storage completely, you can use the `sfNoStorage` class. + +Session expiration occurs automatically after 30 minutes. This default setting can be modified for each environment in the same `factories.yml` configuration file, but this time in the `user` factory, as shown in Listing 6-19. + +Listing 6-19 - Changing Session Lifetime, in `apps/frontend/config/factories.yml` + + all: + user: + class: myUser + param: + timeout: 1800 # Session lifetime in seconds + +To learn more about factories, refer to Chapter 19. + +Action Security +--------------- + +The 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. + +### Access Restriction + +Before 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: + + * Secure actions require users to be authenticated. + * Credentials are named security privileges that allow organizing security by group. + +Restricting 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-20 shows a sample `security.yml`. + +Listing 6-20 - Setting Access Restrictions, in `apps/frontend/modules/mymodule/config/security.yml` + + read: + is_secure: false # All users can request the read action + + update: + is_secure: true # The update action is only for authenticated users + + delete: + is_secure: true # Only for authenticated users + credentials: admin # With the admin credential + + all: + is_secure: false # false is the default value anyway + +Actions 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: + + * If the user is authenticated and has the proper credentials, the action is executed. + * If the user is not identified, he will be redirected to the default login action. + * 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. + +The 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-21. + +Figure 6-1 - The default secure action page + +![The default secure action page](http://www.symfony-project.org/images/book/1_4/F0601.jpg "The default secure action page") + +Listing 6-21 - Default Security Actions Are Defined in `apps/frontend/config/settings.yml` + + all: + .actions: + login_module: default + login_action: login + + secure_module: default + secure_action: secure + +### Granting Access + +To 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 and can be checked with `isAuthenticated()`. Listing 6-22 shows a simple example of user authentication. + +Listing 6-22 - Setting the Authenticated Status of a User + + [php] + class myAccountActions extends sfActions + { + public function executeLogin($request) + { + if ($request->getParameter('login') === 'foobar') + { + $this->getUser()->setAuthenticated(true); + } + } + + public function executeLogout() + { + $this->getUser()->setAuthenticated(false); + } + } + +Credentials are a bit more complex to deal with, since you can check, add, remove, and clear credentials. Listing 6-23 describes the credential methods of the `sfUser` class. + +Listing 6-23 - Dealing with User Credentials in an Action + + [php] + class myAccountActions extends sfActions + { + public function executeDoThingsWithCredentials() + { + $user = $this->getUser(); + + // Add one or more credentials + $user->addCredential('foo'); + $user->addCredentials('foo', 'bar'); + + // Check if the user has a credential + echo $user->hasCredential('foo'); => true + + // Check if the user has both credentials + echo $user->hasCredential(array('foo', 'bar')); => true + + // Check if the user has one of the credentials + echo $user->hasCredential(array('foo', 'bar'), false); => true + + // Remove a credential + $user->removeCredential('foo'); + echo $user->hasCredential('foo'); => false + + // Remove all credentials (useful in the logout process) + $user->clearCredentials(); + echo $user->hasCredential('bar'); => false + } + } + +If 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-24. + +Listing 6-24 - Dealing with User Credentials in a Template + + [php] + + +As 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. + +>**TIP** +>Among the symfony plug-ins, the [`sfGuardPlugin`](http://www.symfony-project.org/plugins/sfGuardPlugin) and [`sfDoctrineGuardPlugin`](http://www.symfony-project.org/plugins/sfDoctrineGuardPlugin) extend the session class to make login and logout easy. Refer to Chapter 17 for more information. + +### Complex Credentials + +The 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-25 shows this example. + +Listing 6-25 - Credentials Combination Syntax + + editArticle: + credentials: [ admin, editor ] # admin AND editor + + publishArticle: + credentials: [ admin, publisher ] # admin AND publisher + + userManagement: + credentials: [[ admin, superuser ]] # admin OR superuser + +Each 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: + + credentials: [[root, [supplier, [owner, quasiowner]], accounts]] + # root OR (supplier AND (owner OR quasiowner)) OR accounts + +Filters +------- + +The 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). + +### The Filter Chain + +Symfony 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). + +Figure 6-3 - Sample filter chain + +![Sample filter chain](http://www.symfony-project.org/images/book/1_4/F0603.png "Sample filter chain") + +This 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-26 for an example. So basically, filters are divided into two parts: + + * The code before the call to `$filterChain->execute()` executes before the action execution. + * The code after the call to `$filterChain->execute()` executes after the action execution and before the rendering. + +Listing 6-26 - Filter Class Struture + + [php] + class myFilter extends sfFilter + { + public function execute ($filterChain) + { + // Code to execute before the action execution + ... + + // Execute next filter in the chain + $filterChain->execute(); + + // Code to execute after the action execution, before the rendering + ... + } + } + +The default filter chain is defined in an application configuration file called `filters.yml`, and is shown in Listing 6-27. This file lists the filters that are to be executed for every request. + +Listing 6-27 - Default Filter Chain, in `frontend/config/filters.yml` + + rendering: ~ + security: ~ + + # Generally, you will want to insert your own filters here + + cache: ~ + execution: ~ + +These 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-28 shows the default parameters for the `rendering` filter. + +Listing 6-28 - Default Parameters of the rendering Filter, in `sfConfig::get('sf_symfony_lib_dir')/config/config/filters.yml` + + rendering: + class: sfRenderingFilter # Filter class + param: # Filter parameters + type: rendering + +By leaving the empty value (`~`) in the application `filters.yml`, you tell symfony to apply the filter with the default settings defined in the core. + +You can customize the filter chain in various ways: + + * Disable some filters from the chain by adding an `enabled: false` parameter. For instance, to disable the `security` filter, write: + + security: + enabled: false + + * Do not remove an entry from the `filters.yml` to disable a filter; symfony would throw an exception in this case. + * 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. + * Override the default class and parameters of the default filters (notably to modify the security system and use your own security filter). + +### Building Your Own Filter + +It is pretty simple to build a filter. Create a class definition similar to the one shown in Listing 6-26, and place it in one of the project's `lib/` folders to take advantage of the autoloading feature. + +As 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. + +These concepts are clearer with an example. Listing 6-29 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. + +Listing 6-29 - Sample Filter Class File, Saved in `apps/frontend/lib/rememberFilter.class.php` + + [php] + class rememberFilter extends sfFilter + { + public function execute($filterChain) + { + // Execute this filter only once + if ($this->isFirstCall()) + { + // Filters don't have direct access to the request and user objects. + // You will need to use the context object to get them + $request = $this->getContext()->getRequest(); + $user = $this->getContext()->getUser(); + + if ($request->getCookie('MyWebSite')) + { + // sign in + $user->setAuthenticated(true); + } + } + + // Execute next filter + $filterChain->execute(); + } + } + +In 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: + + [php] + return $this->getContext()->getController()->forward('mymodule', 'myAction'); + +>**NOTE** +>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. + +### Filter Activation and Parameters + +Creating 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-30. + +Listing 6-30 - Sample Filter Activation File, Saved in `apps/frontend/config/filters.yml` + + rendering: ~ + security: ~ + + remember: # Filters need a unique name + class: rememberFilter + param: + cookie_name: MyWebSite + condition: %APP_ENABLE_REMEMBER_ME% + + cache: ~ + execution: ~ + +When 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-31 demonstrates how to get a filter parameter value. + +Listing 6-31 - Getting the Parameter Value, in `apps/frontend/lib/rememberFilter.class.php` + + [php] + class rememberFilter extends sfFilter + { + public function execute($filterChain) + { + // ... + + if ($request->getCookie($this->getParameter('cookie_name'))) + { + // ... + } + + // ... + } + } + +The `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-30. The remember filter will be executed only if your application `app.yml` shows this: + + all: + enable_remember_me: true + +### Sample Filters + +The 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-32, and activate it on a per-module basis. + +Listing 6-32 - Google Analytics Filter + + [php] + class sfGoogleAnalyticsFilter extends sfFilter + { + public function execute($filterChain) + { + // Nothing to do before the action + $filterChain->execute(); + + // Decorate the response with the tracker code + $googleCode = ' + + '; + $response = $this->getContext()->getResponse(); + $response->setContent(str_ireplace('', $googleCode.'',$response->getContent())); + } + } + +Be aware that this filter is not perfect, as it should not add the tracker on responses that are not HTML. + +Another 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-33. + +Listing 6-33 - Secure Communication Filter + + [php] + class sfSecureFilter extends sfFilter + { + public function execute($filterChain) + { + $context = $this->getContext(); + $request = $context->getRequest(); + + if (!$request->isSecure()) + { + $secure_url = str_replace('http', 'https', $request->getUri()); + + return $context->getController()->redirect($secure_url); + // We don't continue the filter chain + } + else + { + // The request is already secure, so we can continue + $filterChain->execute(); + } + } + } + +Filters 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. + +Module Configuration +-------------------- + +A 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-34 shows an example of a `module.yml` file for the `mymodule` module. + +Listing 6-34 - Module Configuration, in `apps/frontend/modules/mymodule/config/module.yml` + + all: # For all environments + enabled: true + is_internal: false + view_class: sfPHP + partial_view_class: sf + +The 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`). + +The `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. + +The `view_class` 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. + +The `partial_view_class` parameter defines the view class used for partials of this module. It must inherit from `sfPartialView`. + +Summary +------- + +In 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`). + +Combining the power of the session object, the action object, and the security configuration, symfony provides a complete security system, with access restriction and credentials. 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. From 2aa6361808e2047f84d0fb6ac36eed9cb04d141f Mon Sep 17 00:00:00 2001 From: dlondero Date: Sat, 19 Jun 2010 20:10:57 +0200 Subject: [PATCH 3/8] [doc-it][1.4] Going on with translation for chapter 6 --- .../06-Inside-the-Controller-Layer.markdown | 371 +++++++++--------- 1 file changed, 186 insertions(+), 185 deletions(-) diff --git a/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown b/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown index 608b1a7..2e53ea7 100644 --- a/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown +++ b/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown @@ -39,7 +39,7 @@ Il front controller si occupa di distribuire le richieste, questo però signific ### Il front controller di default -Il front controller di default, chiamato `index.php` e posizionato nella directory `web/` del progetto, è un semplice file PHP come si può vedere nel Listing 6-1. +Il front controller di default, chiamato `index.php` e posizionato nella directory `web/` del progetto, è un semplice file PHP come si può vedere nel Listato 6-1. Listing 6-1 - Il front controller di default per l'ambiente di produzione @@ -65,9 +65,9 @@ e questo URL visualizzerà la stessa pagina nell'ambiente di sviluppo: http://localhost/frontend_dev.php/mymodule/index -Creare un nuovo ambientr è semplice quanto creare un nuovo front controller. Per esempio, potrebbe essere necessario avere un ambiente di staging per permettere ai clienti di testare l'applicazione prima di andare in produzione. Per creare questo ambiente di staging basta copiare `web/frontend_dev.php` in `web/frontend_staging.php` e cambiare il valore del secondo argomento della chiamata a `ProjectConfiguration::getApplicationConfiguration()` in `staging`. Ora in tutti i file di configurazione è possibile aggiungere una nuova sezione `staging:` per impostare valori specifici per questo ambiente, come mostrato nel Listing 6-2. +Creare un nuovo ambiente è semplice quanto creare un nuovo front controller. Per esempio, potrebbe essere necessario avere un ambiente di staging per permettere ai clienti di testare l'applicazione prima di andare in produzione. Per creare questo ambiente di staging basta copiare `web/frontend_dev.php` in `web/frontend_staging.php` e cambiare il valore del secondo argomento della chiamata a `ProjectConfiguration::getApplicationConfiguration()` in `staging`. Ora in tutti i file di configurazione è possibile aggiungere una nuova sezione `staging:` per impostare valori specifici per questo ambiente, come mostrato nel Listato 6-2. -Listing 6-2 - Sample `app.yml` with Specific Settings for the Staging Environment +Listing 6-2 - Esempio di `app.yml` con impostazioni specifiche per l'ambiente di staging staging: mail: @@ -78,22 +78,22 @@ Listing 6-2 - Sample `app.yml` with Specific Settings for the Staging Environmen webmaster: webmaster@mysite.com contact: contact@mysite.com -If you want to see how the application reacts in this new environment, call the related front controller: +Per vedere come l'applicazione reagisce in questo ambiente basta chiamare il front controller relativo: http://localhost/frontend_staging.php/mymodule/index -Actions -------- +Azioni +------ -The actions are the heart of an application, because they contain all the application's logic. They call 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. +Le azioni sono il cuore di un'applicazione, questo perchè contengono tutta la logica dell'applicazione stessa. Si occupano di chiamare il modello e di definire le variabili per la vista. Facendo una richiesta web ad un'applicazione symfony l'URL definisce un'azione ed i parametri della richiesta. -### The Action Class +### La classe dell'azione -Actions 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. +Le azioni sono metodi chiamati `executeActionName` di una classe denominata `moduleNameActions` che eredita dalla classe `sfActions` e raggruppati in moduli. La classe azione di un modulo è memorizzata nel file `actions.class.php` nella directory `actions/` del modulo stesso. -Listing 6-3 shows an example of an `actions.class.php` file with only an `index` action for the whole `mymodule` module. +Listato 6-3 mostra un esempio di file `actions.class.php` con una sola azione `index` per l'intero modulo `mymodule`. -Listing 6-3 - Sample Action Class, in `apps/frontend/modules/mymodule/actions/actions.class.php` +Listing 6-3 - Classe di azione d'esempio, in `apps/frontend/modules/mymodule/actions/actions.class.php` [php] class mymoduleActions extends sfActions @@ -105,15 +105,15 @@ Listing 6-3 - Sample Action Class, in `apps/frontend/modules/mymodule/actions/ac } >**CAUTION** ->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. +>Anche se i nomi dei metodi non sono case-sensitive in PHP, questi lo sono in symfony. Perciò è importante non dimenticare che i metodi delle azioni devono iniziare con un `execute` minuscolo seguito dallo stesso identico nome dell'azione con la prima lettera maiuscola. -In 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: +Per poter richiedere un'azione è necessario invocare lo script del front controller passando come parametri i nomi di un modulo e di un'azione. L'impostazione predefinita non fa altro che appendere la coppia `module_name`/`action_name` allo script. Questo significa che l'azione definita nel Listato 6-4 può essere richiamata con questo URL: http://localhost/index.php/mymodule/index -Adding more actions just means adding more `execute` methods to the `sfActions` object, as shown in Listing 6-4. +Aggiungere nuove azioni significa aggiungere ulteriori metodi `execute` all'oggetto `sfActions` come mostrato nel Listato 6-4. -Listing 6-4 - Action Class with Two Actions, in `frontend/modules/mymodule/actions/actions.class.php` +Listing 6-4 - Classe azione con due azioni, in `frontend/modules/mymodule/actions/actions.class.php` [php] class mymoduleActions extends sfActions @@ -129,26 +129,26 @@ Listing 6-4 - Action Class with Two Actions, in `frontend/modules/mymodule/actio } } -If 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. +Se la dimensione di una classe azione tende a crescere troppo, molto probabilmente, necessita di un po' di refactoring per spostare del codice verso lo strato del modello. Le azioni dovrebbero essere mantenute sempre brevi (non più di alcune righe), mentre tutta la business logic dovrebbe essere nel modello. -Still, the number of actions in a module can be important enough to lead you to split it in two modules. +Nonostante questo il numero di azioni in un modulo potrebbe essere così elevato da spingervi a separarlo in due moduli. >**SIDEBAR** >Symfony coding standards > ->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. +>Negli esempi di codice di questo libro, sarà balzato agli occhi il fatto che le parentesi grafe (`{` e `}`) occupano una riga ciascuna. Questo standard permette una più semplice lettura del codice. > ->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. +>Tra gli altri coding standard del framework ricordiamo l'indentazione che è sempre fatta da due spazi bianchi; le tabulazioni non vengono utilizzate. Questo perchè le tabulazioni hanno spazi diversi a seconda dell'editor di testo utilizzato, inoltre codice in cui l'indentazione è mista tra tabulazioni e spazi bianchi è impossibile da leggere. > ->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. +>I file PHP del core e quelli generati da symfony non terminano con il consueto tag di chiusura `?>`. Questo è possibile perchè non è realmente necessario e perchè potrebbe causare problemi all'output nel caso in cui fossero presenti spazi vuoti dopo il tag stesso. > ->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. +>Prestando davvero molta attenzione sarà facile vedere come una riga di codice non finisca mai con uno spazio vuoto in symfony. La ragione questa volta è molto più banale: le righe che terminano con spazi vuoti si vedono molto male nell'editor di testo di Fabien. -### Alternative Action Class Syntax +### Sintassi alternativa per le classi azione -An 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-4 can be written with the two files shown in Listings 6-5 and 6-6. +Una sintassi alternativa per l'azione è a disposizione per distribuire le azioni in file separati, un file per azione. In questo caso ogni classe azione estende `sfAction` (invece di `sfActions`) ed è chiamata `actionNameAction`. L'attuale metodo azione è semplicemente chiamato `execute`. Il nome del file è lo stesso della classe. Questo significa che l'equivalente del Listato 6-4 può essere scritto con i due file dei Listati 6-5 e 6-6. -Listing 6-5 - Single Action File, in `frontend/modules/mymodule/actions/indexAction.class.php` +Listing 6-5 - File azione singolo, in `frontend/modules/mymodule/actions/indexAction.class.php` [php] class indexAction extends sfAction @@ -159,7 +159,7 @@ Listing 6-5 - Single Action File, in `frontend/modules/mymodule/actions/indexAct } } -Listing 6-6 - Single Action File, in `frontend/modules/mymodule/actions/listAction.class.php` +Listing 6-6 - File azione singolo, in `frontend/modules/mymodule/actions/listAction.class.php` [php] class listAction extends sfAction @@ -170,11 +170,11 @@ Listing 6-6 - Single Action File, in `frontend/modules/mymodule/actions/listActi } } -### Retrieving Information in the Action +### Recuperare informazioni nell'azione -The action class offers a way to access controller-related information and the core symfony objects. Listing 6-7 demonstrates how to use them. +La classe azione mette a disposizione dei modi di accesso alle informazioni relative al controller ed agli oggetti del core di symfony. Il Listato 6-7 mostra come utilizzarli. -Listing 6-7 - `sfActions` Common Methods +Listing 6-7 - Metodi comuni `sfActions` [php] class mymoduleActions extends sfActions @@ -201,42 +201,42 @@ Listing 6-7 - `sfActions` Common Methods } >**SIDEBAR** ->The context singleton +>Il singleton context > ->You already saw, in the front controller, a call to `sfContext::createInstance()`. 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: +>Abbiamo già visto, nel front controller, una chiamata a `sfContext::createInstance()`. Nell'azione il metodo `getContext()` ritorna lo stesso singleton. Questo è un oggetto molto utile che contiene una referenza a tutti gli oggetti del core di symfony associati ad una richiesta mettendo a disposizione una via di accesso ad ognuno di loro: > ->`sfController`: The controller object (`->getController()`) +>`sfController`: L'oggetto controllore (`->getController()`) > ->`sfRequest`: The request object (`->getRequest()`) +>`sfRequest`: L'oggetto richiesta (`->getRequest()`) > ->`sfResponse`: The response object (`->getResponse()`) +>`sfResponse`: L'oggetto risposta (`->getResponse()`) > ->`sfUser`: The user session object (`->getUser()`) +>`sfUser`: L'oggetto della sessione utente (`->getUser()`) > ->`sfRouting`: The routing object (`->getRouting()`) +>`sfRouting`: L'oggetto delle rotte (`->getRouting()`) > ->`sfMailer`: The mailer object (`->getMailer()`) +>`sfMailer`: L'oggetto mailer (`->getMailer()`) > ->`sfI18N`: The internationalization object (`->getI18N()`) +>`sfI18N`: L'oggetto dell'internazionalizzazione (`->getI18N()`) > ->`sfLogger`: The logger object (`->getLogger()`) +>`sfLogger`: L'oggetto logger (`->getLogger()`) > ->`sfDatabaseConnection`: The database connection (`->getDatabaseConnection()`) +>`sfDatabaseConnection`: La connessione al database (`->getDatabaseConnection()`) > ->All these core objects are availables through the `sfContext::getInstance()` singleton from any part of the code. However, it's a really bad practice because this will create some hard dependencies making your code really hard to test, reuse and maintain. You will learn in this book how to avoid the usage of `sfContext::getInstance()`. +>Tutti questi oggetti del core sono disponibili tramite il singleton `sfContext::getInstance()` in ogni parte del codice. Tuttavia è una pratica disdicevole perchè crea dipendenze così forti in grado di rendere il codice davvero difficile da testare, riutilizzare e mantenere. In questo libro si potrà imparare come evitare l'utilizzo di `sfContext::getInstance()`. -### Action Termination +### Terminare l'azione -Various 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. +Alla fine dell'esecuzione di un'azione si possono assumere diversi comportamenti. Il valore ritornato dal metodo dell'azione determina come la vista verrà generata. Le costanti della classe `sfView` vengono utilizzate per specificate quale template utilizzare per mostrare il risultato dell'azione. -If there is a default view to call (this is the most common case), the action should end as follows: +Se esiste una vista predefinita da invocare (questo è il caso più comune), l'azione dovrebbe terminare come segue: [php] return sfView::SUCCESS; -Symfony 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-8 for examples of successful action termination. +Symfony cercherà quindi un template chiamato `actionNameSuccess.php`. Questo è definito come comportamento predefinito, quindi anche omettendo la direttiva `return` nel metodo di un'azione symfony cercherà ancora un template `actionNameSuccess.php`. Le azioni vuote scatenano lo stesso comportamento. Nel Listato 6-8 alcuni esempi di corretta conclusione delle azioni. -Listing 6-8 - Actions That Will Call the `indexSuccess.php` and `listSuccess.php` Templates +Listing 6-8 - Azioni che invocano i template `indexSuccess.php` e `listSuccess.php` [php] public function executeIndex() @@ -248,28 +248,28 @@ Listing 6-8 - Actions That Will Call the `indexSuccess.php` and `listSuccess.php { } -If there is an error view to call, the action should end like this: +Se esiste una vista di errore da invocare, l'azione dovrebbe concludersi così: [php] return sfView::ERROR; -Symfony will then look for a template called `actionNameError.php`. +Symfony cercherà quindi un template chiamato `actionNameError.php`. -To call a custom view, use this ending: +Per chiamare una vista personalizzata usare questo finale: [php] return 'MyResult'; -Symfony will then look for a template called `actionNameMyResult.php`. +Symfony cercherà quindi un template chiamato `actionNameMyResult.php`. -If 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: +Nel caso in cui non esista una vista da chiamare--per esempio nel caso in cui un'azione venga eseguita in un processo batch--l'azione dovrebbe chiudersi come segue: [php] return sfView::NONE; -No template will be executed in that case. It means that you can bypass completely the view layer and set the response HTML code directly from an action. As shown in Listing 6-9, 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. +Nessun template verrà eseguito in questo caso. Significa che è possibile aggirare completamente lo strato della vista ed impostare il codice HTML di risposts direttamente in un'azione. Come mostrato nel listato 6-9, symfony mette a disposizione uno specifico metodo `renderText()` per questo caso. Può essere utile quando si ha bisogno di un'azione estremamente responsiva, come per le interazioni Ajax, che verranno affrontate nel Capitolo 11. -Listing 6-9 - Bypassing the View by Echoing the Response and Returning `sfView::NONE` +Listing 6-9 - Aggirare la vista facendo l'echo della risposta e ritornando `sfView::NONE` [php] public function executeIndex() @@ -285,9 +285,9 @@ Listing 6-9 - Bypassing the View by Echoing the Response and Returning `sfView:: return $this->renderText("Hello, World!"); } -In 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-10. +In alcuni casi è necessario inviare una risposta vuota ma con alcune intestazioni definite in essa (specialmente l'intestazione `X-JSON`). Definire le intestazioni tramite l'oggetto `sfResponse`, di cui si parlerà nel capitolo succesivo, e restituire la costante `sfView::HEADER_ONLY`, come mostrato nel Listato 6-10. -Listing 6-10 - Escaping View Rendering and Sending Only Headers +Listing 6-10 - Evitare la creazione della vista inviando solamente le intestazioni [php] public function executeRefresh() @@ -298,7 +298,7 @@ Listing 6-10 - Escaping View Rendering and Sending Only Headers return sfView::HEADER_ONLY; } -If the action must be rendered by a specific template, ignore the `return` statement and use the `setTemplate()` method instead. +Se l'azione dev'essere presentata da un template specifico ignorare la dichiarazione `return` utilizzando invece il metodo `setTemplate()`. [php] public function executeIndex() @@ -306,20 +306,20 @@ If the action must be rendered by a specific template, ignore the `return` state $this->setTemplate('myCustomTemplate'); } -With this code, symfony will look for a `myCustomTemplateSuccess.php` file, instead of `indexSuccess.php`. +Con questo codice symfony cercherà un file `myCustomTemplateSuccess.php` invece che `indexSuccess.php`. -### Skipping to Another Action +### Saltare ad un'altra azione -In 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. +In alcuni casi l'esecuzione di un'azione termina richiedendo l'esecuzione di un'altra azione. Per esempio, un'azione che gestisce l'invio di un form tramite una richiesta POST generalmente redireziona ad un'altra azione dopo aver aggiornato il database. -The action class provides two methods to execute another action: +La classe azione mette a disposizione due metodi per eseguire un'altra azione: - * If the action forwards the call to another action: + * Se l'azione inoltra la chiamata ad un'altra azione: [php] $this->forward('otherModule', 'index'); - * If the action results in a web redirection: + * Se l'azione termina con una redirezione web: [php] $this->redirect('otherModule/index'); @@ -327,15 +327,15 @@ The action class provides two methods to execute another action: >**NOTE** ->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. +>Il codice situato dopo un forward o un redirect in un'azione non viene mai eseguito. Chiamate di questo tipo possono essere considerate come un `return`. Essi sollevano un `sfStopException` per bloccare l'esecuzione di un'azione; questa eccezione è colta successivamente da symfony e semplicemente ignorata. -The 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. +La scelta tra un redirect ed un forward a volte può essere difficoltosa. Per scegliere la soluzione migliore va ricordato che un forward è interno all'applicazione e totalmente trasparente per l'utente. Fintanto che l'utente è interessato l'URL visualizzato sarà uguale a quello richiesto. Al contrario un redirect è un messaggio al browser dell'utente e coinvolge una nuova richiesta da esso con conseguente cambio di URL finale. -If 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. +Se l'azione è chiamata da un form inviato con `method="post"` sarà necessario ricorrere **sempre** ad un redirect. Il vantaggio principale è che se l'utente rinfresca la pagina con la risposta il form non verrà inviato nuovamente; inoltre il pulsante indietro si comporterà come previsto visualizzando il form e non un avviso che chiede all'utente se vuole inviare nuovamente una richiesta POST. -There 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-11 shows an example of a `show` action expecting an `id` parameter. +Esiste un tipo particolare di forward usato molto spesso. Il metodo `forward404()` inoltra ad un'azione "pagina non trovata". Questo metodo viene chiamato spesso quando un parametro necessario all'esecuzione dell'azione non è presente nella richiesta (individuando così un URL errato). Il Listato 6-11 mostra un esempio di azione `show` che si aspetta un parametro `id`. -Listing 6-11 - Use of the `forward404()` Method +Listing 6-11 - Utilizzo del metodo `forward404()` [php] public function executeShow(sfWebRequest $request) @@ -353,46 +353,47 @@ Listing 6-11 - Use of the `forward404()` Method } >**TIP** ->If you are looking for the error 404 action and template, you will find them in the `$sf_symfony_ lib_dir/controller/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. +>Se siete in cerca dell'azione e del template per l'errore 404 sappiate che si trova nella directory `$sf_symfony_ lib_dir/controller/default/`. +>If you are looking for the error 404 action and template, you will find them in the `$sf_symfony_ lib_dir/controller/default/` directory. È possibile personalizzare questa pagina creando un nuovo modulo `default` all'applicazione, facendo l'override di quella proposta dal framework, e definendo al suo interno un'azione `error404` ed un template error404Success. Altrimenti è possibile impostare le costanti `error_404_module` e `error_404_action` nel file `settings.yml` per utilizzare un'azione esistente. -Experience 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-12. +L'esperienza insegna che, la maggior parte delle volte, un'azione esegue un redirect o un forward dopo aver verificato qualcosa, come nel Listato 6-12. Questo è il motivo percui la classe `sfActions` ha alcuni metodi aggiuntivi chiamati `forwardIf()`, `forwardUnless()`, `forward404If()`, `forward404Unless()`, `redirectIf()`, e `redirectUnless()`. Questi parametri prendono semplicemente un parametro aggiuntivo che rappresenta una condizione in grado di scatenare l'esecuzione se verificato positivamente (per i metodi `xxxIf()`) o negativamente (per i metodi `xxxUnless()`), come illustrato nel Listato 6-12. -Listing 6-12 - Use of the `forward404If()` Method +Listing 6-12 - Utilizzo del metodo `forward404If()` [php] - // This action is equivalent to the one shown in Listing 6-11 + // Questa azione è equivalente a quella presentata nel Listato 6-11 public function executeShow(sfWebRequest $request) { $article = Doctrine::getTable('Article')->find($request->getParameter('id')); $this->forward404If(!$article); } - // So is this one + // Allo stesso modo questa public function executeShow(sfWebRequest $request) { $article = Doctrine::getTable('Article')->find($request->getParameter('id')); $this->forward404Unless($article); } -Using these methods will not only keep your code short, but it will also make it more readable. +Utilizzare questi metodi oltre a mantenere il codice compatto permette di renderlo più leggibile. >**TIP** ->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. +>Quando un'azione invoca `forward404()` o gli altri metodi dello stesso tipo, symfony lancia una `sfError404Exception` in grado di gestire la risposta 404. Questo significa che nel caso in cui fosse necessario visualizzare un messaggio 404 da qualche parte senza necessariamente accedere al controllore è possibile farlo lanciando un'eccezione simile. -### Repeating Code for Several Actions of a Module +### Ripetere codice per diversa azioni di un modulo -The 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`. +La convenzione per la nominazione delle azioni come `executeActionName()` (nel caso delle classi `sfActions`) o `execute()` (nel caso delle classi `sfAction`) garantisce che symfony possa trovare il metodo dell'azione. Permette anche di aggiungere altri metodi che non verranno considerati come azioni a patto che non inizino con `execute`. -There 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-13. +Esiste un'altra utile convenzione quando è necessario ripetere diverse dichiarazioni in ogni azione prima della reale esecuzione. È possibile spostare queste dichiarazioni nel metodo `preExecute()` della classe azione. Allo stesso modo è possibile ripetere delle dichiarazioni dopo l'esezione di ogni azione: basta spostarle nel metodo `postExecute()`. La sintassi di questi metodi è visibile nel Listato 6-13. -Listing 6-13 - Using `preExecute()`, `postExecute()`, and Custom Methods in an Action Class +Listing 6-13 - Utilizzo di `preExecute()`, `postExecute()` e metodi personalizzati nella classe azione [php] class mymoduleActions extends sfActions { public function preExecute() { - // The code inserted here is executed at the beginning of each action call + // Il codice inserito qui viene eseguito all'inizio di ogni azione ... } @@ -404,73 +405,73 @@ Listing 6-13 - Using `preExecute()`, `postExecute()`, and Custom Methods in an A public function executeList($request) { ... - $this->myCustomMethod(); // Methods of the action class are accessible + $this->myCustomMethod(); // I metodi della classe azione sono accessibili } public function postExecute() { - // The code inserted here is executed at the end of each action call + // Il codice inserito qui viene eseguito alla fine di ogni azione ... } protected function myCustomMethod() { - // You can also add your own methods, as long as they don't start with "execute" - // In that case, it's better to declare them as protected or private + // È possibile aggiungere i propri metodi, ammesso che non inizino con "execute" + // In questo caso è consigliabile dichiararli come protected o private ... } } >**TIP** ->As the pre/post execute methods are called for **each** actions of the current module, be sure you really need the execute this code for **all** your actions, to avoid unwanted side-effects. +>Dato che i metodi pre/post esecuzione vengono invocati per **ogni** azione del modulo corrente è necessario assicurarsi di aver realmente bisogno di eseguire questo codice per **tutte** le azioni per evitare inattesi side-effect. -Accessing the Request ---------------------- +Accedere alla richiesta +----------------------- -The first argument passed to any action method is the request object, called `sfWebRequest` in symfony. You're already familiar with the `getParameter('myparam')` method, used to retrieve the value of a request parameter by its name. Table 6-1 lists the most useful `sfWebRequest` methods. +Il primo argomento passato ad ogni metodo di un'azione è l'oggetto della richiesta che in symfony si chiama `sfWebRequest`. Si è gia visto il metodo `getParameter('myparam')` usato per recuperare il valore di un parametro della richiesta usando il suo nome. La Tabella 6-1 elenca i metodi `sfWebRequest` più utili. -Table 6-1 - Methods of the `sfWebRequest` Object +Table 6-1 - Metodi dell'oggetto `sfWebRequest` -Name | Function | Sample Output +Nome | Funzione | Output d'esempio -------------------------------- | -------------------------------------- | ----------------------------------------------------------------------- -**Request Information** | | -`isMethod($method)` | Is it a post or a get? | true or false -`getMethod()` | Request method name | `'POST'` -`getHttpHeader('Server')` | Value of a given HTTP header | `'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'` -`getCookie('foo')` | Value of a named cookie | `'bar'` -`isXmlHttpRequest()`* | Is it an Ajax request? | `true` -`isSecure()` | Is it an SSL request? | `true` -**Request Parameters** | | -`hasParameter('foo')` | Is a parameter present in the request? | `true` -`getParameter('foo')` | Value of a named parameter | `'bar'` -`getParameterHolder()->getAll()` | Array of all request parameters | -**URI-Related Information** | | -`getUri()` | Full URI | `'http://localhost/frontend_dev.php/mymodule/myaction'` +**Informazioni della richiesta** | | +`isMethod($method)` | È una post o una get? | true o false +`getMethod()` | Nome del metodo della richiesta | `'POST'` +`getHttpHeader('Server')` | Valore di un'intestazione HTTP | `'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'` +`getCookie('foo')` | Valore di un cookie | `'bar'` +`isXmlHttpRequest()`* | È una richiesta Ajax? | `true` +`isSecure()` | È una richiesta SSL? | `true` +**Parametri della richiesta** | | +`hasParameter('foo')` | Questo parametro è nella richiesta? | `true` +`getParameter('foo')` | Valore di un parametro | `'bar'` +`getParameterHolder()->getAll()` | Array dei parametri della richiesta | +**Informazioni relative URI** | | +`getUri()` | URI completo | `'http://localhost/frontend_dev.php/mymodule/myaction'` `getPathInfo()` | Path info | `'/mymodule/myaction'` `getReferer()`** | Referrer | `'http://localhost/frontend_dev.php/'` `getHost()` | Host name | `'localhost'` -`getScriptName()` | Front controller path and name | `'frontend_dev.php'` -**Client Browser Information** | | -`getLanguages()` | Array of accepted languages | `Array( ` ` [0] => fr ` ` [1] => fr_FR ` ` [2] => en_US ` ` [3] => en )` -`getCharsets()` | Array of accepted charsets | `Array( ` ` [0] => ISO-8859-1 ` ` [1] => UTF-8 ` ` [2] => * )` -getAcceptableContentTypes() | Array of accepted content types | `Array( [0] => text/xml [1] => text/html` +`getScriptName()` | Front controller path e nome | `'frontend_dev.php'` +**Informazioni Client Browser** | | +`getLanguages()` | Array delle lingue accettate | `Array( ` ` [0] => fr ` ` [1] => fr_FR ` ` [2] => en_US ` ` [3] => en )` +`getCharsets()` | Array dei charsets accettati | `Array( ` ` [0] => ISO-8859-1 ` ` [1] => UTF-8 ` ` [2] => * )` +getAcceptableContentTypes() | Array dei content type accettati | `Array( [0] => text/xml [1] => text/html` -`*` *Works with prototype, Prototype, Mootools, and jQuery* +`*` *Funziona con prototype, Prototype, Mootools e jQuery* -`**` *Sometimes blocked by proxies* +`**` *A volte bloccato dai proxy* -You 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. +Non sarà necessario preoccuparsi del fatto che i propri server supportino le variabili `$_SERVER` o `$_ENV`, oppure dei valori predefiniti o di problemi di compatibilità a livello server--i metodi `sfWebRequest` si occuperanno di tutto. Inoltre i loro nomi sono così espliciti da fare in modo che non sia più necessario controllare la documentazione di PHP per vedere come recuperare dei dati dalla richiesta. -User Session ------------- +Sessione utente +--------------- -Symfony 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. +Symfony gestisce automaticamente le sessioni utente ed è in grado di mantenere dati in modo persistente tra le varie richieste degli utenti. Utilizza i meccanismi di gestione delle sessioni inclusi in PHP e li migliora per renderli più configurabili e facili da usare. -### Accessing the User Session +### Accedere alla sessione utente -The session object for the current user is accessed in the action with the `getUser()` method and is an instance of the `sfUser` class. This class contains a parameter holder that allows you to store any user attribute in it. This data will be available to other requests until the end of the user session, as shown in Listing 6-14. User attributes can store any type of data (strings, arrays, and associative arrays). They can be set for every individual user, even if that user is not identified. +L'oggetto di sessione per l'utente corrente è accessibile nell'azione grazie al metodo `getUser()` ed è un'istanza della classe `sfUser`. Tale classe mette a disposizione un contenitore di parametri che offre la possibilità di memorizzare ogni attributo dell'utente al suo interno. Questi dati saranno disponibili per le altre richieste fino alla fine della sessione utente come mostrato nel Listato 6-14. Gli attributi dell'utente possono memorizzare ogni tipo di dato (stringhe, array, array associativi). Essi possono essere impostati per ogni singolo utente anche nel caso in qui questo non fosse identificato. -Listing 6-14 - The `sfUser` Object Can Hold Custom User Attributes Existing Across Requests +Listing 6-14 - L'oggetto `sfUser` può contenere attributi utenti personalizzati tra le richieste [php] class mymoduleActions extends sfActions @@ -479,23 +480,23 @@ Listing 6-14 - The `sfUser` Object Can Hold Custom User Attributes Existing Acro { $nickname = $request->getParameter('nickname'); - // Store data in the user session + // Memorizza dati nella sessione utente $this->getUser()->setAttribute('nickname', $nickname); } public function executeSecondPage() { - // Retrieve data from the user session with a default value + // Recupera dati dalla sessione utente con un valore predefinito $nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward'); } } >**CAUTION** ->You can store objects in the user session, but it is strongly discouraged. This is because the session object is serialized between requests. When the session is deserialized, the class of the stored objects must already be loaded, and that's not always the case. In addition, there can be "stalled" objects if you store Propel or Doctrine objects. +>È possibile memorizzare oggetti nella sessione utente ma è una pratica fermamente sconsigliata. Questo perchè l'oggetto sessione viene serializzato tra le richieste. Quando l'oggetto viene deserializzato la classe degli oggetti memorizzati deve essere ancora caricata e spesso non è così. Inoltre potrebbero esserci degli oggetti "scaduti" nel caso in cui si fossero memorizzati oggetti di Propel o Doctrine. -Like many getters in symfony, the `getAttribute()` method accepts a second argument, specifying the default value to be used when the attribute is not defined. To check whether an attribute has been defined for a user, use the `hasAttribute()` method. 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-15. +Come molti altri getter in symfony, il metodo `getAttribute()` accetta un secondo argomento per specificare il valore predefinito da utilizzare nel caso in cui l'attributo non fosse definito. Per verificare che un attributo sia stato definito per un utente si può usare il metodo `hasAttribute()`. Gli attributi sono memorizzati in un contenitore di parametri a cui si può accedere con il metodo `getAttributeHolder()`. Permette una semplice pulizia degli attributi degli utenti con i soliti metodi dei contenitori di parametri come mostrato nel Listato 6-15. -Listing 6-15 - Removing Data from the User Session +Listing 6-15 - Rimozione di dati dalla sessione utente [php] class mymoduleActions extends sfActions @@ -511,52 +512,52 @@ Listing 6-15 - Removing Data from the User Session } } -The user session attributes are also available in the templates by default via the `$sf_user` variable, which stores the current `sfUser` object, as shown in Listing 6-16. +Gli attributi della sessione utente sono disponibili anche nei template tramite la variabile `$sf_user` che contiene l'attuale oggetto `sfUser` come mostrato nel Listato 6-16. -Listing 6-16 - Templates Also Have Access to the User Session Attributes +Listing 6-16 - Anche i template hanno accesso agli attributi della sessione utente [php]

Hello, getAttribute('nickname') ?>

-### Flash Attributes +### Attributi flash -A recurrent problem with user attributes is the cleaning of the user session once the attribute is not needed anymore. For instance, you may want to display a confirmation after updating data via a form. As the form-handling action makes a redirect, the only way to pass information from this action to the action it redirects to is to store the information in the user session. But once the confirmation message is displayed, you need to clear the attribute; otherwise, it will remain in the session until it expires. +Un problema ricorrente con gli attributi utente riguarda la pulizia della sessione stessa una volta che l'attributo non sia più necessario. Per esempio, si potrebbe voler mostrare una conferma dopo l'aggiornamento di alcuni dati tramite un form. Dato che l'azione che si occupa di gestire il form esegue un redirect l'unico modo per passare informazioni da questa azione a quella in cui si è rediretti è quello di memorizzare queste informazioni nella sessione utente. Una volta che il messaggio di conferma è stato visualizzato è necessario rimuovere l'attributo altrimenti rimarrà nella sessione fino a quando non sarà scaduta. -The flash attribute is an ephemeral attribute that you can define and forget, knowing that it will disappear after the very next request and leave the user session clean for the future. In your action, define the flash attribute like this: +L'attributo flash è un attributo effimero che può essere definito e dimenticao consci del fatto che scomparirà dopo la successiva richiesta lasciando così la sessione utente pulita per il futuro. Nell'azione un attributo flash si definisce così: [php] $this->getUser()->setFlash('notice', $value); -The template will be rendered and delivered to the user, who will then make a new request to another action. In this second action, just get the value of the flash attribute like this: +Il template verrà restituito e consegnato all'utente, che farà poi una nuova richiesta ad un'altra azione. In questa seconda azione basta semplicemente recuperare il valore dell'attributo flash in questo modo: [php] $value = $this->getUser()->getFlash('notice'); -Then forget about it. After delivering this second page, the `notice` flash attribute will be flushed. And even if you don't require it during this second action, the flash will disappear from the session anyway. +Poi ci si può dimenticare di questo. Dopo la consegna di questa seconda pagina l'attributo flash `notice` verrà eliminato. Ed anche se non fosse necessario durante questa seconda azione, il flash verrebbe comunque eliminato dalla sessione. -If you need to access a flash attribute from a template, use the `$sf_user` object: +Per accedere ad un attributo flash in un template utilizzare l'oggetto `$sf_user`: [php] hasFlash('notice')): ?> getFlash('notice') ?> -or just: +o semplicemente: [php] getFlash('notice') ?> -Flash attributes are a clean way of passing information to the very next request. +Gli attributi flash sono un modo pulito per passare informazioni alla richiesta successiva. -### Session Management +### Gestione delle sessioni -Symfony'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. +La funzionalità di gestione delle sessioni di symfony maschera completamente la memorizzazione degli ID di sessione lato client e lato server nei confronti dello sviluppatore. Tuttavia nel caso in cui si volesse modificare il comportamento predefinito dei meccanismi di gestione delle sessioni si sappia che è comunque possibile. Questa è una cosa principalmente per utenti avanzati. -On 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-17. +Lato client le sessioni sono gestite da cookie. Il cookie di sessione di symfony è chiamato `symfony`, è possibile cambiare questo nome modificanfo il file di configurazione `factories.yml` come mostrato nel Listato 6-17. -Listing 6-17 - Changing the Session Cookie Name, in `apps/frontend/config/factories.yml` +Listing 6-17 - Modificare il nome del cookie di sessione, in `apps/frontend/config/factories.yml` all: storage: @@ -565,15 +566,15 @@ Listing 6-17 - Changing the Session Cookie Name, in `apps/frontend/config/factor session_name: my_cookie_name >**TIP** ->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. +>La sessione viene avviata (tramite la funzione PHP `session_start()`) solo se il parametro `auto_start` è impostato a true in `factories.yml` (è il valore predefinito). Se si volesse far partire la sessione utente in modo manuale basterebbe disabilitare questa impostazione dello storage factory. -Symfony'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. +La gestione delle sessioni di symfony è basata sulle sessioni di PHP. Questo significa che nel caso in cui si volesse far gestire le sessioni lato client dai parametri URL invece che dai cookie, basterebbe cambiar l'impostazione `use_trans_sid` nel file php.ini. Questa è un'impostazione non raccomandata. session.use_trans_sid = 1 -On 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-18. +Lato server symfony memorizza le sessioni utente su file come comportamento predefinito. È possibile memorizzarle sul database cambiando il valore del parametro `class` nel file `factories.yml` come mostrato nel Listato 6-18. -Listing 6-18 - Changing the Server Session Storage, in `apps/frontend/config/factories.yml` +Listing 6-18 - Cambiare server session storage, in `apps/frontend/config/factories.yml` all: storage: @@ -586,64 +587,64 @@ Listing 6-18 - Changing the Server Session Storage, in `apps/frontend/config/fac db_data_col: sess_data # Name of the column storing the session data db_time_col: sess_time # Name of the column storing the session timestamp -The `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. +L'impostazione `database` definisce quale connessione al database utilizzare. Symfony userà così `databases.yml` (vedere Capitolo 8) per determinare i parametri di connessione (host, nome database, utente e password). -The available session storage classes are `sfCacheSessionStorage`, `sfMySQLSessionStorage`, `sfMySQLiSessionStorage`, `sfPostgreSQLSessionStorage`, and `sfPDOSessionStorage`; the latter is preferred. To disable session storage completely, you can use the `sfNoStorage` class. +La classi disponibili per il session storage sono `sfCacheSessionStorage`, `sfMySQLSessionStorage`, `sfMySQLiSessionStorage`, `sfPostgreSQLSessionStorage` e `sfPDOSessionStorage`; l'ultima è quella da preferire. Per disabilitare totalmente il session storage si può utilizzaree la classe `sfNoStorage`. -Session expiration occurs automatically after 30 minutes. This default setting can be modified for each environment in the same `factories.yml` configuration file, but this time in the `user` factory, as shown in Listing 6-19. +Le sessioni scadono automaticamente dopo 30 minuti. Questa impostazione predefinita può essere modificata per ogni ambiente nello stesso file di configurazione `factories.yml`, questa volta però nel factory `user` come mostrato nel Listato 6-19. -Listing 6-19 - Changing Session Lifetime, in `apps/frontend/config/factories.yml` +Listing 6-19 - Modificare la durata delle sessioni, in `apps/frontend/config/factories.yml` all: user: class: myUser param: - timeout: 1800 # Session lifetime in seconds + timeout: 1800 # Durata delle sessioni in secondi -To learn more about factories, refer to Chapter 19. +Per conoscere più a fondo i factory fare riferimento al Capitolo 19. -Action Security ---------------- +Sicurezza delle azioni +---------------------- -The 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. +L'abilità di eseguire un'azione può essere ristretta a utenti con specifici privilegi. Gli strumenti messi a disposizione da symfony per questo scopo permettono la creazione di applicazioni sicure, dove gli utenti devono essere autenticati prima di poter accedere alle funzionalità o a parti dell'applicazione. Mettere in sicurezza un'applicazione richiede due fasi: dichiarare i requisiti di sicurezza per ogni azione ed autenticare gli utenti con determinati privilegi in modo da permettergli l'accesso a queste azioni sicure. -### Access Restriction +### Restrizioni d'accesso -Before 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: +Prima di essere eseguita ogni azione passa attraverso un filtro speciale che controlla se l'utente corrente è in possesso dei privilegi per accedere all'azione richiesta. In symfony i privilegi sono composti da due parti: - * Secure actions require users to be authenticated. - * Credentials are named security privileges that allow organizing security by group. + * Le azioni sicure richiedono che l'utente si autenticato. + * Le credenziali sono determinati privilegi di sicurezza che permettono l'organizzazione della sicurezza in gruppi. -Restricting 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-20 shows a sample `security.yml`. +Restringere l'accesso ad un'azione viene fatto semplicemente creando e modificando un file di configurazione YAML chiamato `security.yml` nella directory `config/` del modulo. In questo file si possono specificare i requisiti di sicurezza che l'utente deve soddisfare per ogni singola azione o per tutte le azioni. Il Listato 6-20 mostra un file `security.yml` d'esempio. -Listing 6-20 - Setting Access Restrictions, in `apps/frontend/modules/mymodule/config/security.yml` +Listing 6-20 - Impostare le restrizioni d'accesso, in `apps/frontend/modules/mymodule/config/security.yml` read: - is_secure: false # All users can request the read action + is_secure: false # Tutti gli utenti possono richiedere l'azione di lettura update: - is_secure: true # The update action is only for authenticated users + is_secure: true # L'azione di update è disponibile solo a utenti autenticati delete: - is_secure: true # Only for authenticated users - credentials: admin # With the admin credential + is_secure: true # Solo per utenti autenticati + credentials: admin # Con credenziali admin all: - is_secure: false # false is the default value anyway + is_secure: false # false è comunque il valore predefinito -Actions 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: +Le azioni non sono sicure in modo predefinito, quindi quando non è presente un `security.yml` o non viene menzionata nessuna azione in esso, le azioni sono accessibili a tutti. Nel caso in cui esista un `security.yml`, symfony cerca il nome dell'azione richiesta e, se esiste, verifica il soddisfacimento dei requisiti di sicurezza. Ciò che accade quando un utente prova ad accedere ad un'azione sicura dipende dalle sue credenziali: - * If the user is authenticated and has the proper credentials, the action is executed. - * If the user is not identified, he will be redirected to the default login action. - * 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. + * Se l'utente è autenticato e detiene le credenziali corrette, l'azione viene eseguita. + * Se l'utente non viene riconosciuto viene rediretto all'azione di login predefinita. + * Se l'utente viene riconosciuto ma non detiene le sufficienti credenziali viene rediretto all'azione secure predefinita, mostrata in Figura 6-1. -The 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-21. +Le pagine predefinite di login e secure sono molto semplici, molto probabilmente si avrà la necessità di personalizzarle. È possibile configurare quali azioni chiamare in caso di privilegi insufficienti nell'applicazione nel file `settings.yml` cambiando il valore delle proprietà mostrate nel Listato 6-21. -Figure 6-1 - The default secure action page +Figure 6-1 - La pagina secure predefinita -![The default secure action page](http://www.symfony-project.org/images/book/1_4/F0601.jpg "The default secure action page") +![La pagina secure predefinita](http://www.symfony-project.org/images/book/1_4/F0601.jpg "La pagina secure predefinita") -Listing 6-21 - Default Security Actions Are Defined in `apps/frontend/config/settings.yml` +Listing 6-21 - Le azioni di sicurezza predefinite sono definite in in `apps/frontend/config/settings.yml` all: .actions: @@ -653,11 +654,11 @@ Listing 6-21 - Default Security Actions Are Defined in `apps/frontend/config/set secure_module: default secure_action: secure -### Granting Access +### Assegnare l'accesso -To 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 and can be checked with `isAuthenticated()`. Listing 6-22 shows a simple example of user authentication. +Per accedere ad azione riservate gli utenti devono essere autenticati e/o possedere alcune credenziali. Estendere i privilegi di un utente è permesso dai metodi dell'oggetto `sfUser`. Lo stato di autenticazione di un utente è impostato dal metodo `setAuthenticated()` e può essere verificato con `isAuthenticated()`. Il Listato 6-22 mostra un semplice esempio di autenticazione. -Listing 6-22 - Setting the Authenticated Status of a User +Listing 6-22 - Impostare lo stato di autenticazione di un utente [php] class myAccountActions extends sfActions @@ -676,9 +677,9 @@ Listing 6-22 - Setting the Authenticated Status of a User } } -Credentials are a bit more complex to deal with, since you can check, add, remove, and clear credentials. Listing 6-23 describes the credential methods of the `sfUser` class. +Le credenziali sono leggermente più complesse da utilizzare dato che si possono compiere diverse azioni su di esse come la verifica, aggiunta, rimozione e reset. Il Listato 6-23 descrive i metodi della classe `sfUser`. -Listing 6-23 - Dealing with User Credentials in an Action +Listing 6-23 - Lavorare con le credenziali utente nell'azione [php] class myAccountActions extends sfActions @@ -687,32 +688,32 @@ Listing 6-23 - Dealing with User Credentials in an Action { $user = $this->getUser(); - // Add one or more credentials + // Aggiungere una o più credenziali $user->addCredential('foo'); $user->addCredentials('foo', 'bar'); - // Check if the user has a credential + // Verificare che l'utente abbia una credenziale echo $user->hasCredential('foo'); => true - // Check if the user has both credentials + // Verificare che l'utente abbia entrambe le credenziali echo $user->hasCredential(array('foo', 'bar')); => true - // Check if the user has one of the credentials + // Verificare che l'utente abbia una delle credenziali echo $user->hasCredential(array('foo', 'bar'), false); => true - // Remove a credential + // Rimuovere una credenziale $user->removeCredential('foo'); echo $user->hasCredential('foo'); => false - // Remove all credentials (useful in the logout process) + // Rimuovere tutte le credenziali (utile nel processo di logout) $user->clearCredentials(); echo $user->hasCredential('bar'); => false } } -If 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-24. +Se un utente ha la credenziale `foo`, questo sarà in grado di accedere alle azioni per le quali il `security.yml` richiede tale credenziale. Le credenziali possono anche essere utilizzate per mostrare nei template contenuti solo agli autorizzati come mostrato nel Listato 6-24. -Listing 6-24 - Dealing with User Credentials in a Template +Listing 6-24 - Lavorare con le credenziali utenti in un template [php]
    @@ -723,16 +724,16 @@ Listing 6-24 - Dealing with User Credentials in a Template
-As 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. +Come per lo stato di autenticato le credenziali sono spesso assegnate all'utente durante il processo di login. Ecco perchè l'oggetto `sfUser` viene spesso esteso per aggiungere i metodi di login e logout in modo da impostare lo stato di sicurezza in un posto centralizzato. >**TIP** ->Among the symfony plug-ins, the [`sfGuardPlugin`](http://www.symfony-project.org/plugins/sfGuardPlugin) and [`sfDoctrineGuardPlugin`](http://www.symfony-project.org/plugins/sfDoctrineGuardPlugin) extend the session class to make login and logout easy. Refer to Chapter 17 for more information. +>Tra i plugin di symfony [`sfGuardPlugin`](http://www.symfony-project.org/plugins/sfGuardPlugin) e [`sfDoctrineGuardPlugin`](http://www.symfony-project.org/plugins/sfDoctrineGuardPlugin) estendono la classe della sessione per semplificare login e logout. Fare riferimento al Capitolo 17 per maggiori informazioni. -### Complex Credentials +### Credenziali complesse -The 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-25 shows this example. +La sintassi YAML utilizzata nel file `security.yml` permette di restringere l'accesso agli utenti in possesso di una combinazione di credenziali utilizzando associazioni di tipo AND o OR. Con la combinazione di queste si può costruire un complesso workflow e sistema di gestione dei privilegi--per esempio il back-office di un content management system (CMS) accessibile solo agli utenti con credenziali amministrative, dove gli articoli possono essere editati solo da utenti con la credenziale `editor` e pubblicati solo da quelli con la credenziale `publisher`. Il Listato 6-25 mostra quest'esempio. -Listing 6-25 - Credentials Combination Syntax +Listing 6-25 - Sintassi per la combinazione di credenziali editArticle: credentials: [ admin, editor ] # admin AND editor @@ -743,7 +744,7 @@ Listing 6-25 - Credentials Combination Syntax userManagement: credentials: [[ admin, superuser ]] # admin OR superuser -Each 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: +Ogni volta che si aggiunge un livello di parentesi quadre l'operatore logico cambia da AND a OR. In questo modo si possono creare combinazioni di credenziali molto complesse, come questa: credentials: [[root, [supplier, [owner, quasiowner]], accounts]] # root OR (supplier AND (owner OR quasiowner)) OR accounts From 00c20dc91a63ba421f3b126fbda6434f31b61f7e Mon Sep 17 00:00:00 2001 From: dlondero Date: Sun, 20 Jun 2010 15:16:21 +0200 Subject: [PATCH 4/8] [doc][1.4] Missing backticks for enabled parameter --- gentle-introduction/en/06-Inside-the-Controller-Layer.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gentle-introduction/en/06-Inside-the-Controller-Layer.markdown b/gentle-introduction/en/06-Inside-the-Controller-Layer.markdown index f130b32..ddab0c3 100644 --- a/gentle-introduction/en/06-Inside-the-Controller-Layer.markdown +++ b/gentle-introduction/en/06-Inside-the-Controller-Layer.markdown @@ -974,7 +974,7 @@ Listing 6-34 - Module Configuration, in `apps/frontend/modules/mymodule/config/m view_class: sfPHP partial_view_class: sf -The 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`). +The `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`). The `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. From e344648dbfba1830f56ce3cf03a0ca652df53e77 Mon Sep 17 00:00:00 2001 From: dlondero Date: Sun, 20 Jun 2010 15:44:01 +0200 Subject: [PATCH 5/8] [doc-it][1.4] Chapter 6 completed --- .../06-Inside-the-Controller-Layer.markdown | 161 +++++++++--------- 1 file changed, 81 insertions(+), 80 deletions(-) diff --git a/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown b/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown index 2e53ea7..7d770da 100644 --- a/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown +++ b/gentle-introduction/it/06-Inside-the-Controller-Layer.markdown @@ -3,8 +3,8 @@ Capitolo 6 - All'interno dello strato controllore Il controllore in symfony è lo strato che contiene il codice che collega la business logic alla presentazione, esso è diviso in diversi componenti che si usano per diversi scopi: - * Il fronto controller è il punto d'accesso univoco all'applicazione. Si occupa di caricare la configurazione e determina l'azione da eseguire. - * Le azioni contengono la logica dell'applicazione. Verificano l'integrità della richiesta e preparano i dati necessati allo strato di presentazione. + * Il front controller è il punto d'accesso univoco all'applicazione. Si occupa di caricare la configurazione e determina l'azione da eseguire. + * Le azioni contengono la logica dell'applicazione. Verificano l'integrità della richiesta e preparano i dati necessari allo strato di presentazione. * Richiesta, risposta e oggetti di sessione permettono l'accesso ai parametri della richiesta, alle intestazioni della risposta ed ai dati persistenti dell'utente. Vengono usati molto spesso nello strato controllore. * I filtri sono porzioni di codice eseguito ad ogni richiesta, prima o dopo l'azione. Per esempio i filtri di sicurezza e validazione sono usati comunemente nelle applicazioni web. Si può facilmente estendere il framework creando i propri filtri. @@ -13,7 +13,7 @@ Questo capitolo descrive tutti questi componenti, non fatevi intimidire dal loro Il Front Controller ------------------- -Tutte le richieste web vengono gestite da un singolo fron controller, che rappresenta l'unico punto d'accesso per l'intera applicazione in un ambiente. +Tutte le richieste web vengono gestite da un singolo front controller, che rappresenta l'unico punto d'accesso per l'intera applicazione in un ambiente. Quando il front controller riceve una richiesta utilizza il sistema delle rotte per identificare il nome di un'azione ed il nome di un modulo partendo dall'URL inserito (o cliccato) dall'utente. Per esempio, l'URL della richiesta seguente richiama lo script `index.php` (il front controller) e verrà interpretato come una chiamata all'azione `myAction` del modulo `mymodule`: @@ -85,7 +85,7 @@ Per vedere come l'applicazione reagisce in questo ambiente basta chiamare il fro Azioni ------ -Le azioni sono il cuore di un'applicazione, questo perchè contengono tutta la logica dell'applicazione stessa. Si occupano di chiamare il modello e di definire le variabili per la vista. Facendo una richiesta web ad un'applicazione symfony l'URL definisce un'azione ed i parametri della richiesta. +Le azioni sono il cuore di un'applicazione, questo perché contengono tutta la logica dell'applicazione stessa. Si occupano di chiamare il modello e di definire le variabili per la vista. Facendo una richiesta web ad un'applicazione symfony l'URL definisce un'azione ed i parametri della richiesta. ### La classe dell'azione @@ -136,11 +136,11 @@ Nonostante questo il numero di azioni in un modulo potrebbe essere così elevato >**SIDEBAR** >Symfony coding standards > ->Negli esempi di codice di questo libro, sarà balzato agli occhi il fatto che le parentesi grafe (`{` e `}`) occupano una riga ciascuna. Questo standard permette una più semplice lettura del codice. +>Negli esempi di codice di questo libro, sarà balzato agli occhi il fatto che le parentesi graffe (`{` e `}`) occupano una riga ciascuna. Questo standard permette una più semplice lettura del codice. > >Tra gli altri coding standard del framework ricordiamo l'indentazione che è sempre fatta da due spazi bianchi; le tabulazioni non vengono utilizzate. Questo perchè le tabulazioni hanno spazi diversi a seconda dell'editor di testo utilizzato, inoltre codice in cui l'indentazione è mista tra tabulazioni e spazi bianchi è impossibile da leggere. > ->I file PHP del core e quelli generati da symfony non terminano con il consueto tag di chiusura `?>`. Questo è possibile perchè non è realmente necessario e perchè potrebbe causare problemi all'output nel caso in cui fossero presenti spazi vuoti dopo il tag stesso. +>I file PHP del core e quelli generati da symfony non terminano con il consueto tag di chiusura `?>`. Questo è possibile perché non è realmente necessario e perché potrebbe causare problemi all'output nel caso in cui fossero presenti spazi vuoti dopo il tag stesso. > >Prestando davvero molta attenzione sarà facile vedere come una riga di codice non finisca mai con uno spazio vuoto in symfony. La ragione questa volta è molto più banale: le righe che terminano con spazi vuoti si vedono molto male nell'editor di testo di Fabien. @@ -223,7 +223,7 @@ Listing 6-7 - Metodi comuni `sfActions` > >`sfDatabaseConnection`: La connessione al database (`->getDatabaseConnection()`) > ->Tutti questi oggetti del core sono disponibili tramite il singleton `sfContext::getInstance()` in ogni parte del codice. Tuttavia è una pratica disdicevole perchè crea dipendenze così forti in grado di rendere il codice davvero difficile da testare, riutilizzare e mantenere. In questo libro si potrà imparare come evitare l'utilizzo di `sfContext::getInstance()`. +>Tutti questi oggetti del core sono disponibili tramite il singleton `sfContext::getInstance()` in ogni parte del codice. Tuttavia è una pratica disdicevole perché crea dipendenze così forti in grado di rendere il codice davvero difficile da testare, riutilizzare e mantenere. In questo libro si potrà imparare come evitare l'utilizzo di `sfContext::getInstance()`. ### Terminare l'azione @@ -267,7 +267,7 @@ Nel caso in cui non esista una vista da chiamare--per esempio nel caso in cui un [php] return sfView::NONE; -Nessun template verrà eseguito in questo caso. Significa che è possibile aggirare completamente lo strato della vista ed impostare il codice HTML di risposts direttamente in un'azione. Come mostrato nel listato 6-9, symfony mette a disposizione uno specifico metodo `renderText()` per questo caso. Può essere utile quando si ha bisogno di un'azione estremamente responsiva, come per le interazioni Ajax, che verranno affrontate nel Capitolo 11. +Nessun template verrà eseguito in questo caso. Significa che è possibile aggirare completamente lo strato della vista ed impostare il codice HTML di risposta direttamente in un'azione. Come mostrato nel listato 6-9, symfony mette a disposizione uno specifico metodo `renderText()` per questo caso. Può essere utile quando si ha bisogno di un'azione estremamente responsiva, come per le interazioni Ajax, che verranno affrontate nel Capitolo 11. Listing 6-9 - Aggirare la vista facendo l'echo della risposta e ritornando `sfView::NONE` @@ -285,7 +285,7 @@ Listing 6-9 - Aggirare la vista facendo l'echo della risposta e ritornando `sfVi return $this->renderText("Hello, World!"); } -In alcuni casi è necessario inviare una risposta vuota ma con alcune intestazioni definite in essa (specialmente l'intestazione `X-JSON`). Definire le intestazioni tramite l'oggetto `sfResponse`, di cui si parlerà nel capitolo succesivo, e restituire la costante `sfView::HEADER_ONLY`, come mostrato nel Listato 6-10. +In alcuni casi è necessario inviare una risposta vuota ma con alcune intestazioni definite in essa (specialmente l'intestazione `X-JSON`). Definire le intestazioni tramite l'oggetto `sfResponse`, di cui si parlerà nel capitolo successivo, e restituire la costante `sfView::HEADER_ONLY`, come mostrato nel Listato 6-10. Listing 6-10 - Evitare la creazione della vista inviando solamente le intestazioni @@ -356,7 +356,7 @@ Listing 6-11 - Utilizzo del metodo `forward404()` >Se siete in cerca dell'azione e del template per l'errore 404 sappiate che si trova nella directory `$sf_symfony_ lib_dir/controller/default/`. >If you are looking for the error 404 action and template, you will find them in the `$sf_symfony_ lib_dir/controller/default/` directory. È possibile personalizzare questa pagina creando un nuovo modulo `default` all'applicazione, facendo l'override di quella proposta dal framework, e definendo al suo interno un'azione `error404` ed un template error404Success. Altrimenti è possibile impostare le costanti `error_404_module` e `error_404_action` nel file `settings.yml` per utilizzare un'azione esistente. -L'esperienza insegna che, la maggior parte delle volte, un'azione esegue un redirect o un forward dopo aver verificato qualcosa, come nel Listato 6-12. Questo è il motivo percui la classe `sfActions` ha alcuni metodi aggiuntivi chiamati `forwardIf()`, `forwardUnless()`, `forward404If()`, `forward404Unless()`, `redirectIf()`, e `redirectUnless()`. Questi parametri prendono semplicemente un parametro aggiuntivo che rappresenta una condizione in grado di scatenare l'esecuzione se verificato positivamente (per i metodi `xxxIf()`) o negativamente (per i metodi `xxxUnless()`), come illustrato nel Listato 6-12. +L'esperienza insegna che, la maggior parte delle volte, un'azione esegue un redirect o un forward dopo aver verificato qualcosa, come nel Listato 6-12. Questo è il motivo per cui la classe `sfActions` ha alcuni metodi aggiuntivi chiamati `forwardIf()`, `forwardUnless()`, `forward404If()`, `forward404Unless()`, `redirectIf()`, e `redirectUnless()`. Questi parametri prendono semplicemente un parametro aggiuntivo che rappresenta una condizione in grado di scatenare l'esecuzione se verificato positivamente (per i metodi `xxxIf()`) o negativamente (per i metodi `xxxUnless()`), come illustrato nel Listato 6-12. Listing 6-12 - Utilizzo del metodo `forward404If()` @@ -382,9 +382,9 @@ Utilizzare questi metodi oltre a mantenere il codice compatto permette di render ### Ripetere codice per diversa azioni di un modulo -La convenzione per la nominazione delle azioni come `executeActionName()` (nel caso delle classi `sfActions`) o `execute()` (nel caso delle classi `sfAction`) garantisce che symfony possa trovare il metodo dell'azione. Permette anche di aggiungere altri metodi che non verranno considerati come azioni a patto che non inizino con `execute`. +La convenzione per la denominazione delle azioni come `executeActionName()` (nel caso delle classi `sfActions`) o `execute()` (nel caso delle classi `sfAction`) garantisce che symfony possa trovare il metodo dell'azione. Permette anche di aggiungere altri metodi che non verranno considerati come azioni a patto che non inizino con `execute`. -Esiste un'altra utile convenzione quando è necessario ripetere diverse dichiarazioni in ogni azione prima della reale esecuzione. È possibile spostare queste dichiarazioni nel metodo `preExecute()` della classe azione. Allo stesso modo è possibile ripetere delle dichiarazioni dopo l'esezione di ogni azione: basta spostarle nel metodo `postExecute()`. La sintassi di questi metodi è visibile nel Listato 6-13. +Esiste un'altra utile convenzione quando è necessario ripetere diverse dichiarazioni in ogni azione prima della reale esecuzione. È possibile spostare queste dichiarazioni nel metodo `preExecute()` della classe azione. Allo stesso modo è possibile ripetere delle dichiarazioni dopo l'esecuzione di ogni azione: basta spostarle nel metodo `postExecute()`. La sintassi di questi metodi è visibile nel Listato 6-13. Listing 6-13 - Utilizzo di `preExecute()`, `postExecute()` e metodi personalizzati nella classe azione @@ -492,7 +492,7 @@ Listing 6-14 - L'oggetto `sfUser` può contenere attributi utenti personalizzati } >**CAUTION** ->È possibile memorizzare oggetti nella sessione utente ma è una pratica fermamente sconsigliata. Questo perchè l'oggetto sessione viene serializzato tra le richieste. Quando l'oggetto viene deserializzato la classe degli oggetti memorizzati deve essere ancora caricata e spesso non è così. Inoltre potrebbero esserci degli oggetti "scaduti" nel caso in cui si fossero memorizzati oggetti di Propel o Doctrine. +>È possibile memorizzare oggetti nella sessione utente ma è una pratica fermamente sconsigliata. Questo perché l'oggetto sessione viene serializzato tra le richieste. Quando l'oggetto viene deserializzato la classe degli oggetti memorizzati deve essere ancora caricata e spesso non è così. Inoltre potrebbero esserci degli oggetti "scaduti" nel caso in cui si fossero memorizzati oggetti di Propel o Doctrine. Come molti altri getter in symfony, il metodo `getAttribute()` accetta un secondo argomento per specificare il valore predefinito da utilizzare nel caso in cui l'attributo non fosse definito. Per verificare che un attributo sia stato definito per un utente si può usare il metodo `hasAttribute()`. Gli attributi sono memorizzati in un contenitore di parametri a cui si può accedere con il metodo `getAttributeHolder()`. Permette una semplice pulizia degli attributi degli utenti con i soliti metodi dei contenitori di parametri come mostrato nel Listato 6-15. @@ -555,7 +555,7 @@ Gli attributi flash sono un modo pulito per passare informazioni alla richiesta La funzionalità di gestione delle sessioni di symfony maschera completamente la memorizzazione degli ID di sessione lato client e lato server nei confronti dello sviluppatore. Tuttavia nel caso in cui si volesse modificare il comportamento predefinito dei meccanismi di gestione delle sessioni si sappia che è comunque possibile. Questa è una cosa principalmente per utenti avanzati. -Lato client le sessioni sono gestite da cookie. Il cookie di sessione di symfony è chiamato `symfony`, è possibile cambiare questo nome modificanfo il file di configurazione `factories.yml` come mostrato nel Listato 6-17. +Lato client le sessioni sono gestite da cookie. Il cookie di sessione di symfony è chiamato `symfony`, è possibile cambiare questo nome modificando il file di configurazione `factories.yml` come mostrato nel Listato 6-17. Listing 6-17 - Modificare il nome del cookie di sessione, in `apps/frontend/config/factories.yml` @@ -749,96 +749,97 @@ Ogni volta che si aggiunge un livello di parentesi quadre l'operatore logico cam credentials: [[root, [supplier, [owner, quasiowner]], accounts]] # root OR (supplier AND (owner OR quasiowner)) OR accounts -Filters -------- +Filtri +------ -The 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). +Il processo di sicurezza può essere interpretato come un filtro dal quale devono passare tutte le richieste prima di eseguire l'azione associata. In funzione di alcuni test eseguiti nel filtro l'esecuzione della richiesta viene modificata--per esempio, cambiando l'azione eseguita (`default`/`secure` invece dell'azione richiesta nel caso in cui il filtro di sicurezza lo richieda). Symfony estende quest'idea alle classi di filtri. Si può specificare un numero qualsiasi di filtri da eseguire prima dell'esecuzione dell'azione o prima di restituire la risposta, ripetendolo per ogni richiesta. I filtri possono essere visti come un modo per impacchettare del codice, come si fa con `preExecute()` and `postExecute()`, ma ad un livello più alto (per l'intera applicazione invece che per un singolo modulo). -### The Filter Chain +### La catena dei filtri -Symfony 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). +Symfony attualmente vede il processare una richiesta come una catena di filtri. Quando una richiesta viene ricevuta dal framework il primo filtro (che è sempre il `sfRenderingFilter`) viene eseguito. Ad un certo punto questo chiama il prossimo filtro nella catena, poi il successivo e via dicendo. Quando l'ultimo filtro (che è sempre `sfExecutionFilter`) viene eseguito quello procedente può terminare, e via così fino al filtro di rendering. La Figura 6-3 illustra l'idea di fondo con un diagramma di sequenza, utilizzando una piccola ed ipotetica catena di filtri (quella reale ne contiene molti di più). -Figure 6-3 - Sample filter chain +Figure 6-3 - Catena di filtri d'esempio -![Sample filter chain](http://www.symfony-project.org/images/book/1_4/F0603.png "Sample filter chain") +![Catena di filtri d'esempio](http://www.symfony-project.org/images/book/1_4/F0603.png "Catena di filtri d'esempio") -This 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-26 for an example. So basically, filters are divided into two parts: +Questo processo giustifica la struttura delle classi dei filtri. Tutte quante estendono la classe `sfFilter` e contengono un metodo `execute()` che si aspetta un oggetto di tipo `$filterChain` come parametro. Ad un certo punto in questo in questo metodo il filtro passa il controllo al filtro successivo invocando`$filterChain->execute()`. +Confrontare il Listato 6-26 per un esempio. Quindi i filtri sono divisi principalmente in due parti: - * The code before the call to `$filterChain->execute()` executes before the action execution. - * The code after the call to `$filterChain->execute()` executes after the action execution and before the rendering. + * Il codice prima della chiamata a `$filterChain->execute()` viene eseguito prima dell'esecuzione dell'azione. + * Il codice dopo la chiamata a `$filterChain->execute()` viene eseguito dopo l'esecuzione dell'azione e prima del rendering. -Listing 6-26 - Filter Class Struture +Listing 6-26 - Struttura di una classe filtro [php] class myFilter extends sfFilter { public function execute ($filterChain) { - // Code to execute before the action execution + // Codice da eseguire prima dell'esecuzione dell'azione ... - // Execute next filter in the chain + // Esegue il prossimo filtro della catena $filterChain->execute(); - // Code to execute after the action execution, before the rendering + // Codice da eseguire dopo l'esecuzione dell'azione e prima del rendering ... } } -The default filter chain is defined in an application configuration file called `filters.yml`, and is shown in Listing 6-27. This file lists the filters that are to be executed for every request. +La catena dei filtri predefinita è impostata in un file di configurazione dell'applicazione chiamato `filters.yml`, e viene mostrato nel Listato 6-27. Questo file elenca i filtri da eseguire per ogni richiesta. -Listing 6-27 - Default Filter Chain, in `frontend/config/filters.yml` +Listing 6-27 - Catena dei filtri predefinita, in `frontend/config/filters.yml` rendering: ~ security: ~ - # Generally, you will want to insert your own filters here + # Generalmente, si vorranno inserire i propri filtri qui cache: ~ execution: ~ -These 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-28 shows the default parameters for the `rendering` filter. +Queste dichiarazioni non hanno parametri (il carattere tilde `~` significa "null" in YAML) perché ereditano i parametri definiti nel core di symfony. Nel core symfony definisce le impostazioni `class` e `param` per ognuno di questi filtri. Per esempio il Listato 6-28 mostra i parametri predefiniti per il filtro `rendering`. -Listing 6-28 - Default Parameters of the rendering Filter, in `sfConfig::get('sf_symfony_lib_dir')/config/config/filters.yml` +Listing 6-28 - Parametri predefiniti per il filtro rendering, in `sfConfig::get('sf_symfony_lib_dir')/config/config/filters.yml` rendering: - class: sfRenderingFilter # Filter class - param: # Filter parameters + class: sfRenderingFilter # Classe filtro + param: # Parametri dei filtri type: rendering -By leaving the empty value (`~`) in the application `filters.yml`, you tell symfony to apply the filter with the default settings defined in the core. +Lasciando il valore nullo (`~`) nel file `filters.yml` dell'applicazione, si comunica a symfony l'intenzione di volere applicare il filtro con le impostazioni predefinite dal core. -You can customize the filter chain in various ways: +La catena dei filtri può essere personalizzata in varie maniere: - * Disable some filters from the chain by adding an `enabled: false` parameter. For instance, to disable the `security` filter, write: + * Disabilitare alcuni filtri dalla catena aggiungendo il parametro `enabled: false`. Se per esempio si volesse disabilitare il filtro `security` basterebbe scrivere: security: enabled: false - * Do not remove an entry from the `filters.yml` to disable a filter; symfony would throw an exception in this case. - * 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. - * Override the default class and parameters of the default filters (notably to modify the security system and use your own security filter). + * Non va rimossa la dichiarazione dal file `filters.yml` per disabilitare un filtro; symfony solleverebbe un'eccezione in questo caso. + * Aggiungere le proprie dichiarazioni in un certo punto della catena (di solito dopo il filtro `security`) per includere un filtro personalizzato (come vedremo nella prossima sezione). Prestare attenzione al fatto che il filtro `rendering` sia sempre in prima posizione, così come il filtro `execution` sia in ultima posizione nella catena dei filtri. + * Fare l'override della classe predefinita e dei parametri dei filtri predefiniti (in particolare per modificare il sistema di sicurezza ed utilizzare i propri filtri). -### Building Your Own Filter +### Costruire i propri filtri -It is pretty simple to build a filter. Create a class definition similar to the one shown in Listing 6-26, and place it in one of the project's `lib/` folders to take advantage of the autoloading feature. +Costruire un proprio filtro è una cosa piuttosto semplice. Creare una classe definita in modo simile a quanto mostrato nel Listato 6-26 e posizionarla in una delle directory `lib/` del progetto per sfruttare i vantaggi dell'autoloading. -As 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. +Dato che un'azione può fare il forward o il redirect ad un'altra azione e conseguentemente rilanciare l'intera catena di filtri potrebbe essere necessario limitare l'esecuzione dei propri filtri solamente alla prima azione chiamata dalla richiesta. Il metodo `isFirstCall()` della classe `sfFilter` restituisce un Booleano per questo scopo. Questa chiamata ha senso solamente prima dell'esecuzione dell'azione. -These concepts are clearer with an example. Listing 6-29 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. +Questi concetti saranno più chiari con un esempio. Il Listato 6-29 mostra un filtro utilizzato per l'auto-login degli utenti con un cookie specifico `MyWebSite` che supponiamo sia creato dall'azione di login. È un modo tanto rudimentale quanto funzionante per implementare la funzionalità "remember me" offerta nei moduli di login. -Listing 6-29 - Sample Filter Class File, Saved in `apps/frontend/lib/rememberFilter.class.php` +Listing 6-29 - Classe filtro d'esempio, salvata in `apps/frontend/lib/rememberFilter.class.php` [php] class rememberFilter extends sfFilter { public function execute($filterChain) { - // Execute this filter only once + // Esegue questo filtro solo una volta if ($this->isFirstCall()) { - // Filters don't have direct access to the request and user objects. - // You will need to use the context object to get them + // I filtri non hanno accesso diretto agli oggetti richiesta e utente + // Sarà necessario ricorrere all'utilizzo dell'oggetto context $request = $this->getContext()->getRequest(); $user = $this->getContext()->getUser(); @@ -849,29 +850,29 @@ Listing 6-29 - Sample Filter Class File, Saved in `apps/frontend/lib/rememberFil } } - // Execute next filter + // Esegue il filtro successivo $filterChain->execute(); } } -In 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: +In alcuni casi, invece che continuare l'esecuzione della catena di filtri, potrebbe essere necessario il forward ad una specifica azione alla fine del filtro. `sfFilter` non ha un metodo `forward()`, tuttavia `sfController` lo ha, quindi questo può essere fatto semplicemente chiamando: [php] return $this->getContext()->getController()->forward('mymodule', 'myAction'); >**NOTE** ->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. +>La classe `sfFilter` ha un metodo `initialize()` eseguito alla creazione dell'oggetto filtro. È possibile eseguire l'override del metodo nei filtri personalizzati ne caso in cui fosse necessario lavorare con i parametri dei filtri (definiti in `filters.yml`, come si vedrà in seguito) in modo personale. -### Filter Activation and Parameters +### Attivazione dei filtri e parametri -Creating 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-30. +Creare il file di un filtro non è una condizione sufficiente per attivarlo. Il filtro va aggiunto alla catena di filtri, va dichiarata la classe in `filters.yml`, raggiungibile nella directory `config/` dell'applicazione o del modulo, come mostrato nel Listato 6-30. -Listing 6-30 - Sample Filter Activation File, Saved in `apps/frontend/config/filters.yml` +Listing 6-30 - File d'esempio per l'attivazione di un filtro, salvato in `apps/frontend/config/filters.yml` rendering: ~ security: ~ - remember: # Filters need a unique name + remember: # I filtri hanno bisogno di nomi univoci class: rememberFilter param: cookie_name: MyWebSite @@ -880,9 +881,9 @@ Listing 6-30 - Sample Filter Activation File, Saved in `apps/frontend/config/fil cache: ~ execution: ~ -When 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-31 demonstrates how to get a filter parameter value. +Quando attivo il filtro viene eseguito per ogni singola richiesta. Il file di configurazione di un filtro può contenere una o più definizioni di parametri sotto la chiave `param`. La classe filtro è in grado di recuperare il valore di questi parametri con il metodo `getParameter()`. Il Listato 6-31 dimostra come recuperare il valore di un parametro di un filtro. -Listing 6-31 - Getting the Parameter Value, in `apps/frontend/lib/rememberFilter.class.php` +Listing 6-31 - Recuperare il valore di un parametro, in `apps/frontend/lib/rememberFilter.class.php` [php] class rememberFilter extends sfFilter @@ -900,26 +901,26 @@ Listing 6-31 - Getting the Parameter Value, in `apps/frontend/lib/rememberFilter } } -The `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-30. The remember filter will be executed only if your application `app.yml` shows this: +Il parametro `condition` viene verificato dalla catena dei filtri per capire se il filtro in questione debba essere eseguito. Quindi le dichiarazioni dei filtri possono contare su una configurazione dell'applicazione proprio uguale a quella del Listato 6-30. Il filtro remember verrà eseguito solamente se nell'applicazione il file `app.yml` contiene questo: all: enable_remember_me: true -### Sample Filters +### Filtri d'esempio -The 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-32, and activate it on a per-module basis. +La funzionalità dei filtri è utile per ripetere del codice per ogni azione. Per esempio, se si utilizzasse un sistema di statistiche esterno, molto probabilmente sarebbe necessario inserire una porzione di codice in grado di richiamare uno script tracker esterno in ogni pagina. Questo codice potrebbe essere inserito nel layout globale, tuttavia in questo modo sarebbe attivo per tutta l'applicazione. Altrimenti si potrebbe inserire in un filtro, come mostrato nel Listato 6-32, ed attivato per ogni singolo modulo che lo richieda. -Listing 6-32 - Google Analytics Filter +Listing 6-32 - Filtro Google Analytics [php] class sfGoogleAnalyticsFilter extends sfFilter { public function execute($filterChain) { - // Nothing to do before the action + // Niente da fare prima dell'azione $filterChain->execute(); - // Decorate the response with the tracker code + // Completa la risposta con il codice del tracker $googleCode = ' @@ -931,11 +932,11 @@ Listing 6-32 - Google Analytics Filter } } -Be aware that this filter is not perfect, as it should not add the tracker on responses that are not HTML. +Attenzione però, questo filtro non è perfetto dato che non dovrebbe aggiungere il codice del tracker nelle risposte non HTML. -Another 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-33. +Un altro esempio potrebbe essere rappresentato da un filtro che cambia le richieste ad SSL nel caso non lo fossero già, per rendere più sicura la comunicazione, come nel Listato 6-33. -Listing 6-33 - Secure Communication Filter +Listing 6-33 - Filtro per comunicazione sicura [php] class sfSecureFilter extends sfFilter @@ -954,20 +955,20 @@ Listing 6-33 - Secure Communication Filter } else { - // The request is already secure, so we can continue + // La richiesta è già sicura, si può continuare $filterChain->execute(); } } } -Filters 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. +I filtri vengono utilizzati in modo massivo nel plug-in visto che permettono di estendere le funzionalità di un'applicazione in modo completo. Fare riferimento al Capitolo 17 per saperne di più sui plugin. -Module Configuration --------------------- +Configurazione dei moduli +------------------------- -A 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-34 shows an example of a `module.yml` file for the `mymodule` module. +Alcuni comportamenti dei moduli dipendono dalla configurazione. Per modificarli è necessario creare un file `module.yml` nella directory `config/` del modulo e definirvi le impostazioni per ogni singolo ambiente (oppure nell'intestazione `all:` per tutti gli ambienti). Il Listato 6-34 mostra un esempio di file `module.yml` per il modulo `mymodule`. -Listing 6-34 - Module Configuration, in `apps/frontend/modules/mymodule/config/module.yml` +Listing 6-34 - Configurazione di un modulo, in `apps/frontend/modules/mymodule/config/module.yml` all: # For all environments enabled: true @@ -975,17 +976,17 @@ Listing 6-34 - Module Configuration, in `apps/frontend/modules/mymodule/config/m view_class: sfPHP partial_view_class: sf -The 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`). +Il parametro `enabled` permette di disabilitare tutte le azioni di un modulo. Tutte le azioni verranno redirette all'azione `module_disabled_module`/`module_disabled_action` (come definito in `settings.yml`). -The `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. +Il parametro `is_internal` permette di limitare l'esecuzione di tutte le azioni di un modulo solamente a chiamate interne. Per esempio questo è utile per azioni riguardanti le email che devono poter essere invocate da altre azioni, per mandare messaggi e-mail, ma non dall'esterno. The `view_class` 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. -The `partial_view_class` parameter defines the view class used for partials of this module. It must inherit from `sfPartialView`. +Il parametro `partial_view_class` definisce la classe della vista utilizzata per i partial del modulo in questione. Deve ereditare da `sfPartialView`. -Summary -------- +Sommario +-------- -In 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`). +In symfony lo strato del controllore è diviso in due parti: il front controller, l'unico punto d'accesso per l'applicazione in un dato ambiente, e le azioni che contengono la logica delle pagine. Un'azione ha l'abilità di determinare come verrà eseguita la sua vista restituendo una delle costanti `sfView`. All'interno di un'azione si possono manipolare i diversi elementi del context, inclusi l'oggetto della richiesta (`sfRequest`) e l'oggetto della sessione utente corrente (`sfUser`). -Combining the power of the session object, the action object, and the security configuration, symfony provides a complete security system, with access restriction and credentials. 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. +Combinando assieme la potenza dell'oggetto sessione, l'oggetto azione, le configurazioni di sicurezza, symfony mette a disposizione un completo sistema di sicurezza con restrizione sull'accesso e sistema di credenziali associato. Se i metodi `preExecute()` e `postExecute()` sono stati pensati per il riutilizzo del codice all'interno di un modulo, i filtri permettono lo stesso grado di riutilizzo per tutte le applicazioni facendo eseguire codice del controllore per ogni singola richiesta. From 99455bfeda37ed4870acfc1923b42005399711bc Mon Sep 17 00:00:00 2001 From: dlondero Date: Sun, 20 Jun 2010 18:55:23 +0200 Subject: [PATCH 6/8] [doc][1.4] Typo using url_for. Closes #8725 --- jobeet/en/14.markdown | 6 +++--- jobeet/it/14.markdown | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jobeet/en/14.markdown b/jobeet/en/14.markdown index 03644c0..be30b1a 100644 --- a/jobeet/en/14.markdown +++ b/jobeet/en/14.markdown @@ -134,7 +134,7 @@ Replace the Atom template header with the following code: Jobeet Latest Jobs - + getCreatedAt('U')) ?> @@ -334,7 +334,7 @@ You can use the `_list.atom.php` partial to simplify the job feed template: Jobeet Latest Jobs - + getCreatedAt('U')) ?> @@ -404,4 +404,4 @@ to your websites without effort. Today, we have enhanced the job seeker experience. Tomorrow, we will see how to provide greater exposure to the job posters by providing a Web Service. -__ORM__ \ No newline at end of file +__ORM__ diff --git a/jobeet/it/14.markdown b/jobeet/it/14.markdown index 3c4fc9b..60da9ae 100644 --- a/jobeet/it/14.markdown +++ b/jobeet/it/14.markdown @@ -137,7 +137,7 @@ Sostituiamo l'header del template Atom col codice seguente: Jobeet Latest Jobs - + getCreatedAt('U')) ?> @@ -340,7 +340,7 @@ delle offerte: Jobeet Latest Jobs - + getCreatedAt('U')) ?> From 9135c5e4b88e0b86e05ca45b627b3e7c5db177c3 Mon Sep 17 00:00:00 2001 From: dlondero Date: Fri, 1 Oct 2010 18:50:31 +0200 Subject: [PATCH 7/8] [gentle][it] Chapter 18 Italian Translation --- .../it/18-Performance.markdown | 620 ++++++++++++++++++ 1 file changed, 620 insertions(+) create mode 100644 gentle-introduction/it/18-Performance.markdown diff --git a/gentle-introduction/it/18-Performance.markdown b/gentle-introduction/it/18-Performance.markdown new file mode 100644 index 0000000..61973cc --- /dev/null +++ b/gentle-introduction/it/18-Performance.markdown @@ -0,0 +1,620 @@ +Capitolo 18 - Performance +========================= + +Se ci si aspetta che il proprio sito web possa attirare molte visite i problemi di performance e di ottimizzazione dovrebbero essere argomenti trattati a fondo durante la fase di sviluppo. State sicuri che quella delle performance è stata sempre una delle principali preoccupazioni per gli sviluppatori del core di symfony. + +Mentre i vantaggi ottenuti dall'accelerazione del processo di sviluppo comportano un piccolo overhead, gli sviluppatori del core di symfony sono sempre stati a conoscenza dei requisiti relativi alle performance. Proprio per questo ogni classe ed ogni metodo son stati analizzati ed ottimizzati per essere più veloci possibile. Il piccolo overhead, che può essere misurato confrontando il tempo necessario a visualizzare un "hello, world" con e senza symfony, è minimo. Conseguentemente il framework è scalabile e reagisce positivamente agli stress test. Come ultima prova alcuni siti ad [estremamente](http://sf-to.org/answers) [alto](http://sf-to.org/delicious) [traffico](http://sf-to.org/dailymotion) (questo significa, siti web con milioni di utenti attivi e molti server che erogano interazioni Ajax) usano symfony e sono molto soddisfatti delle sue performance. + +Tuttavia i siti ad alto traffico molte volte si possono permettere di espendere la propria server farm e di fare upgrade hardware man mano che le risorse vengono utilizzate. Quando non si hanno le risorse per agire in questo modo, o quando si vuole essere certi di avere a disposizione l'intera potenza del framework, esistono degli accorgimenti per rendere ulteriormente più veloce la propria applicazione symfony. Questo capitolo elenca alcune delle ottimizzazioni raccomandate per le performance a tutti i livelli del framework che sono principalmente per utenti avanzati. Alcuni di essi sono già stati citati nei capitoli precedenti, è utile tuttavia averli tutti assieme in un unico posto. + +Ottimizzare il server +--------------------- + +Un'applicazione ben ottimizzata dovrebbe poter fare affidamento su un server ben ottimizzato. È importante conoscere le basi relative alle performance di un server per assicurarsi di non avere colli di bottiglia esterni a symfony. Di seguito alcune cose da verificare per essere sicuri che il proprio server non sia inutilmente lento. + +Avere `magic_quotes_gpc` impostato a `true` nel `php.ini` rallenta l'applicazione perchè dice a PHP di eseguire l'escape di tutti gli apici nei parametri della richiesta, tuttavia symfony in seguito eliminerà sistematicamente tutti gli escape con la sola conseguenza di una perdita di tempo e di problemi con l'escape su alcune piattaforme. Quindi è consigliabile disattivare questa impostazione se si ha l'accesso alla configurazione di PHP. + +Utilizzare la release più recente di PHP è sempre un'ottima scelta (PHP 5.3 è più performante di PHP 5.2). Assicurarsi quindi di aggiornare la propria versione di PHP per beneficiare delle migliorie dell'ultima versione. + +L'utilizzo di un acceleratore PHP (come APC, XCache o eAccelerator) è praticamente obbligatorio su un server di produzione, può permettere di ottenere migliori prestazioni mediamente del 50% in più senza compromessi. Assicurarsi di installare un acceleratore per apprezzare la reale velocità di PHP. + +D'altro canto ci si deve assicurare di aver disattivato qualsiasi utility di debug, come Xdebug o APD, sul server di produzione. + +>**NOTE** +>Legittimo chiedersi cosa comporta l'overhead causato dall'extension `mod_rewrite`: è trascurabile. Certamente caricare un'immagine con regole di rewrite è un'operazione più lenta rispetto al caricamento senza regole, ma il rallentamneto è di ordini di grandezza inferiore all'esecuzione di ogni singola operazione di PHP. + +- + +>**TIP** +>Quando un server non è abbastanza è sempre possibile aggiungerne un secondo ed utilizzare un sistema di bilanciamento del carico. Ammesso che la directory `uploads/` sia condivisa tra le macchine e si utilizzi il database per lo storage delle sessioni, symfony risponderà allo stesso modo in un'architettura bilanciata. + +Ottimizzare il modello +---------------------- + +In symfony lo strato del modello ha la reputazione di essere la parte più lenta. Se i benchmark evidenziano la necessità di ottimizzare questo strato, analizziamo alcuni possibili miglioramenti. + +### Ottimizzare l'integrazioen di Propel o Doctrine + +L'inizializzazione dello strato del modello (le classi del core dell'ORM) richiede un po' di tempo per la necessità di caricare alcune classi e creare diversi oggetti. Comunque, grazie a come symfony integra tutti e due gli ORM, questo processo di inizializzazione si verifica solo quando un'azione necessita realmente del modello e questo viene fatto più tardi possibile. Le classi dell'ORM vengono inizializzate solo quando un oggetto del modello autogenerato è oggetto di autoloading. + +Se l'intera applicazione non richiede l'utilizzo dello strato del modello è possibile evitare l'inizializzazione del `sfDatabaseManager` disabilitando completamente lo strato in `settings.yml`: + + all: + .settings: + use_database: false + + +####Miglioramenti di Propel + +Le classi generate del modello (in `lib/model/om/`) sono già ottimizzate non contengono commenti, beneficiano del sistema di autoloading. Fare affidamento sull'autoloading invece che sull'inclusione manuale dei file significa che le classi vengono caricate solo se realmente necessarie. Quindi nel caso in cui una classe del modello non fosse necessario l'autoloading permette di risparmiare tempo di esecuzione, cosa non permessa dal metodo alternativo in cui si utilizza `include`. Stesso discorso per i commenti, documentano l'utilizzo dei metodi generati ma allungano i file del modello risultando in un minore overhead su dischi lenti. Dato che i nomi dei metodi generati sono piuttosto espliciti, i commenti sono disabilitati per impostazione predefinita. + +Queste due ottimizzazioni sono specifiche per symfony, è possibile tornare sui valori predefiniti di Propel modificando due impostazioni nel file `propel.ini` come segue: + + propel.builder.addIncludes = true # Aggiunge gli include nelle classi generate + # invece che fare affidamento al sistema di autoloading + propel.builder.addComments = true # Aggiunge commenti alle classi generate + +### Limitare il numero di oggetti da idratare + +Quando si utilizza un metodo di una classe peer per recuperare degli oggetti la query attraversa il processo di idratazione (creazione e popolamento degli oggetti basata sulle righe del risultato della query). Per esempio per recuperare tutte le righe della tabella `article` con Propel abitualmente si agisce così: + + [php] + $articles = ArticlePeer::doSelect(new Criteria()); + +La variabile `$articles` ottenuta è un array di oggetti della classe `Article`. Ogni oggetto deve essere creato ed inizializzato, cosa che richiede tempo. Questo ha una conseguenza principale: diversamente dalle query dirette al database la velocità delle query di Propel è direttamente proporzionale al numero di risultati che genera. Questo significa che i metodi dei modelli dovrebbero essere ottimizzati per restituire solo un numero ben preciso di risultati. Quando non sono necessari tutti i risultati restituiti da un `Criteria`, è opportuno limitarli usando i metodi `setLimit()` e `setOffset()`. Se per esempio si volessero solo le righe dalla 10 alla 20 di una specifica query si potrebbe migliorare il `Criteria` come nel Listing 18-1. + +Listing 18-1 - Limitare il numero di risultati restituito da un Criteria + + [php] + $c = new Criteria(); + $c->setOffset(10); // Offset del primo record restituito + $c->setLimit(10); // Numero di record restituito + $articles = ArticlePeer::doSelect($c); + +Questo può essere automatizzato utilizzando un sistema di paginazione. L'oggetto `sfPropelPager` gestisce automaticamente l'offset ed il limite di una query di Propel per idratare solamente gli oggetti richiesti da una pagina specifica. + +### Minimizzare il numero di query con le Join + +Durante lo sviluppo di un'applicazione è bene tenere sott'occhio il numero delle query inviate al database da ogni singola richiesta. La web debug toolbar visualizza il numero delle query per ogni pagina, cliccando sulla piccola icona del database è possibile analizzare il codice SQL di tutte queste query. Se il numero delle query cresce in modo anomalo è giunto il momento di valutare l'utilizzo di alcune Join. + +Prima di analizzare i metodi di Join rivediamo cosa accade quando si itera su un array di oggetti usando un getter di Propel per ottenere i dettagli di una classe relazionata come nel Listing 18-2. Questo esempio suppone che lo schema descriva una tabella `article` con una chiave esterna a una tabella `author`. + +Listing 18-2 - Recuperare dettagli su un classe relazionata in un loop + + [php] + // Nell'azione con Propel + $this->articles = ArticlePeer::doSelect(new Criteria()); + // O con Doctrine + $this->articles = Doctrine::getTable('Article')->findAll(); + + // Query invocata dal doSelect() + SELECT article.id, article.title, article.author_id, ... + FROM article + + // Nel template +
    + +
  • getTitle() ?>, + written by getAuthor()->getName() ?>
  • + +
+ +Se l'array `$articles` contenesse dieci oggetti, il metodo `getAuthor()` verrebbe chiamato dieci volte, eseguendo quindi una query ogni volta che viene invocato per idratare un oggetto della classe `Author`, come nel Listing 18-3. + +Listing 18-3 - Getter su chiavi esterne richiedono una query a database + + [php] + // Nel template + $article->getAuthor() + + // Qeury al database invocata da getAuthor() + SELECT author.id, author.name, ... + FROM author + WHERE author.id = ? // ? is article.author_id + +Quindi la pagina del Listing 18-2 richiederà in totale 11 query: una necessaria alla creazione dell'array di oggetti `Article` più le dieci query necessarie per creare un oggetto `Author` alla volta. Si tratta di molte query per la sola visualizzazione di una lista di articoli con i relativi autori. + +####Come ottimizzare le query con Propel + +Se si stesse usando semplice SQL non dovrebbe essere molto difficile ridurre il numero di query ad una sola recuperando le colonne della tabella `article` e quelle della tabella `author` nello stesso momento. Questo è esattamente il comportamento del metodo `doSelectJoinAuthor()` della classe `ArticlePeer`. Questo medoto invoca una query leggermente più complessa della semplice chiamata `doSelect()`, le colonne aggiuntive del result set permettono a Propel di idratare sia gli oggetti `Article` che gli oggetti `Author` relazionati. Il codice del Listing 18-4 mostra esattamente lo stesso risultato del Listing 18-2 ma richiede una singola query al database invece che 11 risultando così più veloce. + +Listing 18-4 - Recuperare dati degli articoli e dei loro autori nella stessa query + + [php] + // Nell'azione + $this->articles = ArticlePeer::doSelectJoinAuthor(new Criteria()); + + // Query al database invocata da doSelectJoinAuthor() + SELECT article.id, article.title, article.author_id, ... + author.id, author.name, ... + FROM article, author + WHERE article.author_id = author.id + + // Nel template (inalterato) +
    + +
  • getTitle() ?>, + written by getAuthor()->getName() ?>
  • + +
+ +Non c'è alcuna differenza tra il risultato fornito dalla chiamata ai metodi `doSelect()` e `doSelectJoinXXX()`; entrambi restituiscono lo stesso array di oggetti (della classe Article nell'esempio). La differenza appare invece quando viene utilizzato un getter su una chiave esterna di questi oggetti. Nel caso di `doSelect()` viene invocata la query ed un oggetto viene idratato con il risultato; nel caso del `doSelectJoinXXX()` l'oggetto esterno esiste già e non è richiesta nessuna query, il processo è così molto più veloce. Se si è a conoscienza di dover utilizzare oggetti relazionati invocare un metodo `doSelectJoinXXX()` per ridurre il numero di query al database e migliorare le performance della pagina. + +Il metodo `doSelectJoinAuthor()` viene generato automaticamente dalla chiamata di `propel-build-model` grazie alla relazione tra le tabelle `article` e `author`. Nel caso in cui esistessero altre chiavi esterne della struttura della tabella degli articoli, per esempio ad una tabella delle categorie, la classe generata `BaseArticlePeer` avrebbe altri metodi Join come mostrato nel Listing 18-5. + +Listing 18-5 - Esempio dei metodi `doSelect` disponibili nella classe `ArticlePeer` + + [php] + // Recupera oggetti Article + doSelect() + + // Recupera oggetti Article e idrata oggetti Author relazionati + doSelectJoinAuthor() + + // Recupera oggetti Article e idrata oggetti Category relazionati + doSelectJoinCategory() + + // Recupera oggetti Article e idrata oggetti relazionati ad eccezione di Author + doSelectJoinAllExceptAuthor() + + // Sinonimo di + doSelectJoinAll() + +Le classi peer contengono anche metodi Join per `doCount()`. Le classi con una controparte i18n (vedere Capitolo 13) mettono a disposizione un metodo `doSelectWithI18n()` che si comporta allo stesso modo dei metodi Join ma per gli oggetti i18n. Per scoprire i metodi Join disponibili nelle classi del modello analizzare le classi peer generate in `lib/model/om/`. Nel caso in cui non fosse presente il metodo Join necessario per una particolare query (per esempio non esistesse un metodo Join generato automaticamente per le relazioni molti-a-molti) è possibile crearlo ed estendere il modello. + +>**TIP** +>Certamente una chiamata a `doSelectJoinXXX()` è leggermente più lenta di una chiamata a `doSelect()`, quindi migliora le prestazioni generali solo se si stanno utilizzando oggetti idratati. + +#### Ottimizzare le query con Doctrine + +Doctrine dispone di un proprio linguaggio per le interrogazioni chiamato DQL, acronimo di *Doctrine Query Language*. La sintassi è molto simile a quella di SQL, tuttavia permette di recuperare insiemi di oggetti piuttosto che di righe. In SQL si vorrebbero recuperare le colonne delle tabelle `article` e `author` nella stessa query. Con DQL la soluzione è piuttosto semplice dato che basta semplicemente aggiungere un join alla query originale, Doctrine idraterà gli oggetti nel modo più appropriato. Il codice seguente mostra come utilizzare il join tra due tabelle: + + [php] + // in the action + Doctrine::getTable('Article') + ->createQuery('a') + ->innerJoin('a.Author') // "a.Author" fa riferimento alla relazione denominata "Author" + ->execute(); + + // Nel template (non modificato) +
    + +
  • getTitle() ?>, + written by getAuthor()->getName() ?>
  • + +
+ + +### Evitare l'utilizzo di array temporanei + +Anche utilizzando Propel gli oggetti vengono già idratati, quindi non c'è nessun bisogno di preparare array temporanei per i template. Gli sviluppatori non abituati all'utilizzo di un ORM spesso cadono in questa trappola. Vogliono preparare un array di stringhe o di interi nonostante il template sia in grado di utilizzare direttamente un array di oggetti esistente. Per esempio si immagini un template che mostra l'elenco di tutti i titoli degli articoli presenti nel database. Uno sviluppatore che non sfrutta l'OOP probabilmente scriverebbe del codice simile a quello riportato nel Listing 18-6. + +Listing 18-6 - Preparare un array nell'azione è superfluo se già se ne possiede uno + + [php] + // In the action + $articles = ArticlePeer::doSelect(new Criteria()); + $titles = array(); + foreach ($articles as $article) + { + $titles[] = $article->getTitle(); + } + $this->titles = $titles; + + // In the template +
    + +
  • + +
+ +Il problema relativo a questo codice è dato dal fatto che l'idratazione è già stata fatta dalla chiamata a `doSelect()` (che costa tempo), rendendo così l'array `$titles` superfluo dato che è possibile scrivere lo stesso codice come nel Listing 18-7. In questo modo il tempo speso per costruire l'array `$titles` può essere guadagnato per migliorare le performance dell'applicazione. + +Listing 18-7 - Utilizzare un array di oggetti esonera dalla creazione di un array temporaneo + + [php] + // In the action + $this->articles = ArticlePeer::doSelect(new Criteria()); + // With Doctrine + $this->articles = Doctrine::getTable('Article')->findAll(); + + // In the template +
    + +
  • getTitle() ?>
  • + +
+ +Se realmente si ha la necessità di costruire un array temporaneo perchè è necessaria qualche operazione sugli oggetti, il modo giusto per farlo è quello di creare un nuovo metodo nella classe del modello che restituisce direttamente quell'array. Se per esempio si avesse bisogno di un array di titoli e numero di commenti per ogni articolo l'azione ed il template dovrebbero assomigliare al Listing 18-8. + +Listing 18-8 - Utilizzare un metodo custom per costruire un array temporaneo + + [php] + // In the action + $this->articles = ArticlePeer::getArticleTitlesWithNbComments(); + + // In the template +
    + +
  • ( comments)
  • + +
+ +Sta poi allo sviluppatore costruire un metodo `getArticleTitlesWithNbComments()` performante nel modello, magari bypassando l'intero layer di astrazione dell'ORM e del database. + +### Bypassare l'ORM + +Quando realmente non si ha bisogno di oggetti ma solo di alcune colonne da varie tabelle, come nell'esempio precedente, si possono creare metodi specifici nel modello per bypassare completamente lo strato dell'ORM. Si può interrogare il database direttamente con PDO, per esempio, e restituire un array costruito sulla base delle proprie esigienze. Listing 18-9 illustra quest'idea. + +Listing 18-9 - Accesso diretto tramite PDO per metodi ottimizzati nel modello, in `lib/model/ArticlePeer.php` + + [php] + // Con Propel + class ArticlePeer extends BaseArticlePeer + { + public static function getArticleTitlesWithNbComments() + { + $connection = Propel::getConnection(); + $query = 'SELECT %s as title, COUNT(%s) AS nb_comments FROM %s LEFT JOIN %s ON %s = %sGROUP BY %s'; + $query = sprintf($query, + ArticlePeer::TITLE, CommentPeer::ID, + ArticlePeer::TABLE_NAME, CommentPeer::TABLE_NAME, + ArticlePeer::ID, CommentPeer::ARTICLE_ID, + ArticlePeer::ID + ); + + $statement = $connection->prepare($query); + $statement->execute(); + + $results = array(); + while ($resultset = $statement->fetch(PDO::FETCH_OBJ)) + { + $results[] = array('title' => $resultset->title, 'nb_comments' => $resultset->nb_comments); + } + + return $results; + } + } + + // Con Doctrine + class ArticleTable extends Doctrine_Table + { + public function getArticleTitlesWithNbComments() + { + return $this->createQuery('a') + ->select('a.title, count(*) as nb_comments') + ->leftJoin('a.Comments') + ->groupBy('a.id') + ->fetchArray(); + } + } + +Quando si iniziano a creare metodi di questo tipo è probabile si finisca con lo scrivere un metodo custom per ogni azione, perdendo quelli che sono i benefici della separazione tra strati per non menzionare poi il fatto che si perde l'indipendenza dal database. + +### Migliorare le prestazioni del database + +Esistono molte tecniche di ottimizzazione specificatamente per il database da utilizzare sia nel caso in cui si stia utilizzando symfony o meno. Questa sezione analizza brevemente le strategie più comuni per l'ottimizzazione dei database, tuttavia una buona padronanza del funzionamento dei database engine ed esperienza di amministrazione sono necessarie per ottenere il meglio dallo strato del modello. + +>**TIP** +>Va ricordato che la web debug toolbar mostra il tempo impiegato da ogni query su una pagina e che ogni miglioria andrebbe monitorata per determinare se realmente migliori le prestazioni. + +Le interrogazioni sulle tabelle sono spesso basate su colonne che non sono la chiave primaria. Per migliorare la velocità di tali interrogazioni è utile definire degli indici nello schema del database. Per aggiungere un indice ad una singola colonna aggiungere la proprietà `index: true` alla definizione della stessa come nel Listing 18-10. + +Listing 18-10 - Aggiungere un index ad una singola colonna, in `config/schema.yml` + + [yml] + # Propel schema + propel: + article: + id: + author_id: + title: { type: varchar(100), index: true } + + + # Doctrine schema + Article: + columns: + author_id: integer + title: string(100) + indexes: + title: + fields: [title] + +L'utilizzo alternativo della sintassi `index: unique` è per definire un indice unico invece di uno classico. Si possono definire inoltre indici su più colonne dallo `schema.yml` (fare riferimento al Capitolo 8 per maggiori dettagli sulla sintassi da utilizzare per gli indici). L'utilizzo degli indici è caldamente consigliato visto che molto spesso rappresentano una buona soluzione per migliorare le performance di una query complessa. + +Dopo aver aggiunto un indice allo schema andrà aggiunto anche al database stesso, ricorrendo ad una query diretta `ADD INDEX` oppure utilizzando il comando `propel-build-all` (che non solo rigenera la struttura delle tabelle ma elimina anche tutti i dati esistenti). + +>**TIP** +>L'utilizzo degli indici tende a rendere più veloci le query di tipo `SELECT` mentre saranno più lente `INSERT`, `UPDATE` e `DELETE`. Inoltre i motori dei database utilizzano solo un indice ad interrogazione e lo scelgono ad ogni query basandosi su un'euristica interna. Aggiungere un indice a volte può essere controproducente in termini di performance, è bene quindi verificarne il risultato. + +Se non diversamente specificato, in symfony ogni richiesta utilizza una singola connessione al database che viene chiusa alla fine della richiesta stessa. Si possono attivare le connessioni persistenti al database per utilizzare un pool di connessioni che restino aperte tra una query e l'altra impostando il parametro `persistent: true` nel file `databases.yml` come mostrato nel Listing 18-11. + +Listing 18-11 - Abilitare il supporto per le connessioni persistenti al database, in `config/databases.yml` + + prod: + propel: + class: sfPropelDatabase + param: + dsn: mysql:dbname=example;host=localhost + username: username + password: password + persistent: true # Utilizza connessioni persistenti + +Questo può o meno migliorare le performance generali del database in funzione di molti fattori. La documentazione sull'argomento è abbondante e facilmente reperibile su Internet. Anche qui è opportuno testare le performance dell'applicazione prima e dopo il cambio di questa impostazione per verificarne l'impatto. + +>**SIDEBAR** +>Suggerimenti specifici per MySQL +> +>Molte impostazioni della configurazione di MySQL, situate nel file my.cnf, possono alterare le performance del database. Assicurarsi di aver letto la [documentazione online](http://dev.mysql.com/doc/refman/5.0/en/option-files.html) su questo argomento. +> +>Uno degli strumenti offerti da MySQL è il log delle query lente. Tutte le interrogazioni SQL che richiedono più tempo in secondi di `long_query_time` per essere eseguite (questa è un'impostazione che può essere modificata in `my.cnf`) vengono registrate in un file che è abbastanza difficile da analizzare manualmente ma che grazie al comando `mysqldumpslow` genera un sommario molto utile. Si tratta di un ottimo strumento per individuare le query che necessitano di ottimizzazione. + +Mettere a punto la vista +------------------------ + +In funzione di come è stato progettato e realizzato lo strato della vista si possono incontrare piccoli rallentamenti o miglioramenti. Questa sezione descrive alcune alternative ed i loro compromessi. + +### Utilizzare il frammento di codice più veloce + +Se non si utilizza il sistema di caching si deve sapere che un `include_component()` è leggermente più lento di un `include_partial()` che a sua volta è leggermente più lento di un semplice `include` in PHP. Questo dipende dal fatto che symfony istanzia una vista per includere un partial ed un oggetto della classe `sfComponent` per includere un component, che nel complesso aggiungono un po' di overhead oltre a quanto già richiesto per includere il file. + +Tuttavia questo overhead è insignificante fino a quando non si includono molti partial o component in un template. Cosa che può accadere in elenchi o tabelle ed ogni volta in cui si utilizza un `include_partial()` helper all'interno di un `foreach`. Quando ci si accorge che un numero considerevole di inclusioni di partial o component hanno un impatto significativo sulle performance è il momento di considerare il caching (vedere Capitolo 12). Se questa non fosse un'opzione praticabile si consiglia di passare all'utilizzo di semplici `include`. + +Lo stesso discorso vale per gli slot, la differenza in termini di performance è sensibile. Il tempo necessario per impostare ed includere uno slot è irrilevante, equivalente all'impostazione di una variabile. Gli slot vengono sempre inseriti in cache assieme al template che li include. + +### Accelerare il routing + +Come spiegato nel Capitolo 9, ogni chiamata ad un helper per i link in un template richiede al sistema delle rotte di processare un URI interno in un URL esterno. Questo avviene trovando una corrispondenza tra l'URI ed i pattern del file `routing.yml`. Symfony lo fa in modo molto semplice: verifica se c'è corrispondenza tra la prima regola e l'URI, se non è così prova con la seguente e così via. Dato che ogni verifica coinvolge le espressioni regolari questa è un'operazione piuttosto pesante in termini di risorse. + +Esiste una semplice scappatoia: utilizzare il nome della rotta invece che la coppia modulo/azione. Questo dirà a symfony quale regola utilizzare ed il sistema delle rotte non perderà tempo cercando una corrispondenza con tutte le regole precedenti. + +In parole povere, si consideri la seguente regola per le rotte, definita nel file `routing.yml`: + + article_by_id: + url: /article/:id + param: { module: article, action: read } + +Quindi invece di creare un link in questo modo: + + [php] + getId()) ?> + +si dovrebbe utilizzare la versione più veloce: + + [php] + $article->getId())) ?> + +La differenza inizia a farsi vedere quando una pagina include alcune dozzine di link collegati alle rotte. + +### Ignorare il template + +Solitamente una risposta è composta da un insieme di intestazioni e di contenuti. Alcune risposte però non necessitano di contenuto. Per esempio alcune interazioni Ajax richiedono solo alcune porzioni di dati dal server per alimentare un programma JavaScript che si occupa di aggiornare diverse parti di una pagina. Per questo tipo di risposte brevi un solo insieme di intestazioni è più veloce da trasmettere. Come visto nel Capitolo 11 un azione può restiture anche solo un intestazione JSON. Listing 18-12 propone un esempio dal Capitolo 11. + +Listing 18-12 - Esempio di azione che restituisce un intestazione JSON + + [php] + public function executeRefresh() + { + $output = '{"title":"My basic letter","name":"Mr Brown"}'; + $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')'); + + return sfView::HEADER_ONLY; + } + +Questo esclude il template ed il layout, la risposta può essere inviata singolarmente. Dato che contiene solamente intestazioni è più leggera e richiederà meno tempo per essere trasmessa all'utente. + +Il Capitolo 6 ha mostrato un altro modo per evitare il caricamento del template restituendo del testo come contenuto dall'azione. Questo infrange la separazione MVC ma può aumentare la responsività di un'azione in modo drastico. Verificare Listing 18-13 per un esempio. + +Listing 18-13 - Esempio di azione che restituisce direttamente testo come contenuto + + [php] + public function executeFastAction() + { + return $this->renderText("Hello, World!"); + } + +Ottimizzare la cache +-------------------- + +Il Capitolo 12 ha già descritto come mettere in cache porzioni di una risposta o la risposta completa. L'utilizzo della cache per le risposte rappresenta una miglioria sostanziale per le performance e dovrebbe essere una delle prime ottimizzazioni da considerare. Per ottenere il massimo dal sistema della cache si consiglia di continuare la lettura, questa sezione svelerà alcuni accorgimenti a cui non si penserebbe. + +### Invalidare selettivamente porzioni di cache + +Durante lo sviluppo di un'applicazione è necessario ripulire la cache in diverse situazioni: + + * Quando si crea una nuova classe: aggiungere una classe ad una delle directory soggette ad autoloading (una delle directory `lib/` del progetto) non è abbastanza perchè symfony possa individuarla automaticamente in ambienti non di sviluppo. È necessario resettare la cache della configurazione dell'autoloading in modo che symfony analizzi nuovamente tutte le directory indicate dal file `autoload.yml` e referenzi la posizione delle classi include le nuove. + * Quando si cambia la configurazione in produzione: la configurazione viene processata solo durante la prima richiesta in produzione. Le richieste successive utilizzano invece la versione memorizzata in cache. Quindi una modifica nella configurazione dell'ambiente di produzione (o qualunque ambiente in cui il debug è impostato a off) non ha effetto fino alla cancellazione della versione memorizzata in cache del file. + * Quando si modifica un template in un ambiente dove la cache per i template è abilitata: i template validi dalla cache vengono sempre utilizzati al posto dei template in produzione, quindi una modifica a un template viene ignorata fino a qualco la cache non viene cancellata o diventa obsoleta. + * Quando si aggiorna un'applicazione con il comando `project:deploy`: questo caso solitamente comprende le tre modifiche appena viste. + +Il problema della cancellazione dell'intera cache è rappresentato dal fatto che la richiesta successiva richiederà un tempo più lungo per essere processata perchè la cache della configurazione deve essere rigenerata. Inoltre i template non modificati verranno anch'essi rimossi dalla cache perdendo i benefici delle richieste precedenti. + +Questo significa che è una buona idea rimuovere dalla cache solamente i file che realmente necessitano di essere rigenerati. Utilizzare le opzioni del task `cache:clear` per definire un sottoinsieme di file della cache da rimuovere come dimostrato nel Listing 18-14. + +Listing 18-14 - Rimuovere solo parti specifiche della cache + + // Rimuovere solamente la cache dell'applicazione frontend + $ php symfony cache:clear frontend + + // Rimuovere solo la cache HTML dell'applicazione frontend + $ php symfony cache:clear frontend template + + // Rimuovere solo la cache dei file di configurazione dell'applicazione frontend + $ php symfony cache:clear frontend config + +Si possono rimuovere i file anche manualmente nella directory `cache/` o eliminare i file di cache dei template selettivamente dall'azione con il metodo `$cacheManager->remove()` come descritto nel Capitolo 12. + +Tutte queste tecniche minimizzeranno l'impatto negativo sulle performanche di ognuna delle modifiche elencate precedentemente. + +>**TIP** +>Quando si aggiorna symfony la cache viene rimossa automaticamente senza intervento manuale (se il parametro `check_symfony_version` è impostato a `true` nel file `settings.yml`). + +### Generare pagine in cache + +Quando si mette in produzione una nuova applicazione la cache dei template è vuota. È necessario aspettare che gli utenti visitino una pagina perchè essa venga inserita in cache. Nei deploy più critici l'overhead del processo di una pagina non è accettabile ed i benefici della cache devono essere disponibili già alla prima richiesta. + +La soluzione è rappresentata dalla visita delle pagine dell'applicazione nell'ambiente di staging (dove la configurazione è simile a quella di produzione) per generare la cache dei template e poi trasferire l'applicazione con la cache in produzione. + +Per visitare le pagine in modo automatico un'opzione è creare uno script di shell che analizza una lista di URL esterni con un browser (curl per esempio). Esiste però una soluzione migliore e più veloce: uno script PHP che utilizza l'oggetto `sfBrowser` già visto al Capitolo 15. Si tratta di un browser interno scritto in PHP (ed utilizzato da `sfTestFunctional` per i test funzionali). Accetta un URL esterno e restituisce una risposta, ma la cosa interessante è che scatena la creazione della cache del template proprio come un browser tradizionale. Dato che inizializza symfony solamente una volta e non utilizza lo strato di trasporto HTTP questo metodo risulta molto veloce. + +Listing 18-15 mostra uno script d'esempio utilizzato per generare cache dei template nell'ambiente di staging. Avviarlo chiamando `php generate_cache.php`. + +Listing 18-15 - Generare la cache dei template, in `generate_cache.php` + + [php] + require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php'); + $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'staging', false); + sfContext::createInstance($configuration); + + // Array di URL da visitare + $uris = array( + '/foo/index', + '/foo/bar/id/1', + '/foo/bar/id/2', + ... + ); + + $b = new sfBrowser(); + foreach ($uris as $uri) + { + $b->get($uri); + } + +### Utilizzare un sistema di storage su database per la cache + +Il sistema predefinito di storage per la cache dei template in symfony è il file system: porzioni di HTML o oggetti di risposta serializzati vengono memorizzati nella directory `cache/` del progetto. Symfony propone anche una via alternativa per la memorizzazione della cache: un database SQLite. Tale database è un semplice file che PHP sa già come interrogare molto efficaciemente in modo nativo. + +Per far utilizzare a symfony lo storage SQLite invece che il file system per la cache dei template modificare il parametro `view_cache` nel file `factories.yml` come segue: + + view_cache: + class: sfSQLiteCache + param: + database: %SF_TEMPLATE_CACHE_DIR%/cache.db + +I benefici offerti dall'utilizzo dello storage SQLite per la cache dei template sono operazioni di lettura e scrittura più veloci quando il numero degli elementi in cache si fa importante. Se l'applicazione fa grosso uso del sistema di caching i file di cache vengono archiviati in una struttura piuttosto profonda; in questo caso il passaggio allo storage SQLite garantirà migliori performance. Si aggiunga che la cancellazione della cache richiede, per il file system storage, di rimuovere molti file dal disco e quest'operazione può durare alcuni secondi durante i queli l'applicazione non sarà disponibile. Con uno storage SQLite la cancellazione della cache consiste in una singola operazione su file: la cancellazione del file del database SQLite. Indipendentemente dal numero di elementi inseriti in cache l'operazione è istantanea. + +### Bypassare symfony + +Forse il modo migliore per velocizzare symfony è quello di bypassarlo completamente...questo è solo in parte per scherzo. Alcune pagine non cambiano e non hanno la necessità di essere riprocessate dal framework ad ogni richiesta. La cache dei template viene già utilizzata per accelerare la consegna delle pagine ma si basa ancora su symfony. + +Un paio di suggerimenti descritti nel Capitolo 12 permettono di bypassare symfony totalmente per alcune pagine. Il primo coinvolge l'utilizzo delle intestazioni HTTP 1.1 per chiedere ai proxy ed ai browser client di mettere in cache le pagine in modo autonomo, così non le richiederanno la prossima volta che la pagina sarà necessaria. Il secondo suggerimento è la super fast cache (automatizzata dal plug-in `sfSuperCachePlugin`) che consiste nel memorizzare una copia della risposta nella directory `web/` modificando le regole di rewrite per fare in modo che Apache cerchi una versione in cache prima di inoltrare la richiesta a symfony. + +Tutti e due questi metodi sono molto efficaci, ed anche se sono applicabili solo a pagine statiche, si fanno carico della gestione di queste pagine senza coinvolgere symfony permettendo così al server di essere completamente disponibile per le richieste più complesse. + +### Mettere in cache il risultato di una chiamata ad una funzione + +Se una funzione non utilizza valori dipendenti dal contesto o non casuali, chiamandola due volte con gli stessi parametri dovrebbe fornire lo stesso risultato. Questo significa che la seconda chiamata potrebbe davvero essere evitata nel caso un cui si fosse memorizzato il primo risultato. Questo è esattamente ciò che la classe `sfFunctionCache` si occupa di fare. Questa classe ha un metodo `call()` che si aspetta un callable ed un array di parametri come argomenti. Quando invocato questo metodo crea un hash md5 con tutti gli argomenti e cerca nella cache una chiave denominata con quell'hash. Se la chiave viene trovata la funzione restituisce il risultato memorizzato nella cache. Altrimenti `sfFunctionCache` esegue la funzione, memorizza il risultato nella cache e lo restituisce. In questo modo la seconda esecuzione del Listing 18-16 sarà più veloce della prima. + +Listing 18-16 - Mettere in cache il risultato di una funzione + + [php] + $cache = new sfFileCache(array('cache_dir' => sfConfig::get('sf_cache_dir').'/function')); + $fc = new sfFunctionCache($cache); + $result1 = $fc->call('cos', array(M_PI)); + $result2 = $fc->call('preg_replace', array('/\s\s+/', ' ', $input)); + +Il costruttore della classe `sfFunctionCache` si aspetta un oggetto di tipo cache. Il primo argomento del metodo `call()` deve essere un callable, quindi può essere il nome di una funzione, un array contenente il nome di una classe ed il nome di un metodo statico oppure un array con il nome di un oggetto ed il nome di un metodo pubblico. Lo stesso vale per l'altro parametro del metodo `call()`, si tratta di un array di argomenti che verranno passati al callable. + +>**CAUTION** +>Se si utilizza un oggetto cache basato su file come nell'esempio è consigliabile utilizzare una directory all'interno della directory `cache/`, così facendo verà svuotata automaticamente dal task `cache:clear`. Se si memorizza la cache della funzione da qualche altra parte non verrà rimossa automaticamente quando si svuoterà la cache utilizzando la linea di comando. + +### Mettere in cache i dati sul server + +Gli acceleratori PHP mettono a disposizione funzioni speciali per memorizzare dati in memoria per poterli riutilizzare su più richieste. Il problema è che hanno tutti sintassi diverse ed ognuno ha il suo modo speciale di assolvere questo compito. Le classi della cache di symfony riescono ad astrarre tutte queste differenze e funzionano con qualsiasi acceleratore si decida di utilizzare. Si analizzi il Listing 18-17. + +Listing 18-17 - Utilizzare un acceleratore PHP per memorizzare dati + + [php] + $cache = new sfAPCCache(); + + // Memorizzare dati nella cache + $cache->set($name, $value, $lifetime); + + // Recuperare i dati + $value = $cache->get($name); + + // Verificare se un dato esiste nella cache + $value_exists = $cache->has($name); + + // Ripulire la cache + $cache->clear(); + +Il metodo `set()` restituisce il valore `false` se il processo non ha funzionato. Il valore inserito in cache può essere qualsiasi cosa (stringa, array, oggetto); la classe `sfAPCCache` lo permette ricorrendo alla serializzazione. Il metodo `get()` restituisce `null` se la variabile richiesta non esiste nella cache. + +>**TIP** +>Per approfondire il discorso della cache in memoria è fondamentale analizzare la classe `sfMemcacheCache`. Mette a disposizione la stessa interfaccia come le altre classi per la cache e può essere d'aiuto per ridurre il carico del database su applicazioni bilanciate. + +Disattivare le funzionalità inutilizzate +---------------------------------------- + +La configurazione standard di symfony attiva le funzionalità più comuni per le applicazione web. Tuttavia, se non si ha la necessità di utilizzarle tutte, sarebbe opportuno disattivarle per risparmiare il tempo necessario alla loro inizializzazione consumato ad ogni richiesta. + +Per esempio nel caso in cui l'applicazione non utilizzi il meccanismo delle sessioni, o si volesse attivare la gestione delle sessioni manualmente, si dovrebbe modificare il valore del parametro `auto_start` impostandolo a `false` sotto la chiave `storage` nel file `factories.yml` come nel Listing 18-18. + +Listing 18-18 - Disabilitare le sessioni, in `frontend/config/factories.yml` + + all: + storage: + class: sfSessionStorage + param: + auto_start: false + +Lo stesso dicasi per le funzionalità del database (come descritto precedentemente nella sezione "Tweaking the Model" in questo capitolo). Se l'applicazione non utilizza un database si può disattivare per un piccolo guadagno nelle prestazioni nel file `settings.yml` (vedere Listing 18-19). + +Listing 18-19 - Disabilitare il database, in `frontend/config/settings.yml` + + all: + .settings: + use_database: false # Funzionalità per database e modello + +Lo stesso vale per le funzionalità di sicurezza (vedere Capitolo 6) che si possono disattivare nel file `filters.yml`, come mostrato nel Listing 18-20. + +Listing 18-20 - Disabilitare funzionalità di sicurezza, in `frontend/config/filters.yml` + + rendering: ~ + security: + enabled: false + + # generally, you will want to insert your own filters here + + cache: ~ + execution: ~ + +Alcune funzionalità sono utili solamente nell'ambiente di sviluppo, non vanno quindi attivate in produzione. Questa è già la situazione standard dato che l'ambiente di produzione di symfony è totalmente ottimizzato per le migliori performance. Tra tutte le funzionalità di sviluppo che hanno un impatto sulle performance, la modalità di debug è sicuramente la più severa. Come per i log di symfony la funzionalità è già disabilitata nell'ambiente di produzione. + +Ci si potrebbe chiedere come ottenere informazioni sulle richieste fallite nell'ambiente di produzione se il logging è disabilitato, facendo notare anche che i problemi non si manifestano solo durante lo sviluppo. Fortunatamente symfony può utilizzare il plugin `sfErrorLoggerPlugin` che lavora in background nell'ambiente di produzione e registra i dettagli degli errori 404 e 500 in un database. È molto più veloce della funzionalità di logging su file dato che i metodi del plug-in vengono invocati solamente quando una richiesta fallisce, mentre il meccanismo di logging se attivo aggiunge un overhead non indifferende indipendentemente dal livello impostato. Controllare le istruzioni di installazione ed il [manuale](http://plugins.symfony-project.org/plugins/sfErrorLoggerPlugin). + +>**TIP** +>È buona abitudine controllare regolarmente i log degli errori del server dato che contengono informazioni molto utili riguardo agli errori 404 e 500. + +Ottimizzare il proprio codice +----------------------------- + +È possibile rendere più performante un'applicazione ottimizzandone il codice. Questa sezione offre alcuni spunti su come fare ciò. + +### Compilazione del core + +Caricare dieci file richiede più operazioni di I/O rispetto al caricamente di un grande file, specialmente su dischi lenti. Caricare un file molto grande richiede più risorse rispetto a caricarne uno più piccolo, specialmente se grossa parte del contenuto del file non è di alcun interesse per il parser PHP, è il caso dei commenti. + +Quindi fondere un grosso numero di file eliminandone i commenti contenuti è un'operazione che migliora le performance. Symfony esegue già tale ottimizzazione, si chiama compilazione del core. All'inizio della prima richiesta (o dopo aver svuotato la cache) un'applicazione symfony concatena tutte le classi del core del framework (`sfActions`, `sfRequest`, `sfView`, e così via) in un unico file, riduce la dimensione del file rimuovendo commenti e doppi spazi e salva tutto nella cache in un file chiamato `config_core_compile.yml.php`. Ogni richiesta seguente caricherà solamente questo singolo file ottimizzato invece che i 30 file che lo compongono. + +Se l'applicazione ha classi che devono essere caricare ogni volta, specialmente se sono classi grandi con molti commenti, può essere un vantaggio aggiungerle al file compilato del core. Per fare questo basta aggiungere un file `core_compile.yml` nella directory `config/` dell'applicazione in cui si elencheranno le classi che si vogliono aggiungere come nel Listing 18-21. + +Listing 18-21 - Aggiungere le proprie classi al file compilato del core, in `frontend/config/core_compile.yml` + + - %SF_ROOT_DIR%/lib/myClass.class.php + - %SF_ROOT_DIR%/apps/frontend/lib/myToolkit.class.php + - %SF_ROOT_DIR%/plugins/myPlugin/lib/myPluginCore.class.php + ... + +### Il task `project:optimize` + +Symfony mette a disposizione anche un altro strumento di ottimizzazione, il task `project:optimize`. Applica varie strategie di ottimizzazione al codice di symfony e dell'applicazione che possono migliorare ulteriormente le performance. + + $ php symfony project:optimize frontend prod + +Per vedere le strategie di ottimizzazione utilizzate nel task basta dare un'occhiata al suo codice sorgente. + +Sommario +-------- +Symfony è già un framework molto ottimizzato e in grado di gestire siti ad alto traffico senza problemi. Ma se davvero si avesse la necessità di ottimizzare ulteriormente le performance della propria applicazione, mettere a punto la configurazione (che sia la configurazione del server, di PHP o le impostazioni dell'applicazione) può fornire un piccolo miglioramento. È consigliabile seguire le best practice per scrivere metodi del modello efficienti; e dato che il database rappresenta sempre un collo di bottiglia per le applicazioni web su di esso andrà riposta particolare attenzione. I template possono beneficiare anch'essi di alcune ottimizzazioni, ma i miglioramenti più evidenti arriveranno dall'utilizzo del sistema di caching. Infine non si esiti nell'analizzare plug-in esistenti dato che alcuni di essi mettono a disposizione tecniche innovative per aumentare ulteriormente la consegna delle pagine web (`sfSuperCache`, `project:optimize`). From 315a2e12684dfee64f4434b0b9159e62a2e9b65c Mon Sep 17 00:00:00 2001 From: dlondero Date: Mon, 4 Oct 2010 11:54:11 +0200 Subject: [PATCH 8/8] [gentle][it] minor fixes --- .../it/18-Performance.markdown | 120 +++++++++--------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/gentle-introduction/it/18-Performance.markdown b/gentle-introduction/it/18-Performance.markdown index 61973cc..df31fb0 100644 --- a/gentle-introduction/it/18-Performance.markdown +++ b/gentle-introduction/it/18-Performance.markdown @@ -5,7 +5,7 @@ Se ci si aspetta che il proprio sito web possa attirare molte visite i problemi Mentre i vantaggi ottenuti dall'accelerazione del processo di sviluppo comportano un piccolo overhead, gli sviluppatori del core di symfony sono sempre stati a conoscenza dei requisiti relativi alle performance. Proprio per questo ogni classe ed ogni metodo son stati analizzati ed ottimizzati per essere più veloci possibile. Il piccolo overhead, che può essere misurato confrontando il tempo necessario a visualizzare un "hello, world" con e senza symfony, è minimo. Conseguentemente il framework è scalabile e reagisce positivamente agli stress test. Come ultima prova alcuni siti ad [estremamente](http://sf-to.org/answers) [alto](http://sf-to.org/delicious) [traffico](http://sf-to.org/dailymotion) (questo significa, siti web con milioni di utenti attivi e molti server che erogano interazioni Ajax) usano symfony e sono molto soddisfatti delle sue performance. -Tuttavia i siti ad alto traffico molte volte si possono permettere di espendere la propria server farm e di fare upgrade hardware man mano che le risorse vengono utilizzate. Quando non si hanno le risorse per agire in questo modo, o quando si vuole essere certi di avere a disposizione l'intera potenza del framework, esistono degli accorgimenti per rendere ulteriormente più veloce la propria applicazione symfony. Questo capitolo elenca alcune delle ottimizzazioni raccomandate per le performance a tutti i livelli del framework che sono principalmente per utenti avanzati. Alcuni di essi sono già stati citati nei capitoli precedenti, è utile tuttavia averli tutti assieme in un unico posto. +Tuttavia i siti ad alto traffico molte volte si possono permettere di espandere la propria server farm e di fare upgrade hardware man mano che le risorse vengono utilizzate. Quando non si hanno le risorse per agire in questo modo, o quando si vuole essere certi di avere a disposizione l'intera potenza del framework, esistono degli accorgimenti per rendere ulteriormente più veloce la propria applicazione symfony. Questo capitolo elenca alcune delle ottimizzazioni raccomandate per le performance a tutti i livelli del framework che sono principalmente per utenti avanzati. Alcuni di essi sono già stati citati nei capitoli precedenti, è utile tuttavia averli tutti assieme in un unico posto. Ottimizzare il server --------------------- @@ -18,10 +18,10 @@ Utilizzare la release più recente di PHP è sempre un'ottima scelta (PHP 5.3 è L'utilizzo di un acceleratore PHP (come APC, XCache o eAccelerator) è praticamente obbligatorio su un server di produzione, può permettere di ottenere migliori prestazioni mediamente del 50% in più senza compromessi. Assicurarsi di installare un acceleratore per apprezzare la reale velocità di PHP. -D'altro canto ci si deve assicurare di aver disattivato qualsiasi utility di debug, come Xdebug o APD, sul server di produzione. +D'altro canto ci si deve assicurare di aver disattivato qualsiasi strumento di debug, come Xdebug o APD, sul server di produzione. >**NOTE** ->Legittimo chiedersi cosa comporta l'overhead causato dall'extension `mod_rewrite`: è trascurabile. Certamente caricare un'immagine con regole di rewrite è un'operazione più lenta rispetto al caricamento senza regole, ma il rallentamneto è di ordini di grandezza inferiore all'esecuzione di ogni singola operazione di PHP. +>Legittimo chiedersi cosa comporta l'overhead causato dall'estensione `mod_rewrite`: è trascurabile. Certamente caricare un'immagine con regole di rewrite è un'operazione più lenta rispetto al caricamento senza regole, ma il rallentamneto è di ordini di grandezza inferiore all'esecuzione di ogni singola operazione di PHP. - @@ -33,9 +33,9 @@ Ottimizzare il modello In symfony lo strato del modello ha la reputazione di essere la parte più lenta. Se i benchmark evidenziano la necessità di ottimizzare questo strato, analizziamo alcuni possibili miglioramenti. -### Ottimizzare l'integrazioen di Propel o Doctrine +### Ottimizzare l'integrazione di Propel o Doctrine -L'inizializzazione dello strato del modello (le classi del core dell'ORM) richiede un po' di tempo per la necessità di caricare alcune classi e creare diversi oggetti. Comunque, grazie a come symfony integra tutti e due gli ORM, questo processo di inizializzazione si verifica solo quando un'azione necessita realmente del modello e questo viene fatto più tardi possibile. Le classi dell'ORM vengono inizializzate solo quando un oggetto del modello autogenerato è oggetto di autoloading. +L'inizializzazione dello strato del modello (le classi del core dell'ORM) richiede un po' di tempo per la necessità di caricare alcune classi e creare diversi oggetti. Comunque, grazie a come symfony integra tutti e due gli ORM, questo processo di inizializzazione si verifica solo quando un'azione necessita realmente del modello e questo viene fatto più tardi possibile. Le classi dell'ORM vengono inizializzate solo quando un oggetto del modello auto generato è oggetto di autoloading. Se l'intera applicazione non richiede l'utilizzo dello strato del modello è possibile evitare l'inizializzazione del `sfDatabaseManager` disabilitando completamente lo strato in `settings.yml`: @@ -61,9 +61,9 @@ Quando si utilizza un metodo di una classe peer per recuperare degli oggetti la [php] $articles = ArticlePeer::doSelect(new Criteria()); -La variabile `$articles` ottenuta è un array di oggetti della classe `Article`. Ogni oggetto deve essere creato ed inizializzato, cosa che richiede tempo. Questo ha una conseguenza principale: diversamente dalle query dirette al database la velocità delle query di Propel è direttamente proporzionale al numero di risultati che genera. Questo significa che i metodi dei modelli dovrebbero essere ottimizzati per restituire solo un numero ben preciso di risultati. Quando non sono necessari tutti i risultati restituiti da un `Criteria`, è opportuno limitarli usando i metodi `setLimit()` e `setOffset()`. Se per esempio si volessero solo le righe dalla 10 alla 20 di una specifica query si potrebbe migliorare il `Criteria` come nel Listing 18-1. +La variabile `$articles` ottenuta è un array di oggetti della classe `Article`. Ogni oggetto deve essere creato ed inizializzato, cosa che richiede tempo. Questo ha una conseguenza principale: diversamente dalle query dirette al database la velocità delle query di Propel è direttamente proporzionale al numero di risultati che genera. Questo significa che i metodi dei modelli dovrebbero essere ottimizzati per restituire solo un numero ben preciso di risultati. Quando non sono necessari tutti i risultati restituiti da un `Criteria`, è opportuno limitarli usando i metodi `setLimit()` e `setOffset()`. Se per esempio si volessero solo le righe dalla 10 alla 20 di una specifica query si potrebbe migliorare il `Criteria` come nel Listato 18-1. -Listing 18-1 - Limitare il numero di risultati restituito da un Criteria +Listato 18-1 - Limitare il numero di risultati restituito da un Criteria [php] $c = new Criteria(); @@ -77,9 +77,9 @@ Questo può essere automatizzato utilizzando un sistema di paginazione. L'oggett Durante lo sviluppo di un'applicazione è bene tenere sott'occhio il numero delle query inviate al database da ogni singola richiesta. La web debug toolbar visualizza il numero delle query per ogni pagina, cliccando sulla piccola icona del database è possibile analizzare il codice SQL di tutte queste query. Se il numero delle query cresce in modo anomalo è giunto il momento di valutare l'utilizzo di alcune Join. -Prima di analizzare i metodi di Join rivediamo cosa accade quando si itera su un array di oggetti usando un getter di Propel per ottenere i dettagli di una classe relazionata come nel Listing 18-2. Questo esempio suppone che lo schema descriva una tabella `article` con una chiave esterna a una tabella `author`. +Prima di analizzare i metodi di Join rivediamo cosa accade quando si itera su un array di oggetti usando un getter di Propel per ottenere i dettagli di una classe relazionata come nel Listato 18-2. Questo esempio suppone che lo schema descriva una tabella `article` con una chiave esterna a una tabella `author`. -Listing 18-2 - Recuperare dettagli su un classe relazionata in un loop +Listato 18-2 - Recuperare dettagli su un classe relazionata in un loop [php] // Nell'azione con Propel @@ -99,9 +99,9 @@ Listing 18-2 - Recuperare dettagli su un classe relazionata in un loop -Se l'array `$articles` contenesse dieci oggetti, il metodo `getAuthor()` verrebbe chiamato dieci volte, eseguendo quindi una query ogni volta che viene invocato per idratare un oggetto della classe `Author`, come nel Listing 18-3. +Se l'array `$articles` contenesse dieci oggetti, il metodo `getAuthor()` verrebbe chiamato dieci volte, eseguendo quindi una query ogni volta che viene invocato per idratare un oggetto della classe `Author`, come nel Listato 18-3. -Listing 18-3 - Getter su chiavi esterne richiedono una query a database +Listato 18-3 - Getter su chiavi esterne richiedono una query a database [php] // Nel template @@ -112,13 +112,13 @@ Listing 18-3 - Getter su chiavi esterne richiedono una query a database FROM author WHERE author.id = ? // ? is article.author_id -Quindi la pagina del Listing 18-2 richiederà in totale 11 query: una necessaria alla creazione dell'array di oggetti `Article` più le dieci query necessarie per creare un oggetto `Author` alla volta. Si tratta di molte query per la sola visualizzazione di una lista di articoli con i relativi autori. +Quindi la pagina del Listato 18-2 richiederà in totale 11 query: una necessaria alla creazione dell'array di oggetti `Article` più le dieci query necessarie per creare un oggetto `Author` alla volta. Si tratta di molte query per la sola visualizzazione di una lista di articoli con i relativi autori. ####Come ottimizzare le query con Propel -Se si stesse usando semplice SQL non dovrebbe essere molto difficile ridurre il numero di query ad una sola recuperando le colonne della tabella `article` e quelle della tabella `author` nello stesso momento. Questo è esattamente il comportamento del metodo `doSelectJoinAuthor()` della classe `ArticlePeer`. Questo medoto invoca una query leggermente più complessa della semplice chiamata `doSelect()`, le colonne aggiuntive del result set permettono a Propel di idratare sia gli oggetti `Article` che gli oggetti `Author` relazionati. Il codice del Listing 18-4 mostra esattamente lo stesso risultato del Listing 18-2 ma richiede una singola query al database invece che 11 risultando così più veloce. +Se si stesse usando semplice SQL non dovrebbe essere molto difficile ridurre il numero di query ad una sola recuperando le colonne della tabella `article` e quelle della tabella `author` nello stesso momento. Questo è esattamente il comportamento del metodo `doSelectJoinAuthor()` della classe `ArticlePeer`. Questo metodo invoca una query leggermente più complessa della semplice chiamata `doSelect()`, le colonne aggiuntive del result set permettono a Propel di idratare sia gli oggetti `Article` che gli oggetti `Author` relazionati. Il codice del Listato 18-4 mostra esattamente lo stesso risultato del Listato 18-2 ma richiede una singola query al database invece che 11 risultando così più veloce. -Listing 18-4 - Recuperare dati degli articoli e dei loro autori nella stessa query +Listato 18-4 - Recuperare dati degli articoli e dei loro autori nella stessa query [php] // Nell'azione @@ -138,11 +138,11 @@ Listing 18-4 - Recuperare dati degli articoli e dei loro autori nella stessa que -Non c'è alcuna differenza tra il risultato fornito dalla chiamata ai metodi `doSelect()` e `doSelectJoinXXX()`; entrambi restituiscono lo stesso array di oggetti (della classe Article nell'esempio). La differenza appare invece quando viene utilizzato un getter su una chiave esterna di questi oggetti. Nel caso di `doSelect()` viene invocata la query ed un oggetto viene idratato con il risultato; nel caso del `doSelectJoinXXX()` l'oggetto esterno esiste già e non è richiesta nessuna query, il processo è così molto più veloce. Se si è a conoscienza di dover utilizzare oggetti relazionati invocare un metodo `doSelectJoinXXX()` per ridurre il numero di query al database e migliorare le performance della pagina. +Non c'è alcuna differenza tra il risultato fornito dalla chiamata ai metodi `doSelect()` e `doSelectJoinXXX()`; entrambi restituiscono lo stesso array di oggetti (della classe Article nell'esempio). La differenza appare invece quando viene utilizzato un getter su una chiave esterna di questi oggetti. Nel caso di `doSelect()` viene invocata la query ed un oggetto viene idratato con il risultato; nel caso del `doSelectJoinXXX()` l'oggetto esterno esiste già e non è richiesta nessuna query, il processo è così molto più veloce. Se si è a conoscenza di dover utilizzare oggetti relazionati invocare un metodo `doSelectJoinXXX()` per ridurre il numero di query al database e migliorare le performance della pagina. -Il metodo `doSelectJoinAuthor()` viene generato automaticamente dalla chiamata di `propel-build-model` grazie alla relazione tra le tabelle `article` e `author`. Nel caso in cui esistessero altre chiavi esterne della struttura della tabella degli articoli, per esempio ad una tabella delle categorie, la classe generata `BaseArticlePeer` avrebbe altri metodi Join come mostrato nel Listing 18-5. +Il metodo `doSelectJoinAuthor()` viene generato automaticamente dalla chiamata di `propel-build-model` grazie alla relazione tra le tabelle `article` e `author`. Nel caso in cui esistessero altre chiavi esterne della struttura della tabella degli articoli, per esempio ad una tabella delle categorie, la classe generata `BaseArticlePeer` avrebbe altri metodi Join come mostrato nel Listato 18-5. -Listing 18-5 - Esempio dei metodi `doSelect` disponibili nella classe `ArticlePeer` +Listato 18-5 - Esempio dei metodi `doSelect` disponibili nella classe `ArticlePeer` [php] // Recupera oggetti Article @@ -170,7 +170,7 @@ Le classi peer contengono anche metodi Join per `doCount()`. Le classi con una c Doctrine dispone di un proprio linguaggio per le interrogazioni chiamato DQL, acronimo di *Doctrine Query Language*. La sintassi è molto simile a quella di SQL, tuttavia permette di recuperare insiemi di oggetti piuttosto che di righe. In SQL si vorrebbero recuperare le colonne delle tabelle `article` e `author` nella stessa query. Con DQL la soluzione è piuttosto semplice dato che basta semplicemente aggiungere un join alla query originale, Doctrine idraterà gli oggetti nel modo più appropriato. Il codice seguente mostra come utilizzare il join tra due tabelle: [php] - // in the action + // Nell'azione Doctrine::getTable('Article') ->createQuery('a') ->innerJoin('a.Author') // "a.Author" fa riferimento alla relazione denominata "Author" @@ -187,9 +187,9 @@ Doctrine dispone di un proprio linguaggio per le interrogazioni chiamato DQL, ac ### Evitare l'utilizzo di array temporanei -Anche utilizzando Propel gli oggetti vengono già idratati, quindi non c'è nessun bisogno di preparare array temporanei per i template. Gli sviluppatori non abituati all'utilizzo di un ORM spesso cadono in questa trappola. Vogliono preparare un array di stringhe o di interi nonostante il template sia in grado di utilizzare direttamente un array di oggetti esistente. Per esempio si immagini un template che mostra l'elenco di tutti i titoli degli articoli presenti nel database. Uno sviluppatore che non sfrutta l'OOP probabilmente scriverebbe del codice simile a quello riportato nel Listing 18-6. +Anche utilizzando Propel gli oggetti vengono già idratati, quindi non c'è nessun bisogno di preparare array temporanei per i template. Gli sviluppatori non abituati all'utilizzo di un ORM spesso cadono in questa trappola. Vogliono preparare un array di stringhe o di interi nonostante il template sia in grado di utilizzare direttamente un array di oggetti esistente. Per esempio si immagini un template che mostra l'elenco di tutti i titoli degli articoli presenti nel database. Uno sviluppatore che non sfrutta l'OOP probabilmente scriverebbe del codice simile a quello riportato nel Listato 18-6. -Listing 18-6 - Preparare un array nell'azione è superfluo se già se ne possiede uno +Listato 18-6 - Preparare un array nell'azione è superfluo se già se ne possiede uno [php] // In the action @@ -208,9 +208,9 @@ Listing 18-6 - Preparare un array nell'azione è superfluo se già se ne possied -Il problema relativo a questo codice è dato dal fatto che l'idratazione è già stata fatta dalla chiamata a `doSelect()` (che costa tempo), rendendo così l'array `$titles` superfluo dato che è possibile scrivere lo stesso codice come nel Listing 18-7. In questo modo il tempo speso per costruire l'array `$titles` può essere guadagnato per migliorare le performance dell'applicazione. +Il problema relativo a questo codice è dato dal fatto che l'idratazione è già stata fatta dalla chiamata a `doSelect()` (che costa tempo), rendendo così l'array `$titles` superfluo dato che è possibile scrivere lo stesso codice come nel Listato 18-7. In questo modo il tempo speso per costruire l'array `$titles` può essere guadagnato per migliorare le performance dell'applicazione. -Listing 18-7 - Utilizzare un array di oggetti esonera dalla creazione di un array temporaneo +Listato 18-7 - Utilizzare un array di oggetti esonera dalla creazione di un array temporaneo [php] // In the action @@ -225,9 +225,9 @@ Listing 18-7 - Utilizzare un array di oggetti esonera dalla creazione di un arra -Se realmente si ha la necessità di costruire un array temporaneo perchè è necessaria qualche operazione sugli oggetti, il modo giusto per farlo è quello di creare un nuovo metodo nella classe del modello che restituisce direttamente quell'array. Se per esempio si avesse bisogno di un array di titoli e numero di commenti per ogni articolo l'azione ed il template dovrebbero assomigliare al Listing 18-8. +Se realmente si ha la necessità di costruire un array temporaneo perchè è necessaria qualche operazione sugli oggetti, il modo giusto per farlo è quello di creare un nuovo metodo nella classe del modello che restituisce direttamente quell'array. Se per esempio si avesse bisogno di un array di titoli e numero di commenti per ogni articolo l'azione ed il template dovrebbero assomigliare al Listato 18-8. -Listing 18-8 - Utilizzare un metodo custom per costruire un array temporaneo +Listato 18-8 - Utilizzare un metodo custom per costruire un array temporaneo [php] // In the action @@ -240,13 +240,13 @@ Listing 18-8 - Utilizzare un metodo custom per costruire un array temporaneo -Sta poi allo sviluppatore costruire un metodo `getArticleTitlesWithNbComments()` performante nel modello, magari bypassando l'intero layer di astrazione dell'ORM e del database. +Sta poi allo sviluppatore costruire un metodo `getArticleTitlesWithNbComments()` performante nel modello, magari bypassando l'intero strato di astrazione dell'ORM e del database. ### Bypassare l'ORM -Quando realmente non si ha bisogno di oggetti ma solo di alcune colonne da varie tabelle, come nell'esempio precedente, si possono creare metodi specifici nel modello per bypassare completamente lo strato dell'ORM. Si può interrogare il database direttamente con PDO, per esempio, e restituire un array costruito sulla base delle proprie esigienze. Listing 18-9 illustra quest'idea. +Quando realmente non si ha bisogno di oggetti ma solo di alcune colonne da varie tabelle, come nell'esempio precedente, si possono creare metodi specifici nel modello per bypassare completamente lo strato dell'ORM. Si può interrogare il database direttamente con PDO, per esempio, e restituire un array costruito sulla base delle proprie esigienze. Listato 18-9 illustra quest'idea. -Listing 18-9 - Accesso diretto tramite PDO per metodi ottimizzati nel modello, in `lib/model/ArticlePeer.php` +Listato 18-9 - Accesso diretto tramite PDO per metodi ottimizzati nel modello, in `lib/model/ArticlePeer.php` [php] // Con Propel @@ -289,7 +289,7 @@ Listing 18-9 - Accesso diretto tramite PDO per metodi ottimizzati nel modello, i } } -Quando si iniziano a creare metodi di questo tipo è probabile si finisca con lo scrivere un metodo custom per ogni azione, perdendo quelli che sono i benefici della separazione tra strati per non menzionare poi il fatto che si perde l'indipendenza dal database. +Quando si iniziano a creare metodi di questo tipo è probabile si finisca con lo scrivere un metodo personalizzato per ogni azione, perdendo quelli che sono i benefici della separazione tra strati per non menzionare poi il fatto che si perde l'indipendenza dal database. ### Migliorare le prestazioni del database @@ -298,9 +298,9 @@ Esistono molte tecniche di ottimizzazione specificatamente per il database da ut >**TIP** >Va ricordato che la web debug toolbar mostra il tempo impiegato da ogni query su una pagina e che ogni miglioria andrebbe monitorata per determinare se realmente migliori le prestazioni. -Le interrogazioni sulle tabelle sono spesso basate su colonne che non sono la chiave primaria. Per migliorare la velocità di tali interrogazioni è utile definire degli indici nello schema del database. Per aggiungere un indice ad una singola colonna aggiungere la proprietà `index: true` alla definizione della stessa come nel Listing 18-10. +Le interrogazioni sulle tabelle sono spesso basate su colonne che non sono la chiave primaria. Per migliorare la velocità di tali interrogazioni è utile definire degli indici nello schema del database. Per aggiungere un indice ad una singola colonna aggiungere la proprietà `index: true` alla definizione della stessa come nel Listato 18-10. -Listing 18-10 - Aggiungere un index ad una singola colonna, in `config/schema.yml` +Listato 18-10 - Aggiungere un index ad una singola colonna, in `config/schema.yml` [yml] # Propel schema @@ -327,9 +327,9 @@ Dopo aver aggiunto un indice allo schema andrà aggiunto anche al database stess >**TIP** >L'utilizzo degli indici tende a rendere più veloci le query di tipo `SELECT` mentre saranno più lente `INSERT`, `UPDATE` e `DELETE`. Inoltre i motori dei database utilizzano solo un indice ad interrogazione e lo scelgono ad ogni query basandosi su un'euristica interna. Aggiungere un indice a volte può essere controproducente in termini di performance, è bene quindi verificarne il risultato. -Se non diversamente specificato, in symfony ogni richiesta utilizza una singola connessione al database che viene chiusa alla fine della richiesta stessa. Si possono attivare le connessioni persistenti al database per utilizzare un pool di connessioni che restino aperte tra una query e l'altra impostando il parametro `persistent: true` nel file `databases.yml` come mostrato nel Listing 18-11. +Se non diversamente specificato, in symfony ogni richiesta utilizza una singola connessione al database che viene chiusa alla fine della richiesta stessa. Si possono attivare le connessioni persistenti al database per utilizzare un pool di connessioni che restino aperte tra una query e l'altra impostando il parametro `persistent: true` nel file `databases.yml` come mostrato nel Listato 18-11. -Listing 18-11 - Abilitare il supporto per le connessioni persistenti al database, in `config/databases.yml` +Listato 18-11 - Abilitare il supporto per le connessioni persistenti al database, in `config/databases.yml` prod: propel: @@ -356,7 +356,7 @@ In funzione di come è stato progettato e realizzato lo strato della vista si po ### Utilizzare il frammento di codice più veloce -Se non si utilizza il sistema di caching si deve sapere che un `include_component()` è leggermente più lento di un `include_partial()` che a sua volta è leggermente più lento di un semplice `include` in PHP. Questo dipende dal fatto che symfony istanzia una vista per includere un partial ed un oggetto della classe `sfComponent` per includere un component, che nel complesso aggiungono un po' di overhead oltre a quanto già richiesto per includere il file. +Se non si utilizza il sistema di cache si deve sapere che un `include_component()` è leggermente più lento di un `include_partial()` che a sua volta è leggermente più lento di un semplice `include` in PHP. Questo dipende dal fatto che symfony istanzia una vista per includere un partial ed un oggetto della classe `sfComponent` per includere un component, che nel complesso aggiungono un po' di overhead oltre a quanto già richiesto per includere il file. Tuttavia questo overhead è insignificante fino a quando non si includono molti partial o component in un template. Cosa che può accadere in elenchi o tabelle ed ogni volta in cui si utilizza un `include_partial()` helper all'interno di un `foreach`. Quando ci si accorge che un numero considerevole di inclusioni di partial o component hanno un impatto significativo sulle performance è il momento di considerare il caching (vedere Capitolo 12). Se questa non fosse un'opzione praticabile si consiglia di passare all'utilizzo di semplici `include`. @@ -388,9 +388,9 @@ La differenza inizia a farsi vedere quando una pagina include alcune dozzine di ### Ignorare il template -Solitamente una risposta è composta da un insieme di intestazioni e di contenuti. Alcune risposte però non necessitano di contenuto. Per esempio alcune interazioni Ajax richiedono solo alcune porzioni di dati dal server per alimentare un programma JavaScript che si occupa di aggiornare diverse parti di una pagina. Per questo tipo di risposte brevi un solo insieme di intestazioni è più veloce da trasmettere. Come visto nel Capitolo 11 un azione può restiture anche solo un intestazione JSON. Listing 18-12 propone un esempio dal Capitolo 11. +Solitamente una risposta è composta da un insieme di intestazioni e di contenuti. Alcune risposte però non necessitano di contenuto. Per esempio alcune interazioni Ajax richiedono solo alcune porzioni di dati dal server per alimentare un programma JavaScript che si occupa di aggiornare diverse parti di una pagina. Per questo tipo di risposte brevi un solo insieme di intestazioni è più veloce da trasmettere. Come visto nel Capitolo 11 un azione può restiture anche solo un intestazione JSON. Listato 18-12 propone un esempio dal Capitolo 11. -Listing 18-12 - Esempio di azione che restituisce un intestazione JSON +Listato 18-12 - Esempio di azione che restituisce un intestazione JSON [php] public function executeRefresh() @@ -403,9 +403,9 @@ Listing 18-12 - Esempio di azione che restituisce un intestazione JSON Questo esclude il template ed il layout, la risposta può essere inviata singolarmente. Dato che contiene solamente intestazioni è più leggera e richiederà meno tempo per essere trasmessa all'utente. -Il Capitolo 6 ha mostrato un altro modo per evitare il caricamento del template restituendo del testo come contenuto dall'azione. Questo infrange la separazione MVC ma può aumentare la responsività di un'azione in modo drastico. Verificare Listing 18-13 per un esempio. +Il Capitolo 6 ha mostrato un altro modo per evitare il caricamento del template restituendo del testo come contenuto dall'azione. Questo infrange la separazione MVC ma può aumentare la velocità di risposta di un'azione in modo drastico. Verificare Listato 18-13 per un esempio. -Listing 18-13 - Esempio di azione che restituisce direttamente testo come contenuto +Listato 18-13 - Esempio di azione che restituisce direttamente testo come contenuto [php] public function executeFastAction() @@ -424,14 +424,14 @@ Durante lo sviluppo di un'applicazione è necessario ripulire la cache in divers * Quando si crea una nuova classe: aggiungere una classe ad una delle directory soggette ad autoloading (una delle directory `lib/` del progetto) non è abbastanza perchè symfony possa individuarla automaticamente in ambienti non di sviluppo. È necessario resettare la cache della configurazione dell'autoloading in modo che symfony analizzi nuovamente tutte le directory indicate dal file `autoload.yml` e referenzi la posizione delle classi include le nuove. * Quando si cambia la configurazione in produzione: la configurazione viene processata solo durante la prima richiesta in produzione. Le richieste successive utilizzano invece la versione memorizzata in cache. Quindi una modifica nella configurazione dell'ambiente di produzione (o qualunque ambiente in cui il debug è impostato a off) non ha effetto fino alla cancellazione della versione memorizzata in cache del file. - * Quando si modifica un template in un ambiente dove la cache per i template è abilitata: i template validi dalla cache vengono sempre utilizzati al posto dei template in produzione, quindi una modifica a un template viene ignorata fino a qualco la cache non viene cancellata o diventa obsoleta. + * Quando si modifica un template in un ambiente dove la cache per i template è abilitata: i template validi dalla cache vengono sempre utilizzati al posto dei template in produzione, quindi una modifica a un template viene ignorata fino a quando la cache non viene cancellata o diventa obsoleta. * Quando si aggiorna un'applicazione con il comando `project:deploy`: questo caso solitamente comprende le tre modifiche appena viste. Il problema della cancellazione dell'intera cache è rappresentato dal fatto che la richiesta successiva richiederà un tempo più lungo per essere processata perchè la cache della configurazione deve essere rigenerata. Inoltre i template non modificati verranno anch'essi rimossi dalla cache perdendo i benefici delle richieste precedenti. -Questo significa che è una buona idea rimuovere dalla cache solamente i file che realmente necessitano di essere rigenerati. Utilizzare le opzioni del task `cache:clear` per definire un sottoinsieme di file della cache da rimuovere come dimostrato nel Listing 18-14. +Questo significa che è una buona idea rimuovere dalla cache solamente i file che realmente necessitano di essere rigenerati. Utilizzare le opzioni del task `cache:clear` per definire un sottoinsieme di file della cache da rimuovere come dimostrato nel Listato 18-14. -Listing 18-14 - Rimuovere solo parti specifiche della cache +Listato 18-14 - Rimuovere solo parti specifiche della cache // Rimuovere solamente la cache dell'applicazione frontend $ php symfony cache:clear frontend @@ -444,7 +444,7 @@ Listing 18-14 - Rimuovere solo parti specifiche della cache Si possono rimuovere i file anche manualmente nella directory `cache/` o eliminare i file di cache dei template selettivamente dall'azione con il metodo `$cacheManager->remove()` come descritto nel Capitolo 12. -Tutte queste tecniche minimizzeranno l'impatto negativo sulle performanche di ognuna delle modifiche elencate precedentemente. +Tutte queste tecniche minimizzeranno l'impatto negativo sulle performance di ognuna delle modifiche elencate precedentemente. >**TIP** >Quando si aggiorna symfony la cache viene rimossa automaticamente senza intervento manuale (se il parametro `check_symfony_version` è impostato a `true` nel file `settings.yml`). @@ -453,13 +453,13 @@ Tutte queste tecniche minimizzeranno l'impatto negativo sulle performanche di og Quando si mette in produzione una nuova applicazione la cache dei template è vuota. È necessario aspettare che gli utenti visitino una pagina perchè essa venga inserita in cache. Nei deploy più critici l'overhead del processo di una pagina non è accettabile ed i benefici della cache devono essere disponibili già alla prima richiesta. -La soluzione è rappresentata dalla visita delle pagine dell'applicazione nell'ambiente di staging (dove la configurazione è simile a quella di produzione) per generare la cache dei template e poi trasferire l'applicazione con la cache in produzione. +La soluzione è rappresentata dalla visita delle pagine dell'applicazione nell'ambiente di stage (dove la configurazione è simile a quella di produzione) per generare la cache dei template e poi trasferire l'applicazione con la cache in produzione. Per visitare le pagine in modo automatico un'opzione è creare uno script di shell che analizza una lista di URL esterni con un browser (curl per esempio). Esiste però una soluzione migliore e più veloce: uno script PHP che utilizza l'oggetto `sfBrowser` già visto al Capitolo 15. Si tratta di un browser interno scritto in PHP (ed utilizzato da `sfTestFunctional` per i test funzionali). Accetta un URL esterno e restituisce una risposta, ma la cosa interessante è che scatena la creazione della cache del template proprio come un browser tradizionale. Dato che inizializza symfony solamente una volta e non utilizza lo strato di trasporto HTTP questo metodo risulta molto veloce. -Listing 18-15 mostra uno script d'esempio utilizzato per generare cache dei template nell'ambiente di staging. Avviarlo chiamando `php generate_cache.php`. +Listato 18-15 mostra uno script d'esempio utilizzato per generare cache dei template nell'ambiente di staging. Avviarlo chiamando `php generate_cache.php`. -Listing 18-15 - Generare la cache dei template, in `generate_cache.php` +Listato 18-15 - Generare la cache dei template, in `generate_cache.php` [php] require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php'); @@ -503,9 +503,9 @@ Tutti e due questi metodi sono molto efficaci, ed anche se sono applicabili solo ### Mettere in cache il risultato di una chiamata ad una funzione -Se una funzione non utilizza valori dipendenti dal contesto o non casuali, chiamandola due volte con gli stessi parametri dovrebbe fornire lo stesso risultato. Questo significa che la seconda chiamata potrebbe davvero essere evitata nel caso un cui si fosse memorizzato il primo risultato. Questo è esattamente ciò che la classe `sfFunctionCache` si occupa di fare. Questa classe ha un metodo `call()` che si aspetta un callable ed un array di parametri come argomenti. Quando invocato questo metodo crea un hash md5 con tutti gli argomenti e cerca nella cache una chiave denominata con quell'hash. Se la chiave viene trovata la funzione restituisce il risultato memorizzato nella cache. Altrimenti `sfFunctionCache` esegue la funzione, memorizza il risultato nella cache e lo restituisce. In questo modo la seconda esecuzione del Listing 18-16 sarà più veloce della prima. +Se una funzione non utilizza valori dipendenti dal contesto o non casuali, chiamandola due volte con gli stessi parametri dovrebbe fornire lo stesso risultato. Questo significa che la seconda chiamata potrebbe davvero essere evitata nel caso un cui si fosse memorizzato il primo risultato. Questo è esattamente ciò che la classe `sfFunctionCache` si occupa di fare. Questa classe ha un metodo `call()` che si aspetta un callable ed un array di parametri come argomenti. Quando invocato questo metodo crea un hash md5 con tutti gli argomenti e cerca nella cache una chiave denominata con quell'hash. Se la chiave viene trovata la funzione restituisce il risultato memorizzato nella cache. Altrimenti `sfFunctionCache` esegue la funzione, memorizza il risultato nella cache e lo restituisce. In questo modo la seconda esecuzione del Listato 18-16 sarà più veloce della prima. -Listing 18-16 - Mettere in cache il risultato di una funzione +Listato 18-16 - Mettere in cache il risultato di una funzione [php] $cache = new sfFileCache(array('cache_dir' => sfConfig::get('sf_cache_dir').'/function')); @@ -516,13 +516,13 @@ Listing 18-16 - Mettere in cache il risultato di una funzione Il costruttore della classe `sfFunctionCache` si aspetta un oggetto di tipo cache. Il primo argomento del metodo `call()` deve essere un callable, quindi può essere il nome di una funzione, un array contenente il nome di una classe ed il nome di un metodo statico oppure un array con il nome di un oggetto ed il nome di un metodo pubblico. Lo stesso vale per l'altro parametro del metodo `call()`, si tratta di un array di argomenti che verranno passati al callable. >**CAUTION** ->Se si utilizza un oggetto cache basato su file come nell'esempio è consigliabile utilizzare una directory all'interno della directory `cache/`, così facendo verà svuotata automaticamente dal task `cache:clear`. Se si memorizza la cache della funzione da qualche altra parte non verrà rimossa automaticamente quando si svuoterà la cache utilizzando la linea di comando. +>Se si utilizza un oggetto cache basato su file come nell'esempio è consigliabile utilizzare una directory all'interno della directory `cache/`, così facendo verrà svuotata automaticamente dal task `cache:clear`. Se si memorizza la cache della funzione da qualche altra parte non verrà rimossa automaticamente quando si svuoterà la cache utilizzando la linea di comando. ### Mettere in cache i dati sul server -Gli acceleratori PHP mettono a disposizione funzioni speciali per memorizzare dati in memoria per poterli riutilizzare su più richieste. Il problema è che hanno tutti sintassi diverse ed ognuno ha il suo modo speciale di assolvere questo compito. Le classi della cache di symfony riescono ad astrarre tutte queste differenze e funzionano con qualsiasi acceleratore si decida di utilizzare. Si analizzi il Listing 18-17. +Gli acceleratori PHP mettono a disposizione funzioni speciali per memorizzare dati in memoria per poterli riutilizzare su più richieste. Il problema è che hanno tutti sintassi diverse ed ognuno ha il suo modo speciale di assolvere questo compito. Le classi della cache di symfony riescono ad astrarre tutte queste differenze e funzionano con qualsiasi acceleratore si decida di utilizzare. Si analizzi il Listato 18-17. -Listing 18-17 - Utilizzare un acceleratore PHP per memorizzare dati +Listato 18-17 - Utilizzare un acceleratore PHP per memorizzare dati [php] $cache = new sfAPCCache(); @@ -549,9 +549,9 @@ Disattivare le funzionalità inutilizzate La configurazione standard di symfony attiva le funzionalità più comuni per le applicazione web. Tuttavia, se non si ha la necessità di utilizzarle tutte, sarebbe opportuno disattivarle per risparmiare il tempo necessario alla loro inizializzazione consumato ad ogni richiesta. -Per esempio nel caso in cui l'applicazione non utilizzi il meccanismo delle sessioni, o si volesse attivare la gestione delle sessioni manualmente, si dovrebbe modificare il valore del parametro `auto_start` impostandolo a `false` sotto la chiave `storage` nel file `factories.yml` come nel Listing 18-18. +Per esempio nel caso in cui l'applicazione non utilizzi il meccanismo delle sessioni, o si volesse attivare la gestione delle sessioni manualmente, si dovrebbe modificare il valore del parametro `auto_start` impostandolo a `false` sotto la chiave `storage` nel file `factories.yml` come nel Listato 18-18. -Listing 18-18 - Disabilitare le sessioni, in `frontend/config/factories.yml` +Listato 18-18 - Disabilitare le sessioni, in `frontend/config/factories.yml` all: storage: @@ -559,17 +559,17 @@ Listing 18-18 - Disabilitare le sessioni, in `frontend/config/factories.yml` param: auto_start: false -Lo stesso dicasi per le funzionalità del database (come descritto precedentemente nella sezione "Tweaking the Model" in questo capitolo). Se l'applicazione non utilizza un database si può disattivare per un piccolo guadagno nelle prestazioni nel file `settings.yml` (vedere Listing 18-19). +Lo stesso dicasi per le funzionalità del database (come descritto precedentemente nella sezione "Tweaking the Model" in questo capitolo). Se l'applicazione non utilizza un database si può disattivare per un piccolo guadagno nelle prestazioni nel file `settings.yml` (vedere Listato 18-19). -Listing 18-19 - Disabilitare il database, in `frontend/config/settings.yml` +Listato 18-19 - Disabilitare il database, in `frontend/config/settings.yml` all: .settings: use_database: false # Funzionalità per database e modello -Lo stesso vale per le funzionalità di sicurezza (vedere Capitolo 6) che si possono disattivare nel file `filters.yml`, come mostrato nel Listing 18-20. +Lo stesso vale per le funzionalità di sicurezza (vedere Capitolo 6) che si possono disattivare nel file `filters.yml`, come mostrato nel Listato 18-20. -Listing 18-20 - Disabilitare funzionalità di sicurezza, in `frontend/config/filters.yml` +Listato 18-20 - Disabilitare funzionalità di sicurezza, in `frontend/config/filters.yml` rendering: ~ security: @@ -582,7 +582,7 @@ Listing 18-20 - Disabilitare funzionalità di sicurezza, in `frontend/config/fil Alcune funzionalità sono utili solamente nell'ambiente di sviluppo, non vanno quindi attivate in produzione. Questa è già la situazione standard dato che l'ambiente di produzione di symfony è totalmente ottimizzato per le migliori performance. Tra tutte le funzionalità di sviluppo che hanno un impatto sulle performance, la modalità di debug è sicuramente la più severa. Come per i log di symfony la funzionalità è già disabilitata nell'ambiente di produzione. -Ci si potrebbe chiedere come ottenere informazioni sulle richieste fallite nell'ambiente di produzione se il logging è disabilitato, facendo notare anche che i problemi non si manifestano solo durante lo sviluppo. Fortunatamente symfony può utilizzare il plugin `sfErrorLoggerPlugin` che lavora in background nell'ambiente di produzione e registra i dettagli degli errori 404 e 500 in un database. È molto più veloce della funzionalità di logging su file dato che i metodi del plug-in vengono invocati solamente quando una richiesta fallisce, mentre il meccanismo di logging se attivo aggiunge un overhead non indifferende indipendentemente dal livello impostato. Controllare le istruzioni di installazione ed il [manuale](http://plugins.symfony-project.org/plugins/sfErrorLoggerPlugin). +Ci si potrebbe chiedere come ottenere informazioni sulle richieste fallite nell'ambiente di produzione se il logging è disabilitato, facendo notare anche che i problemi non si manifestano solo durante lo sviluppo. Fortunatamente symfony può utilizzare il plugin `sfErrorLoggerPlugin` che lavora in background nell'ambiente di produzione e registra i dettagli degli errori 404 e 500 in un database. È molto più veloce della funzionalità di logging su file dato che i metodi del plug-in vengono invocati solamente quando una richiesta fallisce, mentre il meccanismo di logging se attivo aggiunge un overhead non indifferente indipendentemente dal livello impostato. Controllare le istruzioni di installazione ed il [manuale](http://plugins.symfony-project.org/plugins/sfErrorLoggerPlugin). >**TIP** >È buona abitudine controllare regolarmente i log degli errori del server dato che contengono informazioni molto utili riguardo agli errori 404 e 500. @@ -594,13 +594,13 @@ Ottimizzare il proprio codice ### Compilazione del core -Caricare dieci file richiede più operazioni di I/O rispetto al caricamente di un grande file, specialmente su dischi lenti. Caricare un file molto grande richiede più risorse rispetto a caricarne uno più piccolo, specialmente se grossa parte del contenuto del file non è di alcun interesse per il parser PHP, è il caso dei commenti. +Caricare dieci file richiede più operazioni di I/O rispetto al caricamento di un grande file, specialmente su dischi lenti. Caricare un file molto grande richiede più risorse rispetto a caricarne uno più piccolo, specialmente se grossa parte del contenuto del file non è di alcun interesse per il parser PHP, è il caso dei commenti. Quindi fondere un grosso numero di file eliminandone i commenti contenuti è un'operazione che migliora le performance. Symfony esegue già tale ottimizzazione, si chiama compilazione del core. All'inizio della prima richiesta (o dopo aver svuotato la cache) un'applicazione symfony concatena tutte le classi del core del framework (`sfActions`, `sfRequest`, `sfView`, e così via) in un unico file, riduce la dimensione del file rimuovendo commenti e doppi spazi e salva tutto nella cache in un file chiamato `config_core_compile.yml.php`. Ogni richiesta seguente caricherà solamente questo singolo file ottimizzato invece che i 30 file che lo compongono. -Se l'applicazione ha classi che devono essere caricare ogni volta, specialmente se sono classi grandi con molti commenti, può essere un vantaggio aggiungerle al file compilato del core. Per fare questo basta aggiungere un file `core_compile.yml` nella directory `config/` dell'applicazione in cui si elencheranno le classi che si vogliono aggiungere come nel Listing 18-21. +Se l'applicazione ha classi che devono essere caricare ogni volta, specialmente se sono classi grandi con molti commenti, può essere un vantaggio aggiungerle al file compilato del core. Per fare questo basta aggiungere un file `core_compile.yml` nella directory `config/` dell'applicazione in cui si elencheranno le classi che si vogliono aggiungere come nel Listato 18-21. -Listing 18-21 - Aggiungere le proprie classi al file compilato del core, in `frontend/config/core_compile.yml` +Listato 18-21 - Aggiungere le proprie classi al file compilato del core, in `frontend/config/core_compile.yml` - %SF_ROOT_DIR%/lib/myClass.class.php - %SF_ROOT_DIR%/apps/frontend/lib/myToolkit.class.php @@ -617,4 +617,4 @@ Per vedere le strategie di ottimizzazione utilizzate nel task basta dare un'occh Sommario -------- -Symfony è già un framework molto ottimizzato e in grado di gestire siti ad alto traffico senza problemi. Ma se davvero si avesse la necessità di ottimizzare ulteriormente le performance della propria applicazione, mettere a punto la configurazione (che sia la configurazione del server, di PHP o le impostazioni dell'applicazione) può fornire un piccolo miglioramento. È consigliabile seguire le best practice per scrivere metodi del modello efficienti; e dato che il database rappresenta sempre un collo di bottiglia per le applicazioni web su di esso andrà riposta particolare attenzione. I template possono beneficiare anch'essi di alcune ottimizzazioni, ma i miglioramenti più evidenti arriveranno dall'utilizzo del sistema di caching. Infine non si esiti nell'analizzare plug-in esistenti dato che alcuni di essi mettono a disposizione tecniche innovative per aumentare ulteriormente la consegna delle pagine web (`sfSuperCache`, `project:optimize`). +Symfony è già un framework molto ottimizzato e in grado di gestire siti ad alto traffico senza problemi. Ma se davvero si avesse la necessità di ottimizzare ulteriormente le performance della propria applicazione, mettere a punto la configurazione (che sia la configurazione del server, di PHP o le impostazioni dell'applicazione) può fornire un piccolo miglioramento. È consigliabile seguire le best practice per scrivere metodi del modello efficienti; e dato che il database rappresenta sempre un collo di bottiglia per le applicazioni web su di esso andrà riposta particolare attenzione. I template possono beneficiare anch'essi di alcune ottimizzazioni, ma i miglioramenti più evidenti arriveranno dall'utilizzo del sistema della cache. Infine non si esiti nell'analizzare plug-in esistenti dato che alcuni di essi mettono a disposizione tecniche innovative per aumentare ulteriormente la consegna delle pagine web (`sfSuperCache`, `project:optimize`).