MetadataExtractor::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Tonic\Component\ApiLayer\JsonRpcExtensions\Documentation;
4
5
use phpDocumentor\Reflection\DocBlock;
6
use Tonic\Component\ApiLayer\JsonRpc\Method\MethodCollection;
7
use Tonic\Component\Reflection\ReflectionFunctionFactory;
8
use Tonic\Component\Reflection\TypeResolver;
9
10
/**
11
 * Extracts metadata from method collection.
12
 */
13
class MetadataExtractor
14
{
15
    /**
16
     * @var TypeResolver
17
     */
18
    private $typeResolver;
19
20
    /**
21
     * @param TypeResolver $typeResolver
22
     */
23
    public function __construct(TypeResolver $typeResolver)
24
    {
25
        $this->typeResolver = $typeResolver;
26
    }
27
28
    /**
29
     * @param MethodCollection $methodCollection
30
     *
31
     * @return array
32
     */
33
    public function extract(MethodCollection $methodCollection)
34
    {
35
        $metadata = [];
36
        /** @var callable $callable */
37
        foreach ($methodCollection as $methodName => $callable) {
38
            $reflectionFunction = ReflectionFunctionFactory::createFromCallable($callable);
39
40
            $requestObjectClass = $this->determineRequestObjectClass($reflectionFunction);
41
            $responseObjectClass = $this->determineResponseObjectClass($reflectionFunction);
42
43
            $docBlock = new DocBlock($reflectionFunction);
44
45
            $metadata[] = [
46
                'method' => $methodName,
47
                'description' => $docBlock->getShortDescription(),
48
                'parameters' => $this->extractParameters($requestObjectClass),
49
                'returns' => $this->extractParameters($responseObjectClass),
50
            ];
51
        }
52
53
        return $metadata;
54
    }
55
56
    /**
57
     * @param string $class
58
     * @param array  $processed
59
     *
60
     * @return array
61
     */
62
    private function extractParameters($class, $processed = [])
63
    {
64
        if (isset($processed[$class])) {
65
            return [];
66
        }
67
        $processed[$class] = true;
68
69
        $reflectionClass = new \ReflectionClass($class);
70
        $parameters = [];
71
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
72
            $type = $this->determinePropertyType($reflectionProperty);
73
74
            $isCollection = false;
75
            if ($extractedType = $this->typeResolver->extractTypeFromCollectionType($type)) {
76
                $isCollection = true;
77
                $type = $extractedType;
78
            }
79
80
            $docBlock = new DocBlock($reflectionProperty);
81
82
            $parameter = [
83
                'name' => $reflectionProperty->getName(),
84
                'description' => $docBlock->getShortDescription(),
85
                'type' => $type.($isCollection ? '[]' : ''),
86
            ];
87
            if (class_exists($type)) {
88
                $parameter = array_merge($parameter, [
89
                    'type' => 'object'.($isCollection ? '[]' : ''),
90
                    'properties' => $this->extractParameters($type, $processed),
91
                ]);
92
            }
93
94
            $parameters[] = $parameter;
95
        }
96
97
        return $parameters;
98
    }
99
100
    /**
101
     * @param \ReflectionFunctionAbstract $reflectionFunction
102
     *
103
     * @return string
104
     */
105
    private function determineRequestObjectClass(\ReflectionFunctionAbstract $reflectionFunction)
106
    {
107
        $reflectionParameters = $reflectionFunction->getParameters();
108
        /** @var \ReflectionParameter $reflectionParameter */
109
        $reflectionParameter = reset($reflectionParameters);
110
111
        return $reflectionParameter->getClass()->name;
112
    }
113
114
    /**
115
     * @param \ReflectionFunctionAbstract $reflectionFunction
116
     *
117
     * @return string
118
     */
119
    private function determineResponseObjectClass(\ReflectionFunctionAbstract $reflectionFunction)
120
    {
121
        return $this->typeResolver->resolveFunctionReturnType($reflectionFunction);
122
    }
123
124
    /**
125
     * @param \ReflectionProperty $reflectionProperty
126
     *
127
     * @return null|string
128
     */
129
    private function determinePropertyType(\ReflectionProperty $reflectionProperty)
130
    {
131
        return $this->typeResolver->resolvePropertyType($reflectionProperty);
132
    }
133
}
134