1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 
<?php
/**
 * @link      http://github.com/zendframework/zend-servicemanager for the canonical source repository
 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\ServiceManager;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Exception\InvalidServiceException;

/**
 * Abstract plugin manager.
 *
 * Abstract PluginManagerInterface implementation providing:
 *
 * - creation context support. The constructor accepts the parent container
 *   instance, which is then used when creating instances.
 * - plugin validation. Implementations may define the `$instanceOf` property
 *   to indicate what class types constitute valid plugins, omitting the
 *   requirement to define the `validate()` method.
 *
 * The implementation extends `ServiceManager`, thus providing the same set
 * of capabilities as found in that implementation.
 */
abstract class AbstractPluginManager extends ServiceManager implements PluginManagerInterface
{
    /**
     * Whether or not to auto-add a FQCN as an invokable if it exists.
     *
     * @var bool
     */
    protected $autoAddInvokableClass = true;

    /**
     * An object type that the created instance must be instanced of
     *
     * @var null|string
     */
    protected $instanceOf = null;

    /**
     * Constructor.
     *
     * Sets the provided $parentLocator as the creation context for all
     * factories; for $config, {@see \Zend\ServiceManager\ServiceManager::configure()}
     * for details on its accepted structure.
     *
     * @param null|ConfigInterface|ContainerInterface $configInstanceOrParentLocator
     * @param array $config
     */
    public function __construct($configInstanceOrParentLocator = null, array $config = [])
    {
        if (null !== $configInstanceOrParentLocator
            && ! $configInstanceOrParentLocator instanceof ConfigInterface
            && ! $configInstanceOrParentLocator instanceof ContainerInterface
        ) {
            throw new Exception\InvalidArgumentException(sprintf(
                '%s expects a ConfigInterface or ContainerInterface instance as the first argument; received %s',
                __CLASS__,
                (is_object($configInstanceOrParentLocator)
                    ? get_class($configInstanceOrParentLocator)
                    : gettype($configInstanceOrParentLocator)
                )
            ));
        }

        if ($configInstanceOrParentLocator instanceof ConfigInterface) {
            trigger_error(sprintf(
                'Usage of %s as a constructor argument for %s is now deprecated',
                ConfigInterface::class,
                get_class($this)
            ), E_USER_DEPRECATED);
            $config = $configInstanceOrParentLocator->toArray();
        }

        parent::__construct($config);

        if (! $configInstanceOrParentLocator instanceof ContainerInterface) {
            trigger_error(sprintf(
                '%s now expects a %s instance representing the parent container; please update your code',
                __METHOD__,
                ContainerInterface::class
            ), E_USER_DEPRECATED);
        }

        $this->creationContext = $configInstanceOrParentLocator instanceof ContainerInterface
            ? $configInstanceOrParentLocator
            : $this;
    }

    /**
     * Override configure() to validate service instances.
     *
     * If an instance passed in the `services` configuration is invalid for the
     * plugin manager, this method will raise an InvalidServiceException.
     *
     * {@inheritDoc}
     * @throws InvalidServiceException
     */
    public function configure(array $config)
    {
        if (isset($config['services'])) {
            foreach ($config['services'] as $service) {
                $this->validate($service);
            }
        }

        parent::configure($config);

        return $this;
    }

    /**
     * {@inheritDoc}
     *
     * @param string $name Service name of plugin to retrieve.
     * @param null|array $options Options to use when creating the instance.
     * @return mixed
     * @throws Exception\ServiceNotFoundException if the manager does not have
     *     a service definition for the instance, and the service is not
     *     auto-invokable.
     * @throws InvalidServiceException if the plugin created is invalid for the
     *     plugin context.
     */
    public function get($name, array $options = null)
    {
        if (! $this->has($name)) {
            if (! $this->autoAddInvokableClass || ! class_exists($name)) {
                throw new Exception\ServiceNotFoundException(sprintf(
                    'A plugin by the name "%s" was not found in the plugin manager %s',
                    $name,
                    get_class($this)
                ));
            }

            $this->setFactory($name, Factory\InvokableFactory::class);
        }

        $instance = empty($options) ? parent::get($name) : $this->build($name, $options);
        $this->validate($instance);
        return $instance;
    }

    /**
     * {@inheritDoc}
     */
    public function validate($instance)
    {
        if (method_exists($this, 'validatePlugin')) {
            trigger_error(sprintf(
                '%s::validatePlugin() has been deprecated as of 3.0; please define validate() instead',
                get_class($this)
            ), E_USER_DEPRECATED);
            $this->validatePlugin($instance);
            return;
        }

        if (empty($this->instanceOf) || $instance instanceof $this->instanceOf) {
            return;
        }

        throw new InvalidServiceException(sprintf(
            'Plugin manager "%s" expected an instance of type "%s", but "%s" was received',
            __CLASS__,
            $this->instanceOf,
            is_object($instance) ? get_class($instance) : gettype($instance)
        ));
    }

    /**
     * Implemented for backwards compatibility only.
     *
     * Returns the creation context.
     *
     * @deprecated since 3.0.0. The creation context should be passed during
     *     instantiation instead.
     * @param ContainerInterface $container
     * @return void
     */
    public function setServiceLocator(ContainerInterface $container)
    {
        trigger_error(sprintf(
            'Usage of %s is deprecated since v3.0.0; please pass the container to the constructor instead',
            __METHOD__
        ), E_USER_DEPRECATED);
        $this->creationContext = $container;
    }
}