Skip to content
Caution: You are browsing the legacy symfony 1.x part of this website.

20日目: プラグイン

Symfony version
Language
ORM

昨日は、symfonyアプリケーションの国際化とローカライゼーションのしかたを学びました。 再度繰り返しますが、ICU標準とたくさんのヘルパーのおかげで、symfonyではこの作業は本当に楽です。

今日は、プラグインは何ができ、プラグインに何をまとめることができるのか、何のために使うことができるのかについて説明します。

プラグイン

symfonyのプラグイン

symfonyプラグインはプロジェクトファイルのサブセットのパッケージを作成して配布する方法を提供します。 プロジェクトのように、プラグインはクラス、ヘルパー、設定、タスク、モジュール、スキーマとアセットさえも格納できます。

プライベートプラグイン

プラグインの最初の使い方は複数のアプリケーションもしくは異なるプロジェクトの間でコードを楽に共有できるようにすることです。 symfonyアプリケーションはモデルのみを共有することを思い思い出してください。 プラグインはさらに複数のアプリケーションの間でコンポーネントを共有する方法を提供します。

異なるプロジェクトもしくは同じモジュールの同じスキーマを再利用する場合、これらをプラグインに移動させます。 プラグインは単なるディレクトリなので、SVNリポジトリを作りsvn:externalsを指定するもしくは1つのプロジェクトから他のプロジェクトにファイルをコピーすることでプラグインを移動させることができます。

私たちはこれらを"プライベート(非公開の)プラグイン"と呼んでいます。 これらの用途は1人の開発者もしくは企業に限定されているからです。 これらは公に利用できません。

tip

プライベートプラグインからパッケージを作ることもできます。 独自のsymfonyプラグインチャンネルを作成し、plugin:installタスクをとおしてこれらをインストールします。

公開プラグイン

公開プラグインはコミュニティでダウンロードとインストールができます。 このチュートリアルでは、ひとくみの公開プラグイン: sfDoctrineGuardPluginsfFormExtraPluginを使いました。

これらはプライベートプラグインとまったく同じです。 唯一の違いは誰でもプロジェクトにインストールできることです。 後で公開プラグインを公開してsymfonyの公式サイトでホストする方法を見ることになります。

コードを編成する異なる方法

プラグインを考え出す方法とそれらの使い方はいろいろあります。 再利用と共有を忘れましょう。プラグインはあなたのコードを編成するための異なる方法として利用できます。 レイヤーごとにファイルを編成する代わりに: lib/model/ディレクトリのすべてのモデル、templates/ディレクトリのテンプレートなど; ファイルは機能ごとにまとめられます: すべてのjobファイルのセット(モデル、モジュール、テンプレート)、すべてのCMSファイルのセットなど。

プラグインのファイル構造

プラグインはファイルの性質に応じて、あらかじめ定義された構造にわかれるファイルを持つ単なるディレクトリ構造です。 今日は、Jobeet用に書いてきた大部分のコードをsfJobeetPluginに移動させます。 使用する基本的なレイヤーは次の通りです:

sfJobeetPlugin/
  config/
    sfJobeetPluginConfiguration.class.php // プラグインの初期化
    routing.yml                           // ルーティング
    doctrine/
      schema.yml                          // データベーススキーマ
  lib/
    Jobeet.class.php                      // クラス
    helper/                               // ヘルパー
    filter/                               // フィルタークラス
    form/                                 // フォームクラス
    model/                                // モデルクラス
    task/                                 // タスク
  modules/
    job/                                  // モジュール
      actions/
      config/
      templates/
  web/                                    // JS、CSSと画像のようなアセット

Jobeetプラグイン

プラグインのブートストラップはシンプルでplugins/の下で新しいディレクトリを作ります。 Jobeetに関して、sfJobeetPluginディレクトリを作りましょう:

$ mkdir plugins/sfJobeetPlugin

note

すべてのプラグインの名前はPluginで終わらなければなりません。 義務ではありませんが、プレフィックスとしてsfをつけるのはよい習慣です。

Model

最初に、config/doctrine/schema.ymlファイルをplugins/sfJobeetPlugin/config/に移動させます:

$ mkdir plugins/sfJobeetPlugin/config/
$ mkdir plugins/sfJobeetPlugin/config/doctrine
$ mv config/doctrine/schema.yml plugins/sfJobeetPlugin/config/doctrine/schema.yml

note

すべてのコマンドはUnix系環境のものです。 Windowsを利用しているのであれば、Explorerでファイルをドラッグ&ドロップできます。 コードを管理するのにSubversion、もしくは他のツールを使う場合、 これらが提供する組み込みのツールを使います(ファイルを移動させるsvn mvなど)。

モデル、フォーム、フィルターファイルをplugins/sfJobeetPlugin/lib/に移動させます:

$ mkdir plugins/sfJobeetPlugin/lib/
$ mv lib/model/ plugins/sfJobeetPlugin/lib/
$ mv lib/form/ plugins/sfJobeetPlugin/lib/
$ mv lib/filter/ plugins/sfJobeetPlugin/lib/

モデル、フォームとフィルターを移動させた後でクラスを抽象クラスにし、リネームしてプレフィックスとしてPluginをつけなければなりません。

tip

プレフィックスのPluginをつけるのは自動生成クラスのみですべてのクラスではありません。 たとえば手で書いたクラスにはプレフィックスをつけないでください。 プレフィックスが必要なのは自動生成されたクラスのみです。

JobeetAffiliateJobeetAffiliateTableクラスを移動させる例は次の通りです。

$ mv plugins/sfJobeetPlugin/lib/model/doctrine/JobeetAffiliate.class.php plugins/sfJobeetPlugin/lib/model/doctrine/PluginJobeetAffiliate.class.php

コードは次のように更新されます:

abstract class PluginJobeetAffiliate extends BaseJobeetAffiliate
{
  public function preValidate($event)
  {
    $object = $event->getInvoker();
 
    if (!$object->getToken())
    {
      $object->setToken(sha1($object->getEmail().rand(11111, 99999)));
    }
  }
 
  // ...
}

JobeetAffiliateTableクラスを移動させましょう:

$ mv plugins/sfJobeetPlugin/lib/model/doctrine/JobeetAffiliateTable.class.php plugins/sfJobeetPlugin/lib/model/doctrine/PluginJobeetAffiliateTable.class.php

クラスの定義は次のようになります:

abstract class PluginJobeetAffiliateTable extends Doctrine_Table
{
  // ...
}

フォームとフィルタークラスにも同じことを行います。 Pluginをプレフィックスとしてつけるためにこれらをリネームします。

formfiltermodelディレクトリに対してplugins/sfJobeetPlugin/lib/*/doctrine/baseディレクトリを必ず削除してください:

$ rm -rf plugins/sfJobeetPlugin/lib/form/doctrine/base
$ rm -rf plugins/sfJobeetPlugin/lib/filter/doctrine/base
$ rm -rf plugins/sfJobeetPlugin/lib/model/doctrine/base

フォーム、フィルターとモデルクラスを移動させ、リネームして一部を削除したのですべてのクラスをリビルドするタスクを実行します:

$ php symfony doctrine:build-model
$ php symfony doctrine:build-forms
$ php symfony doctrine:build-filters

いくつかの新しく作成されたディレクトリがlib/model/doctrine/sfJobeetPlugin/に格納されるスキーマから作成されたモデルを保有こしていることがわかります。

このディレクトリはスキーマから生成されたトップレベルのモデルと基底クラスを含みます。 たとえばJobeetJobモデルは次のようなクラス構造を持ちます:

  • JobeetJob (PluginJobeetJobを継承) lib/model/doctrine/sfJobeetPlugin/JobeetJob.class.php: すべてのプロジェクトの機能が設定できるトップレベルのクラスです。 ここは機能を追加したりプラグインモデルに付属する機能をオーバーライドできる場所です。

  • PluginJobeetJob (BaseJobeetJobを継承) plugins/sfJobeetPlugin/lib/model/doctrine/PluginJobeetJob.class.php: このクラスはプラグイン固有の機能すべてを格納します。 JobeetJobクラスを修正することでこのクラスと基底クラスをオーバーライドできます。

  • BaseJobeetJob (sfDoctrineRecordを継承) lib/model/doctrine/sfJobeetPlugin/base/BaseJobeetJob.class.php: doctrine:build-modelを実行するたびにYAMLスキーマファイルから自動生成される基底クラス。

  • JobeetJobTable (PluginJobeetJobTableを継承) lib/model/doctrine/sfJobeetPlugin/JobeetJobTable.class.php: Doctrine::getTable('JobeetJob')を呼び出す際に返されるDoctrine_Tableのインスタンスであることを除いてJobeetJobクラスと同じ

  • PluginJobeetJobTable (Doctrine_Tableを継承) lib/model/doctrine/sfJobeetPlugin/JobeetJobTable.class.php: このクラスはDoctrine::getTable('JobeetJob')を呼び出す際に返されるDoctrine_Tableのインスタンス用のプラグイン固有の機能をすべて含みます

この生成された構造によってトップレベルのJobeetJobクラスを編集することでプラグインのモデルをカスタマイズできます。 setTableDefinition()setUp()メソッドをオーバーライドすることでスキーマとカラムをカスタマイズし、リレーションを追加できます。

note

フォームクラスを移動させるとき、かならずconfigure()メソッドをsetup()メソッドに変更しparent::setup()を呼び出してください。 下記のコードは例です。

abstract class PluginJobeetAffiliateForm extends BaseJobeetAffiliateForm
{
  public function setup()
  {
    parent::setup();
  }
 
  // ...
}

すべてのDoctrineフォームが基底クラスを持たないことを確認する必要があります。 これらのファイルはプロジェクトに対してグローバルでありdoctrine:build-formsdoctrine:build-filtersで再生成されます。

プラグインからファイルを削除します:

$ rm plugins/sfJobeetPlugin/lib/form/doctrine/BaseFormDoctrine.class.php
$ rm plugins/sfJobeetPlugin/lib/filter/doctrine/BaseFormFilterDoctrine.class.php

note

symfony 1.2.0もしくは1.2.1を利用する場合、フィルターの基底クラスはplugins/sfJobeetPlugin/lib/filter/base/ディレクトリにあります。

Jobeet.class.phpファイルをプラグインに移動させることもできます:

$ mv lib/Jobeet.class.php plugins/sfJobeetPlugin/lib/

ファイルを移動させたので、キャッシュをクリアします:

$ php symfony cc

tip

APCのようなPHPアクセラレーターを利用している場合はこの時点で動作がおかしくなるので、Apacheを再起動させます。

すべてのモデルファイルはプラグインに移動させたので、すべてがきちんと動作するのか確認するためにテストを実行します:

$ php symfony test:all

ControllerとView

次のロジックのステップはモジュールをプラグインに移動させることです。 モジュールの名前の衝突を避けるために、モジュールの名前にプレフィックスとしてプラグインの名前をつけるのは常によい習慣です:

$ mkdir plugins/sfJobeetPlugin/modules/
$ mv apps/frontend/modules/affiliate plugins/sfJobeetPlugin/modules/sfJobeetAffiliate
$ mv apps/frontend/modules/api plugins/sfJobeetPlugin/modules/sfJobeetApi
$ mv apps/frontend/modules/category plugins/sfJobeetPlugin/modules/sfJobeetCategory
$ mv apps/frontend/modules/job plugins/sfJobeetPlugin/modules/sfJobeetJob
$ mv apps/frontend/modules/language plugins/sfJobeetPlugin/modules/sfJobeetLanguage

それぞれのモジュールに対して、すべてのactions.class.phpcomponents.class.phpファイルのクラスの名前を変更することも必要です(たとえば、affiliateActionsクラスはsfJobeetAffiliateActionsにリネームする必要があります)。

次のテンプレートのinclude_partial()include_component()呼び出しも変更しなければなりません:

  • sfJobeetAffiliate/templates/_form.php (affiliatesfJobeetAffiliateに変更する)
  • sfJobeetCategory/templates/showSuccess.atom.php
  • sfJobeetCategory/templates/showSuccess.php
  • sfJobeetJob/templates/indexSuccess.atom.php
  • sfJobeetJob/templates/indexSuccess.php
  • sfJobeetJob/templates/searchSuccess.php
  • sfJobeetJob/templates/showSuccess.php
  • apps/frontend/templates/layout.php

searchdeleteアクションを更新します:

// plugins/sfJobeetPlugin/modules/sfJobeetJob/actions/actions.class.php
class sfJobeetJobActions extends sfActions
{
  public function executeSearch(sfWebRequest $request)
  {
    if (!$query = $request->getParameter('query'))
    {
      return $this->forward('sfJobeetJob', 'index');
    }
 
    $this->jobs = Doctrine::getTable('JobeetJob') ->getForLuceneQuery($query);
 
    if ($request->isXmlHttpRequest())
    {
      if ('*' == $query || !$this->jobs)
      {
        return $this->renderText('No results.');
      }
      else
      {
        return $this->renderPartial('sfJobeetJob/list', array('jobs' => $this->jobs));
      }
    }
  }
 
  public function executeDelete(sfWebRequest $request)
  {
    $request->checkCSRFProtection();
 
    $jobeet_job = $this->getRoute()->getObject();
    $jobeet_job->delete();
 
    $this->redirect('sfJobeetJob/index');
  }
 
  // ...
}

最後に、これらの修正が考慮されるようにrouting.ymlファイルを修正します:

# apps/frontend/config/routing.yml
affiliate:
  class:   sfDoctrineRouteCollection
  options:
    model:          JobeetAffiliate
    actions:        [new, create]
    object_actions: { wait: GET }
    prefix_path:    /:sf_culture/affiliate
    module:         sfJobeetAffiliate
  requirements:
    sf_culture: (?:fr|en)
 
api_jobs:
  url:     /api/:token/jobs.:sf_format
  class:   sfDoctrineRoute
  param:   { module: sfJobeetApi, action: list }
  options: { model: JobeetJob, type: list, method: getForToken }
  requirements:
    sf_format: (?:xml|json|yaml)
 
category:
  url:     /:sf_culture/category/:slug.:sf_format
  class:   sfDoctrineRoute
  param:   { module: sfJobeetCategory, action: show, sf_format: html }
  options: { model: JobeetCategory, type: object, method: doSelectForSlug }
  requirements:
    sf_format: (?:html|atom)
    sf_culture: (?:fr|en)
 
job_search:
  url:   /:sf_culture/search
  param: { module: sfJobeetJob, action: search }
  requirements:
    sf_culture: (?:fr|en)
 
job:
  class:   sfDoctrineRouteCollection
  options:
    model:          JobeetJob
    column:         token
    object_actions: { publish: PUT, extend: PUT }
    prefix_path:    /:sf_culture/job
    module:         sfJobeetJob
  requirements:
    token: \w+
    sf_culture: (?:fr|en)
 
job_show_user:
  url:     /:sf_culture/job/:company_slug/:location_slug/:id/:position_slug
  class:   sfDoctrineRoute
  options:
    model: JobeetJob
    type: object
    method_for_query: retrieveActiveJob
  param:   { module: sfJobeetJob, action: show }
  requirements:
    id:        \d+
    sf_method: GET
    sf_culture: (?:fr|en)
 
change_language:
  url:   /change_language
  param: { module: sfJobeetLanguage, action: changeLanguage }
 
localized_homepage:
  url:   /:sf_culture/
  param: { module: sfJobeetJob, action: index }
  requirements:
    sf_culture: (?:fr|en)
 
homepage:
  url:   /
  param: { module: sfJobeetJob, action: index }

JobeetのWebサイトを見ようとすると、モジュールが有効にされていないことを知らせる例外が表示されます。 プラグインはプロジェクトのすべてのアプリケーションの間で共有されるので、アプリケーションに必要なモジュールをsettings.yml設定ファイルで有効にする必要があります:

# apps/frontend/config/settings.yml
all:
  .settings:
    enabled_modules:
      - default
      - sfJobeetAffiliate
      - sfJobeetApi
      - sfJobeetCategory
      - sfJobeetJob
      - sfJobeetLanguage

マイグレーションの最後のステップはモジュールの名前に対してテストする機能テストを修正することです。

sidebar

プラグインの有効化

プロジェクトで利用できるプラグインは、ProjectConfigurationクラスで有効にしなければなりません。

このステップはデフォルトのコンフィギュレーションでは必要ありません。 プラグインの一部を除いてすべてのプラグインを有効にする"ブラックリスト"の方法がsymfonyにあるからです:

// config/ProjectConfiguration.class.php
public function setup()
{
  $this->enableAllPluginsExcept(array('sfDoctrinePlugin', 'sfCompat10Plugin'));
}

この方法は古いsymfonyのバージョンとの後方互換性を維持するために必要ですが、"ホワイトリスト"方式のほうがすぐれているので代わりにenablePlugins()メソッドを使います:

// config/ProjectConfiguration.class.php
public function setup()
{
  $this->enablePlugins(array('sfDoctrinePlugin', 'sfDoctrineGuardPlugin', 'sfFormExtraPlugin', 'sfJobeetPlugin'));
}

タスク

タスクはプラグインに簡単に移動させることができます:

$ mv lib/task plugins/sfJobeetPlugin/lib/

国際化ファイル

プラグインはXLIFFファイルも格納できます:

$ mv apps/frontend/i18n plugins/sfJobeetPlugin/

ルーティング

プラグインはルーティングルールも格納できます:

$ mv apps/frontend/config/routing.yml plugins/sfJobeetPlugin/config/

アセット

少し直感に反していますが、プラグインは、画像、スタイルシートとJavaScriptのようなWebアセットも格納できます。 Jobeetプラグインを配布したくないので、本当に意味がありませんが、plugins/sfJobeetPlugin/web/ディレクトリを作ることで実現できます。

プラグインのアセットはブラウザーから閲覧可能なプロジェクトのweb/ディレクトリからアクセスできなければなりません。 plugin:publish-assetsはUnixシステムではシンボリックリンクの作成、Windowsプラットフォームではファイルをコピーすることでこれに対処します:

$ php symfony plugin:publish-assets

ユーザー

求人履歴を扱うmyUserクラスメソッドを移動させることは少し手間がかかります。 JobeetUserクラスを作成しmyUserを継承させることもできます。 しかし、とりわけいくつかのプラグインが新しいメソッドをクラスに追加したい場合、ベターな方法があります。

リスニングできるライフサイクルの間にsymfonyのコアオブジェクトはイベントを通知します。 我々の事例では、未定義のメソッドがsfUserオブジェクトに呼び出されるときに起きるuser.method_not_foundイベントをリスニングする必要があります。

symfonyが初期化されるときにプラグインのコンフィギュレーションクラスがある場合、すべてのプラグインも初期化されます:

// plugins/sfJobeetPlugin/config/sfJobeetPluginConfiguration.class.php
class sfJobeetPluginConfiguration extends sfPluginConfiguration
{
  public function initialize()
  {
    $this->dispatcher->connect('user.method_not_found', array('JobeetUser', 'methodNotFound'));
  }
}

イベントの通知はイベントディスパッチャーオブジェクトであるsfEventDispatcherによって管理されます。 connect()メソッドを呼び出せばリスナーが登録されます。 connect()メソッドはイベントの名前をPHP callableに結びつけます。

note

PHP callableはPHP変数でcall_user_func()関数によって使われis_callable()関数に渡されるときにtrueを返します。 文字列は関数を表し、配列はオブジェクトメソッドもしくはクラスメソッドを表します。

上記のコードによって、myUserオブジェクトはメソッドを見つけられないときはJobeetUserクラスのmethodNotFound()スタティックメソッドを呼び出します。 見つからないメソッドを処理するもしくは処理しないのはmethodNotFound()メソッド次第です。

myUserクラスからすべてのメソッドを削除しJobeetUserクラスを作成します:

// apps/frontend/lib/myUser.class.php
class myUser extends sfBasicSecurityUser
{
}
 
// plugins/sfJobeetPlugin/lib/JobeetUser.class.php
class JobeetUser
{
  static public function methodNotFound(sfEvent $event)
  {
    if (method_exists('JobeetUser', $event['method']))
    {
      $event->setReturnValue(call_user_func_array(
        array('JobeetUser', $event['method']),
        array_merge(array($event->getSubject()), $event['arguments'])
      ));
 
      return true;
    }
  }
 
  static public function isFirstRequest(sfUser $user, $boolean = null)
  {
    if (is_null($boolean))
    {
      return $user->getAttribute('first_request', true);
    }
    else
    {
      $user->setAttribute('first_request', $boolean);
    }
  }
 
  static public function addJobToHistory(sfUser $user, JobeetJob $job)
  {
    $ids = $user->getAttribute('job_history', array());
 
    if (!in_array($job->getId(), $ids))
    {
      array_unshift($ids, $job->getId());
      $user->setAttribute('job_history', array_slice($ids, 0, 3));
    }
  }
 
  static public function getJobHistory(sfUser $user)
  {
    $ids = $user->getAttribute('job_history', array());
 
    if (!empty($ids))
    {
      return Doctrine::getTable('JobeetJob')
        ->createQuery('a')
        ->whereIn('a.id', $ids)
        ->execute();
    } else {
      return array();
    }
  }
 
  static public function resetJobHistory(sfUser $user)
  {
    $user->getAttributeHolder()->remove('job_history');
  }
}

ディスパッチャがmethodNotFound()メソッドを呼び出すとき、sfEventオブジェクトを渡します。

JobeetUserクラスにメソッドが存在する場合、このメソッドが呼び出され、その後で戻り値は通知元オブジェクトに返されます。 そうではない場合、symfonyは次に登録されたリスナーを試すもしくは例外を投げます。

getSubject()メソッドはイベントの通知元オブジェクトを返します。 この場合現在のmyUserオブジェクトです。

いつものように新しいクラスを作るとき、ブラウザーで見るもしくはテストを実行する前にキャッシュをクリアすることをお忘れなく:

$ php symfony cc

デフォルト構造 vs プラグインアーキテクチャ

プラグインのアーキテクチャを利用することで異なる方法でコードを編成できます:

プラグインのアーキテクチャ

プラグインを使う

新しい機能の実装を始めるとき、もしくはWebの古典的な問題を解決しようとする場合、おそらく誰かが同じ問題をすでに解決していてsymfonyのプラグインとして解決方法をパッケージにしています。 symfonyの公開プラグインを探すには、symfony公式サイトのプラグインセクションに移動します。

プラグインはディレクトリに内蔵されるので、インストールする方法は複数あります:

  • plugin:installタスクを使う(プラグインの開発者がプラグインパッケージを作りsymfonyの公式サイトにアップロードする場合のみ機能する)
  • パッケージをダウンロードしてplugins/ディレクトリの下で手動で解凍する(開発者がパッケージをアップロードすることも必要)
  • plugins/でプラグイン用のsvn:externalsを作る(プラグインの作者がSuversionでプラグインをホストする場合のみ機能する)

後者の2つの方法は簡単ですが柔軟性に欠けます。 最初の方法でプロジェクトのsymfonyのバージョンに合わせて最新バージョンをインストールして、 最新の安定版に簡単にアップグレードする、そして簡単にプラグイン間の依存関係を管理できます。

プラグインを寄付する

プラグインのパッケージを作成する

プラグインパッケージを作成するには、必須のファイルをプラグインのディレクトリ構造に追加する必要があります。 最初に、プラグインのルートディレクトリでREADMEファイルを作りプラグインのインストール方法、これが何を提供するもの、しないものを記述します。 READMEファイルはMarkdownフォーマットでフォーマットしなければなりません。 このファイルはsymfony公式サイトでドキュメントのメインピースとして使われます。 symfony plugin dingusを利用してREADMEファイルをHTMLに変換することができます。

sidebar

プラグイン開発のタスク

プライベートかつ/もしくは公開プラグインをよく作ることに気がついたら、sfTaskExtraPluginのタスクのいくつかを利用することを考えてください。 このプラグインは、コアチームによって維持され、プラグインのライフサイクルの効率化を手助けするたくさんのタスクを含みます:

  • generate:plugin
  • plugin:package

LICENSEファイルを作ることも必要です。 ライセンスの選択は簡単な作業ではありませんが、symfonyのプラグインセクションはsymfonyのライセンス(MIT、BSD、LGPL、とPHP)と似たライセンスでリリースされたプラグインのみを表示します。 LICENSEファイルの内容はプラグインの公開ページのlicenseタブの下で表示されます。

最後のステップはプラグインディレクトリのルートでpackage.xmlファイルを作ることです。 このpackage.xmlファイルはPEARパッケージ構文に従います。

note

package.xmlの構文を学ぶベストな方法は既存のプラグインで使われるファイルをコピーすることです。

package.xmlファイルはいくつかの部分で構成されます。 このテンプレートの例は次のようなものです:

<!-- plugins/sfJobeetPlugin/package.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.4.1" 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>sfJobeetPlugin</name>
  <channel>plugins.symfony-project.org</channel>
  <summary>A job board plugin.</summary>
  <description>A job board plugin.</description>
  <lead>
    <name>Fabien POTENCIER</name>
    <user>fabpot</user>
    <email>fabien.potencier@symfony-project.com</email>
    <active>yes</active>
  </lead>
  <date>2008-12-20</date>
  <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.com/license">
    MIT license
  </license>
  <notes />
 
  <contents>
    <!-- CONTENT -->
  </contents>
 
  <dependencies>
   <!-- DEPENDENCIES -->
  </dependencies>
 
  <phprelease>
</phprelease>
 
<changelog>
  <!-- CHANGELOG -->
</changelog>
</package>

<contents>タグはパッケージに設置する必要のあるファイルを含みます:

<contents>
  <dir name="/">
    <file role="data" name="README" />
    <file role="data" name="LICENSE" />
 
    <dir name="config">
      <file role="data" name="config.php" />
      <file role="data" name="schema.yml" />
    </dir>
 
    <!-- ... -->
  </dir>
</contents>

<dependencies>タグはプラグインが持つすべての依存関係の参照: PHP、symfony、と他のプラグインをつけます。 この情報はプロジェクト環境に対してベストなプラグインのバージョンをインストールし存在するのであれば必要なプラグインの依存関係をインストールするためにplugin:installタスクによって使われます。

<dependencies>
  <required>
    <php>
      <min>5.0.0</min>
    </php>
    <pearinstaller>
      <min>1.4.1</min>
    </pearinstaller>
    <package>
      <name>symfony</name>
      <channel>pear.symfony-project.com</channel>
      <min>1.2.0</min>
      <max>1.3.0</max>
      <exclude>1.3.0</exclude>
    </package>
  </required>
</dependencies>

symfonyはバージョンによって微妙に異なるAPIを持つので、ここで行ったように、依存関係を常に宣言すべきです。 最大バージョンと最小バージョンを宣言することでplugin:installはsymfonyの必須バージョンがわかります。

別のプラグインとの依存関係を宣言することも可能です:

<package>
  <name>sfFooPlugin</name>
  <channel>plugins.symfony-project.org</channel>
  <min>1.0.0</min>
  <max>1.2.0</max>
  <exclude>1.2.0</exclude>
</package>

<changelog>タグはオプションですがリリースの間の変更に関する有益な情報が得られます。 この情報は"Changelog"タブとプラグインのフィードで見ることができます。

<changelog>
  <release>
    <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.com/license">
      MIT license
    </license>
    <date>2008-12-20</date>
    <license>MIT</license>
    <notes>
       * fabien: First release of the plugin
    </notes>
  </release>
</changelog>

symfony公式サイトでプラグインを公開する

便利なプラグインを開発してsymfonyのコミュニティと共有したい場合、まだなければsymfony公式サイトのアカウントを作成し新しいプラグインを作成します。

あなたは自動的にプラグインの管理者になりインターフェイスの"admin"タブを見ることになります。 このタブにおいて、プラグインを管理してパッケージをアップロードするために必要なすべての機能が見つかります。

note

プラグインのFAQページにはプラグイン開発者のための有益な情報がたくさんあります。

また明日

プラグインの作成とコミュニティとの共有はsymfony公式サイトへのベストな貢献方法の1つです。 これはとても簡単なので、symfonyのプラグインリポジトリは便利で面白く、しかしおかしなプラグインで満たされています。

This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.