PageRenderTime 107ms CodeModel.GetById 60ms app.highlight 15ms RepoModel.GetById 16ms app.codeStats 0ms

/protected/extensions/doctrine/vendors/Symfony/Component/DependencyInjection/Container.php

https://bitbucket.org/NordLabs/yiidoctrine
PHP | 459 lines | 188 code | 51 blank | 220 comment | 24 complexity | 22e0c18c39d25a084cefd5fc48c378c4 MD5 | raw file
  1<?php
  2
  3/*
  4 * This file is part of the Symfony package.
  5 *
  6 * (c) Fabien Potencier <fabien@symfony.com>
  7 *
  8 * For the full copyright and license information, please view the LICENSE
  9 * file that was distributed with this source code.
 10 */
 11
 12namespace Symfony\Component\DependencyInjection;
 13
 14use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
 15use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
 16use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
 17use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 18use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
 19
 20/**
 21 * Container is a dependency injection container.
 22 *
 23 * It gives access to object instances (services).
 24 *
 25 * Services and parameters are simple key/pair stores.
 26 *
 27 * Parameter and service keys are case insensitive.
 28 *
 29 * A service id can contain lowercased letters, digits, underscores, and dots.
 30 * Underscores are used to separate words, and dots to group services
 31 * under namespaces:
 32 *
 33 * <ul>
 34 *   <li>request</li>
 35 *   <li>mysql_session_storage</li>
 36 *   <li>symfony.mysql_session_storage</li>
 37 * </ul>
 38 *
 39 * A service can also be defined by creating a method named
 40 * getXXXService(), where XXX is the camelized version of the id:
 41 *
 42 * <ul>
 43 *   <li>request -> getRequestService()</li>
 44 *   <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
 45 *   <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
 46 * </ul>
 47 *
 48 * The container can have three possible behaviors when a service does not exist:
 49 *
 50 *  * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
 51 *  * NULL_ON_INVALID_REFERENCE:      Returns null
 52 *  * IGNORE_ON_INVALID_REFERENCE:    Ignores the wrapping command asking for the reference
 53 *                                    (for instance, ignore a setter if the service does not exist)
 54 *
 55 * @author Fabien Potencier <fabien@symfony.com>
 56 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 57 *
 58 * @api
 59 */
 60class Container implements ContainerInterface
 61{
 62    protected $parameterBag;
 63    protected $services;
 64    protected $scopes;
 65    protected $scopeChildren;
 66    protected $scopedServices;
 67    protected $scopeStacks;
 68    protected $loading = array();
 69
 70    /**
 71     * Constructor.
 72     *
 73     * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
 74     *
 75     * @api
 76     */
 77    public function __construct(ParameterBagInterface $parameterBag = null)
 78    {
 79        $this->parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag;
 80
 81        $this->services       = array();
 82        $this->scopes         = array();
 83        $this->scopeChildren  = array();
 84        $this->scopedServices = array();
 85        $this->scopeStacks    = array();
 86
 87        $this->set('service_container', $this);
 88    }
 89
 90    /**
 91     * Compiles the container.
 92     *
 93     * This method does two things:
 94     *
 95     *  * Parameter values are resolved;
 96     *  * The parameter bag is frozen.
 97     *
 98     * @api
 99     */
100    public function compile()
101    {
102        $this->parameterBag->resolve();
103
104        $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
105    }
106
107    /**
108     * Returns true if the container parameter bag are frozen.
109     *
110     * @return Boolean true if the container parameter bag are frozen, false otherwise
111     *
112     * @api
113     */
114    public function isFrozen()
115    {
116        return $this->parameterBag instanceof FrozenParameterBag;
117    }
118
119    /**
120     * Gets the service container parameter bag.
121     *
122     * @return ParameterBagInterface A ParameterBagInterface instance
123     *
124     * @api
125     */
126    public function getParameterBag()
127    {
128        return $this->parameterBag;
129    }
130
131    /**
132     * Gets a parameter.
133     *
134     * @param  string $name The parameter name
135     *
136     * @return mixed  The parameter value
137     *
138     * @throws  \InvalidArgumentException if the parameter is not defined
139     *
140     * @api
141     */
142    public function getParameter($name)
143    {
144        return $this->parameterBag->get($name);
145    }
146
147    /**
148     * Checks if a parameter exists.
149     *
150     * @param  string $name The parameter name
151     *
152     * @return Boolean The presence of parameter in container
153     *
154     * @api
155     */
156    public function hasParameter($name)
157    {
158        return $this->parameterBag->has($name);
159    }
160
161    /**
162     * Sets a parameter.
163     *
164     * @param string $name  The parameter name
165     * @param mixed  $value The parameter value
166     *
167     * @api
168     */
169    public function setParameter($name, $value)
170    {
171        $this->parameterBag->set($name, $value);
172    }
173
174    /**
175     * Sets a service.
176     *
177     * @param string $id      The service identifier
178     * @param object $service The service instance
179     * @param string $scope   The scope of the service
180     *
181     * @api
182     */
183    public function set($id, $service, $scope = self::SCOPE_CONTAINER)
184    {
185        if (self::SCOPE_PROTOTYPE === $scope) {
186            throw new \InvalidArgumentException('You cannot set services of scope "prototype".');
187        }
188
189        $id = strtolower($id);
190
191        if (self::SCOPE_CONTAINER !== $scope) {
192            if (!isset($this->scopedServices[$scope])) {
193                throw new \RuntimeException('You cannot set services of inactive scopes.');
194            }
195
196            $this->scopedServices[$scope][$id] = $service;
197        }
198
199        $this->services[$id] = $service;
200    }
201
202    /**
203     * Returns true if the given service is defined.
204     *
205     * @param  string  $id      The service identifier
206     *
207     * @return Boolean true if the service is defined, false otherwise
208     *
209     * @api
210     */
211    public function has($id)
212    {
213        $id = strtolower($id);
214
215        return isset($this->services[$id]) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service');
216    }
217
218    /**
219     * Gets a service.
220     *
221     * If a service is both defined through a set() method and
222     * with a set*Service() method, the former has always precedence.
223     *
224     * @param  string  $id              The service identifier
225     * @param  integer $invalidBehavior The behavior when the service does not exist
226     *
227     * @return object The associated service
228     *
229     * @throws \InvalidArgumentException if the service is not defined
230     *
231     * @see Reference
232     *
233     * @api
234     */
235    public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE)
236    {
237        $id = strtolower($id);
238
239        if (isset($this->services[$id])) {
240            return $this->services[$id];
241        }
242
243        if (isset($this->loading[$id])) {
244            throw new ServiceCircularReferenceException($id, array_keys($this->loading));
245        }
246
247        if (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_')).'Service')) {
248            $this->loading[$id] = true;
249
250            try {
251                $service = $this->$method();
252            } catch (\Exception $e) {
253                unset($this->loading[$id]);
254                throw $e;
255            }
256
257            unset($this->loading[$id]);
258
259            return $service;
260        }
261
262        if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
263            throw new ServiceNotFoundException($id);
264        }
265    }
266
267    /**
268     * Gets all service ids.
269     *
270     * @return array An array of all defined service ids
271     */
272    public function getServiceIds()
273    {
274        $ids = array();
275        $r = new \ReflectionClass($this);
276        foreach ($r->getMethods() as $method) {
277            if (preg_match('/^get(.+)Service$/', $method->getName(), $match)) {
278                $ids[] = self::underscore($match[1]);
279            }
280        }
281
282        return array_unique(array_merge($ids, array_keys($this->services)));
283    }
284
285    /**
286     * This is called when you enter a scope
287     *
288     * @param string $name
289     *
290     * @return void
291     *
292     * @api
293     */
294    public function enterScope($name)
295    {
296        if (!isset($this->scopes[$name])) {
297            throw new \InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name));
298        }
299
300        if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) {
301            throw new \RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name]));
302        }
303
304        // check if a scope of this name is already active, if so we need to
305        // remove all services of this scope, and those of any of its child
306        // scopes from the global services map
307        if (isset($this->scopedServices[$name])) {
308            $services = array($this->services, $name => $this->scopedServices[$name]);
309            unset($this->scopedServices[$name]);
310
311            foreach ($this->scopeChildren[$name] as $child) {
312                $services[$child] = $this->scopedServices[$child];
313                unset($this->scopedServices[$child]);
314            }
315
316            // update global map
317            $this->services = call_user_func_array('array_diff_key', $services);
318            array_shift($services);
319
320            // add stack entry for this scope so we can restore the removed services later
321            if (!isset($this->scopeStacks[$name])) {
322                $this->scopeStacks[$name] = new \SplStack();
323            }
324            $this->scopeStacks[$name]->push($services);
325        }
326
327        $this->scopedServices[$name] = array();
328    }
329
330    /**
331     * This is called to leave the current scope, and move back to the parent
332     * scope.
333     *
334     * @param string $name The name of the scope to leave
335     *
336     * @return void
337     *
338     * @throws \InvalidArgumentException if the scope is not active
339     *
340     * @api
341     */
342    public function leaveScope($name)
343    {
344        if (!isset($this->scopedServices[$name])) {
345            throw new \InvalidArgumentException(sprintf('The scope "%s" is not active.', $name));
346        }
347
348        // remove all services of this scope, or any of its child scopes from
349        // the global service map
350        $services = array($this->services, $this->scopedServices[$name]);
351        unset($this->scopedServices[$name]);
352        foreach ($this->scopeChildren[$name] as $child) {
353            if (!isset($this->scopedServices[$child])) {
354                continue;
355            }
356
357            $services[] = $this->scopedServices[$child];
358            unset($this->scopedServices[$child]);
359        }
360        $this->services = call_user_func_array('array_diff_key', $services);
361
362        // check if we need to restore services of a previous scope of this type
363        if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) {
364            $services = $this->scopeStacks[$name]->pop();
365            $this->scopedServices += $services;
366
367            array_unshift($services, $this->services);
368            $this->services = call_user_func_array('array_merge', $services);
369        }
370    }
371
372    /**
373     * Adds a scope to the container.
374     *
375     * @param ScopeInterface $scope
376     *
377     * @return void
378     *
379     * @api
380     */
381    public function addScope(ScopeInterface $scope)
382    {
383        $name = $scope->getName();
384        $parentScope = $scope->getParentName();
385
386        if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) {
387            throw new \InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name));
388        }
389        if (isset($this->scopes[$name])) {
390            throw new \InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name));
391        }
392        if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) {
393            throw new \InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope));
394        }
395
396        $this->scopes[$name] = $parentScope;
397        $this->scopeChildren[$name] = array();
398
399        // normalize the child relations
400        while ($parentScope !== self::SCOPE_CONTAINER) {
401            $this->scopeChildren[$parentScope][] = $name;
402            $parentScope = $this->scopes[$parentScope];
403        }
404    }
405
406    /**
407     * Returns whether this container has a certain scope
408     *
409     * @param string $name The name of the scope
410     *
411     * @return Boolean
412     *
413     * @api
414     */
415    public function hasScope($name)
416    {
417        return isset($this->scopes[$name]);
418    }
419
420    /**
421     * Returns whether this scope is currently active
422     *
423     * This does not actually check if the passed scope actually exists.
424     *
425     * @param string $name
426     *
427     * @return Boolean
428     *
429     * @api
430     */
431    public function isScopeActive($name)
432    {
433        return isset($this->scopedServices[$name]);
434    }
435
436    /**
437     * Camelizes a string.
438     *
439     * @param string $id A string to camelize
440     *
441     * @return string The camelized string
442     */
443    static public function camelize($id)
444    {
445        return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $id);
446    }
447
448    /**
449     * A string to underscore.
450     *
451     * @param string $id The string to underscore
452     *
453     * @return string The underscored string
454     */
455    static public function underscore($id)
456    {
457        return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.')));
458    }
459}