Development

/doc/branches/1.1/book/16-Application-Management-Tools.txt

You must first sign up to be able to contribute.

root/doc/branches/1.1/book/16-Application-Management-Tools.txt

Revision 16648, 47.5 kB (checked in by hugo.hamon, 3 months ago)

[doc] fixed wrong log file extension

Line 
1 Chapter 16 - Application Management Tools
2 =========================================
3
4 During both the development and deployment phases, developers require a consistent stream of diagnostic information in order to determine whether the application is working as intended. This information is generally aggregated through logging and debugging utilities. Because of the central role frameworks, such as symfony, play in driving applications, it's crucial that such capabilities are tightly integrated to ensure efficient developmental and operational activities.
5
6 During the life of an application on the production server, the application administrator repeats a large number of tasks, from log rotation to upgrades. A framework must also provide tools to automate these tasks as much as possible.
7
8 This chapter explains how symfony application management tools can answer all these needs.
9
10 Logging
11 -------
12
13 The only way to understand what went wrong during the execution of a request is to review a trace of the execution process. Fortunately, as you'll learn in this section, both PHP and symfony tend to log large amounts of this sort of data.
14
15 ### PHP Logs
16
17 PHP has an `error_reporting` parameter, defined in `php.ini`, that specifies which PHP events are logged. Symfony allows you to override this value, per application and environment, in the `settings.yml` file, as shown in Listing 16-1.
18
19 Listing 16-1 - Setting the Error Reporting Level, in `frontend/config/settings.yml`
20
21     prod:
22      .settings:
23         error_reporting:  <?php echo (E_PARSE | E_COMPILE_ERROR | E_ERROR | E_CORE_ERROR | E_USER_ERROR)."\n" ?>
24
25     dev:
26       .settings:
27         error_reporting:  <?php echo (E_ALL | E_STRICT)."\n" ?>
28
29 In order to avoid performance issues in the production environment, the server logs only the critical PHP errors. However, in the development environment, all types of events are logged, so that the developer can have all the information necessary to trace errors.
30
31 The location of the PHP log files depends on your `php.ini` configuration. If you never bothered about defining this location, PHP probably uses the logging facilities provided by your web server (such as the Apache error logs). In this case, you will find the PHP logs under the web server log directory.
32
33 ### Symfony Logs
34
35 In addition to the standard PHP logs, symfony can log a lot of custom events. You can find all the symfony logs under the `myproject/log/` directory. There is one file per application and per environment. For instance, the development environment log file of the `frontend` application is named `frontend_dev.log`, the production one is named `frontend_prod.log`, and so on.
36
37 If you have a symfony application running, take a look at its log files. The syntax is very simple. For every event, one line is added to the log file of the application. Each line includes the exact time of the event, the nature of the event, the object being processed, and any additional relevant details. Listing 16-2 shows an example of symfony log file content.
38
39 Listing 16-2 - Sample Symfony Log File Content, in `log/frontend_dev.log`
40
41     Nov 15 16:30:25 symfony [info ] {sfAction} call "barActions->executemessages()"
42     Nov 15 16:30:25 symfony [debug] SELECT bd_message.ID, bd_message.SENDER_ID, bd_...
43     Nov 15 16:30:25 symfony [info ] {sfCreole} executeQuery(): SELECT bd_message.ID...
44     Nov 15 16:30:25 symfony [info ] {sfView} set slot "leftbar" (bar/index)
45     Nov 15 16:30:25 symfony [info ] {sfView} set slot "messageblock" (bar/mes...
46     Nov 15 16:30:25 symfony [info ] {sfView} execute view for template "messa...
47     Nov 15 16:30:25 symfony [info ] {sfView} render "/home/production/myproject/...
48     Nov 15 16:30:25 symfony [info ] {sfView} render to client
49
50 You can find many details in these files, including the actual SQL queries sent to the database, the templates called, the chain of calls between objects, and so on.
51
52 **New in symfony 1.1**: The format of the file logs is configurable by overriding the `format` and/or the `time_format` settings in `factories.yml` as shown in Listing 16-3.
53
54 Listing 16-3 - Changing the Log Format
55
56     all:
57       logger:
58         param:
59           sf_file_debug:
60             param:
61               format:      %time% %type% [%priority%] %message%%EOL%
62               time_format: %b %d %H:%M:%S
63
64 #### Symfony Log Level Configuration
65
66 There are eight levels of symfony log messages: `emerg`, `alert`, `crit`, `err`, `warning`, `notice`, `info`, and `debug`, which are the same as the `PEAR::Log` package ([http://pear.php.net/package/Log/](http://pear.php.net/package/Log/)) levels. You can configure the maximum level to be logged in each environment in the `factories.yml` configuration file of each application, as demonstrated in Listing 16-4.
67
68 Listing 16-4 - Default Logging Configuration, in `frontend/config/factories.yml`
69
70     prod:
71       logger:
72         param:
73           level: err
74
75 By default, in all environments except the production environment, all the messages are logged (up to the least important level, the `debug` level). In the production environment, logging is disabled by default; if you change `logging_enabled` to `on` in `settings.yml`, only the most important messages (from `crit` to `emerg`) appear in the logs.
76
77 You can change the logging level in the `factories.yml` file for each environment to limit the type of logged messages.
78
79 >**TIP**
80 >To see if logging is enabled, call `sfConfig::get('sf_logging_enabled')`.
81
82 #### Adding a Log Message
83
84 You can manually add a message in the symfony log file from your code by using one of the techniques described in Listing 16-5.
85
86 Listing 16-5 - Adding a Custom Log Message
87
88     [php]
89     // From an action
90     $this->logMessage($message, $level);
91
92     // From a template
93     <?php use_helper('Debug') ?>
94     <?php log_message($message, $level) ?>
95
96 `$level` can have the same values as in the log messages.
97
98 Alternatively, to write a message in the log from anywhere in your application, use the `sfLogger` methods directly, as shown in Listing 16-6. The available methods bear the same names as the log levels.
99
100 Listing 16-6 - Adding a Custom Log Message from Anywhere
101
102     [php]
103     if (sfConfig::get('sf_logging_enabled'))
104     {
105       sfContext::getInstance()->getLogger()->info($message);
106     }
107
108 >**SIDEBAR**
109 >**New in symfony 1.1**: Customizing the logging
110 >
111 >Symfony's logging system is very simple, yet it is also easy to customize.
112 >The only prerequisite is that logger classes must extend the `sfLogger` class, which defines a `doLog()` method. Symfony calls the `doLog()` method with two parameters: `$message` (the message to be logged), and `$priority` (the log level).
113 >
114 >The `myLogger` class defines a simple logger using the PHP `error_log` function:
115 >
116 >     [php]
117 >     class myLogger extends sfLogger
118 >     {
119 >       protected function doLog($message, $priority)
120 >       {
121 >         error_log(sprintf('%s (%s)', $message, sfLogger::getPriorityName($priority)));
122 >       }
123 >     }
124 >
125 >To create a logger from an existing class, you can just implement the `sfLoggerInterface` interface, which defines a `log()` method. The method takes the same two parameters as the `doLog()` method:
126 >
127 >     [php]
128 >     require_once('Log.php');
129 >     require_once('Log/error_log.php');
130 >
131 >     // Define a thin wrapper to implement the interface
132 >     // for the logger we want to use with symfony
133 >     class Log_my_error_log extends Log_error_log implements sfLoggerInterface
134 >     {
135 >     }
136
137 #### Purging and Rotating Log Files
138
139 Don't forget to periodically purge the `log/` directory of your applications, because these files have the strange habit of growing by several megabytes in a few days, depending, of course, on your traffic. Symfony provides a special `log:clear` task for this purpose, which you can launch regularly by hand or put in a cron table. For example, the following command erases the symfony log files:
140
141     > php symfony log:clear
142
143 For both better performance and security, you probably want to store symfony logs in several small files instead of one single large file. The ideal storage strategy for log files is to back up and empty the main log file regularly, but to keep only a limited number of backups. You can enable such a log rotation with a `period` of `7` days and a `history` (number of backups) of `10`, as shown in Listing 16-7. You would work with one active log file plus ten backup files containing seven days' worth of history each. Whenever the next period of seven days ends, the current active log file goes into backup, and the oldest backup is erased.
144
145 Listing 16-7 - Launching Log Rotation
146
147     > php symfony log:rotate frontend prod --period=7 --history=10
148
149 The backup log files are stored in the `logs/history/` directory and suffixed with the date they were saved.
150
151 Debugging
152 ---------
153
154 No matter how proficient a coder you are, you will eventually make mistakes, even if you use symfony. Detecting and understanding errors is one of the keys of fast application development. Fortunately, symfony provides several debug tools for the developer.
155
156 ### Symfony Debug Mode
157
158 Symfony has a debug mode that facilitates application development and debugging. When it is on, the following happens:
159
160   * The configuration is checked at each request, so a change in any of the configuration files has an immediate effect, without any need to clear the configuration cache.
161   * The error messages display the full stack trace in a clear and useful way, so that you can more efficiently find the faulty element.
162   * More debug tools are available (such as the detail of database queries).
163   * The Propel debug mode is also activated, so any error in a call to a Propel object will display a detailed chain of calls through the Propel architecture.
164
165 On the other hand, when the debug mode is off, processing is handled as follows:
166
167   * The YAML configuration files are parsed only once, then transformed into PHP files stored in the `cache/config/` folder. Every request after the first one ignores the YAML files and uses the cached configuration instead. As a consequence, the processing of requests is much faster.
168   * To allow a reprocessing of the configuration, you must manually clear the configuration cache.
169   * An error during the processing of the request returns a response with code 500 (Internal Server Error), without any explanation of the internal cause of the problem.
170
171 The debug mode is activated per application in the front controller. It is controlled by the value of the third argument passed to the `getApplicationConfiguration()` method call, as shown in Listing 16-8.
172
173 Listing 16-8 - Sample Front Controller with Debug Mode On, in `web/frontend_dev.php`
174
175     [php]
176     <?php
177
178     require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
179
180     $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true);
181     sfContext::createInstance($configuration)->dispatch();
182
183 >**CAUTION**
184 >In your production server, you should not activate the debug mode nor leave any front controller with debug mode on available. Not only will the debug mode slow down the page delivery, but it may also reveal the internals of your application. Even though the debug tools never reveal database connection information, the stack trace of exceptions is full of dangerous information for any ill-intentioned visitor.
185
186 ### Symfony Exceptions
187
188 When an exception occurs in the debug mode, symfony displays a useful exception notice that contains everything you need to find the cause of the problem.
189
190 The exception messages are clearly written and refer to the most probable cause of the problem. They often provide possible solutions to fix the problem, and for most common problems, the exception pages even contain a link to a symfony website page with more details about the exception. The exception page shows where the error occurred in the PHP code (with syntax highlighting), together with the full stack of method calls, as shown in Figure 16-1. You can follow the trace to the first call that caused the problem. The arguments that were passed to the methods are also shown.
191
192 >**NOTE**
193 >Symfony really relies on PHP exceptions for error reporting, which is much better than the way PHP 4 applications work. For instance, the 404 error can be triggered by an `sfError404Exception`.
194
195 Figure 16-1 - Sample exception message for a symfony application
196
197 ![Sample exception message for a symfony application](/images/book/F1601.png "Sample exception message for a symfony application")
198
199 During the development phase, the symfony exceptions will be of great use as you debug your application.
200
201 ### Xdebug Extension
202
203 The Xdebug PHP extension ([http://xdebug.org/](http://xdebug.org/)) allows you to extend the amount of information that is logged by the web server. Symfony integrates the Xdebug messages in its own debug feedback, so it is a good idea to activate this extension when you debug the application. The extension installation depends very much on your platform; refer to the Xdebug website for detailed installation guidelines. Once Xdebug is installed, you need to activate it manually in your `php.ini` file after installation. For *nix platforms, this is done by adding the following line:
204
205     zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20041030/xdebug.so"
206
207 For Windows platforms, the Xdebug activation is triggered by this line:
208
209     extension=php_xdebug.dll
210
211 Listing 16-9 gives an example of Xdebug configuration, which must also be added to the `php.ini` file.
212
213 Listing 16-9 - Sample Xdebug Configuration
214
215     ;xdebug.profiler_enable=1
216     ;xdebug.profiler_output_dir="/tmp/xdebug"
217     xdebug.auto_trace=1             ; enable tracing
218     xdebug.trace_format=0
219     ;xdebug.show_mem_delta=0        ; memory difference
220     ;xdebug.show_local_vars=1
221     ;xdebug.max_nesting_level=100
222
223 You must restart your web server for the Xdebug mode to be activated.
224
225 >**CAUTION**
226 >Don't forget to deactivate Xdebug mode in your production platform. Not doing so will slow down the execution of every page a lot.
227
228 ### Web Debug Toolbar
229
230 The log files contain interesting information, but they are not very easy to read. The most basic task, which is to find the lines logged for a particular request, can be quite tricky if you have several users simultaneously using an application and a long history of events. That's when you start to need a web debug toolbar.
231
232 This toolbar appears as a semitransparent box superimposed over the normal content in the browser, in the top-right corner of the window, as shown in Figure 16-2. It gives access to the symfony log events, the current configuration, the properties of the request and response objects, the details of the database queries issued by the request, and a chart of processing times related to the request.
233
234 Figure 16-2 - The web debug toolbar appears in the top-right corner of the window
235
236 ![The web debug toolbar appears in the top-right corner of the window](/images/book/F1602.jpg "The web debug toolbar appears in the top-right corner of the window")
237
238 The color of the debug toolbar background depends on the highest level of log message issued during the request. If no message passes the `debug` level, the toolbar has a gray background. If a single message reaches the `err` level, the toolbar has a red background.
239
240 >**NOTE**
241 >Don't confuse the debug mode with the web debug toolbar. The debug toolbar can be displayed even when the debug mode if off, although, in that case, it displays much less information.
242
243 To activate the web debug toolbar for an application, open the `settings.yml` file and look for the `web_debug` key. In the `prod` and `test` environments, the default value for `web_debug` is `off`, so you need to activate it manually if you want it. In the `dev` environment, the default configuration has it set to `on`, as shown in Listing 16-10.
244
245 Listing 16-10 - Web Debug Toolbar Activation, in `frontend/config/settings.yml`
246
247     dev:
248       .settings:
249         web_debug:              on
250
251 When displayed, the web debug toolbar offers a lot of information/interaction:
252
253   * Click the symfony logo to toggle the visibility of the toolbar. When reduced, the toolbar doesn't hide the elements located at the top of the page.
254   * Click the vars & config section to show the details of the request, response, settings, globals, and PHP properties, as shown in Figure 16-3. The top line sums up the important configuration settings, such as the debug mode, the cache, and the presence of a PHP accelerator (they appear in red if they are deactivated and in green if they are activated).
255
256 Figure 16-3 - The vars & config section shows all the variables and constants of the request
257
258 ![The vars & config section shows all the variables and constants of the request](/images/book/F1603.png "The vars & config section shows all the variables and constants of the request")
259
260   * When the cache is enabled, a green arrow appears in the toolbar. Click this arrow to reprocess the page, regardless of what is stored in the cache (but the cache is not cleared).
261   * Click the logs & msgs section to reveal the log messages for the current request, as shown in Figure 16-4. According to the importance of the events, they are displayed in gray, yellow, or red lines. You can filter the events that are displayed by category using the links displayed at the top of the list.
262
263 Figure 16-4 - The logs & msgs section shows the log messages for the current request
264
265 ![The logs & msgs section shows the log messages for the current request](/images/book/F1604.png "The logs & msgs section shows the log messages for the current request")
266
267 >**NOTE**
268 >When the current action results from a redirect, only the logs of the latest request are present in the logs & msgs pane, so the log files are still indispensable for good debugging.
269
270   * For requests executing SQL queries, a database icon appears in the toolbar. Click it to see the detail of the queries, as shown in Figure 16-5.
271   * To the right of a clock icon is the total time necessary to process the request. Be aware that the web debug toolbar and the debug mode slow down the request execution, so try to refrain from considering the timings per se, and pay attention to only the differences between the execution time of two pages. Click the clock icon to see details of the processing time category by category, as shown in Figure 16-6. Symfony displays the time spent on specific parts of the request processing. Only the times related to the current request make sense for an optimization, so the time spent in the symfony core is not displayed. That's why these times don't sum up to the total time.
272   * Click the red x at the right end of the toolbar to hide the toolbar.
273
274 Figure 16-5 - The database queries section shows queries executed for the current request
275
276 ![The database queries section shows queries executed for the current request](/images/book/F1605.png "The database queries section shows queries executed for the current request")
277
278 Figure 16-6 - The clock icon shows execution time by category
279
280 ![The clock icon shows execution time by category](/images/book/F1606.png "The clock icon shows execution time by category")
281
282 >**SIDEBAR**
283 >Adding your own timer
284 >
285 >Symfony uses the `sfTimer` class to calculate the time spent on the configuration, the model, the action, and the view. Using the same object, you can time a custom process and display the result with the other timers in the web debug toolbar. This can be very useful when you work on performance optimizations.
286 >
287 >To initialize timing on a specific fragment of code, call the `getTimer()` method. It will return an sfTimer object and start the timing. Call the `addTime()` method on this object to stop the timing. The elapsed time is available through the `getElapsedTime()` method, and displayed in the web debug toolbar with the others.
288 >
289 >     [php]
290 >     // Initialize the timer and start timing
291 >     $timer = sfTimerManager::getTimer('myTimer');
292 >
293 >     // Do things
294 >     ...
295 >
296 >     // Stop the timer and add the elapsed time
297 >     $timer->addTime();
298 >
299 >     // Get the result (and stop the timer if not already stopped)
300 >     $elapsedTime = $timer->getElapsedTime();
301 >
302 >The benefit of giving a name to each timer is that you can call it several times to accumulate timings. For instance, if the `myTimer` timer is used in a utility method that is called twice per request, the second call to the `getTimer('myTimer')` method will restart the timing from the point calculated when `addTime()` was last called, so the timing will add up to the previous one. Calling `getCalls()` on the timer object will give you the number of times the timer was launched, and this data is also displayed in the web debug toolbar.
303 >
304 >     [php]
305 >     // Get the number of calls to the timer
306 >     $nbCalls = $timer->getCalls();
307 >
308 >In Xdebug mode, the log messages are much richer. All the PHP script files and the functions that are called are logged, and symfony knows how to link this information with its internal log. Each line of the log messages table has a double-arrow button, which you can click to see further details about the related request. If something goes wrong, the Xdebug mode gives you the maximum amount of detail to find out why.
309
310 >**NOTE**
311 >The web debug toolbar is not included by default in Ajax responses and documents that have a non-HTML content-type. For the other pages, you can disable the web debug toolbar manually from within an action by simply calling `sfConfig::set('sf_web_debug', false)`.
312
313 ### Manual Debugging
314
315 Getting access to the framework debug messages is nice, but being able to log your own messages is better. Symfony provides shortcuts, accessible from both actions and templates, to help you trace events and/or values during request execution.
316
317 Your custom log messages appear in the symfony log file as well as in the web debug toolbar, just like regular events. (Listing 16-5 gave an example of the custom log message syntax.) A custom message is a good way to check the value of a variable from a template, for instance. Listing 16-11 shows how to use the web debug toolbar for developer's feedback from a template (you can also use `$this->logMessage()` from an action).
318
319 Listing 16-11 - Inserting a Message in the Log for Debugging Purposes
320
321     [php]
322     <?php use_helper('Debug') ?>
323     ...
324     <?php if ($problem): ?>
325       <?php log_message('{sfAction} been there', 'err') ?>
326       ...
327     <?php endif ?>
328
329 The use of the `err` level guarantees that the event will be clearly visible in the list of messages, as shown in Figure 16-7.
330
331 Figure 16-7 - A custom log message appears in the logs & msgs section of the web debug toolbar
332
333 ![A custom log message appears in the logs & msgs section of the web debug toolbar](/images/book/F1607.png "A custom log message appears in the logs & msgs section of the web debug toolbar")
334
335 If you don't want to add a line to the log, but just trace a short message or a value, you should use `debug_message` instead of `log_message`. This action method (a helper with the same name also exists) displays a message in the web debug toolbar, on top of the logs & msgs section. Check Listing 16-12 for an example of using the debug message writer.
336
337 Listing 16-12 - Inserting a Message in the Debug Toolbar
338
339     [php]
340     // From an action
341     $this->debugMessage($message);
342
343     // From a template
344     <?php use_helper('Debug') ?>
345     <?php debug_message($message) ?>
346
347 Using symfony outside of a web context
348 --------------------------------------
349
350 You may want to execute a script from the command line (or via a cron table) with access to all the symfony classes and features, for instance to launch batch e-mail jobs or to periodically update your model through a process-intensive calculation. The simple way of doing this is to create a PHP script that reproduces the first steps of a front controller, so that symfony is properly initialized. You can also use the symfony command line system, to take advantage of arguments parsing and automated database initialization.
351
352 ### Batch Files
353
354 Initializing symfony just takes a couple lines of PHP code. You can take advantage of all symfony features by creating a PHP file, for instance under the `lib/` directory of your project, starting with the lines shown in Listing 16-13.
355
356 Listing 16-13 - Sample Batch Script, in `lib/myScript.php`
357
358     [php]
359     <?php
360
361     require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
362     $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true);
363     sfContext::createInstance($configuration);
364
365     // Remove the following lines if you don't use the database layer
366     $databaseManager = new sfDatabaseManager($configuration);
367     $databaseManager->loadConfiguration();
368
369     // add code here
370
371 This strongly looks like the first lines of a front controller (see Chapter 6), because these lines do the same: initialize symfony, parse a project and an application configuration. Note that the `ProjectConfiguration::getApplicationConfiguration` method expects three parameters:
372
373  * an application name
374  * an environment name
375  * a boolean, defining if the debug features should be enabled or not
376
377 To execute your code, just call the script from the command line:
378
379     > php lib/myScript.php
380
381 ### Custom tasks (new in symfony 1.1)
382
383 An alternative way of creating custom command line scripts using symfony is to write a **symfony task**. Just like the `cache:clear` and the `propel:build-model` tasks, you can launch your own custom tasks from the command line with `php symfony`. Custom tasks benefit from the ability to parse command line arguments and options, can embed their own help text, and can extend existing tasks.
384
385 A custom task is just a class extending `sfBaseTask` and located under a `lib/task/` directory, either under the project root, or in a plugin directory. Its file name must end with 'Task.class.php'. Listing 16-14 shows a sample custom task.
386
387 Listing 16-14 - Sample Task, in `lib/task/testHelloTask.class.php`
388
389     [php]
390
391     class testHelloTask extends sfBaseTask
392     {
393       protected function configure()
394       {
395         $this->namespace = 'test';
396         $this->name = 'hello';
397         $this->briefDescription = 'Says hello';
398       }
399
400       protected function execute()
401       {
402         // your code here
403         $this->log('Hello, world!');
404       }
405     }
406
407 The code written in the `execute` method has access to all the symfony libraries, just like in the previous batch script. The difference is how you call the custom task:
408
409     > php symfony test:hello
410
411 The task name comes from the protected `namespace` and `name` properties (not from the class name, nor from the files name). And since your task is integrated into the symfony command line, it appears in the task list when you just type:
412
413     > php symfony
414
415 Rather than writing a task skeleton by yourself, you can use the symfony `generate:task` task. It creates an empty task, and has plenty of customization options. Make sure you check them by calling:
416
417     > php symfony help generate:task
418
419 Tasks can accept arguments (compulsory parameters, in a predefined order) and options (optional and unordered parameters). Listing 16-15 shows a more complete task, taking advantage of all these features.
420
421 Listing 16-15 - More Complete Sample Task, in `lib/task/mySecondTask.class.php`
422
423     [php]
424     class mySecondTask extends sfBaseTask
425     {
426       protected function configure()
427       {
428         $this->namespace        = 'foo';
429         $this->name             = 'mySecondTask';
430         $this->briefDescription = 'Does some neat things, with style';
431         $this->detailedDescription = <<<EOF
432     The [foo:mySecondTask|INFO] task manages the process of achieving things for you.
433     Call it with:
434
435       [php symfony foo:mySecondTask frontend|INFO]
436
437     You can enable verbose output by using the [verbose|COMMENT] option:
438
439       [php symfony foo:mySecondTask frontend --verbose=on|INFO]
440     EOF;
441         $this->addArgument('application', sfCommandArgument::REQUIRED, 'The application name');
442         $this->addOption('verbose', null, sfCommandOption::PARAMETER_REQUIRED, 'Enables verbose output', false);
443       }
444
445       protected function execute($arguments = array(), $options = array())
446       {
447         // add code here
448        
449       }
450     }
451
452 >**NOTE**
453 >If your task needs access to the database layer, it should extend `sfPropelBaseTask` instead of `sfBaseTask`. The task initialization will then take care of loading the additional Propel classes. You can start a database connection in the `execute()` method by calling:
454 >
455 >    $databaseManager = new sfDatabaseManager($this->configuration);
456 >
457 >If the task configuration defines an `application` and an `env` argument, they are automatically considered when building the task configuration, so that a task can use any of the database connections defined in your `databases.yml`. By default, skeletons generated by a call to `generate:task` include this initialization.
458
459 For more examples on the abilities of the task system, check the source of existing symfony tasks.
460
461 If you declare a task with the same name as an existing task, your class overrides the existing task. That means that a plugin can override a symfony task, and that a project can override a plugin task and a symfony task. Add to that the ability to extend an existing task class, and you have a very extensible command line system!
462
463 Populating a Database
464 ---------------------
465
466 In the process of application development, developers are often faced with the problem of database population. A few specific solutions exist for some database systems, but none can be used on top of the object-relational mapping. Thanks to YAML and the `sfPropelData` object, symfony can automatically transfer data from a text source to a database. Although writing a text file source for data may seem like more work than entering the records by hand using a CRUD interface, it will save you time in the long run. You will find this feature very useful for automatically storing and populating the test data for your application.
467
468 ### Fixture File Syntax
469
470 Symfony can read data files that follow a very simple YAML syntax, provided that they are located under the `data/fixtures/` directory. Fixture files are organized by class, each class section being introduced by the class name as a header. For each class, records labeled with a unique string are defined by a set of `fieldname: value` pairs. Listing 16-16 shows an example of a data file for database population.
471
472 Listing 16-16 - Sample Fixture File, in `data/fixtures/import_data.yml`
473
474     Article:                             ## Insert records in the blog_article table
475       first_post:                        ## First record label
476         title:       My first memories
477         content: |
478           For a long time I used to go to bed early. Sometimes, when I had put
479           out my candle, my eyes would close so quickly that I had not even time
480           to say "I'm going to sleep".
481
482       second_post:                       ## Second record label
483         title:       Things got worse
484         content: |
485           Sometimes he hoped that she would die, painlessly, in some accident,
486           she who was out of doors in the streets, crossing busy thoroughfares,
487           from morning to night.
488
489 Symfony translates the column keys into setter methods by using a camelCase converter (`setTitle()`, `setContent()`). This means that you can define a `password` key even if the actual table doesn't have a `password` field--just define a `setPassword()` method in the `User` object, and you can populate other columns based on the password (for instance, a hashed version of the password).
490
491 The primary key column doesn't need to be defined. Since it is an auto-increment field, the database layer knows how to determine it.
492
493 The `created_at` columns don't need to be set either, because symfony knows that fields named that way must be set to the current system time when created.
494
495 ### Launching the Import
496
497 The `propel:data-load` task imports data from YAML files to a database. The connection settings come from the `databases.yml` file, and therefore need an application name to run. Optionally, you can specify an environment name by adding a `--env` option (`dev` by default).
498
499     > php symfony propel:data-load --env=prod frontend
500
501 This command reads all the YAML fixture files from the `data/fixtures/` directory and inserts the records into the database. By default, it replaces the existing database content, but if you add an `--append` option, the command will not erase the current data.
502
503     > php symfony propel:data-load --append frontend
504
505 You can specify another fixture directory in the call. In this case, add a path relative to the project directory.
506
507     > php symfony propel:data-load frontend --dir[]=data/myfixtures
508
509 ### Using Linked Tables
510
511 You now know how to add records to a single table, but how do you add records with foreign keys to another table? Since the primary key is not included in the fixtures data, you need an alternative way to relate records to one another.
512
513 Let's return to the example in Chapter 8, where a `blog_article` table is linked to a `blog_comment` table, as shown in Figure 16-8.
514
515 Figure 16-8 - A sample database relational model
516
517 ![A sample database relational model](/images/book/F1608.png "A sample database relational model")
518
519 This is where the labels given to the records become really useful. To add a `Comment` field to the `first_post` article, you simply need to append the lines shown in Listing 16-17 to the `import_data.yml` data file.
520
521 Listing 16-17 - Adding a Record to a Related Table, in `data/fixtures/import_data.yml`
522
523     Comment:
524       first_comment:
525         article_id:   first_post
526         author:       Anonymous
527         content:      Your prose is too verbose. Write shorter sentences.
528
529 The `propel:data-load` task will recognize the label that you gave to an article previously in `import_data.yml`, and grab the primary key of the corresponding `Article` record to set the `article_id` field. You don't even see the IDs of the records; you just link them by their labels--it couldn't be simpler.
530
531 The only constraint for linked records is that the objects called in a foreign key must be defined earlier in the file; that is, as you would do if you defined them one by one. The data files are parsed from the top to the bottom, and the order in which the records are written is important.
532
533 **New in symfony 1.1**: This also works for many-to-many relationships, where two classes are related through a third class. For instance, an `Article` can have many `Authors`, and an `Author` can have many `Articles`. You usually use an `ArticleAuthor` class for that, corresponding to an `article_author` table with an `article_id` and an `author_id` columns. Listing 16-18 shows how to write a fixture file to define many-to-many relationships with this model. Notice the plural table name used here--this is what triggers the search for a middle class.
534
535 Listing 16-18 - Adding a Record to a Table Related by a Many-to-Many relationship, in `data/fixtures/import_data.yml`
536
537     Author:
538       first_author:
539         name: John Doe
540         article_authors: [first_post, second_post]
541
542 One data file can contain declarations of several classes. But if you need to insert a lot of data for many different tables, your fixture file might get too long to be easily manipulated.
543
544 The `propel:data-load` task parses all the files it finds in the `fixtures/` directory, so nothing prevents you from splitting a YAML fixture file into smaller pieces. The important thing to keep in mind is that foreign keys impose a processing order for the tables. To make sure that they are parsed in the correct order, prefix the files with an ordinal number.
545
546     100_article_import_data.yml
547     200_comment_import_data.yml
548     300_rating_import_data.yml
549
550 Deploying Applications
551 ----------------------
552
553 Symfony offers shorthand commands to synchronize two versions of a website. These commands are mostly used to deploy a website from a development server to a final host, connected to the Internet.
554
555 ### Freezing a Project for FTP Transfer
556
557 The most common way to deploy a project to production is to transfer all its files by FTP (or SFTP). However, symfony projects use the symfony libraries, and unless you develop in a sandbox (which is not recommended), or if the symfony `lib/` and `data/` directories are linked by `svn:externals`, these libraries are not in the project directory. Whether you use a PEAR installation or symbolic links, reproducing the same file structure in production can be time-consuming and tricky.
558
559 That's why symfony provides a utility to "freeze" a project--to copy all the necessary symfony libraries into the project `data/`, `lib/`, and `web/` directories. The project then becomes a kind of sandbox, an independent, stand-alone application.
560
561     > php symfony project:freeze symfony_data_dir
562
563 The `symfony_data_dir` argument is the path to the symfony `data` directory (if you installed symfony via Subversion or an archive, this directory is in the same directory as symfony `lib`; if you installed the symfony PEAR package, this directory is located in the PEAR data directory).
564
565 Once a project is frozen, you can transfer the project directory into production, and it will work without any need for PEAR, symbolic links, or whatever else.
566
567 >**TIP**
568 >Various frozen projects can work on the same server with different versions of symfony without any problems.
569
570 To revert a project to its initial state, use the `project:unfreeze` task. It erases the `data/symfony/`, `lib/symfony/`, and `web/sf/` directories.
571
572     > php symfony project:unfreeze
573
574 Note that if you had symbolic links to a symfony installation prior to the freeze, symfony will remember them and re-create the symbolic links in the original location.
575
576 ### Using rsync for Incremental File Transfer
577
578 Sending the root project directory by FTP is fine for the first transfer, but when you need to upload an update of your application, where only a few files have changed, FTP is not ideal. You need to either transfer the whole project again, which is a waste of time and bandwidth, or browse to the directories where you know that some files changed, and transfer only the ones with different modification dates. That's a time-consuming job, and it is prone to error. In addition, the website can be unavailable or buggy during the time of the transfer.
579
580 The solution that is supported by symfony is rsync synchronization through an SSH layer. Rsync ([http://samba.anu.edu.au/rsync/](http://samba.anu.edu.au/rsync/)) is a command-line utility that provides fast incremental file transfer, and it's open source. With incremental transfer, only the modified data will be sent. If a file didn't change, it won't be sent to the host. If a file changed only partially, just the differential will be sent. The major advantage is that rsync synchronizations transfer only a small amount of data and are very fast.
581
582 Symfony adds SSH on top of rsync to secure the data transfer. More and more commercial hosts support an SSH tunnel to secure file uploads on their servers, and that's a good practice to avoid security breaches.
583
584 The SSH client called by symfony uses connection settings from the `config/properties.ini` file. Listing 16-19 gives an example of connection settings for a production server. Write the settings of your own production server in this file before any synchronization. You can also define a single parameters setting to provide your own rsync command line parameters.
585
586 Listing 16-19 - Sample Connection Settings for a Server Synchronization, in `myproject/config/properties.ini`
587
588     [symfony]
589       name=myproject
590
591     [production]
592       host=myapp.example.com
593       port=22
594       user=myuser
595       dir=/home/myaccount/myproject/
596
597 >**NOTE**
598 >Don't confuse the production server (the host server, as defined in the `properties.ini` file of the project) with the production environment (the front controller and configuration used in production, as referred to in the configuration files of an application).
599
600 Doing an rsync over SSH requires several commands, and synchronization can occur a lot of times in the life of an application. Fortunately, symfony automates this process with just one command:
601
602     > php symfony project:deploy production
603
604 This command launches the `rsync` command in dry mode; that is, it shows which files must be synchronized but doesn't actually synchronize them. If you want the synchronization to be done, you need to request it explicitly by adding the `--go` option.
605
606     > php symfony project:deploy production --go
607
608 Don't forget to clear the cache in the production server after synchronization.
609
610 >**TIP**
611 >As of symfony 1.1, the `php.yml` configuration file has been removed.
612 >
613 >The only setting you will have to check by hand is `log_errors`, which was set to on by `php.yml`.
614 >
615 >php.yml is replaced by the `check_configuration.php` utility you can find in he `data/bin` folder. It checks your environment against symfony requirements. You can launch it from anywhere:
616 >
617 >     [php]
618 >     $ php /path/to/symfony/data/bin/check_configuration.php
619 >
620 >Even if you can use this utility from the command line, it's strongly recommended to launch it from the web by copying it under your web root directory as PHP can use different `php.ini` configuration files for the command line interface and the web.
621
622 -
623
624 >**SIDEBAR**
625 >Is your application finished?
626 >
627 >Before sending your application to production, you should make sure that it is ready for a public use. Check that the following items are done before actually deciding to deploy the application:
628 >
629 >The error pages should be customized to the look and feel of your application. Refer to Chapter 19 to see how to customize the error 500, error 404, and security pages, and to the "Managing a Production Application" section in this chapter to see how to customize the pages displayed when your site is not available.
630 >
631 >The `default` module should be removed from the `enabled_modules` array in the `settings.yml`, so that no symfony page appear by mistake.
632 >
633 >The session-handling mechanism uses a cookie on the client side, and this cookie is called `symfony` by default. Before deploying your application, you should probably rename it to avoid disclosing the fact that your application uses symfony. Refer to Chapter 6 to see how to customize the cookie name in the `factories.yml` file.
634 >
635 >The `robots.txt` file, located in the project's `web/` directory, is empty by default. You should customize it to inform web spiders and other web robots about which parts of a website they can browse and which they should avoid. Most of the time, this file is used to exclude certain URL spaces from being indexed--for instance, resource-intensive pages, pages that don't need indexing (such as bug archives), or infinite URL spaces in which robots could get trapped.
636 >
637 >Modern browsers request a `favicon.ico` file when a user first browses to your application, to represent the application with an icon in the address bar and bookmarks folder. Providing such a file will not only make your application's look and feel complete, but it will also prevent a lot of 404 errors from appearing in your server logs.
638
639 ### Ignoring Irrelevant Files
640
641 If you synchronize your symfony project with a production host, a few files and directories should not be transferred:
642
643   * All the version control directories (`.svn/`, `CVS/`, and so on) and their content are necessary only for development and integration.
644   * The front controller for the development environment must not be available to the final users. The debugging and logging tools available when using the application through this front controller slow down the application and give information about the core variables of your actions. It is something to keep away from the public.
645   * The `cache/` and `log/` directories of a project must not be erased in the host server each time you do a synchronization. These directories must be ignored as well. If you have a `stats/` directory, it should probably be treated the same way.
646   * The files uploaded by users should not be transferred. One of the good practices of symfony projects is to store the uploaded files in the `web/uploads/` directory. This allows you to exclude all these files from the synchronization by pointing to only one directory.
647
648 To exclude files from rsync synchronizations, open and edit the `rsync_exclude.txt` file under the `myproject/config/` directory. Each line can contain a file, a directory, or a pattern. The symfony file structure is organized logically, and designed to minimize the number of files or directories to exclude manually from the synchronization. See Listing 16-20 for an example.
649
650 Listing 16-20 - Sample rsync Exclusion Settings, in `myproject/config/rsync_exclude.txt`
651
652     .svn
653     /cache/*
654     /log/*
655     /stats/*
656     /web/uploads/*
657     /web/frontend_dev.php
658
659 >**NOTE**
660 >The `cache/` and `log/` directories must not be synchronized with the development server, but they must at least exist in the production server. Create them by hand if the `myproject/` project tree structure doesn't contain them.
661
662 ### Managing a Production Application
663
664 The command that is used most often in production servers is `cache:clear`. You must run it every time you upgrade symfony or your project (for instance, after calling the `project:deploy` task), and every time you change the configuration in production.
665
666     > php symfony cache:clear
667
668 >**TIP**
669 >If the command-line interface is not available in your production server, you can still clear the cache manually by erasing the contents of the `cache/` folder.
670
671 You can temporarily disable your application--for instance, when you need to upgrade a library or a large amount of data.
672
673     > php symfony project:disable APPLICATION_NAME ENVIRONMENT_NAME
674
675 By default, a disabled application displays the `$sf_symfony_lib_dir/exception/data/unavailable.php` page, but if you create your own `unavailable.php` file in your project's `config/` directory, symfony will use it instead.
676
677 The `project:enable` task reenables the application and clears the cache.
678
679     > php symfony project:enable APPLICATION_NAME ENVIRONMENT_NAME
680
681 >**SIDEBAR**
682 >Displaying an unavailable page when clearing the cache
683 >
684 >If you set the `check_lock` parameter to `on` in the `settings.yml` file, symfony will lock the application when the cache is being cleared, and all the requests arriving before the cache is finally cleared are then redirected to a page saying that the application is temporarily unavailable. If the cache is large, the delay to clear it may be longer than a few milliseconds, and if your site's traffic is high, this is a recommended setting. This unavailable page is the same as the one displayed when you call the symfony `disable` task. The `check_lock` parameter is deactivated by default because it has a very slight negative impact on performance.
685 >
686 >The `project:clear-controllers` task clears the `web/` directory of all controllers other than the ones running in a production environment. If you do not include the development front controllers in the `rsync_exclude.txt` file, this command guarantees that a backdoor will not reveal the internals of your application.
687 >
688 >     > php symfony project:clear-controllers
689 >
690 >The permissions of the project files and directories can be broken if you use a checkout from an SVN repository. The `project:permissions` task fixes directory permissions, to change the `log/` and `cache/` permissions to 0777, for example (these directories need to be writable for the framework to work correctly).
691 >
692 >     > php symfony project:permissions
693
694 Summary
695 -------
696
697 By combining PHP logs and symfony logs, you can monitor and debug your application easily. During development, the debug mode, the exceptions, and the web debug toolbar help you locate problems. You can even insert custom messages in the log files or in the toolbar for easier debugging.
698
699 The command-line interface provides a large number of tools that facilitate the management of your applications, during development and production phases. Among others, the data population, freeze, and synchronization tasks are great time-savers.
Note: See TracBrowser for help on using the browser.

The Sensio Labs Network

Since 1998, Sensio Labs has been promoting the Open-Source software movement by providing quality web application development, training, consulting, and supporting several large Open-Source projects.