Completed
Push — master ( 8418b3...91ec6e )
by
unknown
13s queued 11s
created

php/PDepend/Metrics/Analyzer/CodeRankAnalyzer.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file is part of PDepend.
4
 *
5
 * PHP Version 5
6
 *
7
 * Copyright (c) 2008-2017 Manuel Pichler <[email protected]>.
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 *
14
 *   * Redistributions of source code must retain the above copyright
15
 *     notice, this list of conditions and the following disclaimer.
16
 *
17
 *   * Redistributions in binary form must reproduce the above copyright
18
 *     notice, this list of conditions and the following disclaimer in
19
 *     the documentation and/or other materials provided with the
20
 *     distribution.
21
 *
22
 *   * Neither the name of Manuel Pichler nor the names of his
23
 *     contributors may be used to endorse or promote products derived
24
 *     from this software without specific prior written permission.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
 * POSSIBILITY OF SUCH DAMAGE.
38
 *
39
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
40
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41
 */
42
43
namespace PDepend\Metrics\Analyzer;
44
45
use PDepend\Metrics\AbstractAnalyzer;
46
use PDepend\Metrics\Analyzer\CodeRankAnalyzer\StrategyFactory;
47
use PDepend\Metrics\AnalyzerNodeAware;
48
use PDepend\Source\AST\ASTArtifact;
49
use PDepend\Source\AST\ASTArtifactList;
50
51
/**
52
 * Calculates the code rank metric for classes and namespaces.
53
 *
54
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
55
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
56
 */
57
class CodeRankAnalyzer extends AbstractAnalyzer implements AnalyzerNodeAware
58
{
59
    /**
60
     * Metrics provided by the analyzer implementation.
61
     */
62
    const M_CODE_RANK         = 'cr',
63
          M_REVERSE_CODE_RANK = 'rcr';
64
65
    /**
66
     * The used damping factor.
67
     */
68
    const DAMPING_FACTOR = 0.85;
69
70
    /**
71
     * Number of loops for the code range calculation.
72
     */
73
    const ALGORITHM_LOOPS = 25;
74
75
    /**
76
     * Option key for the code rank mode.
77
     */
78
    const STRATEGY_OPTION = 'coderank-mode';
79
80
    /**
81
     * All found nodes.
82
     *
83
     * @var array(string=>array)
84
     */
85
    private $nodes = array();
86
87
    /**
88
     * List of node collect strategies.
89
     *
90
     * @var \PDepend\Metrics\Analyzer\CodeRankAnalyzer\CodeRankStrategyI[]
91
     */
92
    private $strategies = array();
93
94
    /**
95
     * Hash with all calculated node metrics.
96
     *
97
     * <code>
98
     * array(
99
     *     '0375e305-885a-4e91-8b5c-e25bda005438'  =>  array(
100
     *         'loc'    =>  42,
101
     *         'ncloc'  =>  17,
102
     *         'cc'     =>  12
103
     *     ),
104
     *     'e60c22f0-1a63-4c40-893e-ed3b35b84d0b'  =>  array(
105
     *         'loc'    =>  42,
106
     *         'ncloc'  =>  17,
107
     *         'cc'     =>  12
108
     *     )
109
     * )
110
     * </code>
111
     *
112
     * @var array(string=>array)
113
     */
114
    private $nodeMetrics = null;
115
116
    /**
117
     * Processes all {@link \PDepend\Source\AST\ASTNamespace} code nodes.
118
     *
119
     * @param \PDepend\Source\AST\ASTNamespace[] $namespaces
120
     * @return void
121
     */
122 40
    public function analyze($namespaces)
123
    {
124 40
        if ($this->nodeMetrics === null) {
125 40
            $this->fireStartAnalyzer();
126
127 40
            $factory = new StrategyFactory();
128 40
            if (isset($this->options[self::STRATEGY_OPTION])) {
129 28
                foreach ($this->options[self::STRATEGY_OPTION] as $identifier) {
130 28
                    $this->strategies[] = $factory->createStrategy($identifier);
131 28
                }
132 28
            } else {
133 12
                $this->strategies[] = $factory->createDefaultStrategy();
134
            }
135
136
            // Register all listeners
137 40
            foreach ($this->getVisitListeners() as $listener) {
138
                foreach ($this->strategies as $strategy) {
139
                    $strategy->addVisitListener($listener);
140
                }
141 40
            }
142
143 40
            foreach ($namespaces as $namespace) {
144
                // Traverse all strategies
145 40
                foreach ($this->strategies as $strategy) {
146 40
                    $namespace->accept($strategy);
147 40
                }
148 40
            }
149
150
            // Collect all nodes
151 40
            foreach ($this->strategies as $strategy) {
152 40
                $collected    = $strategy->getCollectedNodes();
153 40
                $this->nodes = array_merge_recursive($collected, $this->nodes);
154 40
            }
155
156
            // Init node metrics
157 40
            $this->nodeMetrics = array();
158
159
            // Calculate code rank metrics
160 40
            $this->buildCodeRankMetrics();
161
162 40
            $this->fireEndAnalyzer();
163 40
        }
164 40
    }
165
166
    /**
167
     * This method will return an <b>array</b> with all generated metric values
168
     * for the given <b>$node</b>. If there are no metrics for the requested
169
     * node, this method will return an empty <b>array</b>.
170
     *
171
     * @param  \PDepend\Source\AST\ASTArtifact $artifact
172
     * @return array(string=>mixed)
0 ignored issues
show
The doc-type array(string=>mixed) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
173
     */
174 40
    public function getNodeMetrics(ASTArtifact $artifact)
175
    {
176 40
        if (isset($this->nodeMetrics[$artifact->getId()])) {
177 38
            return $this->nodeMetrics[$artifact->getId()];
178
        }
179 2
        return array();
180
    }
181
182
    /**
183
     * Generates the forward and reverse code rank for the given <b>$nodes</b>.
184
     *
185
     * @return void
186
     */
187 40
    protected function buildCodeRankMetrics()
188
    {
189 40
        foreach (array_keys($this->nodes) as $id) {
190 40
            $this->nodeMetrics[$id] = array(
191 40
                self::M_CODE_RANK          =>  0,
192 40
                self::M_REVERSE_CODE_RANK  =>  0
193 40
            );
194 40
        }
195 40 View Code Duplication
        foreach ($this->computeCodeRank('out', 'in') as $id => $rank) {
196 40
            $this->nodeMetrics[$id][self::M_CODE_RANK] = $rank;
197 40
        }
198 40 View Code Duplication
        foreach ($this->computeCodeRank('in', 'out') as $id => $rank) {
199 40
            $this->nodeMetrics[$id][self::M_REVERSE_CODE_RANK] = $rank;
200 40
        }
201 40
    }
202
203
    /**
204
     * Calculates the code rank for the given <b>$nodes</b> set.
205
     *
206
     * @param string $id1 Identifier for the incoming edges.
207
     * @param string $id2 Identifier for the outgoing edges.
208
     *
209
     * @return array(string=>float)
0 ignored issues
show
The doc-type array(string=>float) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
210
     */
211 40
    protected function computeCodeRank($id1, $id2)
212
    {
213 40
        $dampingFactory = self::DAMPING_FACTOR;
214
215 40
        $ranks = array();
216
217 40
        foreach (array_keys($this->nodes) as $name) {
218 40
            $ranks[$name] = 1;
219 40
        }
220
221 40
        for ($i = 0; $i < self::ALGORITHM_LOOPS; $i++) {
222 40
            foreach ($this->nodes as $name => $info) {
223 40
                $rank = 0;
224 40
                foreach ($info[$id1] as $ref) {
225 40
                    $previousRank = $ranks[$ref];
226 40
                    $refCount     = count($this->nodes[$ref][$id2]);
227
228 40
                    $rank += ($previousRank / $refCount);
229 40
                }
230 40
                $ranks[$name] = ((1 - $dampingFactory)) + $dampingFactory * $rank;
231 40
            }
232 40
        }
233 40
        return $ranks;
234
    }
235
}
236