Completed
Push — 2.x ( 183829...ac0eeb )
by Akihito
01:14
created

Compiler   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 10
dl 0
loc 108
ccs 49
cts 49
cp 1
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A hasNoBinding() 0 6 2
A __construct() 0 13 2
A __sleep() 0 4 1
A __wakeup() 0 4 1
A newInstance() 0 8 1
A compile() 0 13 3
A hasBoundMethod() 0 12 3
A requireFile() 0 7 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
     * @var AopClassName
27 24
     */
28
    private $aopClassName;
29 24
30 1
    /**
31
     * @throws \Doctrine\Common\Annotations\AnnotationException
32 24
     */
33 24
    public function __construct(string $classDir)
34 24
    {
35 24
        if (! is_writable($classDir)) {
36 24
            throw new NotWritableException($classDir);
37
        }
38 24
        $this->classDir = $classDir;
39
        $this->codeGen = new CodeGen(
40 1
            (new ParserFactory)->newInstance(),
41
            new BuilderFactory,
42 1
            new Standard(['shortArraySyntax' => true])
43
        );
44
        $this->aopClassName = new AopClassName;
45 1
    }
46
47 1
    public function __sleep()
48 1
    {
49
        return ['classDir'];
50
    }
51
52
    /**
53 10
     * @throws \Doctrine\Common\Annotations\AnnotationException
54
     */
55 10
    public function __wakeup()
56 10
    {
57 10
        $this->__construct($this->classDir);
58
    }
59 10
60
    /**
61
     * {@inheritdoc}
62
     *
63
     * @throws \ReflectionException
64
     */
65 18
    public function newInstance(string $class, array $args, BindInterface $bind)
66
    {
67 18
        $compiledClass = $this->compile($class, $bind);
68 1
        $instance = (new ReflectionClass($compiledClass))->newInstanceArgs($args);
69
        $instance->bindings = $bind->getBindings();
70 17
71 17
        return $instance;
72 7
    }
73
74 10
    /**
75 10
     * {@inheritdoc}
76
     *
77
     * @throws \ReflectionException
78 1
     */
79
    public function compile(string $class, BindInterface $bind) : string
80 1
    {
81
        if ($this->hasNoBinding($class, $bind)) {
82 9
            return $class;
83
        }
84 9
        $aopClassName = ($this->aopClassName)($class, $bind->toString());
0 ignored issues
show
Bug introduced by
The call to toString() misses a required argument $salt.

This check looks for function calls that miss required arguments.

Loading history...
85
        if (class_exists($aopClassName, false)) {
86
            return $aopClassName;
87 18
        }
88
        $this->requireFile($aopClassName, new ReflectionClass($class), $bind);
89 18
90
        return $aopClassName;
91 18
    }
92
93
    private function hasNoBinding($class, BindInterface $bind) : bool
94 17
    {
95
        $hasMethod = $this->hasBoundMethod($class, $bind);
96 17
97
        return ! $bind->getBindings() && ! $hasMethod;
98 17
    }
99
100
    private function hasBoundMethod(string $class, BindInterface $bind) : bool
101 18
    {
102
        $bindingMethods = array_keys($bind->getBindings());
103 18
        $hasMethod = false;
104 18
        foreach ($bindingMethods as $bindingMethod) {
105 18
            if (method_exists($class, $bindingMethod)) {
106 17
                $hasMethod = true;
107 17
            }
108
        }
109
110
        return $hasMethod;
111 18
    }
112
113
    private function requireFile(string $aopClassName, \ReflectionClass $sourceClass, BindInterface $bind) : void
114 9
    {
115
        $code = $this->codeGen->generate($sourceClass, $bind);
116 9
        $file = $code->save($this->classDir, $aopClassName);
117 9
        require_once $file;
118
        class_exists($aopClassName); // ensue class is created
119 9
    }
120
}
121