Completed
Push — cs ( 9c9fc7...96045f )
by Akihito
02:05
created

Compiler::__wakeup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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