Exemple #1
0
    def compile(self):
        """Compiles the container.

        This method passes the container to compiler
        passes whose job is to manipulate and optimize
        the container.

        The main compiler passes roughly do four things:

            The extension configurations are merged;
            Parameter values are resolved;
            The parameter bag is frozen;
            Extension loading is disabled.

        @api:

        """
        if self.__compiler is None:
            self.__compiler = Compiler()

        if self.__trackResources:
            for cpass in self.__compiler.getPassConfig().getPasses():
                self.addObjectResource(cpass)

        self.__compiler.compile(self)

        self.__extensionConfigs = list()

        Container.compile(self)
    def _process(self, container):
        assert isinstance(container, ContainerBuilder);

        compiler = Compiler();
        passConfig = compiler.getPassConfig();
        passConfig.setOptimizationPasses([
            AnalyzeServiceReferencesPass(True),
            CheckCircularReferencesPass(),
        ]);
        passConfig.setRemovingPasses([]);

        compiler.compile(container);
Exemple #3
0
    def getCompiler(self):
        """Returns the compiler.

        @return: Compiler The compiler

        @api

        """
        if self.__compiler is None:
            self.__compiler = Compiler()

        return self.__compiler
Exemple #4
0
    def getCompilerPassConfig(self):
        """Returns the compiler pass config which can then be modified.:

        @return: PassConfig The compiler pass config

        @api

        """
        if self.__compiler is None:
            self.__compiler = Compiler()

        return self.__compiler.getPassConfig()
    def _process(self, container):
        assert isinstance(container, ContainerBuilder)

        compiler = Compiler()
        passConfig = compiler.getPassConfig()
        passConfig.setOptimizationPasses([
            AnalyzeServiceReferencesPass(True),
            CheckCircularReferencesPass(),
        ])
        passConfig.setRemovingPasses([])

        compiler.compile(container)
Exemple #6
0
    def addCompilerPass(self,
                        cpass,
                        cType=PassConfig.TYPE_BEFORE_OPTIMIZATION):
        """Adds a compiler pass.

        @param: CompilerPassInterface cpass A compiler pass
        @param string                cType The type of compiler pass

        @return ContainerBuilder The current instance

        @api

        """
        assert isinstance(cpass, CompilerPassInterface)

        if self.__compiler is None:
            self.__compiler = Compiler()

        self.__compiler.addPass(cpass, cType)

        self.addObjectResource(cpass)

        return self
Exemple #7
0
class ContainerBuilder(Container, TaggedContainerInterface):
    """ContainerBuilder is a DI container that provides an API to easily describe services.

    @author: Fabien Potencier <*****@*****.**>

    @api

    """
    def __init__(self, parameterBag=None):
        """Sets the track resources flag.

        If you are not using the loaders and therefore don't want
        to depend on the Config component, set this flag to False.

        @param: Boolean track True if you want to track resources, False otherwise:

        """
        self.__trackResources = True
        self.__resources = []
        self.__definitions = dict()
        self.__extensions = dict()
        self.__extensionsByNs = dict()
        self.__extensionConfigs = dict()
        self.__aliases = dict()
        self.__compiler = None
        Container.__init__(self, parameterBag=parameterBag)

    def setResourceTracking(self, track):
        """Sets the track resources flag.

        If you are not using the loaders and therefore don't want
        to depend on the Config component, set this flag to False.

        @param: Boolean track True if you want to track resources, False otherwise:

        """
        self.__trackResources = bool(track)

    def isTrackingResources(self):
        """Checks if resources are tracked.:

        @return: Boolean True if resources are tracked, False otherwise:

        """
        return self.__trackResources

    def registerExtension(self, extension):
        """Registers an extension.

        @param: ExtensionInterface extension An extension instance

        @api

        """
        assert isinstance(extension, ExtensionInterface)

        self.__extensions[extension.getAlias()] = extension
        if extension.getNamespace():
            self.__extensionsByNs[extension.getNamespace()] = extension

    def getExtension(self, name):
        """Returns an extension by alias or namespace.

        @param: string name An alias or a namespace

        @return ExtensionInterface An extension instance

        @raise LogicException if the extension is not registered:

        @api

        """
        if name in self.__extensions:
            return self.__extensions[name]

        if name in self.__extensionsByNs:
            return self.__extensionsByNs[name]

        raise LogicException(
            'Container extension "{0}" is not registered'.format(name))

    def getExtensions(self):
        """Returns all registered extensions.

        @return: ExtensionInterface[] An array of ExtensionInterface

        @api

        """
        return self.__extensions

    def hasExtension(self, name):
        """Checks if we have an extension.:

        @param: string name The name of the extension

        @return Boolean If the extension exists

        @api

        """
        return name in self.__extensions or name in self.__extensionsByNs

    def getResources(self):
        """Returns an array of resources loaded to build this configuration.

        @return: ResourceInterface[] An array of resources

        @api

        """
        return Array.uniq(self.__resources, str)

    def addResource(self, resource):
        """Adds a resource for this configuration.

        @param: ResourceInterface resource A resource instance

        @return ContainerBuilder The current instance

        @api

        """
        assert isinstance(resource, ResourceInterface)

        if not self.__trackResources or not str(resource):
            return self

        self.__resources.append(resource)

        return self

    def setResources(self, resources):
        """Sets the resources for this configuration.

        @param: ResourceInterface[] resources An array of resources

        @return ContainerBuilder The current instance

        @api

        """
        assert isinstance(resources, list)

        if (not self.__trackResources):
            return self

        self.__resources = resources

        return self

    def addObjectResource(self, objectResource):
        """Adds the object class hierarchy(, as resources.):

        @param: object object An object instance

        @return ContainerBuilder The current instance

        @api

        """
        assert isinstance(objectResource, Object)
        if not self.__trackResources:
            return self

        parent = ReflectionObject(objectResource)
        while parent:
            self.addResource(FileResource(parent.getFileName()))
            parent = parent.getParentClass()

        return self

    def loadFromExtension(self, extension, values=None):
        """Loads the configuration for an extension.

        @param: string extension The extension alias or namespace
        @param array  values    An array of values that customizes the extension

        @return ContainerBuilder The current instance
        @raise BadMethodCallException When this ContainerBuilder is frozen

        @raise LogicException if the container is frozen:

        @api

        """
        if values is None:
            values = dict()

        if self.isFrozen():
            raise BadMethodCallException(
                'Cannot load from an extension on a frozen container.')

        namespace = self.getExtension(extension).getAlias()
        if namespace not in self.__extensionConfigs:
            self.__extensionConfigs[namespace] = list()
        self.__extensionConfigs[namespace].append(values)

        return self

    def addCompilerPass(self,
                        cpass,
                        cType=PassConfig.TYPE_BEFORE_OPTIMIZATION):
        """Adds a compiler pass.

        @param: CompilerPassInterface cpass A compiler pass
        @param string                cType The type of compiler pass

        @return ContainerBuilder The current instance

        @api

        """
        assert isinstance(cpass, CompilerPassInterface)

        if self.__compiler is None:
            self.__compiler = Compiler()

        self.__compiler.addPass(cpass, cType)

        self.addObjectResource(cpass)

        return self

    def getCompilerPassConfig(self):
        """Returns the compiler pass config which can then be modified.:

        @return: PassConfig The compiler pass config

        @api

        """
        if self.__compiler is None:
            self.__compiler = Compiler()

        return self.__compiler.getPassConfig()

    def getCompiler(self):
        """Returns the compiler.

        @return: Compiler The compiler

        @api

        """
        if self.__compiler is None:
            self.__compiler = Compiler()

        return self.__compiler

    def getScopes(self):
        """Returns all Scopes.

        @return: dict A dict of scopes

        @api

        """

        return self._scopes

    def getScopeChildren(self):
        """Returns all Scope children.

        @return: dict A dict of scope children.

        @api

        """

        return self._scopeChildren

    def set(self,
            identifier,
            service,
            scope=ContainerInterface.SCOPE_CONTAINER):
        """Sets a service.

        @param: string id      The service identifier:
        @param object service The service instance
        @param string scope   The scope

        @raise BadMethodCallException When this ContainerBuilder is frozen

        @api

        """
        identifier = self._formatIdentifier(identifier)

        if self.isFrozen():
            # setting a synthetic service on a frozen container is alright
            if identifier not in self.__definitions or not self.__definitions[
                    identifier].isSynthetic():
                raise BadMethodCallException(
                    'Setting service "{0}" on a frozen container is not allowed.'
                    .format(identifier))

        self.__definitions.pop(identifier, None)
        self.__aliases.pop(identifier, None)

        Container.set(self, identifier, service, scope)

    def removeDefinition(self, identifier):
        """Removes a service definition.

        @param: string id The service identifier:

        @api

        """
        identifier = self._formatIdentifier(identifier)
        self.__definitions.pop(identifier, None)

    def has(self, identifier):
        """Returns True if the given service is defined.:

        @param: string id The service identifier:

        @return Boolean True if the service is defined, False otherwise:

        @api

        """
        identifier = self._formatIdentifier(identifier)
        return identifier in self.__definitions\
            or identifier in self.__aliases\
            or Container.has(self, identifier)

    def get(self,
            identifier,
            invalidBehavior=ContainerInterface.EXCEPTION_ON_INVALID_REFERENCE):
        """Gets a service.

        @param: string  id              The service identifier:
        @param integer invalidBehavior The behavior when the service does not exist

        @return object The associated service

        @raise InvalidArgumentException if the service is not defined:
        @raise LogicException if the service has a circular reference to itself:

        @see Reference

        @api

        """
        identifier = self._formatIdentifier(identifier)
        try:
            return Container.get(
                self, identifier,
                ContainerInterface.EXCEPTION_ON_INVALID_REFERENCE)
        except InvalidArgumentException as e:
            if identifier in self._loading:
                raise LogicException(
                    'The service "{0}" has a circular reference to itself.'
                    ''.format(identifier), 0, e)

            if not self.hasDefinition(identifier) \
                and identifier in self.__aliases:
                return self.get(self.__aliases[identifier])

            try:
                definition = self.getDefinition(identifier)
            except InvalidArgumentException as e:
                if (ContainerInterface.EXCEPTION_ON_INVALID_REFERENCE !=
                        invalidBehavior):
                    return None
                raise e

            self._loading[identifier] = True

            try:
                service = self.__createService(definition, identifier)
            except Exception as e:
                self._loading.pop(identifier, None)
                raise e

            self._loading.pop(identifier, None)

            return service

    def merge(self, container):
        """Merges a ContainerBuilder with the current ContainerBuilder configuration.

        Service definitions overrides the current defined ones.

        But for parameters, they are overridden by the current ones. It allows
        the parameters passed to the container constructor to have precedence
        over the loaded ones.

        container = ContainerBuilder(array('foo' => 'bar'));
        loader = LoaderXXX(container);
        loader.load('resource_name');
        container.register('foo', stdClass());

        In the above example, even if the loaded resource defines a foo:
        parameter, the value will still be 'bar' as defined in the ContainerBuilder
        constructor.

        @param: ContainerBuilder container The ContainerBuilder instance to merge.


        @raise BadMethodCallException When this ContainerBuilder is frozen

        @api

        """
        assert isinstance(container, ContainerBuilder)
        if self.isFrozen():
            raise BadMethodCallException('Cannot merge on a frozen container.')

        self.addDefinitions(container.getDefinitions())
        self.addAliases(container.getAliases())
        self.getParameterBag().add(container.getParameterBag().all())

        if self.__trackResources:
            for resource in container.getResources():
                self.addResource(resource)

        for name in self.__extensions.keys():
            if name not in self.__extensionConfigs:
                self.__extensionConfigs[name] = list()

            if container.getExtensionConfig(name):
                self.__extensionConfigs[name] = \
                    list(self.__extensionConfigs[name]) +\
                    list(container.getExtensionConfig[name])

    def getExtensionConfig(self, name):
        """Returns the configuration array for the given extension.

        @param: string name The name of the extension

        @return array An array of configuration

        @api

        """
        if not name in self.__extensionConfigs:
            self.__extensionConfigs[name] = list()

        return self.__extensionConfigs[name]

    def prependExtensionConfig(self, name, config):
        """Prepends a config array to the configs of the given extension.

        @param: string name    The name of the extension
        @param: list  config  The config to set

        """
        assert isinstance(config, list)

        if not name in self.__extensionConfigs:
            self.__extensionConfigs[name] = list()

        self.__extensionConfigs[name].insert(0, config)

    def compile(self):
        """Compiles the container.

        This method passes the container to compiler
        passes whose job is to manipulate and optimize
        the container.

        The main compiler passes roughly do four things:

            The extension configurations are merged;
            Parameter values are resolved;
            The parameter bag is frozen;
            Extension loading is disabled.

        @api:

        """
        if self.__compiler is None:
            self.__compiler = Compiler()

        if self.__trackResources:
            for cpass in self.__compiler.getPassConfig().getPasses():
                self.addObjectResource(cpass)

        self.__compiler.compile(self)

        self.__extensionConfigs = list()

        Container.compile(self)

    def getServiceIds(self):
        """Gets all service ids.

        @return: list A list of all defined service ids

        """
        return Array.uniq(
            list(self.getDefinitions().keys()) +\
            list(self.getAliases().keys()) +\
            Container.getServiceIds(self)
        )

    def addAliases(self, aliases):
        """Adds the service aliases.

        @param: dict aliases An dict of aliases

        @api

        """
        assert isinstance(aliases, dict)

        for alias, identifier in aliases.items():
            self.setAlias(alias, identifier)

    def setAliases(self, aliases):
        """Sets the service aliases.

        @param: dict aliases An array of aliases

        @api

        """
        assert isinstance(aliases, dict)

        self.__aliases = dict()
        self.addAliases(aliases)

    def setAlias(self, alias, identifier):
        """Sets an alias for an existing service.

        @param: string        alias The alias to create
        @param string|Alias  identifier    The service to alias

        @raise InvalidArgumentException if the id is not a string or an Alias:
        @raise InvalidArgumentException if the alias is for itself:

        @api

        """
        alias = self.__formatAlias(alias)

        if isinstance(identifier, str):
            identifier = Alias(identifier)
        elif not isinstance(identifier, Alias):
            raise InvalidArgumentException(
                '$id must be a string, or an Alias object.')

        if alias == str(identifier).lower():
            raise InvalidArgumentException(
                'An alias can not reference itself, got a circular reference '
                'on "{0}".'.format(alias))

        self.__definitions.pop(alias, None)

        self.__aliases[alias] = identifier

    def removeAlias(self, alias):
        """Removes an alias.

        @param: string alias The alias to remove

        @api

        """
        alias = self.__formatAlias(alias)
        self.__aliases.pop(alias, None)

    def hasAlias(self, identifier):
        """Returns True if an alias exists under the given identifier.

        @param: string identifier The service identifier

        @return Boolean True if the alias exists, False otherwise:

        @api

        """
        alias = self.__formatAlias(identifier)
        return alias in self.__aliases

    def getAliases(self):
        """Gets all defined aliases.

        @return: Alias[] An array of aliases

        @api

        """
        return self.__aliases

    def getAlias(self, identifier):
        """Gets an alias.

        @param: string id The service identifier:

        @return Alias An Alias instance

        @raise InvalidArgumentException if the alias does not exist:

        @api

        """
        identifier = self.__formatAlias(identifier)

        if not self.hasAlias(identifier):
            raise InvalidArgumentException(
                'The service alias "{0}" does not exist.'
                ''.format(identifier))

        return self.__aliases[identifier]

    def register(self, identifier, className=None):
        """Registers a service definition.

        This methods allows for simple registration of service definition
        with a fluid interface.

        @param: string id    The service identifier
        @param string class The service class

        @return Definition A Definition instance

        @api

        """
        identifier = self._formatIdentifier(identifier)
        return self.setDefinition(identifier, Definition(className))

    def addDefinitions(self, definitions):
        """Adds the service definitions.

        @param: Definition[] definitions An array of service definitions

        @api

        """
        assert isinstance(definitions, dict)

        for identifier, definition in definitions.items():
            self.setDefinition(identifier, definition)

    def setDefinitions(self, definitions):
        """Sets the service definitions.

        @param: Definition[] definitions An array of service definitions

        @api

        """
        assert isinstance(definitions, dict)

        self.__definitions = dict()
        self.addDefinitions(definitions)

    def getDefinitions(self):
        """Gets all service definitions.

        @return: Definition[] An array of Definition instances

        @api

        """
        return self.__definitions

    def setDefinition(self, identifier, definition):
        """Sets a service definition.

        @param: string     id         The service identifier:
        @param Definition definition A Definition instance

        @return Definition the service definition

        @raise BadMethodCallException When this ContainerBuilder is frozen

        @api

        """
        assert isinstance(definition, Definition)

        if self.isFrozen():
            raise BadMethodCallException(
                'Adding definition to a frozen container is not allowed')

        identifier = self._formatIdentifier(identifier)
        self.__aliases.pop(identifier, None)

        self.__definitions[identifier] = definition

        return definition

    def hasDefinition(self, identifier):
        """Returns True if a service definition exists under the given identifier.:

        @param: string id The service identifier:

        @return Boolean True if the service definition exists, False otherwise:

        @api

        """
        identifier = self._formatIdentifier(identifier)
        return identifier in self.__definitions

    def getDefinition(self, identifier):
        """Gets a service definition.

        @param: string id The service identifier:

        @return Definition A Definition instance

        @raise InvalidArgumentException if the service definition does not exist:

        @api

        """
        identifier = self._formatIdentifier(identifier)

        if not self.hasDefinition(identifier):
            raise InvalidArgumentException(
                'The service definition "{0}" does not exist.'
                ''.format(identifier))

        return self.__definitions[identifier]

    def findDefinition(self, identifier):
        """Gets a service definition by id or alias.

        The method "unaliases" recursively to return a Definition instance.

        @param: string id The service identifier or alias:

        @return Definition A Definition instance

        @raise InvalidArgumentException if the service definition does not exist:

        @api

        """
        while self.hasAlias(identifier):
            identifier = str(self.getAlias(identifier))

        return self.getDefinition(identifier)

    def __createService(self, definition, identifier):
        """Creates a service for a service definition.

        @param: Definition definition A service definition instance
        @param string     id         The service identifier:

        @return object The service described by the service definition

        @raise RuntimeException When the scope is inactive
        @raise RuntimeException When the factory definition is incomplete
        @raise RuntimeException When the service is a synthetic service
        @raise InvalidArgumentException When configure callable is not callable

        """
        assert isinstance(definition, Definition)

        if definition.isSynthetic():
            raise RuntimeException(
                'You have requested a synthetic service ("{0}"). '
                'The DIC does not know how to construct this service.'
                ''.format(identifier))

        parameterBag = self.getParameterBag()

        if None is not definition.getFile():
            path = parameterBag.resolveValue(definition.getFile())
            module = SourceFileLoader.load(path)
        else:
            module = None

        value = parameterBag.resolveValue(definition.getArguments())
        value = parameterBag.unescapeValue(value)
        arguments = self.resolveServices(value)

        if not definition.getFactoryMethod() is None:
            if not definition.getFactoryClass() is None:
                factory = parameterBag.resolveValue(
                    definition.getFactoryClass())
                if module is not None:
                    factory = getattr(module, factory)
                else:
                    factory = ClassLoader.load(factory)
            elif not definition.getFactoryService() is None:
                factory = self.get(
                    parameterBag.resolveValue(definition.getFactoryService()))
            else:
                raise RuntimeException(
                    'Cannot create service "{0}" from factory method without '
                    'a factory service or factory class.'
                    ''.format(identifier))

            service = getattr(factory,
                              definition.getFactoryMethod())(*arguments)
        else:
            className = parameterBag.resolveValue(definition.getClass())
            if module is not None:
                service = getattr(module, className)(*arguments)
            else:
                service = ClassLoader.load(className)(*arguments)

        scope = definition.getScope()
        if self.SCOPE_PROTOTYPE != scope:
            if self.SCOPE_CONTAINER != scope and scope not in self._scopedServices:
                raise RuntimeException(
                    'You tried to create the "{0}" service of an inactive '
                    'scope.'.format(identifier))

            lowerId = self._formatIdentifier(identifier)
            self._services[lowerId] = service

            if (self.SCOPE_CONTAINER != scope):
                self._scopedServices[scope][lowerId] = service

        for call in definition.getMethodCalls():
            services = self.getServiceConditionals(call[1])
            ok = True
            for s in services:
                if not self.has(s):
                    ok = False
                    break
            if ok:
                args = self.resolveServices(parameterBag.resolveValue(call[1]))
                getattr(service, call[0])(*args)

        properties = self.resolveServices(
            parameterBag.resolveValue(definition.getProperties()))
        for name, value in properties.items():
            setattr(service, name, value)

        closure = definition.getConfigurator()
        if closure:
            if isinstance(closure, list):
                if isinstance(closure[0], Reference):
                    closure[0] = self.get(str(closure[0]))
                else:
                    closure[0] = parameterBag.resolveValue(closure[0])
                    closure[0] = ClassLoader.load(closure[0])

                closure = getattr(closure[0], closure[1])

            if not Tool.isCallable(closure):
                raise InvalidArgumentException(
                    'The configure callable for class "{0}" is not a callable.'
                    ''.format(type(service).__name__))

            closure(service)

        return service

    def resolveServices(self, value):
        """Replaces service references by the real service instance.

        @param: mixed value A value

        @return mixed The same value with all service references replaced by the real service instances

        """
        if isinstance(value, dict):
            for k, v in value.items():
                value[k] = self.resolveServices(v)
        if isinstance(value, list):
            i = 0
            for v in value:
                value[i] = self.resolveServices(v)
                i += 1
        elif isinstance(value, Reference):
            value = self.get(str(value), value.getInvalidBehavior())
        elif isinstance(value, Definition):
            value = self.__createService(value, None)
        return value

    def findTaggedServiceIds(self, name):
        """Returns service ids for a given tag.

        @param: string name The tag name

        @return array An array of tags

        @api

        """
        tags = dict()
        for identifier, definition in self.getDefinitions().items():
            if definition.getTag(name):
                tags[identifier] = definition.getTag(name)
        return tags

    def findTags(self):
        """Returns all tags the defined services use.

        @return: array An array of tags

        """
        tags = dict()
        for definition in self.getDefinitions().values():
            tags.update(definition.getTags().keys())

        return tags

    @classmethod
    def getServiceConditionals(cls, value):
        """Returns the Service Conditionals.

        @param: mixed value An array of conditionals to return.

        @return array An array of Service conditionals

        """
        services = list()

        if isinstance(value, list):
            for v in value:
                iterable = cls.getServiceConditionals(v)
                services.extend(iterable)
                services = Array.uniq(services)
        elif isinstance(value, Reference):
            services.append(str(value))

        return services

    def __formatAlias(self, identifer):
        return self._formatIdentifier(identifer)