Completed
Push — 2.x ( 32fdaa...87188e )
by Akihito
01:56
created

Compiler::includeGeneratedCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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