1<?php
2/**
3 * Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Greg Sherwood <gsherwood@squiz.net>
10 * @author    Marc McIntyre <mmcintyre@squiz.net>
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    throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_AbstractScopeSniff not found');
18}
19
20/**
21 * Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff.
22 *
23 * Ensures method names and function names are in CamelCaps and
24 * that only magic methods/functions start with a double underscore.
25 *
26 * @category  PHP
27 * @package   PHP_CodeSniffer
28 * @author    Greg Sherwood <gsherwood@squiz.net>
29 * @author    Marc McIntyre <mmcintyre@squiz.net>
30 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
31 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
32 * @version   Release: @package_version@
33 * @link      http://pear.php.net/package/PHP_CodeSniffer
34 */
35class Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff
36{
37
38    /**
39     * A list of all PHP magic methods.
40     *
41     * @var array
42     */
43    protected $magicMethods = array(
44                               'construct'  => true,
45                               'destruct'   => true,
46                               'call'       => true,
47                               'callstatic' => true,
48                               'get'        => true,
49                               'set'        => true,
50                               'isset'      => true,
51                               'unset'      => true,
52                               'sleep'      => true,
53                               'wakeup'     => true,
54                               'tostring'   => true,
55                               'set_state'  => true,
56                               'clone'      => true,
57                               'invoke'     => true,
58                               'debuginfo'  => true,
59                              );
60
61    /**
62     * A list of all PHP non-magic methods starting with a double underscore.
63     *
64     * These come from PHP modules such as SOAPClient.
65     *
66     * @var array
67     */
68    protected $methodsDoubleUnderscore = array(
69                                          'soapcall'               => true,
70                                          'getlastrequest'         => true,
71                                          'getlastresponse'        => true,
72                                          'getlastrequestheaders'  => true,
73                                          'getlastresponseheaders' => true,
74                                          'getfunctions'           => true,
75                                          'gettypes'               => true,
76                                          'dorequest'              => true,
77                                          'setcookie'              => true,
78                                          'setlocation'            => true,
79                                          'setsoapheaders'         => true,
80                                         );
81
82    /**
83     * A list of all PHP magic functions.
84     *
85     * @var array
86     */
87    protected $magicFunctions = array('autoload' => true);
88
89    /**
90     * If TRUE, the string must not have two capital letters next to each other.
91     *
92     * @var bool
93     */
94    public $strict = true;
95
96
97    /**
98     * Constructs a Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff.
99     */
100    public function __construct()
101    {
102        parent::__construct(array(T_CLASS, T_ANON_CLASS, T_INTERFACE, T_TRAIT), array(T_FUNCTION), true);
103
104    }//end __construct()
105
106
107    /**
108     * Processes the tokens within the scope.
109     *
110     * @param PHP_CodeSniffer_File $phpcsFile The file being processed.
111     * @param int                  $stackPtr  The position where this token was
112     *                                        found.
113     * @param int                  $currScope The position of the current scope.
114     *
115     * @return void
116     */
117    protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope)
118    {
119        $methodName = $phpcsFile->getDeclarationName($stackPtr);
120        if ($methodName === null) {
121            // Ignore closures.
122            return;
123        }
124
125        $className = $phpcsFile->getDeclarationName($currScope);
126        $errorData = array($className.'::'.$methodName);
127
128        // Is this a magic method. i.e., is prefixed with "__" ?
129        if (preg_match('|^__[^_]|', $methodName) !== 0) {
130            $magicPart = strtolower(substr($methodName, 2));
131            if (isset($this->magicMethods[$magicPart]) === false
132                && isset($this->methodsDoubleUnderscore[$magicPart]) === false
133            ) {
134                $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
135                $phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData);
136            }
137
138            return;
139        }
140
141        // PHP4 constructors are allowed to break our rules.
142        if ($methodName === $className) {
143            return;
144        }
145
146        // PHP4 destructors are allowed to break our rules.
147        if ($methodName === '_'.$className) {
148            return;
149        }
150
151        // Ignore first underscore in methods prefixed with "_".
152        $methodName = ltrim($methodName, '_');
153
154        $methodProps = $phpcsFile->getMethodProperties($stackPtr);
155        if (PHP_CodeSniffer::isCamelCaps($methodName, false, true, $this->strict) === false) {
156            if ($methodProps['scope_specified'] === true) {
157                $error = '%s method name "%s" is not in camel caps format';
158                $data  = array(
159                          ucfirst($methodProps['scope']),
160                          $errorData[0],
161                         );
162                $phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data);
163            } else {
164                $error = 'Method name "%s" is not in camel caps format';
165                $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
166            }
167
168            $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no');
169            return;
170        } else {
171            $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes');
172        }
173
174    }//end processTokenWithinScope()
175
176
177    /**
178     * Processes the tokens outside the scope.
179     *
180     * @param PHP_CodeSniffer_File $phpcsFile The file being processed.
181     * @param int                  $stackPtr  The position where this token was
182     *                                        found.
183     *
184     * @return void
185     */
186    protected function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
187    {
188        $functionName = $phpcsFile->getDeclarationName($stackPtr);
189        if ($functionName === null) {
190            // Ignore closures.
191            return;
192        }
193
194        $errorData = array($functionName);
195
196        // Is this a magic function. i.e., it is prefixed with "__".
197        if (preg_match('|^__[^_]|', $functionName) !== 0) {
198            $magicPart = strtolower(substr($functionName, 2));
199            if (isset($this->magicFunctions[$magicPart]) === false) {
200                 $error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
201                 $phpcsFile->addError($error, $stackPtr, 'FunctionDoubleUnderscore', $errorData);
202            }
203
204            return;
205        }
206
207        // Ignore first underscore in functions prefixed with "_".
208        $functionName = ltrim($functionName, '_');
209
210        if (PHP_CodeSniffer::isCamelCaps($functionName, false, true, $this->strict) === false) {
211            $error = 'Function name "%s" is not in camel caps format';
212            $phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
213            $phpcsFile->recordMetric($stackPtr, 'CamelCase function name', 'no');
214        } else {
215            $phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes');
216        }
217
218    }//end processTokenOutsideScope()
219
220
221}//end class
222