Completed
Push — 2.x ( f873c2...a5fae6 )
by Akihito
20s
created

Compiler::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 8
cts 8
cp 1
rs 9.8666
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Aop;
6
7
use PhpParser\BuilderFactory;
8
use PhpParser\PrettyPrinter\Standard;
9
use Ray\Aop\Exception\NotWritableException;
10
11
final class Compiler implements CompilerInterface
12
{
13
    /**
14
     * @var string
15
     */
16
    public $classDir;
17
18
    /**
19
     * @var CodeGenInterface
20
     */
21
    private $codeGen;
22
23
    /**
24
     * @throws \Doctrine\Common\Annotations\AnnotationException
25
     */
26
    public function __construct(string $classDir)
27 24
    {
28
        if (! is_writable($classDir)) {
29 24
            throw new NotWritableException($classDir);
30 1
        }
31
        $this->classDir = $classDir;
32 24
        $this->codeGen = new CodeGen(
33 24
            (new ParserFactory)->newInstance(),
34 24
            new BuilderFactory,
35 24
            new Standard(['shortArraySyntax' => true])
36 24
        );
37
    }
38 24
39
    public function __sleep()
40 1
    {
41
        return ['classDir'];
42 1
    }
43
44
    /**
45 1
     * @throws \Doctrine\Common\Annotations\AnnotationException
46
     */
47 1
    public function __wakeup()
48 1
    {
49
        $this->__construct($this->classDir);
50
    }
51
52
    /**
53 10
     * {@inheritdoc}
54
     *
55 10
     * @throws \ReflectionException
56 10
     */
57 10
    public function newInstance(string $class, array $args, BindInterface $bind)
58
    {
59 10
        $compiledClass = $this->compile($class, $bind);
60
        $instance = (new ReflectionClass($compiledClass))->newInstanceArgs($args);
61
        $instance->bindings = $bind->getBindings();
62
63
        return $instance;
64
    }
65 18
66
    /**
67 18
     * {@inheritdoc}
68 1
     *
69
     * @throws \ReflectionException
70 17
     */
71 17
    public function compile(string $class, BindInterface $bind) : string
72 7
    {
73
        if ($this->hasNoBinding($class, $bind)) {
74 10
            return $class;
75 10
        }
76
        $baseName = $this->getBaseName($class, $bind);
77
        $newClassName = 'RayAop\\' . $baseName;
78 1
        if (class_exists($newClassName)) {
79
            return $newClassName;
80 1
        }
81
        $file = "{$this->classDir}/{$baseName}.php";
82 9
        if (file_exists($file)) {
83
            /** @noinspection UntrustedInclusionInspection */
84 9
            /** @noinspection PhpIncludeInspection */
85
            include $file;
86
87 18
            return $newClassName;
88
        }
89 18
        $this->includeGeneratedCode($baseName, new ReflectionClass($class), $file, $bind);
90
91 18
        return $newClassName;
92
    }
93
94 17
    private function hasNoBinding($class, BindInterface $bind) : bool
95
    {
96 17
        $hasMethod = $this->hasBoundMethod($class, $bind);
97
98 17
        return ! $bind->getBindings() && ! $hasMethod;
99
    }
100
101 18
    private function getBaseName($class, BindInterface $bind) : string
102
    {
103 18
        return sprintf('%s_%s', str_replace('\\', '_', $class), $bind->toString(''));
104 18
    }
105 18
106 17
    private function hasBoundMethod(string $class, BindInterface $bind) : bool
107 17
    {
108
        $bindingMethods = array_keys($bind->getBindings());
109
        $hasMethod = false;
110
        foreach ($bindingMethods as $bindingMethod) {
111 18
            if (method_exists($class, $bindingMethod)) {
112
                $hasMethod = true;
113
            }
114 9
        }
115
116 9
        return $hasMethod;
117 9
    }
118
119 9
    private function includeGeneratedCode($newClass, \ReflectionClass $sourceClass, string $file, BindInterface $bind)
120 9
    {
121
        $code = $this->codeGen->generate($newClass, $sourceClass, $bind);
122
        file_put_contents($file, $code . PHP_EOL);
123
        /** @noinspection PhpIncludeInspection */
124
        require $file;
125
    }
126
}
127