Completed
Push — refactor ( d2218a )
by Akihito
01:47
created

Compiler::getAopClassName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Aop;
6
7
use function class_exists;
8
use PhpParser\BuilderFactory;
9
use PhpParser\PrettyPrinter\Standard;
10
use Ray\Aop\Exception\NotWritableException;
11
use function sprintf;
12
13
final class Compiler implements CompilerInterface
14
{
15
    /**
16
     * @var string
17
     */
18
    public $classDir;
19
20
    /**
21
     * @var CodeGenInterface
22
     */
23
    private $codeGen;
24
25
    /**
26
     * @throws \Doctrine\Common\Annotations\AnnotationException
27 24
     */
28
    public function __construct(string $classDir)
29 24
    {
30 1
        if (! is_writable($classDir)) {
31
            throw new NotWritableException($classDir);
32 24
        }
33 24
        $this->classDir = $classDir;
34 24
        $this->codeGen = new CodeGen(
35 24
            (new ParserFactory)->newInstance(),
36 24
            new BuilderFactory,
37
            new Standard(['shortArraySyntax' => true])
38 24
        );
39
    }
40 1
41
    public function __sleep()
42 1
    {
43
        return ['classDir'];
44
    }
45 1
46
    /**
47 1
     * @throws \Doctrine\Common\Annotations\AnnotationException
48 1
     */
49
    public function __wakeup()
50
    {
51
        $this->__construct($this->classDir);
52
    }
53 10
54
    /**
55 10
     * {@inheritdoc}
56 10
     *
57 10
     * @throws \ReflectionException
58
     */
59 10
    public function newInstance(string $class, array $args, BindInterface $bind)
60
    {
61
        $compiledClass = $this->compile($class, $bind);
62
        $instance = (new ReflectionClass($compiledClass))->newInstanceArgs($args);
63
        $instance->bindings = $bind->getBindings();
64
65 18
        return $instance;
66
    }
67 18
68 1
    /**
69
     * {@inheritdoc}
70 17
     *
71 17
     * @throws \ReflectionException
72 7
     */
73
    public function compile(string $class, BindInterface $bind) : string
74 10
    {
75 10
        if ($this->hasNoBinding($class, $bind)) {
76
            return $class;
77
        }
78 1
        $aopClassName = $this->getAopClassName($class, $bind);
79
        if (class_exists($aopClassName, false)) {
80 1
            return $aopClassName;
81
        }
82 9
        $this->requireFile($aopClassName, new ReflectionClass($class), $bind);
83
84 9
        return $aopClassName;
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 hasBoundMethod(string $class, BindInterface $bind) : bool
95
    {
96 17
        $bindingMethods = array_keys($bind->getBindings());
97
        $hasMethod = false;
98 17
        foreach ($bindingMethods as $bindingMethod) {
99
            if (method_exists($class, $bindingMethod)) {
100
                $hasMethod = true;
101 18
            }
102
        }
103 18
104 18
        return $hasMethod;
105 18
    }
106 17
107 17
    private function requireFile(string $aopClassName, \ReflectionClass $sourceClass, BindInterface $bind) : void
108
    {
109
        $code = $this->codeGen->generate($sourceClass, $bind);
110
        $file = $code->save($this->classDir, $aopClassName);
111 18
        require_once $file;
112
        class_exists($aopClassName); // ensue class is created
113
    }
114 9
115
    private function getAopClassName(string $class, BindInterface $bind)
116 9
    {
117 9
        return sprintf('%s_%s', $class, $bind->toString(''));
118
    }
119
}
120