You will probably need to reuse a piece of code that you developed for one of your symfony applications. If you can package this piece of code into a single class, no problem: Drop the class in one of the lib/ folders of another application and the autoloader will take care of the rest. But if the code is spread across more than one file, such as a complete new theme for the administration generator or a combination of JavaScript files and helpers to automate your favorite visual effect, just copying the files is not the best solution.

Plug-ins offer a way to package code disseminated in several files and to reuse this code across several projects. Into a plug-in, you can package classes, filters, event listeners, helpers, configuration, tasks, modules, schemas and model extensions, fixtures, web assets, etc. Plug-ins are easy to install, upgrade, and uninstall. They can be distributed as a .tgz archive, a PEAR package, or a simple checkout of a code repository. The PEAR packaged plug-ins have the advantage of managing dependencies, being easier to upgrade and automatically discovered. The symfony loading mechanisms take plug-ins into account, and the features offered by a plug-in are available in the project as if the plug-in code was part of the framework.

So, basically, a plug-in is a packaged extension for a symfony project. With plug-ins, not only can you reuse your own code across applications, but you can also reuse developments made by other contributors and add third-party extensions to the symfony core.

17.4.1. Finding Symfony Plug-Ins

The symfony project website contains a page dedicated to symfony plug-ins. It is in the symfony wiki and accessible with the following URL:

http://www.symfony-project.org/plugins/

Each plug-in listed there has its own page, with detailed installation instructions and documentation.

Some of these plug-ins are contributions from the community, and some come from the core symfony developers. Among the latter, you will find the following:

  • sfFeed2Plugin: Automates the manipulation of RSS and Atom feeds
  • sfThumbnailPlugin: Creates thumbnails — for instance, for uploaded images
  • sfMediaLibraryPlugin: Allows media upload and management, including an extension for rich text editors to allow authoring of images inside rich text
  • sfShoppingCartPlugin: Allows shopping cart management
  • sfPagerNavigationPlugin: Provides classical and Ajax pager controls, based on an sfPager object
  • sfGuardPlugin: Provides authentication, authorization, and other user management features above the standard security feature of symfony
  • sfPrototypePlugin: Provides prototype and script.aculo.us JavaScript files as a standalone library
  • sfSuperCachePlugin: Writes pages in cache directory under the web root to allow the web server to serve them as fast as possible
  • sfOptimizerPlugin: Optimizes your application's code to make it execute faster in the production environment (see the next chapter for details)
  • sfErrorLoggerPlugin: Logs every 404 and 500 error in a database and provides an administration module to browse these errors
  • sfSslRequirementPlugin: Provides SSL encryption support for actions

The wiki also proposes plug-ins designed to extend your Propel objects, also called behaviors. Among them, you will find the following:

  • sfPropelParanoidBehaviorPlugin: Disables object deletion and replaces it with the updating of a deleted_at column
  • sfPropelOptimisticLockBehaviorPlugin: Implements optimistic locking for Propel objects

You should regularly check out the symfony wiki, because new plug-ins are added all the time, and they bring very useful shortcuts to many aspects of web application programming.

Apart from the symfony wiki, the other ways to distribute plug-ins are to propose a plug-ins archive for download, to host them in a PEAR channel, or to store them in a public version control repository.

17.4.2. Installing a Plug-In

The plug-in installation process differs according to the way it's packaged. Always refer to the included README file and/or installation instructions on the plug-in download page.

Plug-ins are installed applications on a per-project basis. All the methods described in the following sections result in putting all the files of a plug-in into a myproject/plugins/pluginName/ directory.

17.4.2.1. PEAR Plug-Ins ###

Plug-ins listed on the symfony wiki are bundled as PEAR packages attached to a wiki page and made available via the official symfony plugins PEAR channel: plugins.symfony-project.org. To install such a plug-in, use the plugin:install task with a plugin name, as shown in Listing 17-10.

Listing 17-10 - Installing a Plug-In from the Official symfony plugins PEAR Channel / Symfony Wiki

> cd myproject
> php symfony plugin:install pluginName

Alternatively, you can download the plug-in and install it from the disk. In this case, use the path to the package archive, as shown in Listing 17-11.

Listing 17-11 - Installing a Plug-In from a Downloaded PEAR Package

> cd myproject
> php symfony plugin:install /home/path/to/downloads/pluginName.tgz

Some plug-ins are hosted on external PEAR channels. Install them with the plugin:install task, and don't forget to register the channel and mention the channel name, as shown in Listing 17-12.

Listing 17-12 - Installing a Plug-In from a PEAR Channel

> cd myproject
> php symfony plugin:add-channel channel.symfony.pear.example.com
> php symfony plugin:install --channel=channel.symfony.pear.example.com pluginName

These three types of installation all use a PEAR package, so the term "PEAR plug-in" will be used indiscriminately to talk about plug-ins installed from the symfony plugins PEAR channel, an external PEAR channel, or a downloaded PEAR package.

The plugin:install task also takes a number of options, as shown on Listing 17-13.

Listing 17-13 - Installing a Plug-In with some Options

> php symfony plugin:install --stability=beta pluginName
    > php symfony plugin:install --release=1.0.3 pluginName
    > php symfony plugin:install --install-deps pluginName

Tip As for every symfony task, you can have a full explanation of the plugin:install options and arguments by launching php symfony help plugin:install.

17.4.2.2. Archive Plug-Ins ###

Some plug-ins come as a simple archive of files. To install those, just unpack the archive into your project's plugins/ directory. If the plug-in contains a web/ subdirectory, make a copy or a symlink of this directory into the project's web/ directory, as demonstrated in Listing 17-14. Finally, don't forget to clear the cache.

Listing 17-14 - Installing a Plug-In from an Archive

> cd plugins
> tar -zxpf myPlugin.tgz
> cd ..
> ln -sf plugins/myPlugin/web web/myPlugin
> php symfony cc

17.4.2.3. Installing Plug-Ins from a Version Control Repository ###

Plug-ins sometimes have their own source code repository for version control. You can install them by doing a simple checkout in the plugins/ directory, but this can be problematic if your project itself is under version control.

Alternatively, you can declare the plug-in as an external dependency so that every update of your project source code also updates the plug-in source code. For instance, Subversion stores external dependencies in the svn:externals property. So you can add a plug-in by editing this property and updating your source code afterwards, as Listing 17-15 demonstrates.

Listing 17-15 - Installing a Plug-In from a Source Version Repository

> cd myproject
> svn propedit svn:externals plugins
  pluginName   http://svn.example.com/pluginName/trunk
> svn up
> php symfony cc

Note If the plug-in contains a web/ directory, you must create a symlink to it the same way as for an archive plug-in.

17.4.2.4. Activating a Plug-In Module ###

Some plug-ins contain whole modules. The only difference between module plug-ins and classical modules is that module plug-ins don't appear in the myproject/apps/frontend/modules/ directory (to keep them easily upgradeable). They also need to be activated in the settings.yml file, as shown in Listing 17-16.

Listing 17-16 - Activating a Plug-In Module, in frontend/config/settings.yml

all:
  .settings:
    enabled_modules:  [default, sfMyPluginModule]

This is to avoid a situation where the plug-in module is mistakenly made available for an application that doesn't require it, which could open a security breach. Think about a plug-in that provides frontend and backend modules. You will need to enable the frontend modules only in your frontend application, and the backend ones only in the backend application. This is why plug-in modules are not activated by default.

Tip The default module is the only enabled module by default. That's not really a plug-in module, because it resides in the framework, in $sf_symfony_lib_dir/controller/default/. This is the module that provides the congratulations pages, and the default error pages for 404 and credentials required errors. If you don't want to use the symfony default pages, just remove this module from the enabled_modules setting.

17.4.2.5. Listing the Installed Plug-Ins ###

If a glance at your project's plugins/ directory can tell you which plug-ins are installed, the plugin:list task tells you even more: the version number and the channel name of each installed plug-in (see Listing 17-17).

Listing 17-17 - Listing Installed Plug-Ins

> cd myproject
> php symfony plugin:list

Installed plugins:
sfPrototypePlugin               1.0.0-stable # plugins.symfony-project.com (symfony)
sfSuperCachePlugin              1.0.0-stable # plugins.symfony-project.com (symfony)
sfThumbnail                     1.1.0-stable # plugins.symfony-project.com (symfony)

17.4.2.6. Upgrading and Uninstalling Plug-Ins ###

To uninstall a PEAR plug-in, call the plugin:uninstall task from the root project directory, as shown in Listing 17-18. You must prefix the plug-in name with its installation channel if it's different from the default symfony channel (use the plugin:list task to determine this channel).

Listing 17-18 - Uninstalling a Plug-In

> cd myproject
> php symfony plugin:uninstall sfPrototypePlugin
> php symfony cc

To uninstall an archive plug-in or an SVN plug-in, remove manually the plug-in files from the project plugins/ and web/ directories, and clear the cache.

To upgrade a plug-in, either use the plugin:upgrade task (for a PEAR plug-in) or do an svn update (if you grabbed the plug-in from a version control repository). Archive plug-ins can't be upgraded easily.

17.4.3. Anatomy of a Plug-In

Plug-ins are written using the PHP language. If you can understand how an application is organized, you can understand the structure of the plug-ins.

17.4.3.1. Plug-In File Structure ###

A plug-in directory is organized more or less like a project directory. The plug-in files have to be in the right directories in order to be loaded automatically by symfony when needed. Have a look at the plug-in file structure description in Listing 17-19.

Listing 17-19 - File Structure of a Plug-In

pluginName/
  config/
    *schema.yml        // Data schema
    *schema.xml
    config.php         // Specific plug-in configuration
  data/
    generator/
      sfPropelAdmin
        */             // Administration generator themes
          template/
          skeleton/
    fixtures/
      *.yml            // Fixtures files
  lib/
    *.php              // Classes
    helper/
      *.php            // Helpers
    model/
      *.php            // Model classes
    task/
      *Task.class.php  // CLI tasks
  modules/
    */                 // Modules
      actions/
        actions.class.php
      config/
        module.yml
        view.yml
        security.yml
      templates/
        *.php
      validate/
        *.yml
  web/
    *                  // Assets

17.4.3.2. Plug-In Abilities ###

Plug-ins can contain a lot of things. Their content is automatically taken into account by your application at runtime and when calling tasks with the command line. But for plug-ins to work properly, you must respect a few conventions:

  • Database schemas are detected by the propel- tasks. When you call propel-build-model in your project, you rebuild the project model and all the plug-in models with it. Note that a plug-in schema must always have a package attribute under the shape plugins.pluginName. lib.model, as shown in Listing 17-20.

Listing 17-20 - Example Schema Declaration in a Plug-In, in myPlugin/config/schema.yml

propel:
  _attributes:    { package: plugins.myPlugin.lib.model }
  my_plugin_foobar:
    _attributes:    { phpName: myPluginFoobar }
      id:
      name:           { type: varchar, size: 255, index: unique }
      ...
  • The plug-in configuration is to be included in the plug-in bootstrap script (config.php). This file is executed after the application and project configuration, so symfony is already bootstrapped at that time. You can use this file, for instance, to extend existing classes with event listeners and behaviors.
  • Fixtures files located in the plug-in data/fixtures/ directory are processed by the propel:data-load task.
  • Custom classes are autoloaded just like the ones you put in your project lib/ folders.
  • Helpers are automatically found when you call use_helper() in templates. They must be in ahelper/ subdirectory of one of the plug-in's lib/ directory.
  • Model classes in myplugin/lib/model/ specialize the model classes generated by the Propel builder (in myplugin/lib/model/om/ and myplugin/lib/model/map/). They are, of course, autoloaded. Be aware that you cannot override the generated model classes of a plug-in in your own project directories.
  • Tasks are immediately available to the symfony command line as soon as the plug-in is installed. A plugin can either add new tasks, or override an existing one. It is a best practice to use the plug-in name as a namespace for the task. Type php symfony to see the list of available tasks, including the ones added by plug-ins.
  • Modules provide new actions accessible from the outside, provided that you declare them in the enabled_modules setting in your application.
  • Web assets (images, scripts, style sheets, etc.) are made available to the server. When you install a plug-in via the command line, symfony creates a symlink to the project web/ directory if the system allows it, or copies the content of the module web/ directory into the project one. If the plug-in is installed from an archive or a version control repository, you have to copy the plug-in web/ directory by hand (as the README bundled with the plug-in should mention).

Tip A plug-in can add new rules to the routing system, but it cannot use a custom routing.yml configuration file for that. This is because the order in which rules are defined is very important, and the simple cascade configuration system of YAML files in symfony would mess this order up. Instead, plug-ins need to register an event listener on the routing.load_configuration event and manually prepend rules in the listener:

// in plugins/myPlugin/config/config.php
$this->dispatcher->connect('routing.load_configuration', array('myPluginRouting', 'listenToRoutingLoadConfigurationEvent'));

// in plugins/myPlugin/lib/myPluginRouting.php
class myPluginRouting
{
  static public function listenToRoutingLoadConfigurationEvent(sfEvent $event)
  {
    $routing = $event->getSubject();
    // add plug-in routing rules on top of the existing ones
    $routing->prependRoute('my_route', '/my_plugin/:action', array('module' => 'myPluginAdministrationInterface'));
  }
}

17.4.3.3. Manual Plug-In Setup ###

There are some elements that the plugin:install task cannot handle on its own, and which require manual setup during installation:

  • Custom application configuration can be used in the plug-in code (for instance, by using sfConfig::get('app_myplugin_foo')), but you can't put the default values in an app.yml file located in the plug-in config/ directory. To handle default values, use the second argument of the sfConfig::get() method. The settings can still be overridden at the application level (see Listing 17-26 for an example).
  • Custom routing rules have to be added manually to the application routing.yml.
  • Custom filters have to be added manually to the application filters.yml.
  • Custom factories have to be added manually to the application factories.yml.

Generally speaking, all the configuration that should end up in one of the application configuration files has to be added manually. Plug-ins with such manual setup should embed a README file describing installation in detail.

17.4.3.4. Customizing a Plug-In for an Application ###

Whenever you want to customize a plug-in, never alter the code found in the plugins/ directory. If you do so, you will lose all your modifications when you upgrade the plug-in. For customization needs, plug-ins provide custom settings, and they support overriding.

Well-designed plug-ins use settings that can be changed in the application app.yml, as Listing 17-21 demonstrates.

Listing 17-21 - Customizing a Plug-In That Uses the Application Configuration

// example plug-in code
$foo = sfConfig::get('app_my_plugin_foo', 'bar');

// Change the 'foo' default value ('bar') in the application app.yml
all:
  my_plugin:
    foo:       barbar

The module settings and their default values are often described in the plug-in's README file.

You can replace the default contents of a plug-in module by creating a module of the same name in your own application. It is not really overriding, since the elements in your application are used instead of the ones of the plug-in. It works fine if you create templates and configuration files of the same name as the ones of the plug-ins.

On the other hand, if a plug-in wants to offer a module with the ability to override its actions, the actions.class.php in the plug-in module must be empty and inherit from an autoloading class, so that the method of this class can be inherited as well by the actions.class.php of the application module. See Listing 17-22 for an example.

Listing 17-22 - Customizing a Plug-In Action

// In myPlugin/modules/mymodule/lib/myPluginmymoduleActions.class.php
class myPluginmymoduleActions extends sfActions
{
  public function executeIndex()
  {
    // Some code there
  }
}

// In myPlugin/modules/mymodule/actions/actions.class.php

require_once dirname(__FILE__).'/../lib/myPluginmymoduleActions.class.php';

class mymoduleActions extends myPluginmymoduleActions
{
  // Nothing
}

// In frontend/modules/mymodule/actions/actions.class.php
class mymoduleActions extends myPluginmymoduleActions
{
  public function executeIndex()
  {
    // Override the plug-in code there
  }
}

17.4.4. How to Write a Plug-In

Only plug-ins packaged as PEAR packages can be installed with the plugin:install task. Remember that such plug-ins can be distributed via the symfony wiki, a PEAR channel, or a simple file download. So if you want to author a plug-in, it is better to publish it as a PEAR package than as a simple archive. In addition, PEAR packaged plug-ins are easier to upgrade, can declare dependencies, and automatically deploy assets in the web/ directory.

17.4.4.1. File Organization ###

Suppose you have developed a new feature and want to package it as a plug-in. The first step is to organize the files logically so that the symfony loading mechanisms can find them when needed. For that purpose, you have to follow the structure given in Listing 17-19. Listing 17-23 shows an example of file structure for an sfSamplePlugin plug-in.

Listing 17-23 - Example List of Files to Package As a Plug-In

sfSamplePlugin/
  README
  LICENSE
  config/
    schema.yml
  data/
    fixtures/
      fixtures.yml
  lib/
    model/
      sfSampleFooBar.php
      sfSampleFooBarPeer.php
    task/
      sfSampleTask.class.php
    validator/
      sfSampleValidator.class.php
  modules/
    sfSampleModule/
      actions/
        actions.class.php
      config/
        security.yml
      lib/
        BasesfSampleModuleActions.class.php
      templates/
        indexSuccess.php
  web/
    css/
      sfSampleStyle.css
    images/
      sfSampleImage.png

For authoring, the location of the plug-in directory (sfSamplePlugin/ in Listing 17-23) is not important. It can be anywhere on the disk.

Tip Take examples of the existing plug-ins and, for your first attempts at creating a plug-in, try to reproduce their naming conventions and file structure.

17.4.4.2. Creating the package.xml File ###

The next step of plug-in authoring is to add a package.xml file at the root of the plug-in directory. The package.xml follows the PEAR syntax. Have a look at a typical symfony plug-in package.xml in Listing 17-24.

Listing 17-24 - Example package.xml for a Symfony Plug-In

<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.4.6" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
 <name>sfSamplePlugin</name>
 <channel>plugins.symfony-project.org</channel>
 <summary>symfony sample plugin</summary>
 <description>Just a sample plugin to illustrate PEAR packaging</description>
 <lead>
  <name>Fabien POTENCIER</name>
  <user>fabpot</user>
  <email>[email protected]</email>
  <active>yes</active>
 </lead>
 <date>2006-01-18</date>
 <time>15:54:35</time>
 <version>
  <release>1.0.0</release>
  <api>1.0.0</api>
 </version>
 <stability>
  <release>stable</release>
  <api>stable</api>
 </stability>
 <license uri="http://www.symfony-project.org/license">MIT license</license>
 <notes>-</notes>
 <contents>
  <dir name="/">
   <file role="data" name="README" />
   <file role="data" name="LICENSE" />
   <dir name="config">
    <!-- model -->
    <file role="data" name="schema.yml" />
   </dir>
   <dir name="data">
    <dir name="fixtures">
     <!-- fixtures -->
     <file role="data" name="fixtures.yml" />
    </dir>
   </dir>
   <dir name="lib">
    <dir name="model">
     <!-- model classes -->
     <file role="data" name="sfSampleFooBar.php" />
     <file role="data" name="sfSampleFooBarPeer.php" />
    </dir>
    <dir name="task">
     <!-- tasks -->
     <file role="data" name="sfSampleTask.class.php" />
    </dir>
    <dir name="validator">
     <!-- validators -->
     <file role="data" name="sfSampleValidator.class.php" />
    </dir>
   </dir>
   <dir name="modules">
    <dir name="sfSampleModule">
     <file role="data" name="actions/actions.class.php" />
     <file role="data" name="config/security.yml" />
     <file role="data" name="lib/BasesfSampleModuleActions.class.php" />
     <file role="data" name="templates/indexSuccess.php" />
    </dir>
   </dir>
   <dir name="web">
    <dir name="css">
     <!-- stylesheets -->
     <file role="data" name="sfSampleStyle.css" />
    </dir>
    <dir name="images">
     <!-- images -->
     <file role="data" name="sfSampleImage.png" />
    </dir>
   </dir>
  </dir>
 </contents>
 <dependencies>
  <required>
   <php>
    <min>5.1.0</min>
   </php>
   <pearinstaller>
    <min>1.4.1</min>
   </pearinstaller>
   <package>
    <name>symfony</name>
    <channel>pear.symfony-project.com</channel>
    <min>1.1.0</min>
    <max>1.2.0</max>
    <exclude>1.2.0</exclude>
   </package>
  </required>
 </dependencies>
 <phprelease />
 <changelog />
</package>

The interesting parts here are the <contents> and the <dependencies> tags, described next. For the rest of the tags, there is nothing specific to symfony, so you can refer to the PEAR online manual (http://pear.php.net/manual/en/) for more details about the package.xml format.

17.4.4.3. Contents ###

The <contents> tag is the place where you must describe the plug-in file structure. This will tell PEAR which files to copy and where. Describe the file structure with <dir> and <file> tags. All <file> tags must have a role="data" attribute. The <contents> part of Listing 17-24 describes the exact directory structure of Listing 17-23.

Note The use of <dir> tags is not compulsory, since you can use relative paths as name values in the <file> tags. However, it is recommended so that the package.xml file remains readable.

17.4.4.4. Plug-In Dependencies ###

Plug-ins are designed to work with a given set of versions of PHP, PEAR, symfony, PEAR packages, or other plug-ins. Declaring these dependencies in the <dependencies> tag tells PEAR to check that the required packages are already installed, and to raise an exception if not.

You should always declare dependencies on PHP, PEAR, and symfony, at least the ones corresponding to your own installation, as a minimum requirement. If you don't know what to put, add a requirement for PHP 5.1, PEAR 1.4, and symfony 1.1.

It is also recommended to add a maximum version number of symfony for each plug-in. This will cause an error message when trying to use a plug-in with a more advanced version of the framework, and this will oblige the plug-in author to make sure that the plug-in works correctly with this version before releasing it again. It is better to have an alert and to download an upgrade rather than have a plug-in fail silently.

If you specify plugins as dependencies, users will be able to install your plugin and all its dependencies with a single command:

> php symfony plugin:install  — install-deps sfSamplePlugin

17.4.4.5. Building the Plug-In ###

The PEAR component has a command (pear package) that creates the .tgz archive of the package, provided you call the command shown in Listing 17-25 from a directory containing a package.xml.

Listing 17-25 - Packaging a Plug-In As a PEAR Package

> cd sfSamplePlugin
> pear package

Package sfSamplePlugin-1.0.0.tgz done

Once your plug-in is built, check that it works by installing it yourself, as shown in Listing 17-26.

Listing 17-26 - Installing the Plug-In

> cp sfSamplePlugin-1.0.0.tgz /home/production/myproject/
> cd /home/production/myproject/
> php symfony plugin:install sfSamplePlugin-1.0.0.tgz

According to their description in the <contents> tag, the packaged files will end up in different directories of your project. Listing 17-27 shows where the files of the sfSamplePlugin should end up after installation.

Listing 17-27 - The Plug-In Files Are Installed on the plugins/ and web/ Directories

plugins/
  sfSamplePlugin/
    README
    LICENSE
    config/
      schema.yml
    data/
      fixtures/
        fixtures.yml
    lib/
      model/
        sfSampleFooBar.php
        sfSampleFooBarPeer.php
      task/
        sfSampleTask.class.php
      validator/
        sfSampleValidator.class.php
    modules/
      sfSampleModule/
        actions/
          actions.class.php
        config/
          security.yml
        lib/
          BasesfSampleModuleActions.class.php
        templates/
          indexSuccess.php
web/
  sfSamplePlugin/               ## Copy or symlink, depending on system
    css/
      sfSampleStyle.css
    images/
      sfSampleImage.png

Test the way the plug-in behaves in your application. If it works well, you are ready to distribute it across projects — or to contribute it to the symfony community.

17.4.4.6. Hosting Your Plug-In in the Symfony Project Website ###

A symfony plug-in gets the broadest audience when distributed by the symfony-project.org website. Even your own plug-ins can be distributed this way, provided that you follow these steps:

  1. Make sure the README file describes the way to install and use your plug-in, and that the LICENSE file gives the license details. Format your README with the Markdown Formatting syntax (http://daringfireball.net/projects/markdown/syntax).
  2. Create a symfony account (http://www.symfony-project.org/user/new) and create the plugin (http://www.symfony-project.org/plugins/new).
  3. Create a PEAR package for your plug-in by calling the pear package command, and test it. The PEAR package must be named sfSamplePlugin-1.0.0.tgz (1.0.0 is the plug-in version).
  4. Upload your PEAR package (sfSamplePlugin-1.0.0.tgz).
  5. Your plugin must now appear in the list of plugins (http://www.symfony-project.org/plugins/).

If you follow this procedure, users will be able to install your plug-in by simply typing the following command in a project directory:

> php symfony plugin:install sfSamplePlugin

17.4.4.7. Naming Conventions ###

To keep the plugins/ directory clean, ensure all the plug-in names are in camelCase and end with Plugin (for example, shoppingCartPlugin, feedPlugin, and so on). Before naming your plug-in, check that there is no existing plug-in with the same name.

Note Plug-ins relying on Propel should contain Propel in the name. For instance, an authentication plug-in using the Propel data access objects should be called sfPropelAuth.

Plug-ins should always include a LICENSE file describing the conditions of use and the chosen license. You are also advised to add a README file to explain the version changes, purpose of the plug-in, its effect, installation and configuration instructions, etc.