1<?php
2/**
3 * PEAR_Sniffs_NamingConventions_ValidClassNameSniff.
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
16/**
17 * PEAR_Sniffs_NamingConventions_ValidClassNameSniff.
18 *
19 * Ensures class and interface names start with a capital letter
20 * and use _ separators.
21 *
22 * @category  PHP
23 * @package   PHP_CodeSniffer
24 * @author    Greg Sherwood <gsherwood@squiz.net>
25 * @author    Marc McIntyre <mmcintyre@squiz.net>
26 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
27 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
28 * @version   Release: @package_version@
29 * @link      http://pear.php.net/package/PHP_CodeSniffer
30 */
31class PEAR_Sniffs_NamingConventions_ValidClassNameSniff implements PHP_CodeSniffer_Sniff
32{
33
34
35    /**
36     * Returns an array of tokens this test wants to listen for.
37     *
38     * @return array
39     */
40    public function register()
41    {
42        return array(
43                T_CLASS,
44                T_INTERFACE,
45                T_TRAIT,
46               );
47
48    }//end register()
49
50
51    /**
52     * Processes this test, when one of its tokens is encountered.
53     *
54     * @param PHP_CodeSniffer_File $phpcsFile The current file being processed.
55     * @param int                  $stackPtr  The position of the current token
56     *                                        in the stack passed in $tokens.
57     *
58     * @return void
59     */
60    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
61    {
62        $tokens = $phpcsFile->getTokens();
63
64        $className = $phpcsFile->findNext(T_STRING, $stackPtr);
65        $name      = trim($tokens[$className]['content']);
66        $errorData = array(ucfirst($tokens[$stackPtr]['content']));
67
68        // Make sure the first letter is a capital.
69        if (preg_match('|^[A-Z]|', $name) === 0) {
70            $error = '%s name must begin with a capital letter';
71            $phpcsFile->addError($error, $stackPtr, 'StartWithCapital', $errorData);
72        }
73
74        // Check that each new word starts with a capital as well, but don't
75        // check the first word, as it is checked above.
76        $validName = true;
77        $nameBits  = explode('_', $name);
78        $firstBit  = array_shift($nameBits);
79        foreach ($nameBits as $bit) {
80            if ($bit === '' || $bit{0} !== strtoupper($bit{0})) {
81                $validName = false;
82                break;
83            }
84        }
85
86        if ($validName === false) {
87            // Strip underscores because they cause the suggested name
88            // to be incorrect.
89            $nameBits = explode('_', trim($name, '_'));
90            $firstBit = array_shift($nameBits);
91            if ($firstBit === '') {
92                $error = '%s name is not valid';
93                $phpcsFile->addError($error, $stackPtr, 'Invalid', $errorData);
94            } else {
95                $newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
96                foreach ($nameBits as $bit) {
97                    if ($bit !== '') {
98                        $newName .= strtoupper($bit{0}).substr($bit, 1).'_';
99                    }
100                }
101
102                $newName = rtrim($newName, '_');
103                $error   = '%s name is not valid; consider %s instead';
104                $data    = $errorData;
105                $data[]  = $newName;
106                $phpcsFile->addError($error, $stackPtr, 'Invalid', $data);
107            }
108        }//end if
109
110    }//end process()
111
112
113}//end class
114