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

Compiler::__sleep()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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