Completed
Push — 2.x ( 266f66...8e3b41 )
by Akihito
01:57
created

Compiler::hasNoBinding()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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