TRAJOIN is an Application to Translate symfony documents Jointly.
home > 1.2/book > 17-Extending-Symfony.txt
[1] Edit ↑TOPEventually, you will need to alter symfony's behavior. Whether you need to modify the way a certain class behaves or add your own custom features, the moment will inevitably happen becase all clients have specific requirements that no framework can forecast. Actually, this situation is so common that symfony provides a mechanism to extend existing classes at runtime, beyond simple class inheritance. You can even replace the core symfony classes by modifying the factories settings. Once you have built an extension, you can easily package it as a plug-in, so that it can be reused in other applications, or by other symfony users.
PHP does not support multiple inheritence, which means it is not possible to have a class extend more than one other class. Also it is not possible to add new methods to an existing class or override existing methods. To ease these two limitations and to make the framework truly extendable, symfony introduces an event system, inspired by the Cocoa notification center, and based on the Observer design pattern (http://en.wikipedia.org/wiki/Observer_pattern).
Some of the symfony classes "notify the dispatcher of an event" at various
moments of their life. For instance, when the user changes their culture, the
user object notifies that a change_culture event has occurred. This is like
a shout in the project's space, saying: "I'm doing that. Do whatever you want
about it".
You can decide to do something special when an event is fired. For instance,
you could save the user culture to a database table each time the
change_culture event occurrs. In order to do so, you need to
register an event listener, in other words
you must declare a function that will be called when the event
occurs. Listing 17-1 shows how to register a listener on the user's
change_culture event.
リスト 17-1 - イベントリスナを登録する
$dispatcher->connect('user.change_culture', 'changeUserCulture');
function changeUserCulture(sfEvent $event)
{
$user = $event->getSubject();
$culture = $event['culture'];
// ユーザのcultureで何かを行う
}
All events and listener registrations are managed by a special object called
the event dispatcher. This object is available from everywhere in symfony by
way of the sfContext singleton, and most symfony objects offer a
getEventDispatcher() method to get direct access to it. Using the dispatcher's
connect() method, you can register any PHP callable (either a class method or
a function) to be called when an event occurs. The first argument of connect()
is the event identifier, which is a string composed of a namespace and a name.
The second argument is a PHP callable.
Retrieving the event dispatcher from anywhere in the application:$dispatcher = sfContext::getInstance()->getEventDispatcher();
Once the function is registered with the event dispatcher, it waits until the
event is fired. The event dispatcher keeps a record of all event listeners, and
knows which ones to call when an event occurs. When calling these methods or
functions, the dispatcher passes them an sfEvent object as a parameter.
The event object stores information about the notified event. The event notifier
can be retrieved thanks to the getSubject() method, and the event parameters
are accessible by using the event object as an array
(for example, $event['culture'] can be used to retrieve the culture parameter
passed by sfUser when notifying user.change_culture).
To wrap up, the event system allows you to add abilities to an existing class or modify its methods at runtime, without using inheritance.
: In version 1.0, symfony used a similar system but with a different syntax. You may see calls to static methods of ansfMixerclass to register and notify events in symfony 1.0 code, instead of calls to methods of the event dispatcher.sfMixercalls are deprecated, yet they still work in symfony 1.2.
Just like symfony classes notify that events have occurred, your own classes can
offer runtime extensibility and notify of events at certain occasions. For
instance, let's say that your application requests several third-party web
services, and that you have written an sfRestRequest class to wrap the REST
logic of these requests. A good idea would be to trigger an event each time this
class makes a new request. This would make the addition of logging or caching
capabilities easier in the future. Listing 17-2 shows the code you need to add
to an existing fetch() method to make it notify an event listener.
Listing 17-2 - Notifying an Event listener
class sfRestRequest
{
protected $dispatcher = null;
public function __construct(sfEventDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Makes a query to an external web service
*/
public function fetch($uri, $parameters = array())
{
// Notify the dispatcher of the beginning of the fetch process
$this->dispatcher->notify(new sfEvent($this, 'rest_request.fetch_prepare', array(
'uri' => $uri,
'parameters' => $parameters
)));
// Make the request and store the result in a $result variable
// ...
// Notify the dispatcher of the end of the fetch process
$this->dispatcher->notify(new sfEvent($this, 'rest_request.fetch_success', array(
'uri' => $uri,
'parameters' => $parameters,
'result' => $result
)));
return $result;
}
}
The notify() method of the event dispatcher expects an sfEvent object as
an argument; this is the very same object that is passed to the event
listeners. This object always carries a reference to the notifier (that's why
the event instance is initialized with this) and an event identifier.
Optionally, it accepts an associative array of parameters, giving listeners a
way to interact with the notifier's logic.
Only the classes that notify events can be extended by way of the event system. So even if you don't know whether you will need to extend a class in the future or not, it is always a good idea to add notifications in the key methods.
By using the notify() method, you make sure that all the listeners registered
on the notifying event are executed. However, in some cases you need to allow a
listener to stop the event and prevent further listeners from being notified
about it. In this case, you should use notifyUntil() instead of notify().
The dispatcher will then execute all listeners until one returns true, and
then stop the event notification. In other words, notifyUntil() is like a
shout in the project space saying: "I'm doing that. If somebody cares, then I
won't tell anybody else". Listing 17-3 shows how to use this technique in
combination with a magic __call() method to add methods to an existing class
at runtime.
Listing 17-3 - Notifying of an Event Until a Listener Returns True
class sfRestRequest
{
// ...
public function __call($method, $arguments)
{
$event = $this->dispatcher->notifyUntil(new sfEvent($this, 'rest_request.method_not_found', array(
'method' => $method,
'arguments' => $arguments
)));
if (!$event->isProcessed())
{
throw new sfException(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
}
return $event->getReturnValue();
}
}
An event listener registered on the rest_request.method_not_found event can
test the requested $method and decide to handle it, or pass to the next event
listener callable. In Listing 17-4, you can see how a third party class can add
put() and delete() methods to the sfRestRequest class at runtime with
this trick.
Listing 17-4 - Handling a "Notify Until" Event type
class frontendConfiguration extends sfApplicationConfiguration
{
public function configure()
{
// ...
// リスナを登録する
$this->dispatcher->connect('rest_request.method_not_found', array('sfRestRequestExtension', 'listenToMethodNotFound'));
}
}
class sfRestRequestExtension
{
static public function listenToMethodNotFound(sfEvent $event)
{
switch ($event['method'])
{
case 'put':
self::put($event->getSubject(), $event['arguments'])
return true;
case 'delete':
self::delete($event->getSubject(), $event['arguments'])
return true;
default:
return false;
}
}
static protected function put($restRequest, $arguments)
{
// putリクエストを行い結果を変数$resultに保存する
// ...
$event->setReturnValue($result);
}
static protected function delete($restRequest, $arguments)
{
// deleteリクエストを行い結果を変数$resultに保存する
// ...
$event->setReturnValue($result);
}
}
In practice, notifyUntil() offers multiple inheritance capabilities, or
rather mixins (the addition of methods from third-party classes to an existing
class), to PHP. You can now "inject" new methods to objects that you can't
extend by way of inheritance. And this happens at runtime. You are not limited
by the Object Oriented capabilities of PHP anymore when you use symfony.
As the first listener to catch a notifyUntil() event prevents further
notifications, you may worry about the order in which listeners are executed.
This order corresponds to the order in which listeners were registered - first
registered, first executed. In practice, cases where this could be an issue
seldom happen. If you realize that two listeners conflict on a particular
event, perhaps your class should notify several events, for instance one at
the beginning and one at the end of the method execution. And if you use
events to add new methods to an existing class, name your methods wisely so
that other attempts at adding methods don't conflict. Prefixing method names
with the name of the listener class is a good practice.
You can probably imagine how a listener can not only use the information given
by an event, but also modify it, to alter the original logic of the notifier.
If you want to allow this, you should use the filter() method of the event
dispatcher rather than notify(). All event listeners are then called with two
parameters: the event object, and the value to filter. Event listeners must
return the value, whether they altered it or not. Listing 17-5 shows how
filter() can be used to filter a response from a web service and escape
special characters in that response.
Listing 17-5 - Notifying of and Handling a Filter Event
class sfRestRequest
{
// ...
/**
* Make a query to an external web service
*/
public function fetch($uri, $parameters = array())
{
// Make the request and store the result in a $result variable
// ...
// Notify of the end of the fetch process
return $this->dispatcher->filter(new sfEvent($this, 'rest_request.filter_result', array(
'uri' => $uri,
'parameters' => $parameters,
)), $result)->getReturnValue();
}
}
// Add escaping to the web service response
$dispatcher->connect('rest_request.filter_result', 'rest_htmlspecialchars');
function rest_htmlspecialchars(sfEvent $event, $result)
{
return htmlspecialchars($result, ENT_QUOTES, 'UTF-8');
}
Many of symfony's classes have built-in events, allowing you to extend the framework without necessarily changing the classes themselves. Table 17-1 lists these events, together with their types and arguments.
テーブル17-1 - symfonyのイベント
| イベントの名前空間 | イベントの名前 | 型 | 通知者 | 引数 |
|---|---|---|---|---|
| アプリケーション | log | notify | たくさんのクラス | priority |
| throw_exception | notifyUntil | sfException | - | |
| コマンド | log | notify | sfCommand* classes | priority |
| pre_command | notifyUntil | sfTask | arguments, options | |
| post_command | notify | sfTask | - | |
| filter_options | filter | sfTask | command_manager | |
| 設定 | method_not_found | notifyUntil | sfProjectConfiguration | method, arguments |
| コンポーネント | method_not_found | notifyUntil | sfComponent | method, arguments |
| 内容 | load_factories | notify | sfContext | - |
| コントローラ | change_action | notify | sfController | module, action |
| method_not_found | notifyUntil | sfController | method, arguments | |
| page_not_found | notify | sfController | module, action | |
| プラグイン | pre_install | notify | sfPluginManager | channel, plugin, is_package |
| post_install | notify | sfPluginManager | channel, plugin | |
| pre_uninstall | notify | sfPluginManager | channel, plugin | |
| post_uninstall | notify | sfPluginManager | channel, plugin | |
| リクエスト | filter_parameters | filter | sfWebRequest | path_info |
| method_not_found | notifyUntil | sfRequest | method, arguments | |
| レスポンス | method_not_found | notifyUntil | sfResponse | method, arguments |
| filter_content | filter | sfResponse | - | |
| ルーティング | load_configuration | notify | sfRouting | - |
| タスク | cache.clear | notifyUntil | sfCacheClearTask | app, type, env |
| テンプレート | filter_parameters | filter | sfViewParameterHolder | - |
| ユーザ | change_culture | notify | sfUser | culture |
| method_not_found | notifyUntil | sfUser | method, arguments | |
| change_authentication | notify | sfBasicSecurityUser | authenticated | |
| ビュー | configure_format | notify | sfView | format, response, request |
| method_not_found | notifyUntil | sfView | method, arguments | |
| ビュー.キャッシュ | filter_content | filter | sfViewCacheManager | response, uri, new |
You are free to register event listeners on any of these events. Just make sure
that listener callables return a boolean when registered on a
notifyUntil event type, and that they return the filtered value when
registered on a filter event type.
Note that the event namespaces don't necessarily match the class role. For
instance, all symfony classes notify of an application.log event when they
need something to appear in the log files (and in the web debug toolbar):
$dispatcher->notify(new sfEvent($this, 'application.log', array($message)));
Your own classes can do the same and also notify symfony events when it makes sense to do so.
Event listeners need to be registered early in the life of a symfony request.
In practice, the right place to register event listeners is in the application
configuration class. This class has a reference to the event dispatcher that
you can use in the configure() method. Listing 17-6 shows how to register a
listener on one of the rest_request events of the above examples.
Listing 17-6 - Registering a Listener in the Application Configuration Class,
in apps/frontend/config/ApplicationConfiguration.class.php
class frontendConfiguration extends sfApplicationConfiguration
{
public function configure()
{
$this->dispatcher->connect('rest_request.method_not_found', array('sfRestRequestExtension', 'listenToMethodNotFound'));
}
}
Plug-ins (see below) can register their own event listeners. They should do it
in the plug-in's config/config.php script, which is executed during
application initialization and offers access to the event dispatcher through
$this->dispatcher.
Propel BehaviorsPropel behaviors, discussed previously in Chapter 8, use the event system. Actually, they use the symfony 1.0 version of the event system, but that doesn't matter. They package event registering and handling to enable extending Propel-generated objects. Let's look at an example.
The Propel objects corresponding to the tables of the database all have a delete() method, which deletes the related record from the database. But for an
Invoiceclass, for which you can't delete a record, you may want to alter thedelete()method to be able to keep the record in the database and change the value of an is_deleted attribute to true instead. Usual object retrieval methods (doSelect(),retrieveByPk()) would only consider the records for whichis_deletedis false. You would also need to add another method calledforceDelete(), which would allow you to really delete the record. In fact, all these modifications can be packaged into a new class, calledParanoidBehavior. The finalInvoiceclass extends the PropelBaseInvoiceclass and has methods of theParanoidBehaviormixed in.So a behavior is a mixin on a Propel object. Actually, the term \"behavior\" in symfony covers one more thing: the fact that the mixin is packaged as a plug-in. The
ParanoidBehaviorclass just mentioned corresponds to a real symfony plug-in calledsfPropelParanoidBehaviorPlugin. Refer to the symfony wiki (http://trac.symfony-project.org/wiki/sfPropelParanoidBehaviorPlugin) for details on installation and use of this plug-in.One last word about behaviors: To be able to support them, the generated Propel objects must contain quite a number of event notifications. These may slow down execution a little and penalize performance if you don't use behaviors. That's why the events are not enabled by default. In order to add them and enable behavior support, you must first set the
propel.builder.addBehaviorsproperty totruein thepropel.inifile and rebuild the model.
ファクトリ(factory)は特定のタスクのためのクラスの定義です。symfonyはコントローラやセッション機能などのコア機能についてファクトリに依存します。例えば、symfonyが新しいリクエストオブジェクトを作成する必要がある場合、symfonyはこの目的のために使用するクラスの名前のためのファクトリの定義を検索します。リクエスト用のデフォルトのファクトリの定義はsfWebRequestクラスで、symfonyはリクエストを処理するためにこのクラスのオブジェクトを作成します。ファクトリの定義の利点はsymfonyのコア機能をとても簡単に変更できることです: ファクトリの定義を変更し、symfonyは自身の代わりにカスタムのリクエストクラスを使用します。
ファクトリの定義はfactories.yml設定ファイルに保存されます。リスト17-7はデフォルトのファクトリの定義ファイルを示します。それぞれの定義はオートロードされるクラスの名前と(オプションとして)パラメータの一式から構成されます。例えば、セッションストレージのファクトリ(storage:キーの下で設定)は一貫したセッションを可能にするためにクライアントコンピュータ上で作成されたクッキーに名前を付けるsession_nameパラメータを使用します。
リスト17-7 - frontend/config/factories.ymlの中の、デフォルトのファクトリファイル
-
prod:
logger:
class: sfNoLogger
param:
level: err
loggers: ~
cli:
controller:
class: sfConsoleController
request:
class: sfConsoleRequest
response:
class: sfConsoleResponse
test:
storage:
class: sfSessionTestStorage
param:
session_path: %SF_TEST_CACHE_DIR%/sessions
response:
class: sfWebResponse
param:
send_http_headers: false
all:
routing:
class: sfPatternRouting
param:
generate_shortest_url: true
extra_parameters_as_query_string: true
#all:
# controller:
# class: sfFrontWebController
#
# request:
# class: sfWebRequest
# param:
# logging: %SF_LOGGING_ENABLED%
# path_info_array: SERVER
# path_info_key: PATH_INFO
# relative_url_root: ~
# formats:
# txt: text/plain
# js: [application/javascript, application/x-javascript, text/javascript]
# css: text/css
# json: [application/json, application/x-json]
# xml: [text/xml, application/xml, application/x-xml]
# rdf: application/rdf+xml
# atom: application/atom+xml
#
# response:
# class: sfWebResponse
# param:
# logging: %SF_LOGGING_ENABLED%
# charset: %SF_CHARSET%
# send_http_headers: true
#
# user:
# class: myUser
# param:
# timeout: 1800
# logging: %SF_LOGGING_ENABLED%
# use_flash: true
# default_culture: %SF_DEFAULT_CULTURE%
#
# storage:
# class: sfSessionStorage
# param:
# session_name: symfony
#
# view_cache:
# class: sfFileCache
# param:
# automatic_cleaning_factor: 0
# cache_dir: %SF_TEMPLATE_CACHE_DIR%
# lifetime: 86400
# prefix: %SF_APP_DIR%/template
#
# i18n:
# class: sfI18N
# param:
# source: XLIFF
# debug: off
# untranslated_prefix: "[T]"
# untranslated_suffix: "[/T]"
# cache:
# class: sfFileCache
# param:
# automatic_cleaning_factor: 0
# cache_dir: %SF_I18N_CACHE_DIR%
# lifetime: 31556926
# prefix: %SF_APP_DIR%/i18n
#
# routing:
# class: sfPatternRouting
# param:
# load_configuration: true
# suffix: ''
# default_module: default
# default_action: index
# debug: %SF_DEBUG%
# logging: %SF_LOGGING_ENABLED%
# generate_shortest_url: false
# extra_parameters_as_query_string: false
# cache:
# class: sfFileCache
# param:
# automatic_cleaning_factor: 0
# cache_dir: %SF_CONFIG_CACHE_DIR%/routing
# lifetime: 31556926
# prefix: %SF_APP_DIR%/routing
#
# logger:
# class: sfAggregateLogger
# param:
# level: debug
# loggers:
# sf_web_debug:
# class: sfWebDebugLogger
# param:
# level: debug
# condition: %SF_WEB_DEBUG%
# xdebug_logging: true
# web_debug_class: sfWebDebug
# sf_file_debug:
# class: sfFileLogger
# param:
# level: debug
# file: %SF_LOG_DIR%/%SF_APP%_%SF_ENVIRONMENT%.log
ファクトリを変更する最良の方法はデフォルトのファクトリから継承した新しいクラスを作り、新しいメソッドをそのクラスに追加することです。例えば、ユーザセッションのファクトリはmyUserクラス(frontend/lib/ディレクトリに設置)に設定され、sfUserクラスから継承します。既存のファクトリを利用するには同じメカニズムを使います。リスト17-8はリクエストオブジェクトのための新しいファクトリの例を示しています。
リスト17-8 - ファクトリをオーバーライドする
//オートロードされたディレクトリ内でmyRequest.class.phpを作る
// 例えばfrontend/lib/において
<?php
class myRequest extends sfRequest
{
// あなたのコードをここに
}
// factories.ymlでクラスをリクエストファクトリとして宣言する
all:
request:
class: myRequest
サードパーティのクラスによって提供された機能が必要な場合、このクラスをlib/ディレクトリの1つにコピーしたくない場合、おそらくはsymfonyがファイルを探す通常の場所の外側にそのクラスをインストールすることになります。この場合、クラスを利用するには、オートロードを利用するsymfonyのsplオートロード統合機能を使わない限り、requreステートメントを手動でコードに含めることになります。
symfonyは(まだ)すべてのためのツールを提供していません。PDFジェネレータ、Google MapsのAPI、PHPによるLucene検索エンジンの実装など、おそらくZend Frameworkからいくつかのライブラリが必要になります。PHPで直接イメージを操作する、Eメールを読むためにPOP3アカウントに接続する、コンソールのインタフェースを設計することなどを行いたい場合、eZcomponentsからライブラリを選ぶことがあるかもしれません。幸いにして、正しい設定を定義すると、これらのライブラリからのコンポーネントはsymfonyで正常に動作します。
(PEAR経由でサードパーティのライブラリをインストールしない限り)最初に、アプリケーションのapp.ymlファイルの中でライブラリのrootディレクトリへのパスを宣言する必要があります:
all:
zend_lib_dir: /usr/local/zend/library/
ez_lib_dir: /usr/local/ezcomponents/
swift_lib_dir: /usr/local/swiftmailer/
それから、symfonyがオートロードを失敗するとき、考慮するライブラリを指定することでPHPのオートロードシステムを拡張します。リスト17-9のように、オートロードクラスをアプリケーションの設定クラスに登録することでこれを行うことができます(詳細は19章を参照)。
リスト17-9 - apps/frontend/config/ApplicationConfiguration.class.phpの中で、サードパーティのコンポーネントのオートロードを有効にする
class frontendConfiguration extends sfApplicationConfiguration
{
public function initialize()
{
parent::initialize(); // 最初にsymfonyのオートロード機能をロードする
// Zend Frameworkを統合する
if ($sf_zend_lib_dir = sfConfig::get('app_zend_lib_dir'))
{
set_include_path($sf_zend_lib_dir.PATH_SEPARATOR.get_include_path());
require_once($sf_zend_lib_dir.'/Zend/Loader.php');
spl_autoload_register(array('Zend_Loader', 'autoload'));
}
// eZ Componentsを統合する
if ($sf_ez_lib_dir = sfConfig::get('app_ez_lib_dir'))
{
set_include_path($sf_ez_lib_dir.PATH_SEPARATOR.get_include_path());
require_once($sf_ez_lib_dir.'/Base/base.php');
spl_autoload_register(array('ezcBase', 'autoload'));
}
// Swift Mailerを統合する
if ($sf_swift_lib_dir = sfConfig::get('app_swift_lib_dir'))
{
set_include_path($sf_swift_lib_dir.PATH_SEPARATOR.get_include_path());
require_once($sf_swift_lib_dir.'/Swift/ClassLoader.php');
spl_autoload_register(array('Swift_ClassLoader', 'load'));
}
}
}
ロードされていないクラスの新しいオブジェクトを作るときに起きることは単純です:
autoload.ymlファイルで宣言されたパスの中でクラスを探します。spl_autoload_register()によって登録されたコールバックメソッドはそれらの1つがtrueを返すまで次から次へと呼び出されます。そしてZend_Loader::autoload()、ezcBase::autoload()、とSwift_ClassLoader::load()のうちの1つがクラスを見つけるまでこれらが呼び出されます。falseを返す場合、PHPはエラーを生成します。このことは他のフレームワークコンポーネントはオートロードメカニズムから恩恵を受け、独自環境よりもこれらを簡単に使用できることを意味します。例えば、Lucene検索エンジンと同等のものをPHPで実装するためにZend FrameworkのZend_Searchコンポーネントを使いたい場合、通常はrequireステートメントが必要です:
require_once 'Zend/Search/Lucene.php';
$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
// ...
symfonyとsplのオートロード機能によって、上記のコードはよりシンプルになります。requireステートメントを省略できるのでincludeのパスとクラスの位置に悩まなくて済みます:
$doc = new Zend_Search_Lucene_Document(); // クラスがオートロードされた
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
// ...
1つのsymfonyアプリケーションのために開発したコードピースの再利用がおそらく必要になります。このコードのピースを単独のクラスのパッケージにすることができるのであれば、問題ありません: クラスを別のアプリケーションのlib/フォルダの1つに設置すればオートローダが残りを引き受けます。しかし、コードが複数のファイルに散在している場合、例えば、administrationジェネレータ用の完全に新しいテーマ、もしくは好みの視覚効果を自動化するJavaScriptファイルとヘルパの組み合わせなどの場合、ファイルをコピーするだけの方法は最良の解決方法ではありません。
プラグインはいくつかのファイルにまたがるコードをパッケージにする方法と、いくつかのプロジェクトをまたがってこのコードを再利用する方法を提供します。プラグインの中で、クラス、フィルタ、イベントリスナ、ヘルパ、設定、タスク、モジュール、スキーマ、モデルの拡張、フィクスチャ、ウェブアセットなどをパッケージにすることができます。プラグインをインストール、アップグレード、アンインストールする方法は簡単です。これらは.tgzアーカイブ、PEARパッケージ、もしくはコードリポジトリとして配布可能で、コードのリポジトリから簡単にチェックアウトできます。PEARパッケージとなったプラグインは依存関係の管理機能を利用し、アップグレードと自動検出が簡単です。symfonyのロードメカニズムはプラグインを考慮し、プラグインによって提供される機能はあたかもフレームワークの一部であるかのようにプロジェクトで利用できます。
ですので、プラグインは基本的にsymfonyプロジェクトのために拡張機能をパッケージにしたものです。プラグインによってアプリケーションを越えて独自コードを再利用できるだけでなく、別の投稿者によって開発されたものも再利用可能でsymfonyコアにサードパーティの拡張機能を追加できます。
symfonyの公式サイトにはプラグイン専用のページが存在しており、次のURLからアクセスできます:
http://www.symfony-project.org/plugins/
ここに掲載されている各プラグインはそれぞれのページでインストールについての説明やドキュメントが整備されています。
ここにあるプラグインはコミュニティから寄せられたものもあれば、symfonyのコア開発者が開発したものもあります。後者のものについては以下を参照してください。:
sfFeed2Plugin: Automates the manipulation of RSS and Atom feedssfThumbnailPlugin: Creates thumbnails--for instance, for uploaded imagessfMediaLibraryPlugin: Allows media upload and management, including an extension for rich text editors to allow authoring of images inside rich textsfShoppingCartPlugin: Allows shopping cart managementsfGuardPlugin: Provides authentication, authorization, and other user management features above the standard security feature of symfonysfPrototypePlugin: Provides prototype and script.aculo.us JavaScript files as a standalone librarysfSuperCachePlugin: Writes pages in cache directory under the web root to allow the web server to serve them as fast as possiblesfOptimizerPlugin: 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 errorssfSslRequirementPlugin: Provides SSL encryption support for actions公式サイトはビヘイビア(behavior)と呼ばれる、Propelオブジェクトを拡張するために設計されたプラグインも提示します。これらの中で、次のものが見つかります:
sfPropelParanoidBehaviorPlugin: オブジェクトの削除を無効にして、deleted_atカラムの更新で置き換えるsfPropelOptimisticLockBehaviorPlugin: Propelオブジェクト用にオプティミスティックロック(楽観的ロック)を実装する公式サイトの専用ページを定期的に確認すべきです。いつも新しいプラグインが追加され、これらはウェブアプリケーションのプログラミングの多くの面にとても便利なショートカットをもたらしてくれます。
公式サイトの専用ページは別にして、プラグインを配布する他の方法はダウンロードのためのプラグインアーカイブを提供することと、PEARチャネルでプラグインをホストすること、もしくは公開のバージョンコントロールリポジトリに保存することです。
プラグインのインストール作業はプラグインパッケージの作成方法によって異なります。常にREADMEファイルかつ・もしくはプラグインのダウンロードのページ上のインストールの手引きを参照して下さい。
プラグインはプロジェクト単位でインストールされます。次のセクションで説明されるすべての方法ではすべてのプラグインのファイルをmyproject/plugins/pluginName/ディレクトリに設置します。
公式サイトの専用ページの一覧に記載されているプラグインはPEARチャネル: plugins:symfony-project.orgを通して入手できます。プラグインをインストールするためには、リスト17-10で示されるように、プラグインの名前と一緒にplugin:installタスクを使います。
リスト17-10 - 公式サイトのPEARチャネルからプラグインをインストールする
> cd myproject
> php symfony plugin:install pluginName
代わりの方法として、プラグインをダウンロードしてディスクからインストールすることもできます。この場合、リスト17-11で示されるように、パッケージアーカイブへのパスを使います。
リスト17-11 - ダウンロードしたパッケージからプラグインをインストールする
> cd myproject
> php symfony plugin:install /home/path/to/downloads/pluginName.tgz
プラグインの中には外部のPEARチャネルでホストされるものがあります。リスト17-12で示されるように、plugin:installタスクでそれらをインストールしてチャネルを登録してチャネルの名前を記載することを忘れないで下さい。
リスト17-12 - PEARチャネルからプラグインをインストールする
> cd myproject
> php symfony plugin:add-channel channel.symfony.pear.example.com
> php symfony plugin:install --channel=channel.symfony.pear.example.com pluginName
これら3つのタイプのインストール方法はすべてPEARパッケージを使うので、"PEARプラグイン"という用語はsymfonyプラグインのPEARチャネル、外部のPEARチャネル、もしくはダウンロードしたPEARパッケージからインストールしたプラグインを区別無く説明するために使われます。
リスト17-13で示されているように、plugin:installタスクは多くのオプションを取ります。
リスト17-13 - いくつかのオプションを付けてプラグインをインストールする
> php symfony plugin:install --stability=beta pluginName
> php symfony plugin:install --release=1.0.3 pluginName
> php symfony plugin:install --install-deps pluginName
すべてのsymfonyタスクに関しては、php symfony help plugin:installを実行すればplugin:installのオプションと引数の説明を見ることができます
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, don't forget to run the plugin:publish-assets command to create the corresponding symlink under the main web/ folder as shown in listing 17-14. Finally, don't forget to clear the cache.
リスト17-14 - アーカイブからプラグインをインストールする
> cd plugins
> tar -zxpf myPlugin.tgz
> cd ..
> php symfony plugin:publish-assets
> php symfony cc
プラグインはときにバージョン管理システム用の独自のソースコードリポジトリを持つことがあります。plugins/ディレクトリの中でチェックアウトするだけでこれらのプラグインをインストールできますが、プロジェクト自身がバージョン管理システムの管理下にある場合、この作業によって問題が引き起こされる可能性があります。
代わりの方法として、プラグインを外部依存のライブラリとして宣言することが可能で、すべてのプロジェクトのソースコードを更新するとプラグインのソースコードも更新されます。例えば、Subversionはsvn:externalsプロパティで外部依存を保存します。ですので、リスト17-15で示されているように、このプロパティを編集してソースコードを後で更新することでプラグインを追加できます。
リスト17-15 - ソースのバージョン管理リポジトリからプラグインをインストールする
> cd myproject
> svn propedit svn:externals plugins
pluginName http://svn.example.com/pluginName/trunk
> svn up
> php symfony plugin:publish-assets
> php symfony cc
If the plug-in contains aweb/directory, the symfonyplugin:publish-assetscommand has to be run to generate the corresponding symlink under the mainweb/folder of the project.
プラグインの中にはモジュール全体を含むものがあります。プラグインモジュールと古典的なモジュールの違いはプラグインモジュールがmyproject/frontend/modules/ディレクトリに現れないことだけです(簡単にアップグレードできる状態を保つため)。リスト17-16で示されるように、settings.ymlファイルの中でこれらを有効にしなければなりません。
リスト17-16 - frontend/config/settings.ymlの中で、プラグインモジュールを有効にする
all:
.settings:
enabled_modules: [default, sfMyPluginModule]
これはプラグインモジュールを必要としないアプリケーションが誤ってそのプラグインを利用できるように設定する状況を避けるためです。その状況ではセキュリティの欠陥を公開してしまう可能性があります。frontendモジュールとbackendモジュールを提供するプラグインを考えて下さい。frontendモジュールはfrontendアプリケーション専用として、backendモジュールはbackendアプリケーション専用として有効にする必要があります。プラグインモジュールがデフォルトで有効にされない理由はそういうわけです。
The default module is the only enabled module by default. That's not really a plug-in module, because it resides in the framework, insfConfig::get('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 theenabled_modulessetting.
プロジェクトのplugins/ディレクトリをざっと見るとプラグインがインストールされている場所がわかります。そしてplugin:listタスクはより詳細な情報を示します: バージョン番号とインストールしたそれぞれのプラグインのチャネル名です。
リスト17-17 - インストールされたプラグインの一覧
> 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)
PEARのプラグインをアンインストールするには、リスト17-18で示されるように、プロジェクトのrootディレクトリからplugin:uninstallタスクを呼び出します。プラグインの名前にプラグインをインストールしたチャネル名を接頭辞として追加しなければなりません(このチャネルを決めるためにplugin:listタスクを使います)。
リスト17-18 - プラグインをアンインストールする
> cd myproject
> php symfony plugin:uninstall sfPrototypePlugin
> php symfony cc
アーカイブからインストールしたプラグインもしくはSVNリポジトリからインストールしたプラグインをアンインストールするためには、プロジェクトのplugins/とweb/ディレクトリからプラグインのファイルを手動で削除してキャッシュをクリアします。
プラグインをアップグレードするには、plugin:upgradeタスク(PEARプラグインの場合)もしくはsvn updateを実行します(バージョン管理システムのリポジトリからプラグインを入手した場合)。アーカイブからインストールしたプラグインは簡単にアップグレードできません。
プラグインはPHPで書かれています。アプリケーションの編成方法を理解しているのであれば、プラグインの構造を理解できます。
プラグインのディレクトリはおおよそプロジェクトのディレクトリと同じように編成されています。必要な時にsymfonyによって自動的にロードされるようにするためにプラグインファイルは正しいディレクトリに存在しなければなりません。ファイル構造の記述に関してはリスト17-19をご覧下さい。
リスト17-19 - プラグインのファイル構造
pluginName/
config/
*schema.yml // データスキーマ
*schema.xml
config.php // 特定のプラグイン設定
data/
generator/
sfPropelAdmin
*/ // administrationジェネレータテーマ
template/
skeleton/
fixtures/
*.yml // フィクスチャファイル
lib/
*.php // クラス
helper/
*.php // ヘルプ
model/
*.php // モデルクラス
task/
*Task.class.php // CLIタスク
modules/
*/ // モジュール
actions/
actions.class.php
config/
module.yml
view.yml
security.yml
templates/
*.php
validate/
*.yml
web/
* // アセット
プラグインは多くのものを含みます。コマンドラインでタスクを呼び出すときに実行中のアプリケーションはこれらの内容を自動的に考慮します。しかしプラグインを適切に機能させるには、いくつかの規約を遵守しなければなりません:
propel-タスクによって検出されます。propel-build-modelタスクを呼び出すと、プロジェクトモデルとすべてのプラグインモデルがリビルドされます。リスト17-20で示されるように、 プラグインスキーマは常にplugins.pluginName.lib.model形式でpackage属性を持つことに注意して下さい。リスト17-20 - 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 }
...
config.php)に含まれています。このファイルはアプリケーションとプロジェクト設定の後で実行されるので、symfonyはその時点で既に起動しています。例えば、既存のクラスをイベントリスナもしくはビヘイビアで拡張するためです。data/fixtures/ディレクトリに設置されたフィクスチャファイルはpropel:load-dataタスクで処理されます。lib/フォルダに設置されたクラスのようにカスタムクラスはオートロードされます。use_helper()ヘルパを呼び出すときにヘルパは自動的に発見されます。これらはプラグインのlib/ディレクトリの1つのhelper/サブディレクトリに存在しなければなりません。myplugin/lib/model/ディレクトリ内のモデルクラスは(myplugin/lib/model/om/ディレクトリとmyplugin/lib/model/map/ディレクトリ)内部のPropelビルダによって生成されたモデルクラスを専門に扱います。もちろんこれらもオートロードされます。独自プロジェクトのディレクトリ内で生成されたプラグインのモデルクラスはオーバーライドできません。php symfonyを入力して下さい。enabled_modules設定で宣言すれば、モジュールは外部からアクセス可能な新しいアクションを提供します。web/ディレクトリにシンボリックリンクを作るもしくはweb/ディレクトリの内容をプロジェクトのディレクトリにコピーします。プラグインがアーカイブもしくはバージョン管理ツールのリポジトリからインストールされた場合、手動でweb/ディレクトリにコピーしなければなりません(プラグインに添付されているREADMEに記載されています)。: Registering routing rules in a Plug-in A plug-in can add new rules to the routing system, but it is not recomandable to do it by using a customrouting.ymlconfiguration file. 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 therouting.load_configurationevent 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', new sfRoute('/my_plugin/:action', array('module' => 'myPluginAdministrationInterface'))); } }
plugin:installタスクが独自に処理できない要素がいくつかあります。インストール作業の合間にこれらを手動でセットアップする必要があります:
sfConfig::get('app_myplugin_foo')を利用する)、デフォルトの値をプラグインのconfig/ディレクトリに設置されたapp.ymlファイルに設定できません。デフォルトの値を処理するには、sfConfig::get()メソッドの2番目の引数を使います。設定はまだアプリケーションレベルでオーバーライドできます(リスト17-26で例をご覧下さい)。routing.ymlに手動で追加しなければなりません。filters.ymlに手動で追加しなければなりません。factories.ymlに手動で追加しなければなりません。一般的に言えば、アプリケーションの設定ファイルの1つに帰結するようなすべての設定は手動で追加しなければなりません。このような手動のセットアップが必要なプラグインはREADMEファイルで詳細なインストール方法を説明しています。
プラグインをカスタマイズしたいときは、決してplugins/ディレクトリ内で見つかるコードを変更してはなりません。これを行うと、プラグインをアップグレードするときにすべての修正内容が失われてしまいます。必要なカスタマイズを行うために、プラグインはカスタムの設定を提供し、オーバーライドをサポートします。
リスト17-21で示されるように、よく設計されたプラグインはアプリケーションのapp.ymlファイルで変更できる設定を利用します。
リスト17-21 - アプリケーションの設定を利用するプラグインをカスタマイズする
// プラグインのコードの例
$foo = sfConfig::get('app_my_plugin_foo', 'bar');
// アプリケーションのapp.ymlで'foo'のデフォルト値('bar')を変更する
all:
my_plugin:
foo: barbar
モジュールの設定とデフォルトの値はプラグインのREADMEファイルで詳しく説明されています。
独自のアプリケーション内部で同じ名前のモジュールを作成することでプラグインモジュールのデフォルトの内容を置き換えることができます。プラグイン要素の代わりにアプリケーション要素が使われているので、本当の上書きではありません。プラグインの名前と同じ名前のテンプレートと設定ファイルを作ればプラグインモジュールは立派に機能します。
一方で、アクションをオーバーライドする機能を持つモジュールをプラグインに持たせたい場合、プラグインモジュール内のactions.class.phpのメソッドがアプリケーションモジュールのactions.class.phpによって継承できるように、actions.class.phpは空でなければならずオートロードクラスから継承しなければなりません。お手本に関してはリスト17-22を参照して下さい。
リスト17-22 - プラグインのアクションをカスタマイズする
// myPlugin/modules/mymodule/lib/myPluginmymoduleActions.class.phpの中
class myPluginmymoduleActions extends sfActions
{
public function executeIndex()
{
// ここに何らかのコード
}
}
// myPlugin/modules/mymodule/actions/actions.class.phpにて
require_once dirname(__FILE__).'/../lib/myPluginmymoduleActions.class.php';
class mymoduleActions extends myPluginmymoduleActions
{
// 無し
}
// frontend/modules/mymodule/actions/actions.class.phpにて
class mymoduleActions extends myPluginmymoduleActions
{
public function executeIndex()
{
// ここでプラグインのコードをオーバーライドする
}
}
symfony1.1の新しい機能: プラグインのスキーマをカスタマイズするモデルをビルドするとき、symfonyは、下記のルールに従って、プラグインのものを含めて、それぞれの既存のスキーマのためにカスタムのYAMLファイルを探します:
オリジナルのスキーマ名 カスタムのスキーマ名 config/schema.yml schema.custom.yml config/foobar_schema.yml foobar_schema.custom.yml plugins/myPlugin/config/schema.yml myPlugin_schema.custom.yml plugins/myPlugin/config/foo_schema.yml myPlugin_foo_schema.custom.yml カスタムのスキーマはアプリケーションとプラグインの
config/ディレクトリを探すので、プラグインは別のプラグインのスキーマを上書きをして、スキーマ単位で複数のカスタマイズが可能です。symfonyはそれぞれのテーブルの
phpNameに基づいて2つのスキーマをマージします。マージ処理によってテーブル、カラム、カラムの属性の追加もしくは修正できます。例えば、次の一覧はカスタムスキーマがカラムをプラグインのスキーマで定義されたテーブルに追加する方法を示しています。# plugins/myPlugin/config/schema.ymlの中の、オリジナルのスキーマ propel: article: _attributes: { phpName: Article } title: varchar(50) user_id: { type: integer } created_at: # myPlugin_schema.custom.ymlの中の、カスタムスキーマ propel: article: _attributes: { phpName: Article, package: foo.bar.lib.model } stripped_title: varchar(50) # スキーマの結果、内部でマージされモデルとSQLの生成用に内部で使われる propel: article: _attributes: { phpName: Article, package: foo.bar.lib.model } title: varchar(50) user_id: { type: integer } created_at: stripped_title: varchar(50)マージ処理はテーブルの
phpNameをキーとして使うので、スキーマの中で同じphpNameを保つのであれば、データベース内のプラグインテーブルの名前を変更できます。
plugin:installタスクではPEARパッケージ形式のプラグインのみがインストールされます。このようなプラグインは公式サイトの専用ページ、PEARチャネル経由もしくはダウンロードできる通常のファイルとして配布されていることを覚えておいて下さい。プラグインを編集したい場合は、単純なアーカイブよりもPEARパッケージとして公開した方がベターでしょう。加えて、プラグインをPEARパッケージにすればアップグレード作業が簡単になり、依存関係の宣言が可能で、自動的にアセットをweb/ディレクトリにデプロイできます。
新しい機能を開発し、プラグインとしてパッケージにすることを考えてみましょう。最初の段階はファイルを論理的に編成して、symfonyのロードメカニズムが必要なときにこれらのファイルを見つけることができるようにしましょう。この目的のために、リスト17-19で示されているディレクトリ構造に従う必要があります。リスト17-23はsfSamplePluginプラグインのためのファイル構造の例を示しています。
リスト17-23 - プラグインとしてパッケージにするファイルの一覧の例
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
編集に関して、プラグインのディレクトリの位置(リスト17-23のsfSamplePlugin/)は重要ではありません。これはディスク上の任意の場所に設置できます。
既存のプラグインを練習問題として考え、初めてプラグインを作る際には、これらの名前の規約とファイルの構造を再現してみて下さい。
プラグイン編集の次の段階はプラグインディレクトリのrootでpackage.xmlファイルを追加することです。package.xmlはPEARの構文に従います。リスト17-24の典型的なsymfonyプラグインのpackage.xmlをご覧下さい。
リスト17-24 - symfonyプラグイン用のpackage.xml
<?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>fabien.potencier@symfony-project.com</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.3.0</max>
<exclude>1.3.0</exclude>
</package>
</required>
</dependencies>
<phprelease />
<changelog />
</package>
ここで注目すべき部分は<contents>タグと<dependencies>タグで、次に説明します。残りのタグに関しては、symfony固有のものではありませんので、package.xmlフォーマットに関する詳細な内容はPEARオンラインマニュアル(http://pear.php.net/manual/ja/) を参照して下さい。
<contents>タグはプラグインのファイル構造を記述しなければならない場所です。このタグはコピーするファイルとその場所をPEARに伝えます。<dir>タグと<file>タグでファイル構造を記述して下さい。すべての<file>タグはrole="data"属性を持たなければなりません。リスト17-24の<contents>タグの部分はリスト17-23の正しいディレクトリ構造を記載しています。
<dir>タグの使用は義務ではありません。<file>タグ内で相対パスをnameの値として利用できるからです。package.xmlファイルを読みやすくするためにお勧めです。
任意のバージョンのPHP、PEAR、symfony、PEARパッケージ、もしくは他のプラグインの一式で動くようにプラグインは設計されています。<dependencies>タグでこれらの依存関係を宣言すれば必要なパッケージが既にインストールされていることを確認してそうでなければ例外を起動するようPEARに伝えることになります。
最小要件として、少なくとも開発環境に対応したPHP、PEARとsymfonyへの依存関係を常に宣言します。何を追加すればよいのかわからなければ、PHP 5.1、PEAR 1.4とsymfony 1.0の要件を追加して下さい。
それぞれのプラグインに対してsymfonyの最大のバージョン番号を追加することも推奨されます。これによって上位バージョンのsymfonyでプラグインを使うときにエラーメッセージが表示され、プラグインを再リリースする前にこのバージョンでプラグインが正しく動作するのかを確認することをプラグインの作者に義務づけます。無言でプラグインの動作が失敗するよりも警告を発してダウンロードとアップグレードする方がベターです。
プラグインを依存関係のあるものとして指定すれば、ユーザはプラグインとすべての依存関係を1つのコマンドでインストールできるようになります:
> php symfony plugin:install --install-deps sfSamplePlugin
PEARコンポーネントはパッケージの.tgzアーカイブを作るコマンド(pear package)を持ちます。リスト17-25では、package.xmlを含むディレクトリでこのコマンドを呼び出しています。
リスト17-25 - プラグインをPEARパッケージにする
> cd sfSamplePlugin
> pear package
Package sfSamplePlugin-1.0.0.tgz done
一旦プラグインのパッケージがビルドされたら、リスト17-26で示されるように、あなたの環境にこれをインストールして動作を確認して下さい。
リスト17-26 - プラグインをインストールする
> cp sfSamplePlugin-1.0.0.tgz /home/production/myproject/
> cd /home/production/myproject/
> php symfony plugin:install sfSamplePlugin-1.0.0.tgz
<contents>タグにある説明に従って、パッケージにされたファイルは最終的にプロジェクトの異なるディレクトリに設置されます。リスト17-27はインストールの後でsfSamplePluginのファイルが設置される場所を示しています。
リスト17-27 - プラグインファイルはplugin/ディレクトリとweb/ディレクトリにインストールされる
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/ ## システム次第で、コピーもしくはシンボリックリンク
css/
sfSampleStyle.css
images/
sfSampleImage.png
このプラグインの挙動をアプリケーションでテストして下さい。きちんと動くのであれば、プラグインを複数のプロジェクトをまたがって配布するもしくはsymfonyコミュニティに寄付する準備ができています。
symfonyのプラグインは以下の手順に従ってsymfony-project.orgのウェブサイトで配布されるときに最も幅広い利用者を得ます。独自プラグインを次のような方法で配布できます(訳注:最新の配布方法はこちらを参照):
READMEファイルにプラグインのインストール方法と使い方が、LICENSEファイルにはライセンスの詳細が記述されていることを確認する。READMEはMarkdownの構文 (http://daringfireball.net/projects/markdown/syntax) で記述する。pear packageコマンドを呼び出してプラグイン用のPEARパッケージを作りテストする。PEARパッケージの名前はsfSamplePlugin-1.0.0.tgz (1.0.0はプラグインのバージョン)でなければならない。sfSamplePlugin-1.0.0.tgz)。この手続きを行えば、ユーザはプロジェクトのディレクトリで次のコマンドを入力するだけでプラグインをインストールできるようになります:
> php symfony plugin:install sfSamplePlugin
plugin/ディレクトリをきれいに保つために、すべてのプラグインの名前がcamelCaseでありPluginの接尾辞で終わることを確認して下さい(例えば、shoppingCartPlugin、feedPlugin)。プラグインに名前を付ける前に、同じ名前のプラグインが存在しないことを確認して下さい。
Propelに依存するプラグインの名前はPropelを含みます。例えば、Propelのデータアクセスオブジェクトを利用する認証プラグインはsfPropelAuthという名前になります。
プラグインには使用条件と選んだライセンスを説明するLICENSEファイルを常に含めなければなりません。バージョンの履歴、プラグインの目的、効果、インストールと設定の手引きなどを含めることも推奨されます。
symfonyのクラスはアプリケーションレベルで修正できる機能を提供するsfMixerフックを含みます。ミックスイン(mixin)のメカニズムはPHPの制約が禁止している実行時のクラスの多重継承とオーバーライドを可能にします。ですのでそのためにコアクラスを修正しなければならないとしても、またファクトリ(factory)の設定がそこに存在するとしてもsymfonyの機能を簡単に拡張できます。
既に多くの拡張機能(エクステンション)が存在し、プラグインとしてパッケージが作成されています。symfonyのコマンドラインによってインストール、アップグレード、アンインストールするのが簡単です。プラグインをPEARパッケージを作成するのと同じぐらい簡単で、複数のアプリケーションをまたがって再利用できます。
symfony公式サイトのwikiには多くのプラグインが含まれ、あなた自身のプラグインも追加できます。これであなたは方法を理解したので、私達symfonyの開発者はあなたが多くの便利な拡張機能でsymfonyコアを強化して下さることを望んでおります!
下翻訳からコピー完了 (2009-02-11 10:38:14 brtRiver)
Documentation
| Listing 17-9 - Extending ... | |
| brtRiver(2009-02-11 10:48:19) | |
| - prod: log ... | |
| brtRiver(2009-02-11 10:47:21) | |
| The symfony wiki contains ... | |
| brtRiver(2009-02-11 10:37:41) | |
| Many such extensions alre ... | |
| brtRiver(2009-02-11 10:37:23) | |
| The symfony classes conta ... | |
| brtRiver(2009-02-11 10:36:43) | |
| Summary ------- ... | |
| brtRiver(2009-02-11 10:36:26) | |
| Plug-ins should always in ... | |
| brtRiver(2009-02-11 10:36:12) | |
| >**NOTE** >Plug-ins ... | |
| brtRiver(2009-02-11 10:35:56) | |
| To keep the `plugins/` di ... | |
| brtRiver(2009-02-11 10:35:40) | |
| #### Naming Conventions ... | |
| brtRiver(2009-02-11 10:35:23) | |
| > php symfony plug ... | |
| brtRiver(2009-02-11 10:35:08) | |
| If you follow this proced ... | |
| brtRiver(2009-02-11 10:34:54) | |
| 1. Make sure the `READM ... | |
| brtRiver(2009-02-11 10:34:40) | |
| A symfony plug-in gets th ... | |
| brtRiver(2009-02-11 10:34:17) | |
| #### Hosting Your Plug-In ... | |
| brtRiver(2009-02-11 10:33:53) | |
| Test the way the plug-in ... | |
| brtRiver(2009-02-11 10:33:30) | |
| plugins/ sfSamp ... | |
| brtRiver(2009-02-11 10:33:13) | |
| Listing 17-27 - The Plug- ... | |
| brtRiver(2009-02-11 10:32:51) | |
| According to their descri ... | |
| brtRiver(2009-02-11 10:32:31) | |
| > cp sfSamplePlugi ... | |
| brtRiver(2009-02-11 10:32:17) |