Location: PHPKode > projects > Stickleback > stickleback-1.10.0/stickleback/PluginResolver.php
<?php

/**
 * Reads a plugin on the filesystem and constructs descriptor objects
 * * @package    stickleback
 *
 * Copyright (c) 2007 Yahoo! Inc.  All rights reserved.
 * The copyrights embodied in the content of this file are licensed under the BSD
 * open source license
 *
 * @subpackage plugin
 * @author     Matt Zandstra <hide@address.com>
 * @version    CVS: $Id: PluginResolver.php,v 1.13 2008-10-24 19:27:02 zandstra Exp $
 */


/**
 * requires
 */
require_once("stickleback/PluginAutoload.php");
require_once("stickleback/Exception.php");
require_once("stickleback/Logger.php");
require_once("stickleback/PluginSet.php");
require_once("stickleback/FSPluginObserver.php");
require_once("stickleback/PluginXml.php");

/**
 * Reads a plugin on the filesystem and constructs descriptor objects
 *
 * @package    stickleback
 * @subpackage plugin
 * @author     Matt Zandstra <hide@address.com>
 * @version    CVS: $Id: PluginResolver.php,v 1.13 2008-10-24 19:27:02 zandstra Exp $
 */

class stickleback_PluginResolver {

    private $pluginDirs = array();
    private $xml_name;
    private $observers = array();

   /**
    * Constructor
    * @param    array   Directories to search for plugins
    */
    function __construct( array $pluginDirs, $xml_name='sbplugin.xml' ) {
        $this->pluginDirs = $pluginDirs;
        $this->xml_name = $xml_name;
    }

   /**
    * accepts a {@link stickleback_FSPluginObserver} object for later notification
    *
    * @param    array   Directories to search for plugins
    */
    function accept( stickleback_FSPluginObserver $observer ) {
        $this->observers[] = $observer;
    }

   /**
    * work through the directories in the plugin directories looking for valid plugins
    *
    * Observers are notified for each pluginset:
    * {@link stickleback_FSPluginObserver::notifyFoundPlugin()} will be called for
    * every found plugin.
    * {@link stickleback_FSPluginObserver::notifyNonPlugin()} will be called for
    * every found pluginset that turns out not to be a plugin
    *
    * The criteria for recognising a plugin is very basic. Just a folder with a file
    * of the same name (followed by .php). Proper parsing then takes place elsewhere
    * @param    array   Directories to search for plugins
    * @todo     support plugin.xml format 
    */
    function findPlugins() {
        $xml_handler = new stickleback_PluginXml();

        $visited = array();
        foreach ( $this->pluginDirs as $dir ) {
            $dir = self::resolvePath( $dir );
            if ( isset( $visited[$dir] ) ) {
                L::debug("already seen '$dir'");
                continue;
            }
            $visited[$dir]=1;
            $dirit = new DirectoryIterator($dir);
            foreach ($dirit as $pluginset) {
                if ( $pluginset->isDot() || !$pluginset->isDir() ) {
                    continue;
                }
                $dirpath = $dir.DIRECTORY_SEPARATOR.$pluginset;
                $sbpluginxml_path = $dirpath.DIRECTORY_SEPARATOR.$this->xml_name;
                $pluginset_obj = new stickleback_PluginSet( $pluginset->getFilename(), $dirpath );

                $descriptors = $xml_handler->readFile( $sbpluginxml_path );

                foreach ( $descriptors as $desc ) {
                    $this->checkDescriptorAgainstFileSystem( $dirpath, $desc );
                    // REFACTOR: let the registry handle this
                    $desc->setPluginSet( $pluginset_obj );
                    foreach ( $this->observers as $observer ) {
                        $observer->notifyFoundPlugin( $desc );
                    }
                }
            } 
        }
    }

    private function checkDescriptorAgainstFileSystem( $path, stickleback_PluginDescriptor $desc ) {
        // $path is guaranteed absolute 

        $plugin_name = $desc->getId();
        $class_name  = $desc->getType();

 //       check that the plug-in exists on the filesystem and the class exists after inclusion
 //       $classfilepath = $path.DIRECTORY_SEPARATOR."{$plugin_name}.php";
 //       $this->ensure( file_exists( $classfilepath ), "no such class file: '$classfilepath'" );
 //       require_once( $classfilepath );
        $rclass = new ReflectionClass( $class_name );
        $this->ensure( class_exists( $class_name ), "could not find class '$class_name' for plugin '$plugin_name'" );

        /*
        // make sure the class in question _is_ a stickleback_Plugin
        if ( ! $rclass->implementsInterface( 'stickleback_Plugin' ) ) {
            throw new stickleback_Exception("'$class_name' does not implement 'stickleback_Plugin'");
        }
        */

        // check that the plug-in implements the interfaces it promises to
        $honors = $desc->mustImplement();
        foreach ( $honors as $honor ) {
            list( $plugin, $interface ) = explode( ".", $honor );
            //if ( ! $rclass->implementsInterface( $interface ) ) {
            if ( ! interface_exists( $interface ) ) {
                throw new stickleback_Exception("extension point interface '$interface' must exist" );
            }
            if ( ! $rclass->implementsInterface( $interface ) ) {
                throw new stickleback_Exception("plug-in '$class_name' must implement interface: '$interface'" );
            }
        }
        $offers = $desc->getExtensionPointDescriptors();
        foreach ( $offers as $epoint ) {
            $interface_name = $epoint->getType(); 
            if ( ! interface_exists( $interface_name ) ) {
                throw new stickleback_Exception("plug-in '$class_name' offers extension point interface '$interface_name' which does not exist" );
            }    
        }
    }

    private function ensure( $bool, $msg ) {
        if ( ! $bool ) { throw new stickleback_Exception( $msg ); }
    }

   /**
    * static method that uses the include path to search for directories/files on the filesystem
    *
    * If the given entity exists, or if it cannot be found, the original string is returned.
    * Otherwise the matching path is returned
    *
    * @param    string  the file/directory to test for
    * @return   string  the resolved file/directory (or original string)
    */
    static function resolvePath( $dir ) {
        if ( @file_exists( $dir ) ) {
            return $dir;
        }
        $sep = DIRECTORY_SEPARATOR;
        if ( strpos( $dir, DIRECTORY_SEPARATOR ) === 0 ) {
            return $dir;
        }
        $pathArray = explode( PATH_SEPARATOR, get_include_path() );
        foreach ( $pathArray as $path ) {
            $fullpath = $path.$sep.$dir; 
            if ( @file_exists( $fullpath ) ) {
                $fullpath = preg_replace( '/\\'.$sep.'+/', $sep, $fullpath );
                return $fullpath;
            }
        }
        return $dir;
    }
}
?>
Return current item: Stickleback