悪用厳禁! ZF2のPluginClassLoader::addStaticMap

こんな場末の日記をご覧の方は、ZF2のプラグイン機構では、クラス定義の読み込みをPluginClassLoaderが、インスタンス化をPluginBrokerが行うということはご存知ですよね!え、ご存知ない?Wikipedia創設者ZFリードデペロッパーMatthew Weier O'Phinneyからのメッセージをお読みください。

Introducing the ZF2 Plugin Broker
http://weierophinney.net/matthew/archives/248-Introducing-the-ZF2-Plugin-Broker.html


で、LazyLoadingをする場合は、PlginBrokerの拡張PluginSpecBrokerというのが存在します。
(Controller\Actionでは、Loader\PluginSpecBrokerの拡張のController\Action\HelperBrokerを
ヘルパー読み込みの際に使用。View\PhpRendererでは、Loader\PluginBrokerの拡張のView\HelperBrokerを使用)


ということで、プラグイン機構を自前のライブラリで使う場合に以下のように使う場合に以下のような構造を考えるわけですが、

<?php
namespace Diggin\Service\ShortUrl;

class ShortUrl
{
    protected $shortUrlBroker;
   
    // host名=$plugin名としてpreg_replace_callbackに渡すなどする包括的なフィルタ用
    public function unshorten($plugin, $shortenedUrl)
    {       
        if ($shortener = $this->getShortUrlBroker()->load($plugin)) {
                
            if ($shortener instanceof \Zend_Service_ShortUrl_Shortener) {
                return $shortener->unshorten($shortenUrl);
            } else if ($shortener instanceof \Services_ShortUrl) {
            }

            return $shortenedUrl;
        }

        return $shortenedUrl;
    }   
        
    public function getShortUrlBroker() 
    {   
        if (!$this->shortUrlBroker instanceof ShortUrlBroker) {
            $this->shortUrlBroker = new ShortUrlBroker;
        }
        return $this->shortUrlBroker;
    }

}
<?php
namespace Diggin\Service\ShortUrl;
use Zend\Loader\PluginSpecBroker;

class ShortUrlBroker extends PluginSpecBroker
{
    protected $defaultClassLoader = 'Diggin\Service\ShortUrl\ShortUrlClassLoader';
}
<?php
namespace Diggin\Service\ShortUrl;
use Zend\Loader\PluginClassLoader;

class ShortUrlClassLoader extends PluginClassLoader
{
    protected $plugins = array(
        'tinyurl' => 'Zend_Service_ShortUrl_TinyUrlCom',
        'isgd' => 'Zend_Service_ShortUrl_IsGd',
        'metamark' => 'Zend_Service_ShortUrl_MetamarkNet',
    );
}


ここで、メンドくさがって、BrokerにvalidatePlugin()の個別処理を書かないと、以下のような呼び出しをしたときに

<?php
set_include_path(__DIR__.'/library/'. PATH_SEPARATOR .get_include_path());
require_once 'Zend/Loader/StandardAutoloader.php';
$loader = new Zend\Loader\StandardAutoloader;
//$loader->registerNamespace('Diggin');
$loader->setFallbackAutoloader(true);
$loader->register();

//bootstrapなどで
Zend\Loader\PluginClassLoader::addStaticMap(array('tinyurl' => 'Zend\Version'));

//bootstrapなででtypoした場合
Diggin\Service\ShortUrl\ShortUrlClassLoader::addStaticMap(array('tonyurl' => 'User\ShortUrl\Type'));

use Diggin\Service\ShortUrl\ShortUrl;
$shorturl = new ShortUrl;
var_dump($shorturl->getShortUrlBroker()->load('tinyurl')); //Zend\Version

という感じで処理がインスタンス生成後もヘルパーコールの場合処理がそのまま起動されちゃいます。なので、読み込み後インスタンスが適切か確認するvalidatePluginはちゃんと書かないとだめですね。

<?php
namespace Diggin\Service\ShortUrl;
use Zend\Loader\PluginSpecBroker;

class ShortUrlBroker extends PluginSpecBroker
{
    protected $defaultClassLoader = 'Diggin\Service\ShortUrl\ShortUrlClassLoader';

    protected function validatePlugin($plugin)
    {
        if (!$plugin instanceof \Zend_Service_ShortUrl_Shortener) {
            throw new Exception\RuntimeException('Serializer adapters must implement Zend\Serializer\Adapter');
        }
        return true;
    }
}

まあ、この自前コンポネーント設計どうなのよって話もありますが。