Passed
Push — master ( 4b6191...118b18 )
by Julius
01:48
created

NamespaceIndexBuilder::addElementTocEntry()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Julius Härtl <[email protected]>
4
 *
5
 * @author Julius Härtl <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 *  This program is free software: you can redistribute it and/or modify
10
 *  it under the terms of the GNU Affero General Public License as
11
 *  published by the Free Software Foundation, either version 3 of the
12
 *  License, or (at your option) any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU Affero General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU Affero General Public License
20
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace JuliusHaertl\PHPDocToRst\Builder;
25
26
27
use phpDocumentor\Reflection\DocBlock\Tags\Param;
28
use phpDocumentor\Reflection\Fqsen;
29
use phpDocumentor\Reflection\Php\Constant;
30
use phpDocumentor\Reflection\Php\Argument;
31
use phpDocumentor\Reflection\Php\Function_;
32
use phpDocumentor\Reflection\Php\Namespace_;
33
34
/**
35
 * This class will build an index for each namespace.
36
 * It contains a toc for child namespaces, classes, traits, interfaces and functions
37
 *
38
 * @package JuliusHaertl\PHPDocToRst\Builder
39
 */
40
class NamespaceIndexBuilder extends PhpDomainBuilder {
41
42
    const RENDER_INDEX_NAMESPACE = 0;
43
    const RENDER_INDEX_CLASSES = 1;
44
    const RENDER_INDEX_TRAITS = 2;
45
    const RENDER_INDEX_INTERFACES = 3;
46
    const RENDER_INDEX_FUNCTIONS = 4;
47
    const RENDER_INDEX_CONSTANTS = 5;
48
49
    /** @var Namespace_ */
50
    private $currentNamespace;
51
52
    /** @var Namespace_[] */
53
    private $namespaces;
54
55
    /** @var Namespace_[] */
56
    private $childNamespaces = [];
57
58
    /** @var Function_[] */
59
    private $functions;
60
61
    /** @var Constant[] */
62
    private $constants;
63
64
    public function __construct($extensions, $namespaces, Namespace_ $current, $functions, $constants) {
65
        parent::__construct($extensions);
66
        $this->namespaces = $namespaces;
67
        $this->currentNamespace = $current;
68
        $this->functions = $functions;
69
        $this->constants = $constants;
70
        $this->findChildNamespaces();
71
    }
72
73
    /**
74
     * Find child namespaces for current namespace
75
     */
76
    private function findChildNamespaces() {
77
        $currentNamespaceFqsen = (string)$this->currentNamespace->getFqsen();
78
        /** @var Namespace_ $namespace */
79
        foreach ($this->namespaces as $namespace) {
80
            // check if not root and doesn't start with current namespace
81
            if ($currentNamespaceFqsen !== '\\' && strpos((string)$namespace->getFqsen(), $currentNamespaceFqsen . '\\') !== 0) {
82
                continue;
83
            }
84
            if ((string)$namespace->getFqsen() !== $currentNamespaceFqsen && strpos((string)$namespace->getFqsen(), $currentNamespaceFqsen) === 0) {
85
                // only keep first level children
86
                $childrenPath = substr((string)$namespace->getFqsen(), strlen((string)$this->currentNamespace->getFqsen()) + 1);
87
                if (strpos($childrenPath, '\\') === false) {
88
                    $this->childNamespaces[] = $namespace;
89
                }
90
            }
91
        }
92
    }
93
94
    public function render() {
95
        $currentNamespaceFqsen = (string)$this->currentNamespace->getFqsen();
96
        if ($currentNamespaceFqsen !== '\\') {
97
            $label = str_replace('\\', '-', $currentNamespaceFqsen);
98
            $this->addLine('.. _namespace' . $label . ':')->addLine();
99
            $this->addH1(RstBuilder::escape($this->currentNamespace->getName()));
100
            $this->addLine(self::escape($currentNamespaceFqsen))->addLine();
101
        } else {
102
            $label = 'root-namespace';
103
            $this->addLine('.. _namespace-' . $label . ':')->addLine();
104
            $this->addH1(RstBuilder::escape('\\'));
105
        }
106
        $this->addLine();
107
108
        $this->addIndex(self::RENDER_INDEX_NAMESPACE);
109
        $this->addIndex(self::RENDER_INDEX_INTERFACES);
110
        $this->addIndex(self::RENDER_INDEX_CLASSES);
111
        $this->addIndex(self::RENDER_INDEX_TRAITS);
112
113
        if ($this->shouldRenderIndex(self::RENDER_INDEX_CONSTANTS)) {
114
            $this->addConstants($this->constants);
115
        }
116
        $this->addFunctions();
117
    }
118
119
    protected function addIndex($type) {
120
        if ($this->shouldRenderIndex($type)) {
121
            $this->addH2($this->getHeaderForType($type));
122
            $this->addLine('.. toctree::');
123
            $this->indent();
124
            $this->addLine(':maxdepth: 1')->addLine();
125
            /** @var Fqsen $entry */
126
            foreach ($this->getElementList($type) as $entry) {
127
                if (!$this->shouldRenderIndex($type, $entry)) {
128
                    continue;
129
                }
130
                if ($type === self::RENDER_INDEX_NAMESPACE) {
131
                    $this->addLine($entry->getName() . ' <' . $entry->getName() . '/index>');
132
                } else {
133
                    $this->addElementTocEntry($entry);
134
                }
135
            }
136
            $this->unindent();
137
            $this->addLine();
138
        }
139
    }
140
141
    private function addFunctions() {
142
        if (!$this->shouldRenderIndex(self::RENDER_INDEX_FUNCTIONS)) {
143
            return;
144
        }
145
        $this->addH2('Functions');
146
        /** @var Function_ $function */
147
        foreach ($this->functions as $function) {
148
            if (!$this->shouldRenderIndex(self::RENDER_INDEX_FUNCTIONS, $function)) {
149
                continue;
150
            }
151
            $docBlock = $function->getDocBlock();
152
            $params = [];
153
            if ($docBlock !== null) {
154
                /** @var Param $param */
155
                foreach ($docBlock->getTagsByName('param') as $param) {
156
                    $params[$param->getVariableName()] = $param;
1 ignored issue
show
Bug introduced by
The method getVariableName() does not exist on phpDocumentor\Reflection\DocBlock\Tag. It seems like you code against a sub-type of phpDocumentor\Reflection\DocBlock\Tag such as phpDocumentor\Reflection\DocBlock\Tags\Property or phpDocumentor\Reflection\DocBlock\Tags\Var_ or phpDocumentor\Reflection\DocBlock\Tags\Param or phpDocumentor\Reflection...lock\Tags\PropertyWrite or phpDocumentor\Reflection...Block\Tags\PropertyRead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

156
                    $params[$param->/** @scrutinizer ignore-call */ getVariableName()] = $param;
Loading history...
157
                }
158
            }
159
            $args = '';
160
            /** @var Argument $argument */
161
            foreach ($function->getArguments() as $argument) {
162
                // TODO: defaults, types
163
                $args .= '$' . $argument->getName() . ', ';
164
            }
165
            $args = substr($args, 0, -2);
166
            $this->beginPhpDomain('function', $function->getName() . '(' . $args . ')');
167
            $this->addDocBlockDescription($function);
168 View Code Duplication
            if (!empty($params)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
169
                foreach ($function->getArguments() as $argument) {
170
                    /** @var Param $param */
171
                    $param = $params[$argument->getName()];
172
                    if ($param !== null) {
173
                        $this->addMultiline(':param ' . self::escape($param->getType()) . ' $' . $argument->getName() . ': ' . $param->getDescription(), true);
174
                    }
175
                }
176
            }
177
            $this->endPhpDomain('function');
178
        }
179
    }
180
181
    private function addElementTocEntry(Fqsen $entry) {
182
        $currentNamespaceFqsen = (string)$this->currentNamespace->getFqsen();
183
        $subPath = $entry;
184
        if ($currentNamespaceFqsen !== '\\' && substr($entry, 0, strlen($currentNamespaceFqsen)) === $currentNamespaceFqsen) {
185
            $subPath = substr($entry, strlen($currentNamespaceFqsen));
186
        }
187
        $path = substr(str_replace('\\', '/', $subPath), 1);
188
        $this->addLine($entry->getName() . ' <' . $path . '>');
189
    }
190
191
    private function shouldRenderIndex($type, $element = null) {
192
        foreach ($this->extensions as $extension) {
193
            if (!$extension->shouldRenderIndex($type, $element)) {
194
                return false;
195
            }
196
        }
197
        if ($element === null) {
198
            return (count($this->getElementList($type)) > 0);
199
        }
200
        return true;
201
    }
202
203
    private function getHeaderForType($type) {
204
        $headers = [self::RENDER_INDEX_NAMESPACE => 'Namespaces', self::RENDER_INDEX_INTERFACES => 'Interfaces', self::RENDER_INDEX_CLASSES => 'Classes', self::RENDER_INDEX_TRAITS => 'Traits', self::RENDER_INDEX_FUNCTIONS => 'Functions', self::RENDER_INDEX_CONSTANTS => 'Constants'];
205
        return $headers[$type];
206
    }
207
208
    /**
209
     * @param int $type
210
     * @return array
211
     */
212
    private function getElementList($type) {
213
        $elements = [];
214
        switch ($type) {
215
            case self::RENDER_INDEX_NAMESPACE:
216
                $elements = $this->childNamespaces;
217
                break;
218
            case self::RENDER_INDEX_CLASSES:
219
                $elements = $this->currentNamespace->getClasses();
220
                break;
221
            case self::RENDER_INDEX_INTERFACES:
222
                $elements = $this->currentNamespace->getInterfaces();
223
                break;
224
            case self::RENDER_INDEX_TRAITS:
225
                $elements = $this->currentNamespace->getTraits();
226
                break;
227
            case self::RENDER_INDEX_FUNCTIONS:
228
                $elements = $this->functions;
229
                break;
230
            case self::RENDER_INDEX_CONSTANTS:
231
                $elements = $this->constants;
232
                break;
233
        }
234
        return $elements;
235
    }
236
237
}