Subversion Repositories PHPX

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
39 PointedEar 1
<?php
2
/**
3
 * Zend Framework (http://framework.zend.com/)
4
 *
5
 * @link      http://github.com/zendframework/zf2 for the canonical source repository
6
 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7
 * @license   http://framework.zend.com/license/new-bsd New BSD License
8
 */
9
 
10
namespace Zend\ServiceManager;
11
 
12
use ReflectionClass;
13
 
14
class ServiceManager implements ServiceLocatorInterface
15
{
16
 
17
    /**@#+
18
     * Constants
19
     */
20
    const SCOPE_PARENT = 'parent';
21
    const SCOPE_CHILD = 'child';
22
    /**@#-*/
23
 
24
    /**
25
     * Lookup for canonicalized names.
26
     *
27
     * @var array
28
     */
29
    protected $canonicalNames = array();
30
 
31
    /**
32
     * @var bool
33
     */
34
    protected $allowOverride = false;
35
 
36
    /**
37
     * @var array
38
     */
39
    protected $invokableClasses = array();
40
 
41
    /**
42
     * @var string|callable|\Closure|FactoryInterface[]
43
     */
44
    protected $factories = array();
45
 
46
    /**
47
     * @var AbstractFactoryInterface[]
48
     */
49
    protected $abstractFactories = array();
50
 
51
    /**
52
     * @var array
53
     */
54
    protected $pendingAbstractFactoryRequests = array();
55
 
56
    /**
57
     * @var array
58
     */
59
    protected $shared = array();
60
 
61
    /**
62
     * Registered services and cached values
63
     *
64
     * @var array
65
     */
66
    protected $instances = array();
67
 
68
    /**
69
     * @var array
70
     */
71
    protected $aliases = array();
72
 
73
    /**
74
     * @var array
75
     */
76
    protected $initializers = array();
77
 
78
    /**
79
     * @var ServiceManager[]
80
     */
81
    protected $peeringServiceManagers = array();
82
 
83
    /**
84
     * Whether or not to share by default
85
     *
86
     * @var bool
87
     */
88
    protected $shareByDefault = true;
89
 
90
    /**
91
     * @var bool
92
     */
93
    protected $retrieveFromPeeringManagerFirst = false;
94
 
95
    /**
96
     * @var bool Track whether not to throw exceptions during create()
97
     */
98
    protected $throwExceptionInCreate = true;
99
 
100
    /**
101
     * @var array map of characters to be replaced through strtr
102
     */
103
    protected $canonicalNamesReplacements = array('-' => '', '_' => '', ' ' => '', '\\' => '', '/' => '');
104
 
105
    /**
106
     * Constructor
107
     *
108
     * @param ConfigInterface $config
109
     */
110
    public function __construct(ConfigInterface $config = null)
111
    {
112
        if ($config) {
113
            $config->configureServiceManager($this);
114
        }
115
    }
116
 
117
    /**
118
     * Set allow override
119
     *
120
     * @param $allowOverride
121
     * @return ServiceManager
122
     */
123
    public function setAllowOverride($allowOverride)
124
    {
125
        $this->allowOverride = (bool) $allowOverride;
126
        return $this;
127
    }
128
 
129
    /**
130
     * Get allow override
131
     *
132
     * @return bool
133
     */
134
    public function getAllowOverride()
135
    {
136
        return $this->allowOverride;
137
    }
138
 
139
    /**
140
     * Set flag indicating whether services are shared by default
141
     *
142
     * @param  bool $shareByDefault
143
     * @return ServiceManager
144
     * @throws Exception\RuntimeException if allowOverride is false
145
     */
146
    public function setShareByDefault($shareByDefault)
147
    {
148
        if ($this->allowOverride === false) {
149
            throw new Exception\RuntimeException(sprintf(
150
                '%s: cannot alter default shared service setting; container is marked immutable (allow_override is false)',
151
                __METHOD__
152
            ));
153
        }
154
        $this->shareByDefault = (bool) $shareByDefault;
155
        return $this;
156
    }
157
 
158
    /**
159
     * Are services shared by default?
160
     *
161
     * @return bool
162
     */
163
    public function shareByDefault()
164
    {
165
        return $this->shareByDefault;
166
    }
167
 
168
    /**
169
     * Set throw exceptions in create
170
     *
171
     * @param  bool $throwExceptionInCreate
172
     * @return ServiceManager
173
     */
174
    public function setThrowExceptionInCreate($throwExceptionInCreate)
175
    {
176
        $this->throwExceptionInCreate = $throwExceptionInCreate;
177
        return $this;
178
    }
179
 
180
    /**
181
     * Get throw exceptions in create
182
     *
183
     * @return bool
184
     */
185
    public function getThrowExceptionInCreate()
186
    {
187
        return $this->throwExceptionInCreate;
188
    }
189
 
190
    /**
191
     * Set flag indicating whether to pull from peering manager before attempting creation
192
     *
193
     * @param  bool $retrieveFromPeeringManagerFirst
194
     * @return ServiceManager
195
     */
196
    public function setRetrieveFromPeeringManagerFirst($retrieveFromPeeringManagerFirst = true)
197
    {
198
        $this->retrieveFromPeeringManagerFirst = (bool) $retrieveFromPeeringManagerFirst;
199
        return $this;
200
    }
201
 
202
    /**
203
     * Should we retrieve from the peering manager prior to attempting to create a service?
204
     *
205
     * @return bool
206
     */
207
    public function retrieveFromPeeringManagerFirst()
208
    {
209
        return $this->retrieveFromPeeringManagerFirst;
210
    }
211
 
212
    /**
213
     * Set invokable class
214
     *
215
     * @param  string  $name
216
     * @param  string  $invokableClass
217
     * @param  bool $shared
218
     * @return ServiceManager
219
     * @throws Exception\InvalidServiceNameException
220
     */
221
    public function setInvokableClass($name, $invokableClass, $shared = null)
222
    {
223
        $cName = $this->canonicalizeName($name);
224
 
225
        if ($this->has(array($cName, $name), false)) {
226
            if ($this->allowOverride === false) {
227
                throw new Exception\InvalidServiceNameException(sprintf(
228
                    'A service by the name or alias "%s" already exists and cannot be overridden; please use an alternate name',
229
                    $cName
230
                ));
231
            }
232
            $this->unregisterService($cName);
233
        }
234
 
235
        if ($shared === null) {
236
            $shared = $this->shareByDefault();
237
        }
238
 
239
        $this->invokableClasses[$cName] = $invokableClass;
240
        $this->shared[$cName]           = (bool) $shared;
241
 
242
        return $this;
243
    }
244
 
245
    /**
246
     * Set factory
247
     *
248
     * @param  string                           $name
249
     * @param  string|FactoryInterface|callable $factory
250
     * @param  bool                             $shared
251
     * @return ServiceManager
252
     * @throws Exception\InvalidArgumentException
253
     * @throws Exception\InvalidServiceNameException
254
     */
255
    public function setFactory($name, $factory, $shared = null)
256
    {
257
        $cName = $this->canonicalizeName($name);
258
 
259
        if (!is_string($factory) && !$factory instanceof FactoryInterface && !is_callable($factory)) {
260
            throw new Exception\InvalidArgumentException(
261
                'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.'
262
            );
263
        }
264
 
265
        if ($this->has(array($cName, $name), false)) {
266
            if ($this->allowOverride === false) {
267
                throw new Exception\InvalidServiceNameException(sprintf(
268
                    'A service by the name or alias "%s" already exists and cannot be overridden, please use an alternate name',
269
                    $cName
270
                ));
271
            }
272
            $this->unregisterService($cName);
273
        }
274
 
275
        if ($shared === null) {
276
            $shared = $this->shareByDefault();
277
        }
278
 
279
        $this->factories[$cName] = $factory;
280
        $this->shared[$cName]    = (bool) $shared;
281
 
282
        return $this;
283
    }
284
 
285
    /**
286
     * Add abstract factory
287
     *
288
     * @param  AbstractFactoryInterface|string $factory
289
     * @param  bool                            $topOfStack
290
     * @return ServiceManager
291
     * @throws Exception\InvalidArgumentException if the abstract factory is invalid
292
     */
293
    public function addAbstractFactory($factory, $topOfStack = true)
294
    {
295
        if (!is_string($factory) && !$factory instanceof AbstractFactoryInterface) {
296
            throw new Exception\InvalidArgumentException(
297
                'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.'
298
            );
299
        }
300
        if (is_string($factory)) {
301
            if (!class_exists($factory, true)) {
302
                throw new Exception\InvalidArgumentException(
303
                    'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.'
304
                );
305
            }
306
            $refl = new ReflectionClass($factory);
307
            if (!$refl->implementsInterface(__NAMESPACE__ . '\\AbstractFactoryInterface')) {
308
                throw new Exception\InvalidArgumentException(
309
                    'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.'
310
                );
311
            }
312
        }
313
 
314
        if ($topOfStack) {
315
            array_unshift($this->abstractFactories, $factory);
316
        } else {
317
            array_push($this->abstractFactories, $factory);
318
        }
319
        return $this;
320
    }
321
 
322
    /**
323
     * Add initializer
324
     *
325
     * @param  callable|InitializerInterface $initializer
326
     * @param  bool                          $topOfStack
327
     * @return ServiceManager
328
     * @throws Exception\InvalidArgumentException
329
     */
330
    public function addInitializer($initializer, $topOfStack = true)
331
    {
332
        if (!is_callable($initializer) && !$initializer instanceof InitializerInterface) {
333
            if (!is_string($initializer)
334
                || !$this->isSubclassOf($initializer, __NAMESPACE__ . '\InitializerInterface')
335
            ) {
336
                throw new Exception\InvalidArgumentException('$initializer should be callable.');
337
            }
338
            $initializer = new $initializer;
339
        }
340
 
341
        if ($topOfStack) {
342
            array_unshift($this->initializers, $initializer);
343
        } else {
344
            array_push($this->initializers, $initializer);
345
        }
346
        return $this;
347
    }
348
 
349
    /**
350
     * Register a service with the locator
351
     *
352
     * @param  string  $name
353
     * @param  mixed   $service
354
     * @return ServiceManager
355
     * @throws Exception\InvalidServiceNameException
356
     */
357
    public function setService($name, $service)
358
    {
359
        $cName = $this->canonicalizeName($name);
360
 
361
        if ($this->has($cName, false)) {
362
            if ($this->allowOverride === false) {
363
                throw new Exception\InvalidServiceNameException(sprintf(
364
                    '%s: A service by the name "%s" or alias already exists and cannot be overridden, please use an alternate name.',
365
                    __METHOD__,
366
                    $name
367
                ));
368
            }
369
            $this->unregisterService($cName);
370
        }
371
 
372
        $this->instances[$cName] = $service;
373
 
374
        return $this;
375
    }
376
 
377
    /**
378
     * @param  string $name
379
     * @param  bool   $isShared
380
     * @return ServiceManager
381
     * @throws Exception\ServiceNotFoundException
382
     */
383
    public function setShared($name, $isShared)
384
    {
385
        $cName = $this->canonicalizeName($name);
386
 
387
        if (
388
            !isset($this->invokableClasses[$cName])
389
            && !isset($this->factories[$cName])
390
            && !$this->canCreateFromAbstractFactory($cName, $name)
391
        ) {
392
            throw new Exception\ServiceNotFoundException(sprintf(
393
                '%s: A service by the name "%s" was not found and could not be marked as shared',
394
                __METHOD__,
395
                $name
396
            ));
397
        }
398
 
399
        $this->shared[$cName] = (bool) $isShared;
400
        return $this;
401
    }
402
 
403
    /**
404
     * Retrieve a registered instance
405
     *
406
     * @param  string  $name
407
     * @param  bool    $usePeeringServiceManagers
408
     * @throws Exception\ServiceNotFoundException
409
     * @return object|array
410
     */
411
    public function get($name, $usePeeringServiceManagers = true)
412
    {
413
        $cName   = $this->canonicalizeName($name);
414
        $isAlias = false;
415
 
416
        if ($this->hasAlias($cName)) {
417
            $isAlias = true;
418
 
419
            do {
420
                $cName = $this->aliases[$cName];
421
            } while ($this->hasAlias($cName));
422
        }
423
 
424
        $instance                        = null;
425
        $retrieveFromPeeringManagerFirst = $this->retrieveFromPeeringManagerFirst();
426
 
427
        if ($usePeeringServiceManagers && $retrieveFromPeeringManagerFirst) {
428
            $instance = $this->retrieveFromPeeringManager($name);
429
 
430
            if(null !== $instance) {
431
                return $instance;
432
            }
433
        }
434
 
435
        if (isset($this->instances[$cName])) {
436
            return $this->instances[$cName];
437
        }
438
 
439
        if (!$instance) {
440
            if ($this->canCreate(array($cName, $name))) {
441
                $instance = $this->create(array($cName, $name));
442
            } elseif ($usePeeringServiceManagers && !$retrieveFromPeeringManagerFirst) {
443
                $instance = $this->retrieveFromPeeringManager($name);
444
            }
445
        }
446
 
447
        // Still no instance? raise an exception
448
        if ($instance === null && !is_array($instance)) {
449
            if ($isAlias) {
450
                throw new Exception\ServiceNotFoundException(sprintf(
451
                    'An alias "%s" was requested but no service could be found.',
452
                    $name
453
                ));
454
            }
455
 
456
            throw new Exception\ServiceNotFoundException(sprintf(
457
                '%s was unable to fetch or create an instance for %s',
458
                __METHOD__,
459
                $name
460
            ));
461
        }
462
 
463
        if (
464
            ($this->shareByDefault() && !isset($this->shared[$cName]))
465
            || (isset($this->shared[$cName]) && $this->shared[$cName] === true)
466
        ) {
467
            $this->instances[$cName] = $instance;
468
        }
469
 
470
        return $instance;
471
    }
472
 
473
    /**
474
     * Create an instance
475
     *
476
     * @param  string|array $name
477
     * @return bool|object
478
     * @throws Exception\ServiceNotFoundException
479
     * @throws Exception\ServiceNotCreatedException
480
     */
481
    public function create($name)
482
    {
483
        $instance = false;
484
 
485
        if (is_array($name)) {
486
            list($cName, $rName) = $name;
487
        } else {
488
            $rName = $name;
489
            $cName = $this->canonicalizeName($rName);
490
        }
491
 
492
 
493
        if (isset($this->factories[$cName])) {
494
            $instance = $this->createFromFactory($cName, $rName);
495
        }
496
 
497
        if ($instance === false && isset($this->invokableClasses[$cName])) {
498
            $instance = $this->createFromInvokable($cName, $rName);
499
        }
500
 
501
        if ($instance === false && $this->canCreateFromAbstractFactory($cName, $rName)) {
502
            $instance = $this->createFromAbstractFactory($cName, $rName);
503
        }
504
 
505
        if ($this->throwExceptionInCreate == true && $instance === false) {
506
            throw new Exception\ServiceNotFoundException(sprintf(
507
                'No valid instance was found for %s%s',
508
                $cName,
509
                ($rName ? '(alias: ' . $rName . ')' : '')
510
            ));
511
        }
512
 
513
        foreach ($this->initializers as $initializer) {
514
            if ($initializer instanceof InitializerInterface) {
515
                $initializer->initialize($instance, $this);
516
            } elseif (is_object($initializer) && is_callable($initializer)) {
517
                $initializer($instance, $this);
518
            } else {
519
                call_user_func($initializer, $instance, $this);
520
            }
521
        }
522
 
523
        return $instance;
524
    }
525
 
526
    /**
527
     * Determine if we can create an instance.
528
     *
529
     * @param  string|array $name
530
     * @param  bool         $checkAbstractFactories
531
     * @return bool
532
     */
533
    public function canCreate($name, $checkAbstractFactories = true)
534
    {
535
        if (is_array($name)) {
536
            list($cName, $rName) = $name;
537
        } else {
538
            $rName = $name;
539
            $cName = $this->canonicalizeName($rName);
540
        }
541
 
542
        if (
543
            isset($this->invokableClasses[$cName])
544
            || isset($this->factories[$cName])
545
            || isset($this->aliases[$cName])
546
            || isset($this->instances[$cName])
547
        ) {
548
            return true;
549
        }
550
 
551
        if ($checkAbstractFactories && $this->canCreateFromAbstractFactory($cName, $rName)) {
552
            return true;
553
        }
554
 
555
        return false;
556
    }
557
 
558
    /**
559
     * @param  string|array  $name
560
     * @param  bool          $checkAbstractFactories
561
     * @param  bool          $usePeeringServiceManagers
562
     * @return bool
563
     */
564
    public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true)
565
    {
566
        if (is_array($name)) {
567
            list($cName, $rName) = $name;
568
        } else {
569
            $rName = $name;
570
            $cName = $this->canonicalizeName($rName);
571
        }
572
 
573
        if ($this->canCreate(array($cName, $rName), $checkAbstractFactories)) {
574
            return true;
575
        }
576
 
577
        if ($usePeeringServiceManagers) {
578
            foreach ($this->peeringServiceManagers as $peeringServiceManager) {
579
                if ($peeringServiceManager->has($rName)) {
580
                    return true;
581
                }
582
            }
583
        }
584
 
585
        return false;
586
    }
587
 
588
    /**
589
     * Determine if we can create an instance from an abstract factory.
590
     *
591
     * @param  string $cName
592
     * @param  string $rName
593
     * @return bool
594
     */
595
    public function canCreateFromAbstractFactory($cName, $rName)
596
    {
597
        // check abstract factories
598
        foreach ($this->abstractFactories as $index => $abstractFactory) {
599
            // Support string abstract factory class names
600
            if (is_string($abstractFactory) && class_exists($abstractFactory, true)) {
601
                $this->abstractFactories[$index] = $abstractFactory = new $abstractFactory();
602
            }
603
 
604
            if (
605
                isset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)])
606
                && $this->pendingAbstractFactoryRequests[get_class($abstractFactory)] == $rName
607
            ) {
608
                return false;
609
            }
610
 
611
            if ($abstractFactory->canCreateServiceWithName($this, $cName, $rName)) {
612
                return true;
613
            }
614
        }
615
        return false;
616
    }
617
 
618
    /**
619
     * @param  string $alias
620
     * @param  string $nameOrAlias
621
     * @return ServiceManager
622
     * @throws Exception\ServiceNotFoundException
623
     * @throws Exception\InvalidServiceNameException
624
     */
625
    public function setAlias($alias, $nameOrAlias)
626
    {
627
        if (!is_string($alias) || !is_string($nameOrAlias)) {
628
            throw new Exception\InvalidServiceNameException('Service or alias names must be strings.');
629
        }
630
 
631
        $cAlias = $this->canonicalizeName($alias);
632
        $nameOrAlias = $this->canonicalizeName($nameOrAlias);
633
 
634
        if ($alias == '' || $nameOrAlias == '') {
635
            throw new Exception\InvalidServiceNameException('Invalid service name alias');
636
        }
637
 
638
        if ($this->allowOverride === false && $this->has(array($cAlias, $alias), false)) {
639
            throw new Exception\InvalidServiceNameException(sprintf(
640
                'An alias by the name "%s" or "%s" already exists',
641
                $cAlias,
642
                $alias
643
            ));
644
        }
645
 
646
        $this->aliases[$cAlias] = $nameOrAlias;
647
        return $this;
648
    }
649
 
650
    /**
651
     * Determine if we have an alias
652
     *
653
     * @param  string $alias
654
     * @return bool
655
     */
656
    public function hasAlias($alias)
657
    {
658
        $alias = $this->canonicalizeName($alias);
659
        return (isset($this->aliases[$alias]));
660
    }
661
 
662
    /**
663
     * Create scoped service manager
664
     *
665
     * @param  string $peering
666
     * @return ServiceManager
667
     */
668
    public function createScopedServiceManager($peering = self::SCOPE_PARENT)
669
    {
670
        $scopedServiceManager = new ServiceManager();
671
        if ($peering == self::SCOPE_PARENT) {
672
            $scopedServiceManager->peeringServiceManagers[] = $this;
673
        }
674
        if ($peering == self::SCOPE_CHILD) {
675
            $this->peeringServiceManagers[] = $scopedServiceManager;
676
        }
677
        return $scopedServiceManager;
678
    }
679
 
680
    /**
681
     * Add a peering relationship
682
     *
683
     * @param  ServiceManager $manager
684
     * @param  string         $peering
685
     * @return ServiceManager
686
     */
687
    public function addPeeringServiceManager(ServiceManager $manager, $peering = self::SCOPE_PARENT)
688
    {
689
        if ($peering == self::SCOPE_PARENT) {
690
            $this->peeringServiceManagers[] = $manager;
691
        }
692
        if ($peering == self::SCOPE_CHILD) {
693
            $manager->peeringServiceManagers[] = $this;
694
        }
695
        return $this;
696
    }
697
 
698
    /**
699
     * Canonicalize name
700
     *
701
     * @param  string $name
702
     * @return string
703
     */
704
    protected function canonicalizeName($name)
705
    {
706
        if (isset($this->canonicalNames[$name])) {
707
            return $this->canonicalNames[$name];
708
        }
709
 
710
        // this is just for performance instead of using str_replace
711
        return $this->canonicalNames[$name] = strtolower(strtr($name, $this->canonicalNamesReplacements));
712
    }
713
 
714
    /**
715
     * Create service via callback
716
     *
717
     * @param  callable $callable
718
     * @param  string   $cName
719
     * @param  string   $rName
720
     * @throws Exception\ServiceNotCreatedException
721
     * @throws Exception\ServiceNotFoundException
722
     * @throws Exception\CircularDependencyFoundException
723
     * @return object
724
     */
725
    protected function createServiceViaCallback($callable, $cName, $rName)
726
    {
727
        static $circularDependencyResolver = array();
728
        $depKey = spl_object_hash($this) . '-' . $cName;
729
 
730
        if (isset($circularDependencyResolver[$depKey])) {
731
            $circularDependencyResolver = array();
732
            throw new Exception\CircularDependencyFoundException('Circular dependency for LazyServiceLoader was found for instance ' . $rName);
733
        }
734
 
735
        try {
736
            $circularDependencyResolver[$depKey] = true;
737
            $instance = call_user_func($callable, $this, $cName, $rName);
738
            unset($circularDependencyResolver[$depKey]);
739
        } catch (Exception\ServiceNotFoundException $e) {
740
            unset($circularDependencyResolver[$depKey]);
741
            throw $e;
742
        } catch (\Exception $e) {
743
            unset($circularDependencyResolver[$depKey]);
744
            throw new Exception\ServiceNotCreatedException(
745
                sprintf('An exception was raised while creating "%s"; no instance returned', $rName),
746
                $e->getCode(),
747
                $e
748
            );
749
        }
750
        if ($instance === null) {
751
            throw new Exception\ServiceNotCreatedException('The factory was called but did not return an instance.');
752
        }
753
 
754
        return $instance;
755
    }
756
 
757
    /**
758
     * Retrieve a keyed list of all registered services. Handy for debugging!
759
     *
760
     * @return array
761
     */
762
    public function getRegisteredServices()
763
    {
764
        return array(
765
            'invokableClasses' => array_keys($this->invokableClasses),
766
            'factories' => array_keys($this->factories),
767
            'aliases' => array_keys($this->aliases),
768
            'instances' => array_keys($this->instances),
769
        );
770
    }
771
 
772
    /**
773
     * Retrieve a keyed list of all canonical names. Handy for debugging!
774
     *
775
     * @return array
776
     */
777
    public function getCanonicalNames()
778
    {
779
        return $this->canonicalNames;
780
    }
781
 
782
    /**
783
     * Allows to override the canonical names lookup map with predefined
784
     * values.
785
     *
786
     * @param array $canonicalNames
787
     * @return ServiceManager
788
     */
789
    public function setCanonicalNames($canonicalNames)
790
    {
791
        $this->canonicalNames = $canonicalNames;
792
 
793
        return $this;
794
    }
795
 
796
    /**
797
     * Attempt to retrieve an instance via a peering manager
798
     *
799
     * @param  string $name
800
     * @return mixed
801
     */
802
    protected function retrieveFromPeeringManager($name)
803
    {
804
        foreach ($this->peeringServiceManagers as $peeringServiceManager) {
805
            if ($peeringServiceManager->has($name)) {
806
                return $peeringServiceManager->get($name);
807
            }
808
        }
809
 
810
        $name = $this->canonicalizeName($name);
811
 
812
        if ($this->hasAlias($name)) {
813
            do {
814
                $name = $this->aliases[$name];
815
            } while ($this->hasAlias($name));
816
        }
817
 
818
        foreach ($this->peeringServiceManagers as $peeringServiceManager) {
819
            if ($peeringServiceManager->has($name)) {
820
                return $peeringServiceManager->get($name);
821
            }
822
        }
823
 
824
        return null;
825
    }
826
 
827
    /**
828
     * Attempt to create an instance via an invokable class
829
     *
830
     * @param  string $canonicalName
831
     * @param  string $requestedName
832
     * @return null|\stdClass
833
     * @throws Exception\ServiceNotFoundException If resolved class does not exist
834
     */
835
    protected function createFromInvokable($canonicalName, $requestedName)
836
    {
837
        $invokable = $this->invokableClasses[$canonicalName];
838
        if (!class_exists($invokable)) {
839
            throw new Exception\ServiceNotFoundException(sprintf(
840
                '%s: failed retrieving "%s%s" via invokable class "%s"; class does not exist',
841
                __METHOD__,
842
                $canonicalName,
843
                ($requestedName ? '(alias: ' . $requestedName . ')' : ''),
844
                $invokable
845
            ));
846
        }
847
        $instance = new $invokable;
848
        return $instance;
849
    }
850
 
851
    /**
852
     * Attempt to create an instance via a factory
853
     *
854
     * @param  string $canonicalName
855
     * @param  string $requestedName
856
     * @return mixed
857
     * @throws Exception\ServiceNotCreatedException If factory is not callable
858
     */
859
    protected function createFromFactory($canonicalName, $requestedName)
860
    {
861
        $factory = $this->factories[$canonicalName];
862
        if (is_string($factory) && class_exists($factory, true)) {
863
            $factory = new $factory;
864
            $this->factories[$canonicalName] = $factory;
865
        }
866
        if ($factory instanceof FactoryInterface) {
867
            $instance = $this->createServiceViaCallback(array($factory, 'createService'), $canonicalName, $requestedName);
868
        } elseif (is_callable($factory)) {
869
            $instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName);
870
        } else {
871
            throw new Exception\ServiceNotCreatedException(sprintf(
872
                'While attempting to create %s%s an invalid factory was registered for this instance type.',
873
                $canonicalName,
874
                ($requestedName ? '(alias: ' . $requestedName . ')' : '')
875
            ));
876
        }
877
        return $instance;
878
    }
879
 
880
    /**
881
     * Attempt to create an instance via an abstract factory
882
     *
883
     * @param  string $canonicalName
884
     * @param  string $requestedName
885
     * @return object|null
886
     * @throws Exception\ServiceNotCreatedException If abstract factory is not callable
887
     */
888
    protected function createFromAbstractFactory($canonicalName, $requestedName)
889
    {
890
        foreach ($this->abstractFactories as $index => $abstractFactory) {
891
            // support factories as strings
892
            if (is_string($abstractFactory) && class_exists($abstractFactory, true)) {
893
                $this->abstractFactories[$index] = $abstractFactory = new $abstractFactory;
894
            } elseif (!$abstractFactory instanceof AbstractFactoryInterface) {
895
                throw new Exception\ServiceNotCreatedException(sprintf(
896
                    'While attempting to create %s%s an abstract factory could not produce a valid instance.',
897
                    $canonicalName,
898
                    ($requestedName ? '(alias: ' . $requestedName . ')' : '')
899
                ));
900
            }
901
            try {
902
                if ($abstractFactory->canCreateServiceWithName($this, $canonicalName, $requestedName)) {
903
                    $this->pendingAbstractFactoryRequests[get_class($abstractFactory)] = $requestedName;
904
                    $instance = $this->createServiceViaCallback(
905
                        array($abstractFactory, 'createServiceWithName'),
906
                        $canonicalName,
907
                        $requestedName
908
                    );
909
                    unset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]);
910
                } else {
911
                    $instance = false;
912
                }
913
            } catch (\Exception $e) {
914
                unset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]);
915
                throw new Exception\ServiceNotCreatedException(
916
                    sprintf(
917
                        'An abstract factory could not create an instance of %s%s.',
918
                        $canonicalName,
919
                        ($requestedName ? '(alias: ' . $requestedName . ')' : '')
920
                    ),
921
                    $e->getCode(),
922
                    $e
923
                );
924
            }
925
            if (is_object($instance)) {
926
                break;
927
            }
928
        }
929
 
930
        return $instance;
931
    }
932
 
933
    /**
934
     * Checks if the object has this class as one of its parents
935
     *
936
     * @see https://bugs.php.net/bug.php?id=53727
937
     * @see https://github.com/zendframework/zf2/pull/1807
938
     *
939
     * @param string $className
940
     * @param string $type
941
     * @return bool
942
     */
943
    protected static function isSubclassOf($className, $type)
944
    {
945
        if (is_subclass_of($className, $type)) {
946
            return true;
947
        }
948
        if (version_compare(PHP_VERSION, '5.3.7', '>=')) {
949
            return false;
950
        }
951
        if (!interface_exists($type)) {
952
            return false;
953
        }
954
        $r = new ReflectionClass($className);
955
        return $r->implementsInterface($type);
956
    }
957
 
958
    /**
959
     * Unregister a service
960
     *
961
     * Called when $allowOverride is true and we detect that a service being
962
     * added to the instance already exists. This will remove the duplicate
963
     * entry, and also any shared flags previously registered.
964
     *
965
     * @param  string $canonical
966
     * @return void
967
     */
968
    protected function unregisterService($canonical)
969
    {
970
        $types = array('invokableClasses', 'factories', 'aliases');
971
        foreach ($types as $type) {
972
            if (isset($this->{$type}[$canonical])) {
973
                unset($this->{$type}[$canonical]);
974
                break;
975
            }
976
        }
977
 
978
        if (isset($this->instances[$canonical])) {
979
            unset($this->instances[$canonical]);
980
        }
981
 
982
        if (isset($this->shared[$canonical])) {
983
            unset($this->shared[$canonical]);
984
        }
985
    }
986
}