1<?php
2/**
3 * This file is part of the CodeAnalysis add-on for PHP_CodeSniffer.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Greg Sherwood <gsherwood@squiz.net>
10 * @author    Manuel Pichler <mapi@manuel-pichler.de>
11 * @copyright 2007-2014 Manuel Pichler. All rights reserved.
12 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License
13 * @link      http://pear.php.net/package/PHP_CodeSniffer
14 */
15
16/**
17 * Detects unnecessary overridden methods that simply call their parent.
18 *
19 * This rule is based on the PMD rule catalog. The Useless Overriding Method
20 * sniff detects the use of methods that only call their parent classes's method
21 * with the same name and arguments. These methods are not required.
22 *
23 * <code>
24 * class FooBar {
25 *   public function __construct($a, $b) {
26 *     parent::__construct($a, $b);
27 *   }
28 * }
29 * </code>
30 *
31 * @category  PHP
32 * @package   PHP_CodeSniffer
33 * @author    Manuel Pichler <mapi@manuel-pichler.de>
34 * @copyright 2007-2014 Manuel Pichler. All rights reserved.
35 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License
36 * @version   Release: @package_version@
37 * @link      http://pear.php.net/package/PHP_CodeSniffer
38 */
39class Generic_Sniffs_CodeAnalysis_UselessOverridingMethodSniff implements PHP_CodeSniffer_Sniff
40{
41
42
43    /**
44     * Registers the tokens that this sniff wants to listen for.
45     *
46     * @return int[]
47     */
48    public function register()
49    {
50        return array(T_FUNCTION);
51
52    }//end register()
53
54
55    /**
56     * Processes this test, when one of its tokens is encountered.
57     *
58     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
59     * @param int                  $stackPtr  The position of the current token
60     *                                        in the stack passed in $tokens.
61     *
62     * @return void
63     */
64    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
65    {
66        $tokens = $phpcsFile->getTokens();
67        $token  = $tokens[$stackPtr];
68
69        // Skip function without body.
70        if (isset($token['scope_opener']) === false) {
71            return;
72        }
73
74        // Get function name.
75        $methodName = $phpcsFile->getDeclarationName($stackPtr);
76
77        // Get all parameters from method signature.
78        $signature = array();
79        foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
80            $signature[] = $param['name'];
81        }
82
83        $next = ++$token['scope_opener'];
84        $end  = --$token['scope_closer'];
85
86        for (; $next <= $end; ++$next) {
87            $code = $tokens[$next]['code'];
88
89            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$code]) === true) {
90                continue;
91            } else if ($code === T_RETURN) {
92                continue;
93            }
94
95            break;
96        }
97
98        // Any token except 'parent' indicates correct code.
99        if ($tokens[$next]['code'] !== T_PARENT) {
100            return;
101        }
102
103        // Find next non empty token index, should be double colon.
104        $next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
105
106        // Skip for invalid code.
107        if ($next === false || $tokens[$next]['code'] !== T_DOUBLE_COLON) {
108            return;
109        }
110
111        // Find next non empty token index, should be the function name.
112        $next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
113
114        // Skip for invalid code or other method.
115        if ($next === false || $tokens[$next]['content'] !== $methodName) {
116            return;
117        }
118
119        // Find next non empty token index, should be the open parenthesis.
120        $next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
121
122        // Skip for invalid code.
123        if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
124            return;
125        }
126
127        $parameters       = array('');
128        $parenthesisCount = 1;
129        $count            = count($tokens);
130        for (++$next; $next < $count; ++$next) {
131            $code = $tokens[$next]['code'];
132
133            if ($code === T_OPEN_PARENTHESIS) {
134                ++$parenthesisCount;
135            } else if ($code === T_CLOSE_PARENTHESIS) {
136                --$parenthesisCount;
137            } else if ($parenthesisCount === 1 && $code === T_COMMA) {
138                $parameters[] = '';
139            } else if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$code]) === false) {
140                $parameters[(count($parameters) - 1)] .= $tokens[$next]['content'];
141            }
142
143            if ($parenthesisCount === 0) {
144                break;
145            }
146        }//end for
147
148        $next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
149        if ($next === false || $tokens[$next]['code'] !== T_SEMICOLON) {
150            return;
151        }
152
153        // Check rest of the scope.
154        for (++$next; $next <= $end; ++$next) {
155            $code = $tokens[$next]['code'];
156            // Skip for any other content.
157            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$code]) === false) {
158                return;
159            }
160        }
161
162        $parameters = array_map('trim', $parameters);
163        $parameters = array_filter($parameters);
164
165        if (count($parameters) === count($signature) && $parameters === $signature) {
166            $phpcsFile->addWarning('Possible useless method overriding detected', $stackPtr, 'Found');
167        }
168
169    }//end process()
170
171
172}//end class
173