Completed
Push — master ( f04f52...152406 )
by Anton
03:17
created

ProxyFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
cc 1
nc 1
nop 5
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license MIT
6
 * @author  Valentin V (Vvval)
7
 */
8
declare(strict_types=1);
9
10
namespace Cycle\ORM\Promise;
11
12
use Cycle\ORM\ORMInterface;
13
use Cycle\ORM\Promise\Declaration\Declarations;
14
use Cycle\ORM\Promise\Declaration\Extractor;
15
use Cycle\ORM\Promise\Exception\ProxyFactoryException;
16
use Cycle\ORM\Promise\Materizalizer\EvalMaterializer;
17
use Cycle\ORM\PromiseFactoryInterface;
18
use Cycle\ORM\Schema;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Cycle\ORM\Promise\Schema.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
19
use Doctrine\Instantiator\Instantiator;
20
use Spiral\Core\Container\SingletonInterface;
21
22
final class ProxyFactory implements PromiseFactoryInterface, SingletonInterface
23
{
24
    /** @var Printer */
25
    private $printer;
26
27
    /** @var MaterializerInterface */
28
    private $materializer;
29
30
    /** @var Names */
31
    private $names;
32
33
    /** @var Instantiator */
34
    private $instantiator;
35
36
    /** @var Extractor */
37
    private $extractor;
38
39
    /** @var array */
40
    private $resolved = [];
41
42
    /**
43
     * @param Printer|null               $printer
44
     * @param MaterializerInterface|null $materializer
45
     * @param Names|null                 $names
46
     * @param Instantiator|null          $instantiator
47
     * @param Extractor|null             $extractor
48
     */
49
    public function __construct(
50
        Printer $printer = null,
51
        MaterializerInterface $materializer = null,
52
        Names $names = null,
53
        Instantiator $instantiator = null,
54
        Extractor $extractor = null
55
    ) {
56
        $this->printer = $printer ?? new Printer();
57
        $this->materializer = $materializer ?? new EvalMaterializer();
58
        $this->names = $names ?? new Names();
59
        $this->instantiator = $instantiator ?? new Instantiator();
60
        $this->extractor = $extractor ?? new Extractor();
61
    }
62
63
    /**
64
     * @param ORMInterface $orm
65
     * @param string       $role
66
     * @param array        $scope
67
     * @return PromiseInterface
68
     *
69
     * @throws ProxyFactoryException
70
     * @throws \Doctrine\Instantiator\Exception\ExceptionInterface
71
     */
72
    public function promise(ORMInterface $orm, string $role, array $scope): PromiseInterface
73
    {
74
        $class = $orm->getSchema()->define($role, Schema::ENTITY);
75
        if (empty($class)) {
76
            return new PromiseOne($orm, $role, $scope);
77
        }
78
79
        try {
80
            $reflection = new \ReflectionClass($class);
81
        } catch (\ReflectionException $e) {
82
            throw ProxyFactoryException::wrap($e);
83
        }
84
85
        if (isset($this->resolved[$role])) {
86
            return $this->instantiate($reflection, $this->resolved[$role], $orm, $role, $scope);
87
        }
88
89
        $parent = Declarations::createParentFromReflection($reflection);
90
        $class = Declarations::createClassFromName($this->names->make($reflection), $parent);
91
92
        if (!class_exists($class->getFullName())) {
93
            $this->materializer->materialize(
94
                $this->printer->make($reflection, $class, $parent),
95
                $class->getShortName(),
96
                $reflection
97
            );
98
        }
99
100
        $this->resolved[$role] = $class->getFullName();
101
102
        return $this->instantiate($reflection, $this->resolved[$role], $orm, $role, $scope);
103
    }
104
105
    /**
106
     * @param \ReflectionClass $reflection
107
     * @param string           $className
108
     * @param ORMInterface     $orm
109
     * @param string           $role
110
     * @param array            $scope
111
     * @return PromiseInterface
112
     *
113
     * @throws \Doctrine\Instantiator\Exception\ExceptionInterface
114
     */
115
    private function instantiate(
116
        \ReflectionClass $reflection,
117
        string $className,
118
        ORMInterface $orm,
119
        string $role,
120
        array $scope
121
    ): PromiseInterface {
122
        $structure = $this->extractor->extract($reflection);
123
124
        /** @var PromiseInterface $instance */
125
        $instance = $this->instantiator->instantiate($className);
126
        $instance->{$this->printer->initMethodName($structure)}($orm, $role, $scope);
127
128
        return $instance;
129
    }
130
}