Completed
Push — master ( d569a5...28eb0b )
by Vitaly
02:23
created

Inject::validate()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 23
ccs 12
cts 12
cp 1
rs 8.5906
cc 5
eloc 11
nc 4
nop 3
crap 5
1
<?php
2
declare(strict_types = 1);
3
4
/**
5
 * Created by PhpStorm.
6
 * User: root
7
 * Date: 27.07.2016
8
 * Time: 1:55.
9
 */
10
namespace samsonframework\container\annotation;
11
12
use samsonframework\container\metadata\PropertyMetadata;
13
14
/**
15
 * Injection annotation class.
16
 *
17
 * @Annotation
18
 */
19
class Inject implements PropertyInterface
20
{
21
    /** @var string Injectable dependency */
22
    protected $dependency;
23
24
    /**
25
     * Inject constructor.
26
     *
27
     * @param array $valueOrValues
28
     */
29 9
    public function __construct(array $valueOrValues)
30
    {
31
        // Get first argument from annotation
32 9
        $this->dependency = $valueOrValues['value'] ?? null;
33
34
        // Convert empty dependency to null
35 9
        $this->dependency = $this->dependency !== '' ? $this->dependency : null;
36
37
        // Removed first namespace separator if present
38 9
        $this->dependency = is_string($this->dependency) ? ltrim($this->dependency, '\\') : $this->dependency;
39 9
    }
40
41
    /** {@inheritdoc} */
42 9
    public function toPropertyMetadata(PropertyMetadata $propertyMetadata)
43
    {
44
        // Get @Inject("value")
45 9
        $propertyMetadata->dependency = $this->dependency;
46
47 9
        $this->validate(
48 9
            $propertyMetadata->typeHint,
49 9
            $propertyMetadata->dependency,
50 9
            $propertyMetadata->classMetadata->nameSpace
51
        );
52 7
    }
53
54
    /**
55
     * Validate dependency.
56
     *
57
     * @param string $type
58
     * @param string $dependency
59
     * @param string $namespace
60
     */
61 9
    protected function validate(&$type, &$dependency, $namespace)
62
    {
63 9
        $dependency = $this->buildFullClassName($dependency, $namespace);
64 9
        $type = $this->buildFullClassName($type, $namespace);
65
66
        // Check for inheritance violation
67 9
        if ($this->checkInheritanceViolation($type, $dependency)) {
68 1
            throw new \InvalidArgumentException(
69 1
                '@Inject dependency violates ' . $type . ' inheritance with ' . $dependency
70
            );
71
        }
72
73 8
        if ($this->checkInterfaceWithoutClassName($type, $dependency)) {
74 1
            throw new \InvalidArgumentException(
75
                'Cannot @Inject interface, inherited class name should be specified
76 1
                ');
77
        }
78
79
        // Empty @Inject with type hint - use type hine as dependency
80 7
        if ($dependency === null && $type !== null) {
81 2
            $dependency = $type;
82
        }
83 7
    }
84
85
    /**
86
     * Build full class name.
87
     *
88
     * @param string $className Full or short class name
89
     * @param string $namespace Name space
90
     *
91
     * @return string Full class name
92
     */
93 9 View Code Duplication
    protected function buildFullClassName($className, $namespace)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
    {
95
        // Check if we need to append namespace to dependency
96 9
        if ($className !== null && strpos($className, '\\') === false) {
97 2
            return $namespace . '\\' . $className;
98
        }
99
100 8
        return $className;
101
    }
102
103
    /**
104
     * Check if @Inject violates inheritance.
105
     *
106
     * @param string $type       Property/Parameter type
107
     * @param string $dependency @Inject value
108
     *
109
     * @return bool True if @Inject violates inheritance
110
     */
111 9
    protected function checkInheritanceViolation($type, $dependency) : bool
112
    {
113
        // Check for inheritance violation
114 9
        if ($dependency !== null && $type !== null) {
115 6
            $inheritance = array_merge(
116 6
                [$dependency],
117
                class_parents($dependency),
118
                class_implements($dependency)
119
            );
120 6
            return !in_array($type, $inheritance, true);
121
        }
122
123 4
        return false;
124
    }
125
126
    /**
127
     * Check if @Inject has no class name and type hint is interface.
128
     *
129
     * @param string $type       Property/Parameter type
130
     * @param string $dependency @Inject value
131
     *
132
     * @return bool True if @Inject has no class name and type hint is interface.
133
     */
134 8
    protected function checkInterfaceWithoutClassName($type, $dependency) : bool
135
    {
136 8
        return $type !== null
137 8
        && $dependency === null
138 8
        && (new \ReflectionClass($type))->isInterface();
139
    }
140
}
141