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:disable
check_lock
option is checked in settings.yml
In 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
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.yml
prod:
.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 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
Powered by jQuery UI and symfony. Symfony Check
The content this site is published under a Creative Commons License. Some parts of the content are a remix of the official symfony documentation.