Completed
Push — cs ( 9c9fc7 )
by Akihito
01:43
created

Compiler::compile()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.25

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 9
cts 12
cp 0.75
rs 9.584
c 0
b 0
f 0
cc 4
nc 4
nop 2
crap 4.25
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 3
    public function __construct(string $classDir)
24
    {
25 3
        if (! is_writable($classDir)) {
26 1
            throw new NotWritableException($classDir);
27
        }
28 3
        $this->classDir = $classDir;
29 3
        $this->codeGen = new CodeGen(
30 3
            (new ParserFactory)->newInstance(),
31 3
            new BuilderFactory,
32 3
            new Standard(['shortArraySyntax' => true])
33
        );
34 3
    }
35
36
    public function __sleep()
37
    {
38
        return ['classDir'];
39
    }
40
41
    public function __wakeup()
42
    {
43
        $this->__construct($this->classDir);
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49
    public function newInstance($class, array $args, BindInterface $bind)
50
    {
51
        $compiledClass = $this->compile($class, $bind);
52
        $instance = (new ReflectionClass($compiledClass))->newInstanceArgs($args);
53
        $instance->bindings = $bind->getBindings();
54
55
        return $instance;
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 2
    public function compile($class, BindInterface $bind) : string
62
    {
63 2
        if ($this->hasNoBinding($class, $bind)) {
64 1
            return $class;
65
        }
66 1
        $newClass = $this->getNewClassName($class, $bind);
67 1
        if (class_exists($newClass)) {
68
            return $newClass;
69
        }
70 1
        $file = "{$this->classDir}/{$newClass}.php";
71 1
        if (file_exists($file)) {
72
            /** @noinspection UntrustedInclusionInspection */
73
            /** @noinspection PhpIncludeInspection */
74
            include $file;
75
76
            return $newClass;
77
        }
78 1
        $this->includeGeneratedCode($newClass, new ReflectionClass($class), $file, $bind);
79
80 1
        return $newClass;
81
    }
82
83 2
    private function hasNoBinding($class, BindInterface $bind) : bool
84
    {
85 2
        $hasMethod = $this->hasBoundMethod($class, $bind);
86
87 2
        return ! $bind->getBindings() && ! $hasMethod;
88
    }
89
90 1
    private function getNewClassName($class, BindInterface $bind) : string
91
    {
92 1
        return sprintf('%s_%s', str_replace('\\', '_', $class), $bind->toString(''));
93
    }
94
95 2
    private function hasBoundMethod(string $class, BindInterface $bind) : bool
96
    {
97 2
        $bindingMethods = array_keys($bind->getBindings());
98 2
        $hasMethod = false;
99 2
        foreach ($bindingMethods as $bindingMethod) {
100 1
            if (method_exists($class, $bindingMethod)) {
101 1
                $hasMethod = true;
102
            }
103
        }
104
105 2
        return $hasMethod;
106
    }
107
108 1
    private function includeGeneratedCode($newClass, \ReflectionClass $sourceClass, string $file, BindInterface $bind)
109
    {
110 1
        $code = $this->codeGen->generate($newClass, $sourceClass, $bind);
111 1
        file_put_contents($file, $code . PHP_EOL);
112
        /** @noinspection PhpIncludeInspection */
113 1
        require $file;
114 1
    }
115
}
116