How to add a custom extension
Overview
Symfony offers various helpers to accelerate template development (see more in the View chapter). But the need for custom helpers can arise for every project, application, or module. Symfony uses naming conventions to allow the easy inclusion of custom helpers so that they can be used the same way as the standard ones, via a simple function call in the templates. Custom classes are also easy to add thanks to the class auto-loading feature.
Custom helper
Naming conventions
To make custom helpers available to your templates, simply create a PHP file ending with Helper.php and place it in a subdirectory of the PHP include_path named helper. The directory where the helper is placed will determine its availability. For instance:
| Helper placed in |
Is available for |
lib/helper/ |
all applications of the project |
myapp/lib/helper/ |
all the modules of the 'myapp' application |
mymodule/lib/helper/ |
all the templates of the 'mymodule' module |
To use a custom helper in a template, you must first declare it on top of the template, the same way you do for other helpers:
<?php use_helper('Name') ?>
Note: If you create a helper file with the same name as one of the existing helper packages of symfony, it will override the one in the framework. It means that you can redefine all the symfony helpers at the application or project level. Beware that your custom file is read instead of the one in the framework, so it is not actually inheritance, and helpers not included in your version will not be available in the code.
Example
Let's say that your application uses the wiki syntax to format the data entered by the user. The wiki to HTML conversion may occur quite often, so you will refactor the code in a 'wiki_to_html()' helper function:
Here is the WikiHelper.php file stored in the myapp/lib/helper/ directory:
<?php
function wiki_to_html($text)
{
require_once 'Wiki.class.php';
$wiki = new Wiki();
return $wiki->transform($text);
}
?>
Here is an example template using the helper:
<?php use_helper('Wiki') ?>
...
<p><?php echo wiki_to_html('Text with a WikiLink.') ?></p>
Custom classes
Naming conventions
If you need to extend your actions with a custom class, you can take advantage of the class auto-loading feature. If the class file has the same name as the class itself, symfony will auto-load it when needed, provided it is located in one of the three directories below:
| Class placed in |
Is available for |
lib/ |
all applications of the project |
myapp/lib/ |
all the modules of the 'myapp' application |
myapp/modules/mymodule/lib/ |
all the templates of the 'mymodule' module |
Example
For instance, create a new myTools.class.php under the myproject/lib/ directory:
class myTools
{
public static function stripText($text)
{
$text = strtolower($text);
// strip all non word chars
$text = preg_replace('/\W/', ' ', $text);
// replace all white space sections with a dash
$text = preg_replace('/\ +/', '-', $text);
// trim dashes
$text = preg_replace('/\-$/', '', $text);
$text = preg_replace('/^\-/', '', $text);
return $text;
}
}
Now, from any action of your project, you can use the stripText method of this class without declaring it previously. Just ask:
$my_stripped_text = myTools::stripText($my_text);
...and symfony will load the myTools class automatically.
Accessing context objects
In a custom helper, shortcuts like $sf_request or $sf_user won't work. In a custom class, the action-style calls ($this->getRequest(), $this->getUser(), etc.) won't work either. This is because these functions and classes are not in a request context. So, in order to access the context objects, you have to use the sfContext singleton (sfContext::getInstance()). For instance, to get the request parameters, write:
$id = sfContext::getInstance()->getRequest()->getParameter('id');
Class autoloading
The autoloading feature allows you to create an new object without requiring the file that contains its class definition by hand:
$obj = new MyClass();
// If MyClass is autoloaded, this will work without a require
The autoloading is configured to work with the lib/ directories described above, according to the default autoload.yml configuration file (found in $pear_data_dir/symfony/config/autoload.yml):
autoload:
symfony_core:
name: symfony core classes
ext: .class.php
path: %SF_SYMFONY_LIB_DIR%
recursive: on
exclude: [vendor]
symfony_orm:
name: symfony orm classes
files:
Propel: %SF_SYMFONY_LIB_DIR%/addon/propel/sfPropelAutoload.php
Criteria: %SF_SYMFONY_LIB_DIR%/vendor/propel/util/Criteria.php
SQLException: %SF_SYMFONY_LIB_DIR%/vendor/creole/SQLException.php
DatabaseMap: %SF_SYMFONY_LIB_DIR%/vendor/propel/map/DatabaseMap.php
symfony_plugins:
name: symfony plugins
ext: .class.php
path: %SF_PLUGIN_DIR%
recursive: on
project:
name: project classes
ext: .class.php
path: %SF_LIB_DIR%
recursive: on
exclude: [model, plugins, symfony]
model:
name: project model classes
ext: .php
path: %SF_MODEL_LIB_DIR%
application:
name: application classes
ext: .class.php
path: %SF_APP_LIB_DIR%
If you want to add other places for symfony to look into, create your own autoload.yml file in an application config/ directory and add in new rules. Each autoloading rule has a label, both for visual organization and ability to override it.
For instance, if you added your own MyClass class in /usr/local/mycustomclasses/, you could add:
mycustomclasses:
name: classes that I developed myself
ext: .php
path: /usr/local/mycustomclasses/
recursive: on
Third-party libraries
If you need a functionality provided by a third-party class, and if you don't want to copy this class in one of the symfony lib/ dirs, you will probably install it outside of the usual places where symfony looks for files. In that case, using this class will imply a manual require in your code, unless you use the symfony bridge to take advantage of the autoloading.
Symfony doesn't (yet) provide tools for everything. If you need a PDF generator, an API to Google Maps or a PHP implementation of the Lucene search engine, you will probably need a few libraries from the Zend Framework. If you want to manipulate images directly in PHP, connect to a POP3 account to read emails, or design a console interface for Linux, you will choose the libraries from eZcomponents.
Fortunately, if you define the right settings, the components from both these libraries will work out of the box in symfony.
The first think that you need to declare (unless you installed the third party libraries via PEAR) is the path to the root directory of the libraries. This is to be done in the application settings.yml:
.settings:
zend_lib_dir: /usr/local/zend/library/
ez_lib_dir: /usr/local/ezcomponents/
Then, extend the autoload routine by telling which library to look at when the autoloading fails with symfony:
.settings:
autoloading_functions: [[sfZendFrameworkBridge, autoload], [sfEzComponentsBridge, autoload]]
What will happen when you create a new object of an unloaded class is:
- The symfony autoloading function (
Symfony::autoload()) first looks for a class in the paths declared in the autoload.yml
- If none is found, then the callback functions declared in the
sf_autoloading_functions setting will be called one after the other, until one of them returns true:
sfZendFrameworkBridge::autoload()
sfEzComponentsBridge::autoload()
- If these also return
false, then symfony will throw an exception saying that the class doesn't exist.
Note that this setting is distinct from the rules defined in the autoload.yml. The autoloading_functions setting specifies bridge classes, and the autoload.yml specifies paths and rules for searching.
The available bridges are stored in the $pear_php_dir/symfony/addon/bridge/ directory.
Plug-ins
Symfony can also be extended through plug-ins. To see how to install, use or create your own plu-in, refer to the related chapter.
|