MethodReflectionExtension::getMethodsFromSpec()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2.032

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 8
cts 10
cp 0.8
rs 9.6333
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.032
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * phpDocumentor
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @link      http://phpdoc.org
12
 */
13
14
namespace phpDocumentor\GraphViz\PHPStan;
15
16
use InvalidArgumentException;
17
use phpDocumentor\GraphViz\Edge;
18
use phpDocumentor\GraphViz\Graph;
19
use phpDocumentor\GraphViz\Node;
20
use PHPStan\Reflection\ClassReflection;
21
use PHPStan\Reflection\MethodReflection;
22
use PHPStan\Reflection\MethodsClassReflectionExtension;
23
use PHPStan\Type\BooleanType;
24
use PHPStan\Type\FloatType;
25
use PHPStan\Type\StringType;
26
use PHPStan\Type\Type;
27
use RuntimeException;
28
use SimpleXMLElement;
29
use function array_key_exists;
30
use function array_map;
31
use function file_get_contents;
32
use function in_array;
33
use function simplexml_load_string;
34
use function sprintf;
35
use function str_replace;
36
use function stripos;
37
use function strtolower;
38
use function substr;
39
40
final class MethodReflectionExtension implements MethodsClassReflectionExtension
41
{
42
    private const SUPPORTED_CLASSES = [
43
        Node::class => 'node',
44
        Graph::class => 'graph',
45
        Edge::class => 'edge',
46
    ];
47
48 4
    public function hasMethod(ClassReflection $classReflection, string $methodName) : bool
49
    {
50 4
        if (!array_key_exists($classReflection->getName(), self::SUPPORTED_CLASSES)) {
51
            return false;
52
        }
53
54 4
        $methods           = $this->getMethodsFromSpec(self::SUPPORTED_CLASSES[$classReflection->getName()]);
55 4
        $expectedAttribute = $this->getAttributeFromMethodName($methodName);
56
57 4
        return in_array($expectedAttribute, $methods, true);
58
    }
59
60 2
    public function getMethod(ClassReflection $classReflection, string $methodName) : MethodReflection
61
    {
62 2
        if (stripos($methodName, 'get') === 0) {
63
            return new AttributeGetterMethodReflection($classReflection, $methodName);
64
        }
65
66 2
        $attributeName = $this->getAttributeFromMethodName($methodName);
67
68 2
        return new AttributeSetterMethodReflection(
69 2
            $classReflection,
70 2
            $methodName,
71 2
            $this->getAttributeInputType($attributeName)
72
        );
73
    }
74
75
    /**
76
     * @return string[]
77
     */
78 4
    private function getMethodsFromSpec(string $className) : array
79
    {
80 4
        $simpleXml = $this->getAttributesXmlDoc();
81
82 4
        $elements = $simpleXml->xpath(sprintf("xsd:complexType[@name='%s']/xsd:attribute", $className));
83
84 4
        if ($elements === false) {
85
            throw new InvalidArgumentException(
86
                sprintf('Class "%s" does not exist in Graphviz spec', $className)
87
            );
88
        }
89
90 4
        return array_map(
91
            static function (SimpleXMLElement $attribute) : string {
92 4
                return strtolower((string) $attribute['ref']);
93 4
            },
94 4
            $elements
95
        );
96
    }
97
98 2
    private function getAttributeInputType(string $ref) : Type
99
    {
100 2
        $simpleXml  = $this->getAttributesXmlDoc();
101 2
        $attributes = $simpleXml->xpath(sprintf("xsd:attribute[@name='%s']", $ref));
102
103 2
        if (empty($attributes)) {
104
            return new StringType();
105
        }
106
107 2
        $type = $attributes[0]['type'];
108 2
        $type = str_replace('xsd:', '', (string) $type);
109 2
        switch ($type) {
110 2
            case 'boolean':
111
                return new BooleanType();
112 2
            case 'decimal':
113 1
                return new FloatType();
114 1
            case 'string':
115
            default:
116 1
                return new StringType();
117
        }
118
    }
119
120 6
    private function getAttributesXmlDoc() : SimpleXMLElement
121
    {
122 6
        $fileContent = file_get_contents(__DIR__ . '/assets/attributes.xml');
123
124 6
        if ($fileContent === false) {
125
            throw new RuntimeException('Cannot read attributes spec');
126
        }
127
128 6
        $xml = simplexml_load_string($fileContent);
129 6
        if ($xml === false) {
130
            throw new RuntimeException('Cannot read attributes spec');
131
        }
132
133 6
        return $xml;
134
    }
135
136 6
    private function getAttributeFromMethodName(string $methodName) : string
137
    {
138 6
        return strtolower(substr($methodName, 3));
139
    }
140
}
141