Completed
Push — develop ( 80740b...61b5c3 )
by Mike
10:20
created

UsesTag   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Importance

Changes 0
Metric Value
dl 0
loc 148
rs 10
c 0
b 0
f 0
wmc 15
lcom 0
cbo 0

1 Method

Rating   Name   Duplication   Size   Complexity  
F process() 0 135 15
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of phpDocumentor.
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @author    Mike van Riel <[email protected]>
11
 * @copyright 2010-2018 Mike van Riel / Naenius (http://www.naenius.com)
12
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
13
 * @link      http://phpdoc.org
14
 */
15
16
namespace phpDocumentor\Transformer\Behaviour\Tag;
17
18
/**
19
 * Behaviour that adds support for the uses tag
20
 */
21
class UsesTag
22
{
23
    /**
24
     * Find all return tags that contain 'self' or '$this' and replace those
25
     * terms for the name of the current class' type.
26
     *
27
     * @param \DOMDocument $xml Structure source to apply behaviour onto.
28
     *
29
     * @todo split method into submethods
30
     *
31
     * @return \DOMDocument
32
     */
33
    public function process(\DOMDocument $xml)
34
    {
35
        $xpath = new \DOMXPath($xml);
36
        $nodes = $xpath->query('//tag[@name=\'uses\']');
37
38
        /** @var \DOMElement $node */
39
        foreach ($nodes as $node) {
40
            $refers = $node->getAttribute('refers');
41
            $refers_array = explode('::', $refers);
42
43
            // determine the type so we know where to put the @usedby tag on
44
            $type = 'class';
45
            if (isset($refers_array[1])) {
46
                // starts with $ = property, ends with () = method,
47
                // otherwise constant
48
                $type = $refers_array[1][0] === '$' ? 'property' : 'constant';
49
                $type = substr($refers_array[1], -2) === '()' ? 'method' : $type;
50
            }
51
52
            switch ($type) {
53
                case 'class':
54
                    // escape single quotes in the class name
55
                    $xpath_refers = 'concat(\'' . str_replace(
56
                        ["'", '"'],
57
                        ['\', "\'", \'', '\', \'"\' , \''],
58
                        $refers
59
                    ) . "', '')";
60
61
                    $qry = '/project/file/class[full_name=' . $xpath_refers . ']';
62
                    break;
63
                default:
64
                    $class_name = $refers_array[0];
65
66
                    // escape single quotes in the class name
67
                    $xpath_class_name = 'concat(\'' . str_replace(
68
                        ["'", '"'],
69
                        ['\', "\'", \'', '\', \'"\' , \''],
70
                        $class_name
71
                    ) . "', '')";
72
73
                    // escape single quotes in the method name
74
                    $xpath_method_name = 'concat(\'' . str_replace(
75
                        ["'", '"'],
76
                        ['\', "\'", \'', '\', \'"\' , \''],
77
                        rtrim($refers_array[1], '()')
78
                    ) . "', '')";
79
80
                    $qry = '/project/file/class[full_name=' . $xpath_class_name
81
                        . ']/' . $type . '[name=' . $xpath_method_name . ']';
82
                    break;
83
            }
84
85
            /** @noinspection PhpUsageOfSilenceOperatorInspection as there is no pre-validation possible */
86
            $referral_nodes = @$xpath->query($qry);
87
88
            // if the query is wrong; output a Critical error and continue to
89
            // the next @uses
90
            if ($referral_nodes === false) {
91
                continue;
92
            }
93
94
            // check if the result is unique; if not we error and continue
95
            // to the next @uses
96
            if ($referral_nodes->length > 1) {
97
                continue;
98
            }
99
100
            // if there is one matching element; link them together
101
            if ($referral_nodes->length > 0) {
102
                /** @var \DOMElement $referral */
103
                $referral = $referral_nodes->item(0);
104
                $docblock = $referral->getElementsByTagName('docblock');
105
                if ($docblock->length < 1) {
106
                    $docblock = new \DOMElement('docblock');
107
                    $referral->appendChild($docblock);
108
                } else {
109
                    $docblock = $docblock->item(0);
110
                }
111
112
                $used_by = new \DOMElement('tag');
113
                $docblock->appendChild($used_by);
114
                $used_by->setAttribute('name', 'used_by');
115
                $used_by->setAttribute('line', '');
116
117
                // gather the name of the referring element and set that as refers
118
                // attribute
119
                if ($node->parentNode->parentNode->nodeName === 'class') {
120
                    // if the element where the @uses is in is a class; nothing
121
                    // more than the class name need to returned
122
123
                    /** @var \DOMElement $grandParentNode */
124
                    $grandParentNode = $node->parentNode->parentNode;
125
                    $referral_name = $grandParentNode
126
                        ->getElementsByTagName('full_name')->item(0)->nodeValue;
127
                } else {
128
                    $referral_class_name = null;
129
                    if ($node->parentNode->parentNode->nodeName === 'method') {
130
                        // gather the name of the class where the @uses is in
131
132
                        /** @var \DOMElement $greatGrandParentNode */
133
                        $greatGrandParentNode = $node->parentNode->parentNode->parentNode;
134
                        $referral_class_name = $greatGrandParentNode
135
                            ->getElementsByTagName('full_name')->item(0)
136
                            ->nodeValue;
137
                    }
138
139
                    // gather the name of the subelement of the class where
140
                    // the @uses is in
141
142
                    /** @var \DOMElement $grandParentNode */
143
                    $grandParentNode = $node->parentNode->parentNode;
144
                    $referral_name = $grandParentNode
145
                        ->getElementsByTagName('name')->item(0)->nodeValue;
146
147
                    // if it is a method; suffix with ()
148
                    if ($node->parentNode->parentNode->nodeName === 'method'
149
                        || $node->parentNode->parentNode->nodeName === 'function'
150
                    ) {
151
                        $referral_name .= '()';
152
                    }
153
154
                    // only prefix class name if this is a class member
155
                    if ($referral_class_name) {
156
                        $referral_name = $referral_class_name . '::'
157
                            . $referral_name;
158
                    }
159
                }
160
161
                $used_by->setAttribute('description', $referral_name);
162
                $used_by->setAttribute('refers', $referral_name);
163
            }
164
        }
165
166
        return $xml;
167
    }
168
}
169