When the URL entered by the user doesn't match any route or when an sfError404Exception occurs, symfony tries to access to the default/error404 action.
To customize the "Oops! Page Not Found" page, add the executeError404() method in apps/frontend/modules/default/actions/actions.class.php:
/**
* Error page for page not found (404) error
*
*/
public function executeError404()
{
}
Then create the apps/frontend/modules/default/templates/error404Success.php template.
If you want, you can grab the original file and modify it.
In an action that works fine, add on top :
throw new sfError404Exception('Only for test, don\'t forget to remove it!');
Test the related page in the production environement.
default module is protectedMake sure to un-secure the error404 action in apps/frontend/modules/default/config/security.yml:
error404:
is_secure: false
If you want to use an other module, modify apps/frontend/config/settings.yml:
all:
.actions:
error_404_module: foo # To be called when a 404 error is raised
error_404_action: bar # Or when the requested URL doesn't match any route
Clear your cache.
php symfony cc
This is the page displayed in the production environment when symfony catches an error.
Symfony looks for a error.html.php file and let you choose where you prefer to put it:
At mySfProject/apps/frontend/config/error/error.html.php
Useful if you want to have a different version for each application
At mySfProject/config/error/error.html.php
The main configuration folder of your project
If you want, you can grab the original file and modify it.
In an action that works fine, add on top:
throw new Exception('Only for test, don\'t forget to remove it!');
Test the related page in the production environement.
When a non authenticated user tries to access a page defined as secure in security.yml, symfony tries to access to the default/login action.
To customize the "Login Required" page, add the executeLogin() method in the apps/frontend/modules/default/actions/actions.class.php file:
/**
* Warning page for restricted area - requires credentials
*
*/
public function executeLogin()
{
}
Then create the apps/frontend/modules/default/templates/loginSuccess.php template.
If you want, you can grab the original file and modify it.
If you want to use an other module, modify apps/frontend/config/settings.yml:
all:
.actions:
login_module: foo # To be called when a non-authenticated user
login_action: bar # Tries to access a secure page
Make sure that in your module, the signin action is un-secure. In apps/frontend/modules/foo/config/security.yml:
bar:
is_secure: false
Clear your cache.
php symfony cc
Read the related symfony documentation
When a user doesn't have the credentials required for an action, symfony tries to access to the default/secure action.
To customize the "Credentials Required" page, add the executeLogin() method in the apps/frontend/modules/default/actions/actions.class.php file:
/**
* Warning page for restricted area - requires login
*
*/
public function executeSecure()
{
}
Then create the apps/frontend/modules/default/templates/secureSuccess.php template.
If you want, you can grab the original file and modify it.
In one of your modules, add in apps/frontend/modules/yourModule/config/security.yml:
all:
credentials: [ aCredentialYouDontHave ]
If you want to use an other module, modify apps/frontend/config/settings.yml:
all:
.actions:
secure_module: foo # To be called when a user doesn't have
secure_action: bar # The credentials required for an action
Clear your cache.
php symfony cc
When a user requests a module declared as disabled in /apps/frontend/modules/mymodule/config/module.yml, symfony tries to access to the default/disabled action.
To customize the "This Module is Unavailable" page, add the executeDisabled() method in the apps/frontend/modules/default/actions/actions.class.php file:
/**
* Module disabled
*
*/
public function executeDisabled()
{
}
Then create the apps/frontend/modules/default/templates/disabledSuccess.php template.
If you want, you can grab the original file and modify it.
In one of your modules, add in apps/frontend/modules/yourModule/config/module.yml:
all:
enabled: false
default module is protectedMake sure to un-secure the disabled action in apps/frontend/modules/default/config/security.yml:
disabled:
is_secure: off
If you want to use an other module, modify apps/frontend/config/settings.yml:
all:
.actions:
module_disabled_module: foo # To be called when a user requests
module_disabled_action: bar # A module disabled in the module.yml
Clear your cache.
php symfony cc
A symfony website can be displayed as unavailable:
php symfony project:disablecheck_lock option is checked in settings.ymlIn these cases, symfony looks for a unavailable.php file and let you choose where you prefer to put it:
At mySfProject/apps/frontend/config/unavailable.php
Useful if you want to have a different version for each application
In mySfProject/config/unavailable.php
The main configuration folder of your project
If you want, you can grab the original file and modify it.
Don't forget to clear your cache.
Disable your frontend in dev environement.
php symfony project:disable frontend dev
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.
Some useful links :
The session-handling mechanism uses a cookie on the client side, and this cookie is called "symfony" by default.
In the apps/frontend/config/factories.yml file, add for the all environment:
storage:
class: sfSessionStorage
param:
session_name: my_project
If the sfGuardPlugin is installed, you can modify the "Remember me" cookie name in myProject/config/app.yml:
all:
sf_guard_plugin:
remember_cookie_name: myProjectRememberMe
To test if a server is correctly set up to host symfony, Sensio has written a test script. Download it and put it in the web directory.
The script has to be launched in a navigator and through a command line :
php check_configuration.php
Read the related symfony documentation
When it comes to security, the very first rule is that all data sent by users should be validated before being stored on the server.
Experience shows that some developers give poor, little or no attention at all to validating file uploads.
This is mostly due to laziness. However, sometimes the purpose is to build a more flexible form. Example: a CV upload field that accepts any file extension.
This is a huge security mistake.
Why? Because these files by default are stored in the uploads directory which is publicly accessible.
If one of your users succeeds in uploading a php file, such as attack.php through one of your forms, then he will be able to run the script just by using the http//your-sf-project.com/uploads/attack.php uri.
If the aforementioned php file contained malicious code then the hacker could get access to your database settings, user details, delete data etc.
It is absolutely critical that uploaded files are validated.
Read again the file validator documentation. Do all of your validators have customised mime_types or a mime_categories option ?
You should also prevent your forms from accepting the .htaccess mime type.
If you have access to the httpd.conf file, add the following rule to your virtualhost:
<VirtualHost *:80>
...
...
<Directory "/path/to/my/sfProject/web/uploads">
php_flag engine off
</Directory>
</VirtualHost >
If you don’t have access to the httpd.conf of your host, add a new .htaccess file in your /path/to/my/sfProject/web/uploads directory:
php_flag engine off
uploads directory when you canSome uploaded files - like user avatars - need to be publicly accessible and are displayed very often by the server, the uploads directory is the right place for them.
But many other user files are private or rarely displayed. All these files could be stored in the data directory (documentation).
1/ Create a files sub directory: mkdir /path/to/my/sfProject/data/files
2/ Change the path option in your forms:
$this->validatorSchema['driver_licence_pic'] = new sfValidatorFile(array(
'mime_types' => 'web_images',
'path' => sfConfig::get('sf_data_dir').'/files/driver_licence',
));
3/ And add a dedicated route to the file.
By default, symfony puts lang="en" on every single page of your project.
If your application deals with other languages than english, go to your apps/frontend/templates/layout.php file and change the second line:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $sf_user->getCulture() ?>"↵
lang="<?php echo $sf_user->getCulture() ?>">
In the apps/frontend/config/settings.yml file, you can delete the application name from your uri by activing the no_script_name option.
This option is enabled for the first generated application (most often, the frontend), but not for the others.
Because without the application name, symfony wouldn't know which one to use when the applications share the same domain.
We have to find an other way to let symfony know which application to use.
A solution can be to use several domains :
Personnally, I prefer use some sub domains for each application :
You juste have to add a new virtual host in httpd.conf :
<VirtualHost *:80>
ServerName admin.my-app.com
DirectoryIndex backend.php
DocumentRoot "/path/to/the/web/directory"
<Directory "/path/to/the/web/directory">
AllowOverride All
Allow from All
</Directory>
Alias /sf /path/to/the/symfony/data/web/sf
<Directory "/path/to/the/symfony/data/web/sf">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>
In that case, you probably can't access to the httpd.conf.
So let's play with the /web/.htaccess. Just after :
<IfModule mod_rewrite.c>
RewriteEngine On
Add the following lines:
# The admin subdomain returns to the backend
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{HTTP_HOST} ^admin\.my-app\..*
RewriteRule ^(.*)$ backend.php [QSA,L]
settings.ymlprod:
.settings:
no_script_name: true
Don't forget to clear the cache.
php symfony cc
For the production server, you probably want the best performance possible. Installing a PHP accelerator will give you the best improvement for your money.
APC is one of the most popular ones.
The set up is as simple as :
pecl install apc
Sometimes the installation is just not possible. Read the documentation.
You can enable the cache system by adding the following method to mySfProject/config/ProjectConfiguration.class.php :
/**
* Configure the Doctrine engine
**/
public function configureDoctrine(Doctrine_Manager $manager)
{
$manager->setAttribute(Doctrine::ATTR_QUERY_CACHE, new Doctrine_Cache_Apc());
}
The .htaccess file is very usefull: it's flexible, it works everywhere and the modifications are processed instantaneously.
But the drawback is that the rules written in it can't be cached by apache, the server has to scan the file for every single request.
If you move the .htaccess rules to your virtual host configuration file, the rules will be cached and apache performance will improve.
Unfortunately, you can't access to the apache configuration files on shared hosts, that's why symfony uses the .htaccess file by default.
So sorry, this tip only works for dedicated server users.
Most often, you'll find the virtual host configurations in the bottom of the httpd.conf, but sometimes, they are moved in some vhosts.conf file.
Your virtual host should look like to something like this:
<VirtualHost *:80>
ServerName my-symfony-project.com
DirectoryIndex index.php
DocumentRoot "/path-to-your-sf-project/web"
<Directory "/path-to-your-sf-project/web">
AllowOverride All
Allow from All
</Directory>
Alias /sf /path-to-your-sf-project/lib/vendor/symfony/data/web/sf/
<Directory "/path-to-your-sf-project/lib/vendor/symfony/data/web/sf/">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>
You need to add your rules to <Directory "/path-to-your-sf-project/web"> :
<Directory "/path-to-your-sf-project/web">
AllowOverride None
Allow from All
Options FollowSymLinks ExecCGI
RewriteEngine On
# uncomment the following line, if you are having trouble
# getting no_script_name to work
#RewriteBase /
# we skip all files with .something
#RewriteCond %{REQUEST_URI} \..+$
#RewriteCond %{REQUEST_URI} !\.html$
#RewriteRule .* - [L]
# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
# no, so we redirect to our front web controller
RewriteRule ^(.*)$ index.php [QSA,L]
</Directory>
my_project/web/.htaccess fileUnless you added extra rules in it, the file is now useless. If you don't remove the htaccess, apache will still parse the file on every request
Apache provides the configtest tool to check that apache configuration files are ok.
On many linux servers, you can access to apache commands by /etc/init.d/apachectl makeMeSandwich.
Rights errors just need the sudo prefix to be fixed: sudo /etc/init.d/apachectl makeMeSandwich
/etc/init.d/apachectl apachectl configtest
If there's no error:
/etc/init.d/apachectl apachectl restart
Your project really should have at least one README file where this kind of modifications are reported.
This tip was given by Jérôme Macias on the symfony-check thread.
Error logs are a precious help to find what goes wrong on your production server.
In apps/frontend/config/settings.yml, activate the option:
prod:
.settings:
logging_enabled: true
Then, in apps/frontend/config/factories.yml:
prod:
logger:
class: sfAggregateLogger
param:
level: err
loggers:
sf_file_debug:
class: sfFileLogger
param:
level: err
file: %SF_LOG_DIR%/%SF_APP%_%SF_ENVIRONMENT%.log
Clear your cache.
php symfony cc
Don't forget to set up a cron dedicated to the log rotation.
Read the related symfony documentation.
To prevent that only production related scripts are published on the production server, symfony provides you two tools : the php symfony project:clear-controllers command and the myproject/config/rsync_exclude.txt file.
The rsync_exclude.txt file filters by default the scripts related to the dev environment. But if you use some other ones, they have to be added:
/web/*_cache.php
/web/*_test.php
These files aren't very dangerous, but they also should be filtered :
Thumbs.db
.DS_Store
You really should add theses files to rsync_exclude.txt :
/config/databases.yml
/data/sql/schema.sql
You can create a myproject/config/rsync_include.txt file that will be able to force the transfer of files or directories.
Read the related symfony documentation
Useless for sf1.3/1.4 projects
By default, the output escaping is disabled. In the /apps/frontend/config/settings.yml file, add:
all:
.settings:
# Output escaping settings
escaping_strategy: true
escaping_method: ESC_SPECIALCHARS
Read the related symfony documentation
Useless for sf1.3/1.4 project
symfony has the ability to protect each form of your project against CSRF attacks.
To enable this feature, you have to prompt a csrf key in apps/frontend/config/settings.yml :
all:
.settings:
csrf_secret: chooseYouOwnSecretKey
This solution inject in each form a token unique for a given user and for a given form. Be carefull, forms that use the csrf protection can no longer be cached.
Read the related symfony documentation
The bigger your website becomes, the slower some symfony tasks will be.
At some point, these operations will substantially slow down your application and disturb your users.
Displaying a clean maintenance screen is usually a better choice than providing a degrated navigation experience to your users.
To redirect automatically the users to the customized unavailable.php page when you launch tasks like php symfony cc, modify the apps/frontend/config/settings.yml file:
all:
.settings:
check_lock: true
The symfony routing system is one of the killer features of the framework.
<p>Please have a look on <php echo link_to('our products', 'product/index') ?>.</p>
Indicate in your template which module/action you want and then let routing.yml do all the job.
You can use the default rule:
default:
url: /:module/:action/*
Or add a dedicated one:
product_list:
url: /our-products
param: { module: product, action: index }
Clear your cache and all the related links and uri will be modified, all over the application.
It feels like magic, but it has a cost.
Each time the url request product/index is found in a template, symfony has to scan all the rules defined in routing.yml and guess which one can handle this request.
The default rule ? An other one ?
This search is done for all the links...
If your project have many pages and many routing rules, you can speed up your application by using rule labels instead of a module/action pairs in your templates:
<p>Please have a look on <php echo link_to('our products', '@product_list') ?>.</p>
This solution will speed up the routing, but the drawback is that internal links can become a little less self-evident.
It's up to you.
Read the related symfony documentation
Suggestions, corrections and comments are very welcome.
Symfony-Check sources are available on Google code
Powered by jQuery UI and symfony. symfony check is an UI Studio creation
The content this site is published under a Creative Commons License. Some parts of the content are a remix of the official symfony documentation.
Illustrations drawned by the talented Pierre Brillault, all rights reserved.
