<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 15617 -->
<sect1 id="zend.loader.pluginloader">
    <title>プラグインのロード</title>

    <para>
        Zend Framework の多くのコンポーネントはプラグイン方式を採用しており、
        クラスのプレフィックスとクラスファイルへのパスを指定して動的に機能を読み込むことができます。
        このファイルは <code>include_path</code> にある必要がなく、
        また標準の命名規約に従っている必要もありません。
        <classname>Zend_Loader_PluginLoader</classname> は、この処理のための共通機能を提供します。
    </para>

    <para>
        <code>PluginLoader</code> の基本的な使用法は Zend Framework
        の命名規約に従った形式です。各クラスを個別のファイルに分けて、
        アンダースコアをディレクトリ区切り文字としてパスを解決します。
        オプションのクラスプレフィックスを指定して、
        特定のプラグインクラスをロードする際の先頭にそれを付加することができます。
        また、パスの検索は LIFO (後入れ先出し) 方式で行います。
        LIFO 方式の検索とクラスプレフィックスを用いることで、
        プラグインに独自の名前空間を指定することができます。
        そして、事前に登録されているプラグインを上書きできるようになります。
    </para>

    <sect2 id="zend.loader.pluginloader.usage">
        <title>基本的な使用例</title>

        <para>
            次のようなディレクトリ構造とクラスファイル群の構成を考えてみましょう。
            トップディレクトリと library ディレクトリは
            include_path に含まれているものとします。
        </para>

        <programlisting language="txt"><![CDATA[
application/
    modules/
        foo/
            views/
                helpers/
                    FormLabel.php
                    FormSubmit.php
        bar/
            views/
                helpers/
                    FormSubmit.php
library/
    Zend/
        View/
            Helper/
                FormLabel.php
                FormSubmit.php
                FormText.php
]]></programlisting>

        <para>
            では、プラグインローダーを作成してビューヘルパー群の場所を指定しましょう。
        </para>

        <programlisting language="php"><![CDATA[
$loader = new Zend_Loader_PluginLoader();
$loader->addPrefixPath('Zend_View_Helper', 'Zend/View/Helper/')
       ->addPrefixPath('Foo_View_Helper',
                       'application/modules/foo/views/helpers')
       ->addPrefixPath('Bar_View_Helper',
                       'application/modules/bar/views/helpers');
]]></programlisting>

        <para>
            こうしておけば、クラス名のプレフィックスを除いた部分を指定するだけで
            ビューヘルパーをロードできるようになります。
        </para>

        <programlisting language="php"><![CDATA[
// 'FormText' ヘルパーをロードします
$formTextClass = $loader->load('FormText'); // 'Zend_View_Helper_FormText';

// 'FormLabel' ヘルパーをロードします
$formLabelClass = $loader->load('FormLabel'); // 'Foo_View_Helper_FormLabel'

// 'FormSubmit' ヘルパーをロードします
$formSubmitClass = $loader->load('FormSubmit'); // 'Bar_View_Helper_FormSubmit'
]]></programlisting>

        <para>
            クラスを読み込めたら、次はそのインスタンスを作成します。
        </para>

        <note>
            <para>
                時には、同じプレフィックスを複数のパスで使用することもあるかもしれません。
                <classname>Zend_Loader_PluginLoader</classname>
                は、実際には各プレフィックスに対応するパスを配列で管理しています。
                そして、最後に登録されたパスから順に検索を行います。
                これは、incubator のコンポーネントを使用する場合などに便利です。
            </para>
        </note>

        <note>
            <title>インスタンス作成時のパスの定義</title>

            <para>
                プレフィックスとパス
                (あるいはプレフィックスと複数のパス) のペアの配列を、
                オプションのパラメータとしてコンストラクタに渡すことができます。
            </para>

            <programlisting language="php"><![CDATA[
$loader = new Zend_Loader_PluginLoader(array(
    'Zend_View_Helper' => 'Zend/View/Helper/',
    'Foo_View_Helper'  => 'application/modules/foo/views/helpers',
    'Bar_View_Helper'  => 'application/modules/bar/views/helpers'
));
]]></programlisting>
        </note>

        <para>
            <classname>Zend_Loader_PluginLoader</classname> には、
            複数のプラグインの間でオブジェクトを共有する機能もあります。
            その際にシングルトンインスタンスを作成する必要はありません。
            この機能は、静的レジストリを用いて実現しています。
            インスタンスを作成する際に、
            コンストラクタの 2 番目のパラメータでレジストリを指定します。
        </para>

        <programlisting language="php"><![CDATA[
// プラグインを静的レジストリ 'foobar' に保存します
$loader = new Zend_Loader_PluginLoader(array(), 'foobar');
]]></programlisting>

        <para>
            <code>PluginLoader</code> がインスタンス化したその他のコンポーネントで
            このレジストリ名を使用すると、すでに読み込まれているパスやプラグインを使用できます。
        </para>
    </sect2>

    <sect2 id="zend.loader.pluginloader.paths">
        <title>プラグインのパスの操作</title>

        <para>
            先ほどのセクションの例では、プラグインローダーでパスを追加する方法を示しました。
            では、すでに読み込まれているパスを調べたりそれを削除したりしたい場合は、
            いったいどうすればいいのでしょうか?
        </para>

        <itemizedlist>
            <listitem><para>
                    <code>getPaths($prefix = null)</code> は、
                    <code>$prefix</code> を省略した場合は
                    すべてのパスをプレフィックス / パスのペアで返します。
                    <code>$prefix</code> を指定した場合は、
                    そのプレフィックスに対応するパスのみを返します。
            </para></listitem>

            <listitem><para>
                    <code>clearPaths($prefix = null)</code> は、
                    デフォルトですべての登録済みパスをクリアします。
                    <code>$prefix</code> を指定した場合は、
                    そのプレフィックスに関連づけられたパスが登録されている場合にそれだけをクリアします。
            </para></listitem>

            <listitem><para>
                    <code>removePrefixPath($prefix, $path = null)</code> は、
                    指定したプレフィックスに関連づけられた特定のパスを削除します。
                    <code>$path</code> を省略した場合は、
                    そのプレフィックスのすべてのパスを削除します。
                    <code>$path</code> を指定した場合は、
                    そのパスが存在すればそのパスだけを削除します。
            </para></listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.loader.pluginloader.checks">
        <title>プラグインの確認とクラス名の取得</title>

        <para>
            時には、プラグインクラスがロードされているかどうかを調べてから何かを行いたいこともあるでしょう。
            <code>isLoaded()</code> は、プラグイン名を受け取ってその状態を返します。
        </para>

        <para>
            <code>PluginLoader</code> の使用法としてもうひとつよくあるのが、
            読み込まれているクラスの完全なクラス名を調べることです。
            <code>getClassName()</code> がこの機能を実現しています。
            一般に、これは <code>isLoaded()</code> と組み合わせて使用します。
        </para>

        <programlisting language="php"><![CDATA[
if ($loader->isLoaded('Adapter')) {
    $class   = $loader->getClassName('Adapter');
    $adapter = call_user_func(array($class, 'getInstance'));
}
]]></programlisting>
    </sect2>

    <sect2 id="zend.loader.pluginloader.performance">
        <title>プラグインのパフォーマンスの向上</title>

        <para>
            プラグインの読み込みは、非常に重い操作となりえます。
            まず各プレフィックスについてループ処理をする必要があり、
            各プレフィックス上のパスをすべて調べ、
            期待通りのクラスに対応するファイルを探すという操作になるからです。
            ファイルは存在するもののそこでクラスが定義されていないといった場合は
            PHP のエラースタックにエラーが追加されますが、
            これもまた負荷のかかる操作です。
            ここで問題となるのが「プラグイン機能の柔軟性を保ちつつ
            パフォーマンスも向上させるにはどうすればいいか？」ということです。
        </para>

        <para>
            <classname>Zend_Loader_PluginLoader</classname>
            では、このような場合のためのオプトイン機能として
            ファイルインクルードキャッシュを提供しています。
            これを有効にすると、インクルードに成功したすべてのファイルを含む
            単一のファイルを作成します。起動ファイルからこれをコールすることができます。
            この方式を使用すると、実運用サーバ上でのパフォーマンスが劇的に向上します。
        </para>

        <example id="zend.loader.pluginloader.performance.example">
            <title>PluginLoader クラスのファイルインクルードキャッシュの使用法</title>

            <para>
                クラスファイルのインクルードキャッシュを使用するには、
                次のコードを起動ファイルに追加します。
            </para>

            <programlisting language="php"><![CDATA[
$classFileIncCache = APPLICATION_PATH .  '/../data/pluginLoaderCache.php';
if (file_exists($classFileIncCache)) {
    include_once $classFileIncCache;
}
Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
]]></programlisting>

            <para>
                もちろん、パスやファイル名は必要に応じて変更することになります。
                このコードはできるだけ早い段階で実行されるようにしなければなりません。
                そうすることで、プラグイン機構をもつコンポーネントが確実にキャッシュを使用できるようにします。
            </para>

            <para>
                開発段階ではキャッシュを無効にしたいこともあるでしょう。
                その方法のひとつとしては、ある設定項目を切り替えることで
                プラグインローダーがキャッシュするかしないかを指定するというものがあります。
            </para>

            <programlisting language="php"><![CDATA[
$classFileIncCache = APPLICATION_PATH .  '/../data/pluginLoaderCache.php';
if (file_exists($classFileIncCache)) {
    include_once $classFileIncCache;
}
if ($config->enablePluginLoaderCache) {
    Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
}
]]></programlisting>

            <para>
                これを使えば、コードを直接変更しなくても設定ファイルだけの変更だけですませることができます。
            </para>
        </example>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
