Completed
Pull Request — 2.x (#91)
by Akihito
02:02
created

Compiler::newInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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