Completed
Push — 2.x ( 4c102d...e762e8 )
by Akihito
02:20
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
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
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
12
final class Compiler implements CompilerInterface
13
{
14
    /**
15
     * @var string
16
     */
17
    public $classDir;
18
19
    /**
20
     * @var CodeGenInterface
21
     */
22
    private $codeGen;
23
24
    /**
25
     * @var AopClassName
26
     */
27 24
    private $aopClassName;
28
29 24
    /**
30 1
     * @throws \Doctrine\Common\Annotations\AnnotationException
31
     */
32 24
    public function __construct(string $classDir)
33 24
    {
34 24
        if (! is_writable($classDir)) {
35 24
            throw new NotWritableException($classDir);
36 24
        }
37
        $this->classDir = $classDir;
38 24
        $this->codeGen = new CodeGen(
39
            (new ParserFactory)->newInstance(),
40 1
            new BuilderFactory,
41
            new Standard(['shortArraySyntax' => true])
42 1
        );
43
        $this->aopClassName = new AopClassName;
44
    }
45 1
46
    public function __sleep()
47 1
    {
48 1
        return ['classDir'];
49
    }
50
51
    /**
52
     * @throws \Doctrine\Common\Annotations\AnnotationException
53 10
     */
54
    public function __wakeup()
55 10
    {
56 10
        $this->__construct($this->classDir);
57 10
    }
58
59 10
    /**
60
     * {@inheritdoc}
61
     */
62
    public function newInstance(string $class, array $args, BindInterface $bind)
63
    {
64
        $compiledClass = $this->compile($class, $bind);
65 18
        $instance = (new ReflectionClass($compiledClass))->newInstanceArgs($args);
66
        assert(isset($instance->bindings));
67 18
        $instance->bindings = $bind->getBindings();
68 1
69
        return $instance;
70 17
    }
71 17
72 7
    /**
73
     * {@inheritdoc}
74 10
     *
75 10
     * @param class-string $class
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
76
     *
77
     * @return class-string
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
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(''));
85
        if (class_exists($aopClassName, false)) {
86
            return $aopClassName;
87 18
        }
88
        $this->requireFile($aopClassName, new ReflectionClass($class), $bind);
89 18
        assert(class_exists($aopClassName));
90
91 18
        return $aopClassName;
92
    }
93
94 17
    /**
95
     * @param class-string $class
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
96 17
     */
97
    private function hasNoBinding(string $class, BindInterface $bind) : bool
98 17
    {
99
        $hasMethod = $this->hasBoundMethod($class, $bind);
100
101 18
        return ! $bind->getBindings() && ! $hasMethod;
102
    }
103 18
104 18
    /**
105 18
     * @param class-string $class
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
106 17
     */
107 17
    private function hasBoundMethod(string $class, BindInterface $bind) : bool
108
    {
109
        $bindingMethods = array_keys($bind->getBindings());
110
        $hasMethod = false;
111 18
        foreach ($bindingMethods as $bindingMethod) {
112
            if (method_exists($class, $bindingMethod)) {
113
                $hasMethod = true;
114 9
            }
115
        }
116 9
117 9
        return $hasMethod;
118
    }
119 9
120 9
    /**
121
     * @param \ReflectionClass<object> $sourceClass
0 ignored issues
show
Documentation introduced by
The doc-type \ReflectionClass<object> could not be parsed: Expected "|" or "end of type", but got "<" at position 16. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
122
     */
123
    private function requireFile(string $aopClassName, \ReflectionClass $sourceClass, BindInterface $bind) : void
124
    {
125
        $code = $this->codeGen->generate($sourceClass, $bind);
126
        $file = $code->save($this->classDir, $aopClassName);
127
        assert(file_exists($file));
128
        require_once $file;
129
        class_exists($aopClassName); // ensue class is created
130
    }
131
}
132