1<?php
2/**
3 * Generic_Sniffs_NamingConventions_ConstructorNameSniff.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Greg Sherwood <gsherwood@squiz.net>
10 * @author    Leif Wickland <lwickland@rightnow.com>
11 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
12 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
13 * @link      http://pear.php.net/package/PHP_CodeSniffer
14 */
15
16if (class_exists('PHP_CodeSniffer_Standards_AbstractScopeSniff', true) === false) {
17    $error = 'Class PHP_CodeSniffer_Standards_AbstractScopeSniff not found';
18    throw new PHP_CodeSniffer_Exception($error);
19}
20
21/**
22 * Generic_Sniffs_NamingConventions_ConstructorNameSniff.
23 *
24 * Favor PHP 5 constructor syntax, which uses "function __construct()".
25 * Avoid PHP 4 constructor syntax, which uses "function ClassName()".
26 *
27 * @category PHP
28 * @package  PHP_CodeSniffer
29 * @author   Leif Wickland <lwickland@rightnow.com>
30 * @license  https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
31 * @version  Release: @package_version@
32 * @link     http://pear.php.net/package/PHP_CodeSniffer
33 */
34class Generic_Sniffs_NamingConventions_ConstructorNameSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff
35{
36
37    /**
38     * The name of the class we are currently checking.
39     *
40     * @var string
41     */
42    private $_currentClass = '';
43
44    /**
45     * A list of functions in the current class.
46     *
47     * @var string[]
48     */
49    private $_functionList = array();
50
51
52    /**
53     * Constructs the test with the tokens it wishes to listen for.
54     */
55    public function __construct()
56    {
57        parent::__construct(array(T_CLASS, T_ANON_CLASS, T_INTERFACE), array(T_FUNCTION), true);
58
59    }//end __construct()
60
61
62    /**
63     * Processes this test when one of its tokens is encountered.
64     *
65     * @param PHP_CodeSniffer_File $phpcsFile The current file being scanned.
66     * @param int                  $stackPtr  The position of the current token
67     *                                        in the stack passed in $tokens.
68     * @param int                  $currScope A pointer to the start of the scope.
69     *
70     * @return void
71     */
72    protected function processTokenWithinScope(
73        PHP_CodeSniffer_File $phpcsFile,
74        $stackPtr,
75        $currScope
76    ) {
77        $className = $phpcsFile->getDeclarationName($currScope);
78        if ($className !== $this->_currentClass) {
79            $this->loadFunctionNamesInScope($phpcsFile, $currScope);
80            $this->_currentClass = $className;
81        }
82
83        $methodName = $phpcsFile->getDeclarationName($stackPtr);
84
85        if (strcasecmp($methodName, $className) === 0) {
86            if (in_array('__construct', $this->_functionList) === false) {
87                $error = 'PHP4 style constructors are not allowed; use "__construct()" instead';
88                $phpcsFile->addError($error, $stackPtr, 'OldStyle');
89            }
90        } else if (strcasecmp($methodName, '__construct') !== 0) {
91            // Not a constructor.
92            return;
93        }
94
95        $tokens = $phpcsFile->getTokens();
96
97        $parentClassName = $phpcsFile->findExtendedClassName($currScope);
98        if ($parentClassName === false) {
99            return;
100        }
101
102        // Stop if the constructor doesn't have a body, like when it is abstract.
103        if (isset($tokens[$stackPtr]['scope_closer']) === false) {
104            return;
105        }
106
107        $endFunctionIndex = $tokens[$stackPtr]['scope_closer'];
108        $startIndex       = $stackPtr;
109        while (($doubleColonIndex = $phpcsFile->findNext(T_DOUBLE_COLON, $startIndex, $endFunctionIndex)) !== false) {
110            if ($tokens[($doubleColonIndex + 1)]['code'] === T_STRING
111                && $tokens[($doubleColonIndex + 1)]['content'] === $parentClassName
112            ) {
113                $error = 'PHP4 style calls to parent constructors are not allowed; use "parent::__construct()" instead';
114                $phpcsFile->addError($error, ($doubleColonIndex + 1), 'OldStyleCall');
115            }
116
117            $startIndex = ($doubleColonIndex + 1);
118        }
119
120    }//end processTokenWithinScope()
121
122
123    /**
124     * Extracts all the function names found in the given scope.
125     *
126     * @param PHP_CodeSniffer_File $phpcsFile The current file being scanned.
127     * @param int                  $currScope A pointer to the start of the scope.
128     *
129     * @return void
130     */
131    protected function loadFunctionNamesInScope(PHP_CodeSniffer_File $phpcsFile, $currScope)
132    {
133        $this->_functionList = array();
134        $tokens = $phpcsFile->getTokens();
135
136        for ($i = ($tokens[$currScope]['scope_opener'] + 1); $i < $tokens[$currScope]['scope_closer']; $i++) {
137            if ($tokens[$i]['code'] !== T_FUNCTION) {
138                continue;
139            }
140
141            $next = $phpcsFile->findNext(T_STRING, $i);
142            $this->_functionList[] = trim($tokens[$next]['content']);
143        }
144
145    }//end loadFunctionNamesInScope()
146
147
148}//end class
149