├── .gitignore
├── CodeIgniter4
├── Tests
│ ├── WhiteSpace
│ │ ├── BooleanNotSpaceAfterUnitTest.inc
│ │ ├── BooleanNotSpaceAfterUnitTest.inc.fixed
│ │ ├── DisallowTabsInAlignmentUnitTest.inc
│ │ ├── DisallowTabsInAlignmentUnitTest.inc.fixed
│ │ ├── VerticalEmptyLinesUnitTest.inc.fixed
│ │ ├── VerticalEmptyLinesUnitTest.inc
│ │ ├── BooleanNotSpaceAfterUnitTest.php
│ │ ├── DisallowTabsInAlignmentUnitTest.php
│ │ └── VerticalEmptyLinesUnitTest.php
│ ├── NamingConventions
│ │ ├── ValidVariableNameUnitTest.inc
│ │ ├── ValidMethodNameUnitTest.inc
│ │ ├── ValidVariableNameUnitTest.php
│ │ └── ValidMethodNameUnitTest.php
│ ├── Operators
│ │ ├── BooleanOrUnitTest.inc
│ │ ├── BooleanOrUnitTest.inc.fixed
│ │ ├── BooleanAndUnitTest.inc
│ │ ├── BooleanAndUnitTest.inc.fixed
│ │ ├── BooleanOrUnitTest.php
│ │ └── BooleanAndUnitTest.php
│ ├── ControlStructures
│ │ ├── AllmanControlSignatureUnitTest.php
│ │ ├── AllmanControlSignatureUnitTest.inc
│ │ └── AllmanControlSignatureUnitTest.inc.fixed
│ └── Arrays
│ │ ├── ArrayDeclarationUnitTest.php
│ │ ├── ArrayDeclarationUnitTest.inc
│ │ └── ArrayDeclarationUnitTest.inc.fixed
├── Sniffs
│ ├── PHP
│ │ ├── DiscouragedFunctionsSniff.php
│ │ └── ForbiddenFunctionsSniff.php
│ ├── Operators
│ │ ├── BooleanOrSniff.php
│ │ ├── BooleanAndSniff.php
│ │ ├── IsIdenticalSniff.php
│ │ └── IsNotIdenticalSniff.php
│ ├── Files
│ │ ├── OneClassPerFileSniff.php
│ │ ├── FilenameMatchesClassSniff.php
│ │ └── HelperFileSniff.php
│ ├── WhiteSpace
│ │ ├── BooleanNotSpaceAfterSniff.php
│ │ ├── VerticalEmptyLinesSniff.php
│ │ ├── DisallowTabsInAlignmentSniff.php
│ │ └── FunctionClosingBraceSpaceSniff.php
│ ├── NamingConventions
│ │ ├── ValidFunctionNameSniff.php
│ │ ├── ValidMethodNameSniff.php
│ │ └── ValidVariableNameSniff.php
│ ├── ControlStructures
│ │ ├── ControlStructureSpacingSniff.php
│ │ └── AllmanControlSignatureSniff.php
│ ├── Commenting
│ │ ├── ClassCommentSniff.php
│ │ └── FileCommentSniff.php
│ └── Functions
│ │ └── FunctionDeclarationSniff.php
├── Util
│ └── Common.php
└── ruleset.xml
├── CONTRIBUTING.md
├── phpunit.xml.dist
├── composer.json
├── LICENSE
├── .travis.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.phar
2 | composer.lock
3 | /vendor/
4 | /build
5 | phpunit.xml
6 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/WhiteSpace/BooleanNotSpaceAfterUnitTest.inc:
--------------------------------------------------------------------------------
1 |
7 |
8 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/Operators/BooleanOrUnitTest.inc:
--------------------------------------------------------------------------------
1 | 201,
10 | ];
11 |
12 | $a = 'a';
13 | $array = 'array';
14 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/Operators/BooleanAndUnitTest.inc:
--------------------------------------------------------------------------------
1 | 201,
10 | ];
11 |
12 | $a = 'a';
13 | $array = 'array';
14 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/NamingConventions/ValidMethodNameUnitTest.inc:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/WhiteSpace/VerticalEmptyLinesUnitTest.inc.fixed:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ./vendor/squizlabs/php_codesniffer/tests/AllTests.php
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ./CodeIgniter4
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codeigniter4/codeigniter4-standard",
3 | "type": "phpcodesniffer-standard",
4 | "description": "CodeIgniter 4 Standard for PHP_CodeSniffer 3.",
5 | "license":"MIT",
6 | "abandoned": "codeigniter/coding-standard",
7 | "authors": [
8 | {
9 | "name": "Louis Linehan",
10 | "email": "louis.linehan@gmail.com",
11 | "homepage": "https://github.com/louisl",
12 | "role": "Developer"
13 | }
14 | ],
15 | "require": {
16 | },
17 | "require-dev": {
18 | "squizlabs/php_codesniffer": "^3.1",
19 | "satooshi/php-coveralls": "^1.0",
20 | "phpunit/phpunit": "^7.0",
21 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7"
22 | },
23 | "scripts": {
24 | "test": "phpunit --filter CodeIgniter4"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 British Columbia Institute of Technology (https://bcit.ca/)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/PHP/DiscouragedFunctionsSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\PHP;
12 |
13 | use CodeIgniter4\Sniffs\PHP\ForbiddenFunctionsSniff;
14 | use PHP_CodeSniffer\Sniffs\Sniff;
15 | use PHP_CodeSniffer\Files\File;
16 |
17 | /**
18 | * Discouraged Functions Sniff
19 | *
20 | * Discourages the use of debug functions.
21 | *
22 | * @author Louis Linehan
23 | */
24 | class DiscouragedFunctionsSniff extends ForbiddenFunctionsSniff
25 | {
26 |
27 | /**
28 | * A list of discouraged functions with their alternatives.
29 | *
30 | * The value is NULL if no alternative exists. IE, the
31 | * function should just not be used.
32 | *
33 | * @var array|null)
34 | */
35 | public $forbiddenFunctions = [
36 | 'error_log' => null,
37 | 'print_r' => null,
38 | 'var_dump' => null,
39 | ];
40 |
41 | /**
42 | * Set error to false to show warnings.
43 | *
44 | * @var boolean
45 | */
46 | public $error = false;
47 |
48 | }//end class
49 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/WhiteSpace/BooleanNotSpaceAfterUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\WhiteSpace;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class BooleanNotSpaceAfterUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Returns the lines where errors should occur.
21 | *
22 | * The key of the array should represent the line number and the value
23 | * should represent the number of errors that should occur on that line.
24 | *
25 | * @return array
26 | */
27 | public function getErrorList()
28 | {
29 | return array(
30 | 3 => 1,
31 | 4 => 0,
32 | );
33 |
34 | }//end getErrorList()
35 |
36 |
37 | /**
38 | * Returns the lines where warnings should occur.
39 | *
40 | * The key of the array should represent the line number and the value
41 | * should represent the number of warnings that should occur on that line.
42 | *
43 | * @return array
44 | */
45 | public function getWarningList()
46 | {
47 | return array();
48 |
49 | }//end getWarningList()
50 |
51 |
52 | }//end class
53 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/Operators/BooleanOrUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\Operators;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class BooleanOrUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Returns the lines where errors should occur.
21 | *
22 | * The key of the array should represent the line number and the value
23 | * should represent the number of errors that should occur on that line.
24 | *
25 | * @return array
26 | */
27 | public function getErrorList()
28 | {
29 | return array(
30 | 3 => 1,
31 | 4 => 1,
32 | 5 => 1,
33 | 6 => 2,
34 | );
35 |
36 | }//end getErrorList()
37 |
38 |
39 | /**
40 | * Returns the lines where warnings should occur.
41 | *
42 | * The key of the array should represent the line number and the value
43 | * should represent the number of warnings that should occur on that line.
44 | *
45 | * @return array
46 | */
47 | public function getWarningList()
48 | {
49 | return array();
50 |
51 | }//end getWarningList()
52 |
53 |
54 | }//end class
55 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/Operators/BooleanAndUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\Operators;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class BooleanAndUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Returns the lines where errors should occur.
21 | *
22 | * The key of the array should represent the line number and the value
23 | * should represent the number of errors that should occur on that line.
24 | *
25 | * @return array
26 | */
27 | public function getErrorList()
28 | {
29 | return array(
30 | 3 => 1,
31 | 4 => 1,
32 | 5 => 1,
33 | 7 => 2,
34 | );
35 |
36 | }//end getErrorList()
37 |
38 |
39 | /**
40 | * Returns the lines where warnings should occur.
41 | *
42 | * The key of the array should represent the line number and the value
43 | * should represent the number of warnings that should occur on that line.
44 | *
45 | * @return array
46 | */
47 | public function getWarningList()
48 | {
49 | return array();
50 |
51 | }//end getWarningList()
52 |
53 |
54 | }//end class
55 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/NamingConventions/ValidVariableNameUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\NamingConventions;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class ValidVariableNameUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Returns the lines where errors should occur.
21 | *
22 | * The key of the array should represent the line number and the value
23 | * should represent the number of errors that should occur on that line.
24 | *
25 | * @return array
26 | */
27 | public function getErrorList()
28 | {
29 | return array(
30 | 3 => 1,
31 | 4 => 1,
32 | 5 => 1,
33 | );
34 |
35 | }//end getErrorList()
36 |
37 |
38 | /**
39 | * Returns the lines where warnings should occur.
40 | *
41 | * The key of the array should represent the line number and the value
42 | * should represent the number of warnings that should occur on that line.
43 | *
44 | * @return array
45 | */
46 | public function getWarningList()
47 | {
48 | return array();
49 |
50 | }//end getWarningList()
51 |
52 |
53 | }//end class
54 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/WhiteSpace/DisallowTabsInAlignmentUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\WhiteSpace;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class DisallowTabsInAlignmentUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Returns the lines where errors should occur.
21 | *
22 | * The key of the array should represent the line number and the value
23 | * should represent the number of errors that should occur on that line.
24 | *
25 | * @return array
26 | */
27 | public function getErrorList()
28 | {
29 | return array(
30 | 9 => 1,
31 | 12 => 1,
32 | 13 => 1,
33 | );
34 |
35 | }//end getErrorList()
36 |
37 |
38 | /**
39 | * Returns the lines where warnings should occur.
40 | *
41 | * The key of the array should represent the line number and the value
42 | * should represent the number of warnings that should occur on that line.
43 | *
44 | * @return array
45 | */
46 | public function getWarningList()
47 | {
48 | return array();
49 |
50 | }//end getWarningList()
51 |
52 |
53 | }//end class
54 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/NamingConventions/ValidMethodNameUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\NamingConventions;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class ValidMethodNameUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Returns the lines where errors should occur.
21 | *
22 | * The key of the array should represent the line number and the value
23 | * should represent the number of errors that should occur on that line.
24 | *
25 | * @return array
26 | */
27 | public function getErrorList()
28 | {
29 | return array(
30 | 5 => 0,
31 | 6 => 1,
32 | 7 => 1,
33 | 8 => 1,
34 | 9 => 1,
35 | );
36 |
37 | }//end getErrorList()
38 |
39 |
40 | /**
41 | * Returns the lines where warnings should occur.
42 | *
43 | * The key of the array should represent the line number and the value
44 | * should represent the number of warnings that should occur on that line.
45 | *
46 | * @return array
47 | */
48 | public function getWarningList()
49 | {
50 | return array();
51 |
52 | }//end getWarningList()
53 |
54 |
55 | }//end class
56 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/WhiteSpace/VerticalEmptyLinesUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\WhiteSpace;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class VerticalEmptyLinesUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Returns the lines where errors should occur.
21 | *
22 | * The key of the array should represent the line number and the value
23 | * should represent the number of errors that should occur on that line.
24 | *
25 | * @return array
26 | */
27 | public function getErrorList()
28 | {
29 | return array(
30 | 13 => 1,
31 | 19 => 1,
32 | 23 => 1,
33 | 24 => 1,
34 | 30 => 1,
35 | 38 => 1,
36 | 41 => 1,
37 | 44 => 1,
38 | 45 => 1,
39 | 46 => 1,
40 | 47 => 1,
41 | );
42 |
43 | }//end getErrorList()
44 |
45 |
46 | /**
47 | * Returns the lines where warnings should occur.
48 | *
49 | * The key of the array should represent the line number and the value
50 | * should represent the number of warnings that should occur on that line.
51 | *
52 | * @return array
53 | */
54 | public function getWarningList()
55 | {
56 | return array();
57 |
58 | }//end getWarningList()
59 |
60 |
61 | }//end class
62 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Operators/BooleanOrSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Operators;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Boolean Or Sniff
18 | *
19 | * Check that the 'or' operator is the boolean version '||'.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class BooleanOrSniff implements Sniff
24 | {
25 |
26 |
27 | /**
28 | * Returns an array of tokens this test wants to listen for.
29 | *
30 | * @return array
31 | */
32 | public function register()
33 | {
34 | return [T_LOGICAL_OR];
35 |
36 | }//end register()
37 |
38 |
39 | /**
40 | * Processes this test, when one of its tokens is encountered.
41 | *
42 | * @param File $phpcsFile The current file being scanned.
43 | * @param int $stackPtr The position of the current token
44 | * in the stack passed in $tokens.
45 | *
46 | * @return void
47 | */
48 | public function process(File $phpcsFile, $stackPtr)
49 | {
50 | $tokens = $phpcsFile->getTokens();
51 |
52 | if ($tokens[$stackPtr]['code'] === T_LOGICAL_OR) {
53 | $error = '"%s" is not allowed, use "||" instead';
54 | $data = [$tokens[$stackPtr]['content']];
55 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'LogicalOrNotAllowed', $data);
56 | if ($fix === true) {
57 | $phpcsFile->fixer->beginChangeset();
58 | $phpcsFile->fixer->replaceToken($stackPtr, '||');
59 | $phpcsFile->fixer->endChangeset();
60 | }
61 | }
62 |
63 | }//end process()
64 |
65 |
66 | }//end class
67 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Operators/BooleanAndSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Operators;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Boolean And Sniff
18 | *
19 | * Check that the 'and' operator is the boolean version '&&'.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class BooleanAndSniff implements Sniff
24 | {
25 |
26 |
27 | /**
28 | * Returns an array of tokens this test wants to listen for.
29 | *
30 | * @return array
31 | */
32 | public function register()
33 | {
34 | return [T_LOGICAL_AND];
35 |
36 | }//end register()
37 |
38 |
39 | /**
40 | * Processes this test, when one of its tokens is encountered.
41 | *
42 | * @param File $phpcsFile The current file being scanned.
43 | * @param int $stackPtr The position of the current token
44 | * in the stack passed in $tokens.
45 | *
46 | * @return void
47 | */
48 | public function process(File $phpcsFile, $stackPtr)
49 | {
50 | $tokens = $phpcsFile->getTokens();
51 |
52 | if ($tokens[$stackPtr]['code'] === T_LOGICAL_AND) {
53 | $error = '"%s" is not allowed, use "&&" instead';
54 | $data = [$tokens[$stackPtr]['content']];
55 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'LogicalAndNotAllowed', $data);
56 | if ($fix === true) {
57 | $phpcsFile->fixer->beginChangeset();
58 | $phpcsFile->fixer->replaceToken($stackPtr, '&&');
59 | $phpcsFile->fixer->endChangeset();
60 | }
61 | }
62 |
63 | }//end process()
64 |
65 |
66 | }//end class
67 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Operators/IsIdenticalSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Operators;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Is Identical Sniff
18 | *
19 | * Check for is equal '==' operator, should use is identical '==='.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class IsIdenticalSniff implements Sniff
24 | {
25 |
26 |
27 | /**
28 | * Returns an array of tokens this test wants to listen for.
29 | *
30 | * @return array
31 | */
32 | public function register()
33 | {
34 | return [T_IS_EQUAL];
35 |
36 | }//end register()
37 |
38 |
39 | /**
40 | * Processes this test, when one of its tokens is encountered.
41 | *
42 | * @param File $phpcsFile The current file being scanned.
43 | * @param int $stackPtr The position of the current token
44 | * in the stack passed in $tokens.
45 | *
46 | * @return void
47 | */
48 | public function process(File $phpcsFile, $stackPtr)
49 | {
50 | $tokens = $phpcsFile->getTokens();
51 |
52 | if ($tokens[$stackPtr]['code'] === T_IS_EQUAL) {
53 | $error = '"%s" is not allowed, use "===" instead';
54 | $data = [$tokens[$stackPtr]['content']];
55 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'IsEqualNotAllowed', $data);
56 | if ($fix === true) {
57 | $phpcsFile->fixer->beginChangeset();
58 | $phpcsFile->fixer->replaceToken($stackPtr, '===');
59 | $phpcsFile->fixer->endChangeset();
60 | }
61 | }
62 |
63 | }//end process()
64 |
65 |
66 | }//end class
67 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Operators/IsNotIdenticalSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Operators;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Is Not Identical Sniff
18 | *
19 | * Check for is not equal '!=' operator, should use is not identical '!=='.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class IsNotIdenticalSniff implements Sniff
24 | {
25 |
26 |
27 | /**
28 | * Returns an array of tokens this test wants to listen for.
29 | *
30 | * @return array
31 | */
32 | public function register()
33 | {
34 | return [T_IS_NOT_EQUAL];
35 |
36 | }//end register()
37 |
38 |
39 | /**
40 | * Processes this test, when one of its tokens is encountered.
41 | *
42 | * @param File $phpcsFile The current file being scanned.
43 | * @param int $stackPtr The position of the current token
44 | * in the stack passed in $tokens.
45 | *
46 | * @return void
47 | */
48 | public function process(File $phpcsFile, $stackPtr)
49 | {
50 | $tokens = $phpcsFile->getTokens();
51 |
52 | if ($tokens[$stackPtr]['code'] === T_IS_NOT_EQUAL) {
53 | $error = '"%s" is not allowed, use "!==" instead';
54 | $data = [$tokens[$stackPtr]['content']];
55 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'IsNotEqualNotAllowed', $data);
56 | if ($fix === true) {
57 | $phpcsFile->fixer->beginChangeset();
58 | $phpcsFile->fixer->replaceToken($stackPtr, '!==');
59 | $phpcsFile->fixer->endChangeset();
60 | }
61 | }
62 |
63 | }//end process()
64 |
65 |
66 | }//end class
67 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Files/OneClassPerFileSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Files;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * One Class Per File Sniff
18 | *
19 | * Checks that only one class is declared per file. Unless the file
20 | * is allowed multiple classes.
21 | *
22 | * @author Louis Linehan
23 | */
24 | class OneClassPerFileSniff implements Sniff
25 | {
26 |
27 | /**
28 | * Files that are allowed multiple classes
29 | *
30 | * @var array
31 | */
32 | public $filesAllowedMultiClass = [
33 | 'Exception.php',
34 | 'Exceptions.php',
35 | 'CustomExceptions.php',
36 | 'Response.php',
37 | ];
38 |
39 |
40 | /**
41 | * Returns an array of tokens this test wants to listen for.
42 | *
43 | * @return array
44 | */
45 | public function register()
46 | {
47 | return [T_CLASS];
48 |
49 | }//end register()
50 |
51 |
52 | /**
53 | * Processes this sniff, when one of its tokens is encountered.
54 | *
55 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
56 | * @param int $stackPtr The position of the current token in
57 | * the stack passed in $tokens.
58 | *
59 | * @return void
60 | */
61 | public function process(File $phpcsFile, $stackPtr)
62 | {
63 | $nextClass = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
64 |
65 | $fileName = basename($phpcsFile->getFilename());
66 |
67 | if ($nextClass !== false) {
68 | if (in_array($fileName, $this->filesAllowedMultiClass) === false) {
69 | $error = 'Only one class is allowed in a file';
70 | $phpcsFile->addError($error, $nextClass, 'MultipleFound');
71 | }
72 | }
73 |
74 | }//end process()
75 |
76 |
77 | }//end class
78 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Travis CI
2 |
3 | # Use new container based environment
4 | sudo: false
5 |
6 | # Declare project language.
7 | language: php
8 |
9 | env:
10 | global:
11 | # Name and folder of the the standard to test.
12 | - STANDARD="CodeIgniter4"
13 | # Upload covarage to coveralls.
14 | - COVERALLS="1"
15 | # Xdebug3 needs this.
16 | - XDEBUG_MODE=coverage
17 |
18 | matrix:
19 | fast_finish: true
20 |
21 | # Declare versions of PHP to use. Use one decimal max.
22 | include:
23 | # aliased to a recent 5.4.x version
24 | # - php: '5.4'
25 | # aliased to a recent 5.5.x version
26 | # - php: '5.5'
27 | # aliased to a recent 5.6.x version
28 | # - php: '5.6'
29 | # aliased to a recent 7.x version
30 | # - php: '7.0'
31 | # aliased to a recent 7.x version
32 | # - php: '7.1'
33 | - php: '7.2'
34 | - php: '7.3'
35 | - php: '7.4'
36 | # - php: '8.0'
37 | # aliased to a recent hhvm version
38 | # - php: 'hhvm'
39 | # php nightly
40 | # - php: 'nightly'
41 |
42 | # allow_failures:
43 | # - php: 'hhvm'
44 | # - php: 'nightly'
45 |
46 | before_install:
47 | # Remove xdebug. Needed for coverage.
48 | # - phpenv config-rm xdebug.ini
49 |
50 | install:
51 | # Update composer to latest version.
52 | - composer self-update
53 | # Install project composer deps in composer.json
54 | - composer install --no-interaction
55 |
56 | before_script:
57 | # Rehash the php environment if testing on several PHP versions.
58 | # - phpenv rehash
59 |
60 | script:
61 | # Check for PHP syntax errors.
62 | - find -L . -path ./vendor -prune -o -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l
63 | # - Check files match the PHPCS standard.
64 | - ./vendor/bin/phpcs --ignore=*/Tests/* ./$STANDARD/ --standard=./vendor/squizlabs/php_codesniffer/phpcs.xml.dist
65 | # Change the default standard.
66 | - ./vendor/bin/phpcs --config-set installed_paths $TRAVIS_BUILD_DIR/$STANDARD
67 | # Verify it's installed.
68 | - ./vendor/bin/phpcs -i
69 | # Run unit tests for the standard.
70 | - ./vendor/bin/phpunit --debug --filter $STANDARD
71 |
72 | after_success:
73 | - if [[ "$COVERALLS" == "1" && "$TRAVIS_PHP_VERSION" == "7.4" ]]; then ./vendor/bin/coveralls -v -x ./build/logs/coverage/clover/clover.xml; fi
74 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/WhiteSpace/BooleanNotSpaceAfterSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\WhiteSpace;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Boolean Not Space After Sniff
18 | *
19 | * Checks there is a space after '!'.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class BooleanNotSpaceAfterSniff implements Sniff
24 | {
25 |
26 |
27 | /**
28 | * Returns an array of tokens this test wants to listen for.
29 | *
30 | * @return array
31 | */
32 | public function register()
33 | {
34 | return [T_BOOLEAN_NOT];
35 |
36 | }//end register()
37 |
38 |
39 | /**
40 | * Processes this test, when one of its tokens is encountered.
41 | *
42 | * @param File $phpcsFile The current file being scanned.
43 | * @param int $stackPtr The position of the current token
44 | * in the stack passed in $tokens.
45 | *
46 | * @return void
47 | */
48 | public function process(File $phpcsFile, $stackPtr)
49 | {
50 | $tokens = $phpcsFile->getTokens();
51 |
52 | $nextToken = $tokens[($stackPtr + 1)];
53 | if (T_WHITESPACE !== $nextToken['code']) {
54 | $error = 'There must be a space after !';
55 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'BooleanNotNoWhiteSpaceAfter');
56 |
57 | if ($fix === true) {
58 | $nextContentPtr = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
59 | $phpcsFile->fixer->beginChangeset();
60 | for ($i = ($nextContentPtr + 1); $i < $stackPtr; $i++) {
61 | $phpcsFile->fixer->replaceToken($i, '');
62 | }
63 |
64 | $phpcsFile->fixer->addContent(($stackPtr), ' ');
65 | $phpcsFile->fixer->endChangeset();
66 | $phpcsFile->recordMetric($stackPtr, 'Boolean not space after', 'yes');
67 | }//end if
68 | }//end if
69 |
70 | }//end process()
71 |
72 |
73 | }//end class
74 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/WhiteSpace/VerticalEmptyLinesSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\WhiteSpace;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 | use CodeIgniter4\Util\Common;
16 |
17 | /**
18 | * Vertical Empty Lines Sniff
19 | *
20 | * Checks for consecutive empty vertical lines.
21 | *
22 | * @author Louis Linehan
23 | */
24 | class VerticalEmptyLinesSniff implements Sniff
25 | {
26 |
27 | /**
28 | * Consecutive empty vertical lines allowed.
29 | *
30 | * @var integer
31 | */
32 | public $allowed = 1;
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 [T_OPEN_TAG];
43 |
44 | }//end register()
45 |
46 |
47 | /**
48 | * Processes this test, when one of its tokens is encountered.
49 | *
50 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
51 | * @param int $stackPtr The position of the current token in
52 | * the stack passed in $tokens.
53 | *
54 | * @return int
55 | */
56 | public function process(File $phpcsFile, $stackPtr)
57 | {
58 | $errors = [];
59 | $tokens = $phpcsFile->getTokens();
60 | for ($i = 1; $i < $phpcsFile->numTokens; $i++) {
61 | $nextContentPtr = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true);
62 |
63 | $lines = ($tokens[$nextContentPtr]['line'] - $tokens[$i]['line'] - 1);
64 | $errorLine = (($nextContentPtr - $lines) + $this->allowed - 1);
65 |
66 | if ($lines > ($this->allowed) && in_array($errorLine, $errors) === false) {
67 | $errors[] = $errorLine;
68 |
69 | $data = [
70 | $this->allowed,
71 | Common::pluralize('line', $this->allowed),
72 | ];
73 | $error = 'Expected only %s empty %s';
74 | $fix = $phpcsFile->addFixableError($error, $errorLine, 'VerticalEmptyLines', $data);
75 | if ($fix === true) {
76 | $phpcsFile->fixer->replaceToken($errorLine, '');
77 | }
78 | }
79 | }
80 |
81 | // Ignore the rest of the file.
82 | return ($phpcsFile->numTokens + 1);
83 |
84 | }//end process()
85 |
86 |
87 | }//end class
88 |
--------------------------------------------------------------------------------
/CodeIgniter4/Util/Common.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Util;
12 |
13 | use PHP_CodeSniffer\Util\Common as BaseCommon;
14 |
15 | /**
16 | * Common
17 | *
18 | * Extends common functions.
19 | *
20 | * @author Louis Linehan
21 | */
22 | class Common extends BaseCommon
23 | {
24 |
25 | /**
26 | * A list of all PHP magic methods.
27 | *
28 | * @var array
29 | */
30 | public static $magicMethods = [
31 | 'construct' => true,
32 | 'destruct' => true,
33 | 'call' => true,
34 | 'callstatic' => true,
35 | 'get' => true,
36 | 'set' => true,
37 | 'isset' => true,
38 | 'unset' => true,
39 | 'sleep' => true,
40 | 'wakeup' => true,
41 | 'tostring' => true,
42 | 'set_state' => true,
43 | 'clone' => true,
44 | 'invoke' => true,
45 | 'debuginfo' => true,
46 | ];
47 |
48 | /**
49 | * Allowed public methodNames
50 | *
51 | * @var array
52 | */
53 | public static $publicMethodNames = ['_remap' => true];
54 |
55 |
56 | /**
57 | * Is lower snake case
58 | *
59 | * @param string $string The string to verify.
60 | *
61 | * @return boolean
62 | */
63 | public static function isLowerSnakeCase($string)
64 | {
65 | if (strcmp($string, strtolower($string)) !== 0) {
66 | return false;
67 | }
68 |
69 | if (strpos($string, ' ') !== false) {
70 | return false;
71 | }
72 |
73 | return true;
74 |
75 | }//end isLowerSnakeCase()
76 |
77 |
78 | /**
79 | * Has an underscore prefix
80 | *
81 | * @param string $string The string to verify.
82 | *
83 | * @return boolean
84 | */
85 | public static function hasUnderscorePrefix($string)
86 | {
87 | if (strpos($string, '_') !== 0) {
88 | return false;
89 | }
90 |
91 | return true;
92 |
93 | }//end hasUnderscorePrefix()
94 |
95 |
96 | /**
97 | * Pluralize
98 | *
99 | * Basic pluralize intended for use in error messages
100 | * tab/s, space/s, error/s etc.
101 | *
102 | * @param string $string String.
103 | * @param float $num Number.
104 | *
105 | * @return string
106 | */
107 | public static function pluralize($string, $num)
108 | {
109 | if ($num > 1) {
110 | return $string.'s';
111 | } else {
112 | return $string;
113 | }
114 |
115 | }//end pluralize()
116 |
117 |
118 | }//end class
119 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Files/FilenameMatchesClassSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter\Sniffs\Files;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Filename Matches Class Sniff
18 | *
19 | * Checks that the filename matches the class name.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class FilenameMatchesClassSniff implements Sniff
24 | {
25 |
26 | /**
27 | * If the file has a bad filename.
28 | *
29 | * Change to true and check it later to avoid displaying multiple errors.
30 | *
31 | * @var boolean
32 | */
33 | protected $badFilename = false;
34 |
35 |
36 | /**
37 | * Returns an array of tokens this test wants to listen for.
38 | *
39 | * @return array
40 | */
41 | public function register()
42 | {
43 | return [
44 | T_CLASS,
45 | T_INTERFACE,
46 | T_TRAIT,
47 | ];
48 |
49 | }//end register()
50 |
51 |
52 | /**
53 | * Processes this sniff, when one of its tokens is encountered.
54 | *
55 | * @param File $phpcsFile The file being scanned.
56 | * @param int $stackPtr The position of the current token in
57 | * the stack passed in $tokens.
58 | *
59 | * @return int
60 | */
61 | public function process(File $phpcsFile, $stackPtr)
62 | {
63 |
64 | $tokens = $phpcsFile->getTokens();
65 |
66 | $fileName = basename($phpcsFile->getFilename());
67 |
68 | if (strpos($fileName, '_helper.php') !== false) {
69 | return;
70 | }
71 |
72 | $className = trim($phpcsFile->getDeclarationName($stackPtr));
73 |
74 | if (strpos($className, 'Migration') === 0 && strpos($fileName, '_') !== false) {
75 | return;
76 | }
77 |
78 | $nextContentPtr = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
79 | $type = $tokens[$stackPtr]['content'];
80 |
81 | if ($fileName !== $className.'.php' && $this->badFilename === false) {
82 | $data = [
83 | $fileName,
84 | $className.'.php',
85 | ];
86 | $error = 'Filename "%s" doesn\'t match the expected filename "%s"';
87 | $phpcsFile->addError($error, $nextContentPtr, ucfirst($type).'BadFilename', $data);
88 | $phpcsFile->recordMetric($nextContentPtr, 'Filename matches '.$type, 'no');
89 | $this->badFilename = true;
90 | } else {
91 | $phpcsFile->recordMetric($nextContentPtr, 'Filename matches '.$type, 'yes');
92 | }
93 |
94 | // Ignore the rest of the file.
95 | return ($phpcsFile->numTokens + 1);
96 |
97 | }//end process()
98 |
99 |
100 | }//end class
101 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Files/HelperFileSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Files;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Helper File Sniff
18 | *
19 | * Checks *_helper.php files only contain functions
20 | * and that the filename is lower snake_case.
21 | *
22 | * @author Louis Linehan
23 | */
24 | class HelperFileSniff implements Sniff
25 | {
26 |
27 | /**
28 | * Files that are allowed multiple classes
29 | *
30 | * @var array
31 | */
32 | public $unwantedTokens = [
33 | T_CLASS,
34 | T_ANON_CLASS,
35 | T_INTERFACE,
36 | T_TRAIT,
37 | ];
38 |
39 | /**
40 | * If the file has a bad filename.
41 | *
42 | * Change to true and check it later to avoid displaying multiple errors.
43 | *
44 | * @var boolean
45 | */
46 | protected $badFilename = false;
47 |
48 |
49 | /**
50 | * Returns an array of tokens this test wants to listen for.
51 | *
52 | * @return array
53 | */
54 | public function register()
55 | {
56 | return [
57 | T_FUNCTION,
58 | T_CLASS,
59 | T_ANON_CLASS,
60 | T_INTERFACE,
61 | T_TRAIT,
62 | ];
63 |
64 | }//end register()
65 |
66 |
67 | /**
68 | * Processes this sniff, when one of its tokens is encountered.
69 | *
70 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
71 | * @param int $stackPtr The position of the current token in
72 | * the stack passed in $tokens.
73 | *
74 | * @return void
75 | */
76 | public function process(File $phpcsFile, $stackPtr)
77 | {
78 | $fileName = basename($phpcsFile->getFilename());
79 | if (strpos($fileName, '_helper.php') === false) {
80 | return;
81 | } else {
82 | // Check the filename.
83 | $expectedFilename = preg_replace('/_{2,}/', '_', strtolower($fileName));
84 | if ($fileName !== $expectedFilename && $this->badFilename === false) {
85 | $data = [
86 | $fileName,
87 | $expectedFilename,
88 | ];
89 | $error = 'Helper filename "%s" doesn\'t match the expected filename "%s"';
90 | $phpcsFile->addError($error, 1, 'HelperBadFilename', $data);
91 | $this->badFilename = true;
92 | }
93 |
94 | // Check for class, interface, trait etc.
95 | $tokens = $phpcsFile->getTokens();
96 | if (in_array($tokens[$stackPtr]['code'], $this->unwantedTokens) === true) {
97 | $error = 'Helper files must only contain functions';
98 | $phpcsFile->addError($error, $stackPtr, 'HelperOnlyFunctions');
99 | }
100 | }//end if
101 |
102 | }//end process()
103 |
104 |
105 | }//end class
106 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/ControlStructures/AllmanControlSignatureUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\ControlStructures;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class AllmanControlSignatureUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Get a list of CLI values to set before the file is tested.
21 | *
22 | * @param string $testFile The name of the file being tested.
23 | * @param \PHP_CodeSniffer\Config $config The config data for the test run.
24 | *
25 | * @return void
26 | */
27 | public function setCliValues($testFile, $config)
28 | {
29 | $config->tabWidth = 4;
30 |
31 | }//end setCliValues()
32 |
33 |
34 | /**
35 | * Returns the lines where errors should occur.
36 | *
37 | * The key of the array should represent the line number and the value
38 | * should represent the number of errors that should occur on that line.
39 | *
40 | * @return array
41 | */
42 | public function getErrorList()
43 | {
44 | return array(
45 | 3 => 1,
46 | 5 => 1,
47 | 10 => 1,
48 | 18 => 4,
49 | 20 => 1,
50 | 22 => 1,
51 | 24 => 1,
52 | 28 => 2,
53 | 32 => 3,
54 | 34 => 1,
55 | 38 => 2,
56 | 42 => 3,
57 | 44 => 1,
58 | 48 => 2,
59 | 52 => 3,
60 | 54 => 1,
61 | 56 => 2,
62 | 60 => 1,
63 | 62 => 2,
64 | 66 => 7,
65 | 68 => 1,
66 | 70 => 2,
67 | 74 => 1,
68 | 76 => 3,
69 | 80 => 7,
70 | 82 => 2,
71 | 86 => 2,
72 | 90 => 2,
73 | 95 => 1,
74 | 99 => 1,
75 | 102 => 1,
76 | 104 => 2,
77 | 108 => 5,
78 | 112 => 2,
79 | 113 => 1,
80 | 115 => 2,
81 | 120 => 2,
82 | 122 => 1,
83 | 123 => 1,
84 | 126 => 1,
85 | 130 => 2,
86 | 148 => 1,
87 | 151 => 1,
88 | 154 => 1,
89 | 175 => 1,
90 | 185 => 2,
91 | 206 => 1,
92 | 208 => 2,
93 | );
94 |
95 | }//end getErrorList()
96 |
97 |
98 | /**
99 | * Returns the lines where warnings should occur.
100 | *
101 | * The key of the array should represent the line number and the value
102 | * should represent the number of warnings that should occur on that line.
103 | *
104 | * @return array
105 | */
106 | public function getWarningList()
107 | {
108 | return array();
109 |
110 | }//end getWarningList()
111 |
112 |
113 | }//end class
114 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/Arrays/ArrayDeclarationUnitTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Tests\Arrays;
12 |
13 | use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14 |
15 | class ArrayDeclarationUnitTest extends AbstractSniffUnitTest
16 | {
17 |
18 |
19 | /**
20 | * Get a list of CLI values to set before the file is tested.
21 | *
22 | * @param string $testFile The name of the file being tested.
23 | * @param \PHP_CodeSniffer\Config $config The config data for the test run.
24 | *
25 | * @return void
26 | */
27 | public function setCliValues($testFile, $config)
28 | {
29 | $config->tabWidth = 4;
30 |
31 | }//end setCliValues()
32 |
33 |
34 | /**
35 | * Returns the lines where errors should occur.
36 | *
37 | * The key of the array should represent the line number and the value
38 | * should represent the number of errors that should occur on that line.
39 | *
40 | * @return array
41 | */
42 | public function getErrorList()
43 | {
44 | return array(
45 | 6 => 1,
46 | 7 => 1,
47 | 8 => 1,
48 | 9 => 1,
49 | 10 => 1,
50 | 11 => 1,
51 | 12 => 1,
52 | 13 => 1,
53 | 14 => 2,
54 | 15 => 1,
55 | 16 => 1,
56 | 22 => 1,
57 | 29 => 1,
58 | 30 => 1,
59 | 36 => 1,
60 | 37 => 2,
61 | 42 => 2,
62 | 43 => 2,
63 | 44 => 2,
64 | 45 => 2,
65 | 47 => 2,
66 | 48 => 2,
67 | 49 => 2,
68 | 52 => 2,
69 | 56 => 2,
70 | 60 => 1,
71 | 61 => 1,
72 | 62 => 1,
73 | 64 => 1,
74 | 70 => 1,
75 | 71 => 1,
76 | 77 => 1,
77 | 80 => 1,
78 | 81 => 1,
79 | 82 => 1,
80 | 85 => 2,
81 | 87 => 1,
82 | 89 => 1,
83 | 92 => 1,
84 | 93 => 1,
85 | 98 => 1,
86 | 99 => 1,
87 | 100 => 1,
88 | 101 => 1,
89 | 104 => 1,
90 | 105 => 1,
91 | 106 => 1,
92 | 107 => 1,
93 | 112 => 1,
94 | 113 => 1,
95 | 116 => 1,
96 | 117 => 1,
97 | 118 => 1,
98 | 119 => 1,
99 | 120 => 1,
100 | );
101 |
102 | }//end getErrorList()
103 |
104 |
105 | /**
106 | * Returns the lines where warnings should occur.
107 | *
108 | * The key of the array should represent the line number and the value
109 | * should represent the number of warnings that should occur on that line.
110 | *
111 | * @return array
112 | */
113 | public function getWarningList()
114 | {
115 | return array();
116 |
117 | }//end getWarningList()
118 |
119 |
120 | }//end class
121 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/NamingConventions/ValidFunctionNameSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\NamingConventions;
12 |
13 | use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
14 | use CodeIgniter4\Util\Common;
15 | use PHP_CodeSniffer\Files\File;
16 |
17 | /**
18 | * Valid Function Name Sniff
19 | *
20 | * @author Louis Linehan
21 | */
22 | class ValidFunctionNameSniff extends AbstractScopeSniff
23 | {
24 |
25 |
26 | /**
27 | * Defines which token(s) in which scope(s) will be proceed.
28 | */
29 | public function __construct()
30 | {
31 | parent::__construct([T_CLASS, T_ANON_CLASS, T_INTERFACE, T_TRAIT], [T_FUNCTION], true);
32 |
33 | }//end __construct()
34 |
35 |
36 | /**
37 | * Processes the tokens outside the scope.
38 | *
39 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
40 | * @param int $stackPtr The position where this token was
41 | * found.
42 | *
43 | * @return void
44 | */
45 | protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
46 | {
47 | $functionName = $phpcsFile->getDeclarationName($stackPtr);
48 | if ($functionName === null) {
49 | return;
50 | }
51 |
52 | // Is this a magic function. i.e., it is prefixed with "__"?
53 | if (preg_match('|^__[^_]|', $functionName) !== 0) {
54 | $magicPart = strtolower(substr($functionName, 2));
55 | if (isset(Common::$magicMethods[$magicPart]) === false) {
56 | $errorData = [$functionName];
57 | $error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
58 | $phpcsFile->addError($error, $stackPtr, 'FunctionDoubleUnderscore', $errorData);
59 | }
60 |
61 | return;
62 | }
63 |
64 | if (Common::isLowerSnakeCase($functionName) === false
65 | || $functionName !== strtolower($functionName)
66 | ) {
67 | $errorData = [$functionName];
68 | $error = 'Function "%s" must be snake_case';
69 | $phpcsFile->addError($error, $stackPtr, 'FunctionNotSnakeCase', $errorData);
70 | }
71 |
72 | $warningLimit = 50;
73 | if (strlen($functionName) > $warningLimit) {
74 | $errorData = [
75 | $functionName,
76 | $warningLimit,
77 | ];
78 | $warning = 'Function "%s" is over "%s" chars';
79 | $phpcsFile->addWarning($warning, $stackPtr, 'FunctionNameIsLong', $errorData);
80 | }
81 |
82 | }//end processTokenOutsideScope()
83 |
84 |
85 | /**
86 | * Processes the tokens within the scope.
87 | *
88 | * @param File $phpcsFile The file being processed.
89 | * @param int $stackPtr The position where this token was
90 | * found.
91 | * @param int $currScope The position of the current scope.
92 | *
93 | * @return void
94 | */
95 | protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
96 | {
97 |
98 | }//end processTokenWithinScope()
99 |
100 |
101 | }//end class
102 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/ControlStructures/AllmanControlSignatureUnitTest.inc:
--------------------------------------------------------------------------------
1 | 0);
6 |
7 | do
8 | {
9 | echo $i;
10 | } while ($i > 0);
11 |
12 | do
13 | {
14 | echo $i;
15 | }
16 | while ($i > 0);
17 |
18 | do { echo $i; } while ($i > 0);
19 |
20 | do{
21 | echo $i;
22 | }while($i > 0);
23 |
24 | while ($i < 1) {
25 | echo $i;
26 | }
27 |
28 | while($i < 1){
29 | echo $i;
30 | }
31 |
32 | while ($i < 1) { echo $i; }
33 |
34 | for ($i = 1; $i < 1; $i++) {
35 | echo $i;
36 | }
37 |
38 | for($i = 1; $i < 1; $i++){
39 | echo $i;
40 | }
41 |
42 | for ($i = 1; $i < 1; $i++) { echo $i; }
43 |
44 | if ($i == 0) {
45 | $i = 1;
46 | }
47 |
48 | if($i == 0){
49 | $i = 1;
50 | }
51 |
52 | if ($i == 0) { $i = 1; }
53 |
54 | if ($i == 0) {
55 | $i = 1;
56 | } else {
57 | $i = 0;
58 | }
59 |
60 | if ($i == 0) {
61 | $i = 1;
62 | }else{
63 | $i = 0;
64 | }
65 |
66 | if ($i == 0) { $i = 1; } else { $i = 0; }
67 |
68 | if ($i == 0) {
69 | $i = 1;
70 | } else if ($i == 2) {
71 | $i = 0;
72 | }
73 |
74 | if ($i == 0) {
75 | $i = 1;
76 | }else if($i == 2){
77 | $i = 0;
78 | }
79 |
80 | if ($i == 0) { $i = 1; } else if ($i == 2) { $i = 0; }
81 |
82 | if ($i == 0) { // comments are allowed
83 | $i = 1;
84 | }
85 |
86 | if ($i == 0) {// comments are allowed
87 | $i = 1;
88 | }
89 |
90 | if ($i == 0) { /* comments are allowed*/
91 | $i = 1;
92 | }
93 |
94 | if ($i == 0)
95 | { // this is ok
96 | $i = 1;
97 | }
98 |
99 | if ($i == 0) /* this is ok */ {
100 | }
101 |
102 | try {
103 | $code = 'this';
104 | } catch (Exception $e) {
105 | // Caught!
106 | }
107 |
108 | try { $code = 'this'; } catch (Exception $e) {
109 | // Caught!
110 | }
111 |
112 | do { echo $i;
113 | } while ($i > 0);
114 |
115 | if ($i === 0) {
116 |
117 | $i = 1
118 | }
119 |
120 | if ($a) {
121 |
122 | }
123 | elseif ($b) {
124 | }
125 |
126 | foreach ($items as $item) {
127 | echo $item;
128 | }
129 |
130 | foreach($items as $item){
131 | echo $item;
132 | }
133 |
134 | if ($a && $b) // && $c)
135 | {
136 | }
137 |
138 | if ($a == 5) :
139 | echo "a equals 5";
140 | echo "...";
141 | elseif ($a == 6) :
142 | echo "a equals 6";
143 | echo "!!!";
144 | else :
145 | echo "a is neither 5 nor 6";
146 | endif;
147 |
148 | try {
149 | // try body
150 | }
151 | catch (FirstExceptionType $e) {
152 | // catch body
153 | }
154 | catch (OtherExceptionType $e) {
155 | // catch body
156 | }
157 |
158 | switch($foo) {
159 |
160 | case 'bar':
161 | break;
162 |
163 | }
164 |
165 | if ($foo) :
166 | endif;
167 |
168 | ?>
169 |
170 | getRow()): ?>
171 | = $val ?>
172 |
173 |
174 |
177 |
182 |
190 |
191 |
192 |
193 |
194 |
195 | hello
196 |
197 |
198 |
200 | hello
201 |
202 |
203 | 201,
7 | 'unsupported_response_type' => 400,
8 | 'invalid_scope' => 400,
9 | 'temporarily_unavailable' => 400,
10 | 'invalid_grant' => [
11 | 'invalid_data' => 400,
12 | 'access_denied' => 401,
13 | 'forbidden' => 403,
14 | 'resource_not_found' => 404],
15 | 'conflict' => 409,
16 | ];
17 |
18 | public $globals = [
19 | 'before' => [
20 | // 'csrf'
21 | ],
22 | 'after' => [
23 | 'toolbar'
24 | ]
25 | ];
26 |
27 | protected $defaultHTTPMethods = [
28 | 'options',
29 | 'get',
30 | 'head',
31 | ];
32 |
33 | protected function isValidLuhn()
34 | {
35 | $sumTable = [
36 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
37 | [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
38 | ];
39 | }
40 |
41 | protected $cards = [
42 | 'American Express' => ['name' => 'amex', 'length' => '15', 'prefixes' => '34,37', 'checkdigit' => true],
43 | 'DinersClub' => ['name' => 'dinersclub', 'length' => '14,16', 'prefixes' => '300,301,302,303,304,305,309,36,38,39,54,55', 'checkdigit' => true],
44 | 'MasterCard' => ['name' => 'mastercard', 'length' => '16', 'prefixes' => '51,52,53,54,55,22,23,24,25,26,27', 'checkdigit' => true],
45 | 'Visa' => ['name' => 'visa', 'length' => '13,16,19', 'prefixes' => '4', 'checkdigit' => true],
46 | // Canadian Cards
47 | 'CIBC Convenience Card' => ['name' => 'cibc', 'length' => '16', 'prefixes' => '4506', 'checkdigit' => false],
48 | 'Royal Bank of Canada Client Card' => ['name' => 'rbc', 'length' => '16', 'prefixes' => '45', 'checkdigit' => false],
49 | 'TD Canada Trust Access Card' => ['name' => 'tdtrust', 'length' => '16', 'prefixes' => '589297', 'checkdigit' => false],
50 | ];
51 |
52 | $a = array
53 | // comment
54 | ( 'a', 'b' );
55 |
56 | $a = array /* comment */ ( 'a', 'b' );
57 |
58 | protected static $statusCodes = [
59 | // 1xx: Informational
60 | 100 => 'Continue',
61 | 101 => 'Switching Protocols',
62 | 102 => 'Processing', // http://www.iana.org/go/rfc2518
63 | // 2xx: Success
64 | 200 => 'OK',
65 | ];
66 |
67 | public function error()
68 | {
69 | return [
70 | 'code' => '',
71 | 'message' => pg_last_error($this->connID)
72 | ];
73 | }
74 |
75 | public function error2()
76 | {
77 | $this->QBWhere[] = ['condition' => $like_statement, 'escape' => $escape];
78 |
79 | $this->QBWhere[] = [
80 | 'condition' => $like_statement,
81 | 'escape' => $escape,
82 | ];
83 | }
84 |
85 | public $arr = array( );
86 |
87 | public $arr = [ ];
88 |
89 | public $arr = [1,];
90 |
91 | public $arr = [
92 | 'a'=> 1,
93 | 'b'=> 2,
94 | 'c' => 3,
95 | ];
96 |
97 | $cache->save($cacheKeyPagination, [
98 | 'page' => $this->pager->getCurrentPage(),
99 | 'perPage' => $this->pager->getPerPage(),
100 | 'total' => $this->pager->getPerPage() * $this->pager->getPageCount(),
101 | ] , 15);
102 |
103 | return preg_replace(
104 | array(
105 | '#
#i',
106 | '#
`]+)).*?\>#i'
107 | ), '\\2', $str
108 | );
109 |
110 | $this->db->table($this->table)
111 | ->insert([
112 | 'version' => $version,
113 | 'name' => $this->name,
114 | ]);
115 |
116 | $message->setCustomProperty('user_info', array(
117 | 'id' => $id,
118 | 'name' => 'Test message'
119 | )
120 | );
121 |
122 | $users = [
123 | [
124 | 'id' => 1,
125 | 'name' => 'John',
126 | 'email' => 'john@example.com',
127 | 'fact' => 'Loves coding',
128 | ],
129 | [
130 | 'id' => 2,
131 | 'name' => 'Jim',
132 | 'email' => 'jim@example.com',
133 | 'fact' => 'Developed on CodeIgniter',
134 | ],
135 | [
136 | 'id' => 3,
137 | 'name' => 'Jane',
138 | 'email' => 'jane@example.com',
139 | 'fact' => 'Lives in the USA', [
140 | 'hobbies' => [
141 | 'guitar',
142 | 'cycling',
143 | ],
144 | ]
145 | ],
146 | ];
147 | }
148 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/ControlStructures/AllmanControlSignatureUnitTest.inc.fixed:
--------------------------------------------------------------------------------
1 | 0);
8 |
9 | do
10 | {
11 | echo $i;
12 | }
13 | while ($i > 0);
14 |
15 | do
16 | {
17 | echo $i;
18 | }
19 | while ($i > 0);
20 |
21 | do
22 | {
23 | echo $i;
24 | }
25 | while ($i > 0);
26 |
27 | do
28 | {
29 | echo $i;
30 | }
31 | while($i > 0);
32 |
33 | while ($i < 1)
34 | {
35 | echo $i;
36 | }
37 |
38 | while ($i < 1)
39 | {
40 | echo $i;
41 | }
42 |
43 | while ($i < 1)
44 | {
45 | echo $i;
46 | }
47 |
48 | for ($i = 1; $i < 1; $i++)
49 | {
50 | echo $i;
51 | }
52 |
53 | for ($i = 1; $i < 1; $i++)
54 | {
55 | echo $i;
56 | }
57 |
58 | for ($i = 1; $i < 1; $i++)
59 | {
60 | echo $i;
61 | }
62 |
63 | if ($i == 0)
64 | {
65 | $i = 1;
66 | }
67 |
68 | if ($i == 0)
69 | {
70 | $i = 1;
71 | }
72 |
73 | if ($i == 0)
74 | {
75 | $i = 1;
76 | }
77 |
78 | if ($i == 0)
79 | {
80 | $i = 1;
81 | }
82 | else
83 | {
84 | $i = 0;
85 | }
86 |
87 | if ($i == 0)
88 | {
89 | $i = 1;
90 | }
91 | else
92 | {
93 | $i = 0;
94 | }
95 |
96 | if ($i == 0)
97 | {
98 | $i = 1;
99 | }
100 | else
101 | {
102 | $i = 0;
103 | }
104 |
105 | if ($i == 0)
106 | {
107 | $i = 1;
108 | }
109 | else if ($i == 2)
110 | {
111 | $i = 0;
112 | }
113 |
114 | if ($i == 0)
115 | {
116 | $i = 1;
117 | }
118 | else if ($i == 2)
119 | {
120 | $i = 0;
121 | }
122 |
123 | if ($i == 0)
124 | {
125 | $i = 1;
126 | }
127 | else if ($i == 2)
128 | {
129 | $i = 0;
130 | }
131 |
132 | if ($i == 0)
133 | {
134 | // comments are allowed
135 | $i = 1;
136 | }
137 |
138 | if ($i == 0)
139 | {
140 | // comments are allowed
141 | $i = 1;
142 | }
143 |
144 | if ($i == 0)
145 | {
146 | /* comments are allowed*/
147 | $i = 1;
148 | }
149 |
150 | if ($i == 0)
151 | {
152 | // this is ok
153 | $i = 1;
154 | }
155 |
156 | if ($i == 0) /* this is ok */
157 | {
158 | }
159 |
160 | try
161 | {
162 | $code = 'this';
163 | }
164 | catch (Exception $e)
165 | {
166 | // Caught!
167 | }
168 |
169 | try
170 | {
171 | $code = 'this';
172 | }
173 | catch (Exception $e)
174 | {
175 | // Caught!
176 | }
177 |
178 | do
179 | {
180 | echo $i;
181 | }
182 | while ($i > 0);
183 |
184 | if ($i === 0)
185 | {
186 | $i = 1
187 | }
188 |
189 | if ($a)
190 | {
191 | }
192 | elseif ($b)
193 | {
194 | }
195 |
196 | foreach ($items as $item)
197 | {
198 | echo $item;
199 | }
200 |
201 | foreach ($items as $item)
202 | {
203 | echo $item;
204 | }
205 |
206 | if ($a && $b) // && $c)
207 | {
208 | }
209 |
210 | if ($a == 5) :
211 | echo "a equals 5";
212 | echo "...";
213 | elseif ($a == 6) :
214 | echo "a equals 6";
215 | echo "!!!";
216 | else :
217 | echo "a is neither 5 nor 6";
218 | endif;
219 |
220 | try
221 | {
222 | // try body
223 | }
224 | catch (FirstExceptionType $e)
225 | {
226 | // catch body
227 | }
228 | catch (OtherExceptionType $e)
229 | {
230 | // catch body
231 | }
232 |
233 | switch($foo) {
234 |
235 | case 'bar':
236 | break;
237 |
238 | }
239 |
240 | if ($foo) :
241 | endif;
242 |
243 | ?>
244 |
245 | getRow()): ?>
246 | = $val ?>
247 |
248 |
249 |
253 |
258 |
268 |
269 |
270 |
271 |
272 |
273 | hello
274 |
275 |
276 |
278 | hello
279 |
280 |
281 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\WhiteSpace;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Disallow Tabs In Alignment Sniff
18 | *
19 | * Checks for use of tabs after indendation.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class DisallowTabsInAlignmentSniff implements Sniff
24 | {
25 |
26 | /**
27 | * The --tab-width CLI value that is being used.
28 | *
29 | * @var integer
30 | */
31 | private $tabWidth = null;
32 |
33 |
34 | /**
35 | * Returns an array of tokens this test wants to listen for.
36 | *
37 | * @return array
38 | */
39 | public function register()
40 | {
41 | return [T_OPEN_TAG];
42 |
43 | }//end register()
44 |
45 |
46 | /**
47 | * Processes this test, when one of its tokens is encountered.
48 | *
49 | * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
50 | * @param int $stackPtr The position of the current token in
51 | * the stack passed in $tokens.
52 | *
53 | * @return void
54 | */
55 | public function process(File $phpcsFile, $stackPtr)
56 | {
57 |
58 | if ($this->tabWidth === null) {
59 | if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) {
60 | // We have no idea how wide tabs are, so assume 4 spaces for fixing.
61 | // It shouldn't really matter because alignment and spacing sniffs
62 | // elsewhere in the standard should fix things up.
63 | $this->tabWidth = 4;
64 | } else {
65 | $this->tabWidth = $phpcsFile->config->tabWidth;
66 | }
67 | }
68 |
69 | $checkTokens = [
70 | T_WHITESPACE => true,
71 | T_INLINE_HTML => true,
72 | T_DOC_COMMENT_WHITESPACE => true,
73 | ];
74 |
75 | $tokens = $phpcsFile->getTokens();
76 |
77 | for ($i = ($stackPtr); $i < $phpcsFile->numTokens; $i++) {
78 | // Skip whitespace at the start of a new line and tokens not consdered white space.
79 | if ($tokens[$i]['column'] === 1 || isset($checkTokens[$tokens[$i]['code']]) === false) {
80 | continue;
81 | }
82 |
83 | // If tabs are being converted to spaces by the tokeniser, the
84 | // original content should be checked instead of the converted content.
85 | if (isset($tokens[$i]['orig_content']) === true) {
86 | $content = $tokens[$i]['orig_content'];
87 | } else {
88 | $content = $tokens[$i]['content'];
89 | }
90 |
91 | if (strpos($content, "\t") !== false) {
92 | // Try to maintain intended alignment by counting tabs and spaces.
93 | $countTabs = substr_count($content, "\t");
94 | $countSpaces = substr_count($content, " ");
95 |
96 | if ($countTabs === 1) {
97 | $tabsPlural = '';
98 | } else {
99 | $tabsPlural = 's';
100 | }
101 |
102 | if ($countSpaces === 1) {
103 | $spacesPlural = '';
104 | } else {
105 | $spacesPlural = 's';
106 | }
107 |
108 | $data = [
109 | $countTabs,
110 | $tabsPlural,
111 | $countSpaces,
112 | $spacesPlural,
113 | ];
114 | $error = 'Spaces must be used for alignment; %s tab%s and %s space%s found';
115 |
116 | // The fix might make some lines misaligned if the tab didn't fill the number
117 | // of 'tabWidth' spaces, other alignment and spacing sniffs should fix that.
118 | $fix = $phpcsFile->addFixableError($error, $i, 'TabsUsedInAlignment', $data);
119 | if ($fix === true) {
120 | $phpcsFile->fixer->beginChangeset();
121 | $spaces = str_repeat(' ', (($this->tabWidth * $countTabs) + $countSpaces));
122 | $phpcsFile->fixer->replaceToken($i, $spaces);
123 | $phpcsFile->fixer->endChangeset();
124 | }//end if
125 | }//end if
126 | }//end for
127 |
128 | // Ignore the rest of the file.
129 | return ($phpcsFile->numTokens + 1);
130 |
131 | }//end process()
132 |
133 |
134 | }//end class
135 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/NamingConventions/ValidMethodNameSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\NamingConventions;
12 |
13 | use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
14 | use CodeIgniter4\Util\Common;
15 | use PHP_CodeSniffer\Files\File;
16 |
17 | /**
18 | * Valid Method Name Sniff
19 | *
20 | * Checks class methods are lowerCameCase.
21 | * Checks public methods are not prefixed with "_" except
22 | * methods defined in allowedPublicMethodNames.
23 | * Checks private and protected methods are prefixed with "_".
24 | * Checks functions are snake_case.
25 | * Warns if names are longer than 50 characters.
26 | *
27 | * @author Louis Linehan
28 | */
29 | class ValidMethodNameSniff extends AbstractScopeSniff
30 | {
31 |
32 |
33 | /**
34 | * Defines which token(s) in which scope(s) will be proceed.
35 | */
36 | public function __construct()
37 | {
38 | parent::__construct([T_CLASS, T_ANON_CLASS, T_INTERFACE, T_TRAIT], [T_FUNCTION], true);
39 |
40 | }//end __construct()
41 |
42 |
43 | /**
44 | * Processes a token within the scope that this test is listening to.
45 | *
46 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
47 | * @param int $stackPtr The position in the stack where
48 | * this token was found.
49 | *
50 | * @return void
51 | */
52 | protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
53 | {
54 |
55 | }//end processTokenOutsideScope()
56 |
57 |
58 | /**
59 | * Processes the tokens within the scope.
60 | *
61 | * @param File $phpcsFile The file being processed.
62 | * @param int $stackPtr The position where this token was
63 | * found.
64 | * @param int $currScope The position of the current scope.
65 | *
66 | * @return void
67 | */
68 | protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
69 | {
70 | $methodName = $phpcsFile->getDeclarationName($stackPtr);
71 | if ($methodName === null) {
72 | // Ignore closures.
73 | return;
74 | }
75 |
76 | $className = $phpcsFile->getDeclarationName($currScope);
77 |
78 | // Is this a magic method. i.e., is prefixed with "__"?
79 | if (preg_match('|^__[^_]|', $methodName) !== 0) {
80 | $magicPart = strtolower(substr($methodName, 2));
81 | if (isset(Common::$magicMethods[$magicPart]) === false) {
82 | $errorData = [$className.'::'.$methodName];
83 | $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
84 | $phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData);
85 | }
86 |
87 | return;
88 | }
89 |
90 | // Get the method name without underscore prefix if it exists.
91 | if (strrpos($methodName, '_') === 0) {
92 | $namePart = substr($methodName, 1);
93 | } else {
94 | $namePart = $methodName;
95 | }
96 |
97 | // Naming check.
98 | if (Common::isCamelCaps($namePart, false, true, false) === false) {
99 | $errorData = [$methodName];
100 | $error = 'Method "%s" must be lowerCamelCase';
101 | $phpcsFile->addError($error, $stackPtr, 'MethodNotLowerCamelCase', $errorData);
102 | }
103 |
104 | // Methods must not be prefixed with an underscore except those in publicMethodNames.
105 | if (strrpos($methodName, '_') === 0) {
106 | if (isset(Common::$publicMethodNames[$methodName]) === false) {
107 | $methodProps = $phpcsFile->getMethodProperties($stackPtr);
108 | $scope = $methodProps['scope'];
109 | $errorData = [$className.'::'.$methodName];
110 | $error = ucfirst($scope).' method "%s" must not be prefixed with an underscore';
111 | $phpcsFile->addError($error, $stackPtr, 'MethodMustNotHaveUnderscore', $errorData);
112 | }
113 | }
114 |
115 | // Warn if method name is over 50 chars.
116 | $warningLimit = 50;
117 | if (strlen($methodName) > $warningLimit) {
118 | $errorData = [
119 | $methodName,
120 | $warningLimit,
121 | ];
122 |
123 | $warning = 'Method "%s" is over "%s" chars';
124 | $phpcsFile->addWarning($warning, $stackPtr, 'MethodNameIsLong', $errorData);
125 | }
126 |
127 | }//end processTokenWithinScope()
128 |
129 |
130 | }//end class
131 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Deprecated
2 |
3 | The new [CodeIgniter Coding Standard](https://github.com/CodeIgniter/coding-standard) is officially published!
4 | This switch uses PHP CS Fixer instead of Code Sniffer and applies a lot of changes (most notably, PSR-12 compliance).
5 | Visit the repo for more information. This library will remain in place but may not be maintained and is no longer
6 | considered compliant with official CodeIgniter 4 framework styles.
7 |
8 | # CodeIgniter4-Standard
9 |
10 | [CodeIgniter](https://codeigniter.com) 4 coding standard for use with [PHP_CodeSniffer 3](https://github.com/squizlabs/PHP_CodeSniffer).
11 |
12 | Version 1
13 |
14 | | Master | Develop |
15 | | :---: | :---: |
16 | | [](https://travis-ci.org/bcit-ci/CodeIgniter4-Standard) | [](https://travis-ci.org/bcit-ci/CodeIgniter4-Standard) |
17 | | [](https://coveralls.io/github/bcit-ci/CodeIgniter4-Standard?branch=master) | [](https://coveralls.io/github/bcit-ci/CodeIgniter4-Standard?branch=develop) |
18 |
19 | ***This is currently a work in progress.***
20 |
21 | *Requested at: https://github.com/bcit-ci/CodeIgniter4/issues/182*
22 |
23 | ## Requirements
24 |
25 | [PHP_CodeSniffer 3](https://github.com/squizlabs/PHP_CodeSniffer). (3.1.1 or greater).
26 |
27 | PHP (7.1 or greater) with mbstring extension.
28 |
29 | ## Install
30 |
31 | ### Composer install
32 |
33 | `cd /Path/To/MyProject`
34 | `composer require codeigniter4/codeigniter4-standard --dev`
35 |
36 | Set the `phpcs standard path` and `phpcbf standard path` in your editor/plugin config to:
37 |
38 | `/Path/To/MyProject/vendor/codeigniter4/codeigniter4-standard/CodeIgniter4/ruleset.xml`
39 |
40 | ### Download install
41 |
42 | Download [CodeIgniter4-Standard](https://github.com/bcit-ci/CodeIgniter4-Standard/archive/v1.0.1.zip).
43 |
44 | Set `standard ` paths to your local filesystem:
45 |
46 | `'/Path/To/CodeIgniter4-Standard/CodeIgniter4/ruleset.xml'`
47 |
48 | ### Global install
49 |
50 | Globally [install PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer/blob/master/README.md) with one of the various methods.
51 |
52 | Once complete you should be able to execute `phpcs -i` on the command line.
53 |
54 | You should see something like:-
55 |
56 | `The installed coding standards are MySource, PEAR, PSR1, PSR2, Squiz and Zend.`
57 |
58 | Either clone this repository...
59 |
60 | `git clone -b master --depth 1 https://github.com/bcit-ci/CodeIgniter4-Standard.git`.
61 |
62 | or use composer...
63 |
64 | `composer global require codeigniter4/codeigniter4-standard`
65 |
66 | or download.
67 |
68 | Take note of the paths where they were installed.
69 |
70 | Create a symbolic link to the `CodeIgniter4-Standard/CodeIgniter4` directory in `php_codesniffer/src/Standards/` eg.
71 |
72 | `ln -s ~/Documents/Projects/CodeIgniter4-Standard/CodeIgniter4 ~/.composer/vendor/squizlabs/php_codesniffer/src/Standards/CodeIgniter4`
73 |
74 | or copy the `CodeIgniter4-Standard/CodeIgniter4` directory to `php_codesniffer/src/Standards/`
75 |
76 | Executing `phpcs -i` should now show CodeIgniter4 installed eg.
77 |
78 | `The installed coding standards are CodeIgniter4, MySource, PEAR, PSR1, PSR2, Squiz and Zend.`
79 |
80 | You should now be able to set 'CodeIgniter4' as the phpcs standard in the plugin/editor/IDE of your choice.
81 |
82 | ### Command line use
83 |
84 | #### Sniffing errors & warnings (reporting).
85 |
86 | Single file...
87 |
88 | `phpcs /Path/To/MyFile.php --standard='/Path/To/CodeIgniter4-Standard/CodeIgniter4/ruleset.xml'`
89 |
90 | or if globally installed.
91 |
92 | `phpcs /Path/To/MyFile.php --standard=CodeIgniter4`
93 |
94 | Directory (recursive).
95 |
96 | `phpcs /Path/To/MyProject --standard='/Path/To/CodeIgniter4-Standard/CodeIgniter4/ruleset.xml'`
97 |
98 | or if globally installed.
99 |
100 | `phpcs /Path/To/MyProject --standard=CodeIgniter4`
101 |
102 | #### Fixing fixable errors.
103 |
104 | Single file.
105 |
106 | `phpcbf /Path/To/MyFile.php --standard='/Path/To/CodeIgniter4-Standard/CodeIgniter4/ruleset.xml'`
107 |
108 | or if globally installed.
109 |
110 | `phpcbf /Path/To/MyFile.php --standard=CodeIgniter4`
111 |
112 | Directory (recursive).
113 |
114 | `phpcbf /Path/To/MyProject --standard='/Path/To/CodeIgniter4-Standard/CodeIgniter4/ruleset.xml'`
115 |
116 | or if globally installed.
117 |
118 | `phpcbf /Path/To/MyProject --standard=CodeIgniter4`
119 |
120 | ## Credits
121 |
122 | Thanks to Greg Sherwood, Marc McIntyre, Andy Grunwald, Thomas Ernest and Erik Torsner, for providing open source code which helped me build this standard and a big thanks to [Squiz Labs](http://www.squizlabs.com) for creating [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer).
123 |
124 | Thanks to [EllisLab](https://ellislab.com) for originally creating CodeIgniter and the [British Columbia Institute of Technology](https://bcit.ca/) for continuing the project. Thanks to all the developers and contibutors working on [CodeIgniter 4](https://github.com/bcit-ci/CodeIgniter4).
125 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/ControlStructures/ControlStructureSpacingSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\ControlStructures;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Control Structure Spacing Sniff
18 | *
19 | * Checks that control structures have the correct spacing around brackets.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class ControlStructureSpacingSniff implements Sniff
24 | {
25 |
26 | /**
27 | * How many spaces should follow the opening bracket.
28 | *
29 | * @var integer
30 | */
31 | public $requiredSpacesAfterOpen = 0;
32 |
33 | /**
34 | * How many spaces should precede the closing bracket.
35 | *
36 | * @var integer
37 | */
38 | public $requiredSpacesBeforeClose = 0;
39 |
40 |
41 | /**
42 | * Returns an array of tokens this test wants to listen for.
43 | *
44 | * @return array
45 | */
46 | public function register()
47 | {
48 | return [
49 | T_IF,
50 | T_WHILE,
51 | T_FOREACH,
52 | T_FOR,
53 | T_SWITCH,
54 | T_DO,
55 | T_ELSE,
56 | T_ELSEIF,
57 | T_TRY,
58 | T_CATCH,
59 | ];
60 |
61 | }//end register()
62 |
63 |
64 | /**
65 | * Processes this test, when one of its tokens is encountered.
66 | *
67 | * @param File $phpcsFile The file being scanned.
68 | * @param int $stackPtr The position of the current token
69 | * in the stack passed in $tokens.
70 | *
71 | * @return void
72 | */
73 | public function process(File $phpcsFile, $stackPtr)
74 | {
75 | $this->requiredSpacesAfterOpen = (int) $this->requiredSpacesAfterOpen;
76 | $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
77 | $tokens = $phpcsFile->getTokens();
78 |
79 | if (isset($tokens[$stackPtr]['parenthesis_opener']) === false
80 | || isset($tokens[$stackPtr]['parenthesis_closer']) === false
81 | ) {
82 | return;
83 | }
84 |
85 | $parenOpener = $tokens[$stackPtr]['parenthesis_opener'];
86 | $parenCloser = $tokens[$stackPtr]['parenthesis_closer'];
87 | $nextContentPtr = $phpcsFile->findNext(T_WHITESPACE, ($parenOpener + 1), null, true);
88 |
89 | $spaceAfterOpen = 0;
90 | if ($tokens[($parenOpener + 1)]['code'] === T_WHITESPACE) {
91 | if (strpos($tokens[($parenOpener + 1)]['content'], $phpcsFile->eolChar) !== false) {
92 | $spaceAfterOpen = 'newline';
93 | } else {
94 | $spaceAfterOpen = strlen($tokens[($parenOpener + 1)]['content']);
95 | }
96 | }
97 |
98 | if ($spaceAfterOpen !== $this->requiredSpacesAfterOpen) {
99 | $error = 'Expected %s spaces after opening bracket; %s found';
100 | $data = [
101 | $this->requiredSpacesAfterOpen,
102 | $spaceAfterOpen,
103 | ];
104 | $fix = $phpcsFile->addFixableError($error, ($parenOpener + 1), 'SpacingAfterOpenBrace', $data);
105 | if ($fix === true) {
106 | $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
107 | if ($spaceAfterOpen === 0) {
108 | $phpcsFile->fixer->addContent($parenOpener, $padding);
109 | } else if ($spaceAfterOpen === 'newline') {
110 | $phpcsFile->fixer->replaceToken(($parenOpener + 1), '');
111 | } else {
112 | $phpcsFile->fixer->replaceToken(($parenOpener + 1), $padding);
113 | }
114 | }
115 | }
116 |
117 | // Spaces before control structure close parenthesis.
118 | if ($tokens[$parenOpener]['line'] === $tokens[$parenCloser]['line']) {
119 | $spaceBeforeClose = 0;
120 | if ($tokens[($parenCloser - 1)]['code'] === T_WHITESPACE) {
121 | $spaceBeforeClose = strlen(ltrim($tokens[($parenCloser - 1)]['content'], $phpcsFile->eolChar));
122 | }
123 |
124 | if ($spaceBeforeClose !== $this->requiredSpacesBeforeClose) {
125 | $error = 'Expected %s spaces before closing bracket; %s found';
126 | $data = [
127 | $this->requiredSpacesBeforeClose,
128 | $spaceBeforeClose,
129 | ];
130 | $fix = $phpcsFile->addFixableError($error, ($parenCloser - 1), 'SpaceBeforeCloseBrace', $data);
131 | if ($fix === true) {
132 | $padding = str_repeat(' ', $this->requiredSpacesBeforeClose);
133 | if ($spaceBeforeClose === 0) {
134 | $phpcsFile->fixer->addContentBefore($parenCloser, $padding);
135 | } else {
136 | $phpcsFile->fixer->replaceToken(($parenCloser - 1), $padding);
137 | }
138 | }
139 | }
140 | }//end if
141 |
142 | }//end process()
143 |
144 |
145 | }//end class
146 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Commenting/ClassCommentSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Commenting;
12 |
13 | use CodeIgniter4\Sniffs\Commenting\FileCommentSniff;
14 | use PHP_CodeSniffer\Sniffs\Sniff;
15 | use PHP_CodeSniffer\Files\File;
16 | use PHP_CodeSniffer\Util\Tokens;
17 |
18 | /**
19 | * Class Comment Sniff
20 | *
21 | * Parses and verifies the doc comments for classes.
22 | *
23 | * @author Louis Linehan
24 | */
25 | class ClassCommentSniff extends FileCommentSniff
26 | {
27 |
28 | /**
29 | * Tags in correct order and related info.
30 | *
31 | * @var array
32 | */
33 | protected $tags = [
34 | '@package' => [
35 | 'required' => false,
36 | 'allow_multiple' => false,
37 | ],
38 | '@subpackage' => [
39 | 'required' => false,
40 | 'allow_multiple' => false,
41 | ],
42 | '@category' => [
43 | 'required' => false,
44 | 'allow_multiple' => false,
45 | ],
46 | '@author' => [
47 | 'required' => false,
48 | 'allow_multiple' => true,
49 | ],
50 | '@copyright' => [
51 | 'required' => false,
52 | 'allow_multiple' => true,
53 | ],
54 | '@license' => [
55 | 'required' => false,
56 | 'allow_multiple' => false,
57 | ],
58 | '@link' => [
59 | 'required' => false,
60 | 'allow_multiple' => true,
61 | ],
62 | '@since' => [
63 | 'required' => false,
64 | 'allow_multiple' => false,
65 | ],
66 | '@version' => [
67 | 'required' => false,
68 | 'allow_multiple' => false,
69 | ],
70 | '@see' => [
71 | 'required' => false,
72 | 'allow_multiple' => true,
73 | ],
74 | '@deprecated' => [
75 | 'required' => false,
76 | 'allow_multiple' => false,
77 | ],
78 | ];
79 |
80 |
81 | /**
82 | * Returns an array of tokens this test wants to listen for.
83 | *
84 | * @return array
85 | */
86 | public function register()
87 | {
88 | return [
89 | T_CLASS,
90 | T_INTERFACE,
91 | ];
92 |
93 | }//end register()
94 |
95 |
96 | /**
97 | * Processes this test, when one of its tokens is encountered.
98 | *
99 | * @param File $phpcsFile The file being scanned.
100 | * @param int $stackPtr The position of the current token
101 | * in the stack passed in $tokens.
102 | *
103 | * @return void
104 | */
105 | public function process(File $phpcsFile, $stackPtr)
106 | {
107 | $this->currentFile = $phpcsFile;
108 |
109 | $tokens = $phpcsFile->getTokens();
110 | $type = strtolower($tokens[$stackPtr]['content']);
111 | $errorData = [$type];
112 |
113 | $find = Tokens::$methodPrefixes;
114 | $find[] = T_WHITESPACE;
115 |
116 | $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
117 | if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
118 | && $tokens[$commentEnd]['code'] !== T_COMMENT
119 | ) {
120 | $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing');
121 | $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no');
122 | return;
123 | } else {
124 | $phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes');
125 | }
126 |
127 | // Try and determine if this is a file comment instead of a class comment.
128 | // We assume that if this is the first comment after the open PHP tag, then
129 | // it is most likely a file comment instead of a class comment.
130 | if ($tokens[$commentEnd]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
131 | $start = ($tokens[$commentEnd]['comment_opener'] - 1);
132 | } else {
133 | $start = $phpcsFile->findPrevious(T_COMMENT, ($commentEnd - 1), null, true);
134 | }
135 |
136 | $prev = $phpcsFile->findPrevious(T_WHITESPACE, $start, null, true);
137 | if ($tokens[$prev]['code'] === T_OPEN_TAG) {
138 | $prevOpen = $phpcsFile->findPrevious(T_OPEN_TAG, ($prev - 1));
139 | if ($prevOpen === false) {
140 | // This is a comment directly after the first open tag,
141 | // so probably a file comment.
142 | $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing');
143 | return;
144 | }
145 | }
146 |
147 | if ($tokens[$commentEnd]['code'] === T_COMMENT) {
148 | $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle');
149 | return;
150 | }
151 |
152 | // Check each tag.
153 | $this->processTags($phpcsFile, $stackPtr, $tokens[$commentEnd]['comment_opener']);
154 |
155 | }//end process()
156 |
157 |
158 | }//end class
159 |
--------------------------------------------------------------------------------
/CodeIgniter4/Tests/Arrays/ArrayDeclarationUnitTest.inc.fixed:
--------------------------------------------------------------------------------
1 | 201,
7 | 'unsupported_response_type' => 400,
8 | 'invalid_scope' => 400,
9 | 'temporarily_unavailable' => 400,
10 | 'invalid_grant' => [
11 | 'invalid_data' => 400,
12 | 'access_denied' => 401,
13 | 'forbidden' => 403,
14 | 'resource_not_found' => 404,
15 | ],
16 | 'conflict' => 409,
17 | ];
18 |
19 | public $globals = [
20 | 'before' => [
21 | // 'csrf'
22 | ],
23 | 'after' => [
24 | 'toolbar'
25 | ],
26 | ];
27 |
28 | protected $defaultHTTPMethods = [
29 | 'options',
30 | 'get',
31 | 'head',
32 | ];
33 |
34 | protected function isValidLuhn()
35 | {
36 | $sumTable = [
37 | [
38 | 0,
39 | 1,
40 | 2,
41 | 3,
42 | 4,
43 | 5,
44 | 6,
45 | 7,
46 | 8,
47 | 9,
48 | ],
49 | [
50 | 0,
51 | 2,
52 | 4,
53 | 6,
54 | 8,
55 | 1,
56 | 3,
57 | 5,
58 | 7,
59 | 9,
60 | ],
61 | ];
62 | }
63 |
64 | protected $cards = [
65 | 'American Express' => [
66 | 'name' => 'amex',
67 | 'length' => '15',
68 | 'prefixes' => '34,37',
69 | 'checkdigit' => true,
70 | ],
71 | 'DinersClub' => [
72 | 'name' => 'dinersclub',
73 | 'length' => '14,16',
74 | 'prefixes' => '300,301,302,303,304,305,309,36,38,39,54,55',
75 | 'checkdigit' => true,
76 | ],
77 | 'MasterCard' => [
78 | 'name' => 'mastercard',
79 | 'length' => '16',
80 | 'prefixes' => '51,52,53,54,55,22,23,24,25,26,27',
81 | 'checkdigit' => true,
82 | ],
83 | 'Visa' => [
84 | 'name' => 'visa',
85 | 'length' => '13,16,19',
86 | 'prefixes' => '4',
87 | 'checkdigit' => true,
88 | ],
89 | // Canadian Cards
90 | 'CIBC Convenience Card' => [
91 | 'name' => 'cibc',
92 | 'length' => '16',
93 | 'prefixes' => '4506',
94 | 'checkdigit' => false,
95 | ],
96 | 'Royal Bank of Canada Client Card' => [
97 | 'name' => 'rbc',
98 | 'length' => '16',
99 | 'prefixes' => '45',
100 | 'checkdigit' => false,
101 | ],
102 | 'TD Canada Trust Access Card' => [
103 | 'name' => 'tdtrust',
104 | 'length' => '16',
105 | 'prefixes' => '589297',
106 | 'checkdigit' => false,
107 | ],
108 | ];
109 |
110 | $a =
111 | // comment
112 | [
113 | 'a',
114 | 'b',
115 | ];
116 |
117 | $a = /* comment */ [
118 | 'a',
119 | 'b',
120 | ];
121 |
122 | protected static $statusCodes = [
123 | // 1xx: Informational
124 | 100 => 'Continue',
125 | 101 => 'Switching Protocols',
126 | 102 => 'Processing', // http://www.iana.org/go/rfc2518
127 | // 2xx: Success
128 | 200 => 'OK',
129 | ];
130 |
131 | public function error()
132 | {
133 | return [
134 | 'code' => '',
135 | 'message' => pg_last_error($this->connID),
136 | ];
137 | }
138 |
139 | public function error2()
140 | {
141 | $this->QBWhere[] = [
142 | 'condition' => $like_statement,
143 | 'escape' => $escape,
144 | ];
145 |
146 | $this->QBWhere[] = [
147 | 'condition' => $like_statement,
148 | 'escape' => $escape,
149 | ];
150 | }
151 |
152 | public $arr = [];
153 |
154 | public $arr = [];
155 |
156 | public $arr = [1];
157 |
158 | public $arr = [
159 | 'a' => 1,
160 | 'b' => 2,
161 | 'c' => 3,
162 | ];
163 |
164 | $cache->save($cacheKeyPagination, [
165 | 'page' => $this->pager->getCurrentPage(),
166 | 'perPage' => $this->pager->getPerPage(),
167 | 'total' => $this->pager->getPerPage() * $this->pager->getPageCount(),
168 | ] , 15);
169 |
170 | return preg_replace([
171 | '#
#i',
172 | '#
`]+)).*?\>#i',
173 | ], '\\2', $str
174 | );
175 |
176 | $this->db->table($this->table)
177 | ->insert([
178 | 'version' => $version,
179 | 'name' => $this->name,
180 | ]);
181 |
182 | $message->setCustomProperty('user_info', [
183 | 'id' => $id,
184 | 'name' => 'Test message',
185 | ]);
186 |
187 | $users = [
188 | [
189 | 'id' => 1,
190 | 'name' => 'John',
191 | 'email' => 'john@example.com',
192 | 'fact' => 'Loves coding',
193 | ],
194 | [
195 | 'id' => 2,
196 | 'name' => 'Jim',
197 | 'email' => 'jim@example.com',
198 | 'fact' => 'Developed on CodeIgniter',
199 | ],
200 | [
201 | 'id' => 3,
202 | 'name' => 'Jane',
203 | 'email' => 'jane@example.com',
204 | 'fact' => 'Lives in the USA', [
205 | 'hobbies' => [
206 | 'guitar',
207 | 'cycling',
208 | ],
209 | ]
210 | ],
211 | ];
212 | }
213 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\WhiteSpace;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Function Closing Brace Space Sniff
18 | *
19 | * Checks that there is [allowedLines|allowedNestedLines] empty lines before the
20 | * closing brace of a function.
21 | *
22 | * @author Louis Linehan
23 | */
24 | class FunctionClosingBraceSpaceSniff implements Sniff
25 | {
26 |
27 | /**
28 | * A list of tokenizers this sniff supports.
29 | *
30 | * @var array
31 | */
32 | public $supportedTokenizers = [
33 | 'PHP',
34 | 'JS',
35 | ];
36 |
37 | /**
38 | * Allowed lines before a closing function bracket.
39 | *
40 | * @var array
41 | */
42 | public $allowedLines = 0;
43 |
44 | /**
45 | * Allowed spaces before a closing function bracket.
46 | *
47 | * @var array
48 | */
49 | public $allowedNestedLines = 0;
50 |
51 |
52 | /**
53 | * Returns an array of tokens this test wants to listen for.
54 | *
55 | * @return array
56 | */
57 | public function register()
58 | {
59 | return [
60 | T_FUNCTION,
61 | T_CLOSURE,
62 | ];
63 |
64 | }//end register()
65 |
66 |
67 | /**
68 | * Processes this test, when one of its tokens is encountered.
69 | *
70 | * @param File $phpcsFile The file being scanned.
71 | * @param int $stackPtr The position of the current token
72 | * in the stack passed in $tokens.
73 | *
74 | * @return void
75 | */
76 | public function process(File $phpcsFile, $stackPtr)
77 | {
78 | $tokens = $phpcsFile->getTokens();
79 |
80 | if (isset($tokens[$stackPtr]['scope_closer']) === false) {
81 | // Probably an interface method.
82 | return;
83 | }
84 |
85 | $closeBrace = $tokens[$stackPtr]['scope_closer'];
86 | $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), null, true);
87 |
88 | // Special case for empty JS functions.
89 | if ($phpcsFile->tokenizerType === 'JS' && $prevContent === $tokens[$stackPtr]['scope_opener']) {
90 | // In this case, the opening and closing brace must be
91 | // right next to each other.
92 | if ($tokens[$stackPtr]['scope_closer'] !== ($tokens[$stackPtr]['scope_opener'] + 1)) {
93 | $error = 'The opening and closing braces of empty functions must be directly next to each other; e.g., function () {}';
94 | $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBetween');
95 | if ($fix === true) {
96 | $phpcsFile->fixer->beginChangeset();
97 | for ($i = ($tokens[$stackPtr]['scope_opener'] + 1); $i < $closeBrace; $i++) {
98 | $phpcsFile->fixer->replaceToken($i, '');
99 | }
100 |
101 | $phpcsFile->fixer->endChangeset();
102 | }
103 | }
104 |
105 | return;
106 | }
107 |
108 | $nestedFunction = false;
109 | if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true
110 | || $phpcsFile->hasCondition($stackPtr, T_CLOSURE) === true
111 | || isset($tokens[$stackPtr]['nested_parenthesis']) === true
112 | ) {
113 | $nestedFunction = true;
114 | }
115 |
116 | $braceLine = $tokens[$closeBrace]['line'];
117 | $prevLine = $tokens[$prevContent]['line'];
118 | $found = ($braceLine - $prevLine - 1);
119 |
120 | $afterKeyword = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
121 | $beforeKeyword = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
122 | if ($nestedFunction === true) {
123 | if ($found < 0) {
124 | $error = 'Closing brace of nested function must be on a new line';
125 | $fix = $phpcsFile->addFixableError($error, $closeBrace, 'ContentBeforeClose');
126 | if ($fix === true) {
127 | $phpcsFile->fixer->addNewlineBefore($closeBrace);
128 | }
129 | } else if ($found > $this->allowedNestedLines) {
130 | $error = 'Expected %s blank lines before closing brace of nested function; %s found';
131 | $data = [
132 | $this->allowedNestedLines,
133 | $found,
134 | ];
135 | $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBeforeNestedClose', $data);
136 |
137 | if ($fix === true) {
138 | $phpcsFile->fixer->beginChangeset();
139 | $changeMade = false;
140 | for ($i = ($prevContent + 1); $i < $closeBrace; $i++) {
141 | // Try to maintain indentation.
142 | if ($tokens[$i]['line'] === ($braceLine - 1)) {
143 | break;
144 | }
145 |
146 | $phpcsFile->fixer->replaceToken($i, '');
147 | $changeMade = true;
148 | }
149 |
150 | // Special case for when the last content contains the newline
151 | // token as well, like with a comment.
152 | if ($changeMade === false) {
153 | $phpcsFile->fixer->replaceToken(($prevContent + 1), '');
154 | }
155 |
156 | $phpcsFile->fixer->endChangeset();
157 | }//end if
158 | }//end if
159 | } else {
160 | if ($found !== (int) $this->allowedLines) {
161 | if ($this->allowedLines === 1) {
162 | $plural = '';
163 | } else {
164 | $plural = 's';
165 | }
166 |
167 | $error = 'Expected %s blank line%s before closing function brace; %s found';
168 | $data = [
169 | $this->allowedLines,
170 | $plural,
171 | $found,
172 | ];
173 | $fix = $phpcsFile->addFixableError($error, $closeBrace, 'SpacingBeforeClose', $data);
174 |
175 | if ($fix === true) {
176 | if ($found > $this->allowedLines) {
177 | $phpcsFile->fixer->beginChangeset();
178 | for ($i = ($prevContent + 1); $i < ($closeBrace); $i++) {
179 | $phpcsFile->fixer->replaceToken($i, '');
180 | }
181 |
182 | $phpcsFile->fixer->replaceToken(($closeBrace - 1), $phpcsFile->eolChar);
183 | $phpcsFile->fixer->endChangeset();
184 | } else {
185 | $phpcsFile->fixer->addNewlineBefore($closeBrace);
186 | }
187 | }
188 | }//end if
189 | }//end if
190 |
191 | }//end process()
192 |
193 |
194 | }//end class
195 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/PHP/ForbiddenFunctionsSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\PHP;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Forbidden Functions Sniff
18 | *
19 | * Discourages the use of alias functions that are kept in PHP for compatibility
20 | * with older versions. Can be used to forbid the use of any function.
21 | *
22 | * @author Louis Linehan
23 | */
24 | class ForbiddenFunctionsSniff implements Sniff
25 | {
26 |
27 | /**
28 | * A list of forbidden functions with their alternatives.
29 | *
30 | * The value is NULL if no alternative exists. IE, the
31 | * function should just not be used.
32 | *
33 | * @var array|null)
34 | */
35 | public $forbiddenFunctions = ['sizeof' => 'count'];
36 |
37 | /**
38 | * A cache of forbidden function names, for faster lookups.
39 | *
40 | * @var array(string)
41 | */
42 | protected $forbiddenFunctionNames = [];
43 |
44 | /**
45 | * If true, forbidden functions will be considered regular expressions.
46 | *
47 | * @var boolean
48 | */
49 | protected $patternMatch = false;
50 |
51 | /**
52 | * If true, an error will be thrown; otherwise a warning.
53 | *
54 | * @var boolean
55 | */
56 | public $error = true;
57 |
58 |
59 | /**
60 | * Returns an array of tokens this test wants to listen for.
61 | *
62 | * @return array
63 | */
64 | public function register()
65 | {
66 | // Everyone has had a chance to figure out what forbidden functions
67 | // they want to check for, so now we can cache out the list.
68 | $this->forbiddenFunctionNames = array_keys($this->forbiddenFunctions);
69 |
70 | if ($this->patternMatch === true) {
71 | foreach ($this->forbiddenFunctionNames as $i => $name) {
72 | $this->forbiddenFunctionNames[$i] = '/'.$name.'/i';
73 | }
74 |
75 | return [T_STRING];
76 | }
77 |
78 | // If we are not pattern matching, we need to work out what
79 | // tokens to listen for.
80 | $string = 'forbiddenFunctionNames as $name) {
82 | $string .= $name.'();';
83 | }
84 |
85 | $register = [];
86 |
87 | $tokens = token_get_all($string);
88 | array_shift($tokens);
89 | foreach ($tokens as $token) {
90 | if (is_array($token) === true) {
91 | $register[] = $token[0];
92 | }
93 | }
94 |
95 | return array_unique($register);
96 |
97 | }//end register()
98 |
99 |
100 | /**
101 | * Processes this test, when one of its tokens is encountered.
102 | *
103 | * @param File $phpcsFile The file being scanned.
104 | * @param int $stackPtr The position of the current token in
105 | * the stack passed in $tokens.
106 | *
107 | * @return void
108 | */
109 | public function process(File $phpcsFile, $stackPtr)
110 | {
111 | $tokens = $phpcsFile->getTokens();
112 |
113 | $ignore = [
114 | T_DOUBLE_COLON => true,
115 | T_FUNCTION => true,
116 | T_CONST => true,
117 | T_PUBLIC => true,
118 | T_PRIVATE => true,
119 | T_PROTECTED => true,
120 | T_AS => true,
121 | T_NEW => true,
122 | T_INSTEADOF => true,
123 | T_NS_SEPARATOR => true,
124 | T_IMPLEMENTS => true,
125 | ];
126 |
127 | $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
128 |
129 | // If function call is directly preceded by a NS_SEPARATOR it points to the
130 | // global namespace, so we should still catch it.
131 | if ($tokens[$prevToken]['code'] === T_NS_SEPARATOR) {
132 | $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($prevToken - 1), null, true);
133 | if ($tokens[$prevToken]['code'] === T_STRING) {
134 | // Not in the global namespace.
135 | return;
136 | }
137 | }
138 |
139 | if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
140 | // Not a call to a PHP function.
141 | return;
142 | }
143 |
144 | $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
145 | if (isset($ignore[$tokens[$nextToken]['code']]) === true) {
146 | // Not a call to a PHP function.
147 | return;
148 | }
149 |
150 | if ($tokens[$stackPtr]['code'] === T_STRING && $tokens[$nextToken]['code'] !== T_OPEN_PARENTHESIS) {
151 | // Not a call to a PHP function.
152 | return;
153 | }
154 |
155 | $function = strtolower($tokens[$stackPtr]['content']);
156 | $pattern = null;
157 |
158 | if ($this->patternMatch === true) {
159 | $count = 0;
160 | $pattern = preg_replace(
161 | $this->forbiddenFunctionNames,
162 | $this->forbiddenFunctionNames,
163 | $function,
164 | 1,
165 | $count
166 | );
167 |
168 | if ($count === 0) {
169 | return;
170 | }
171 |
172 | // Remove the pattern delimiters and modifier.
173 | $pattern = substr($pattern, 1, -2);
174 | } else {
175 | if (in_array($function, $this->forbiddenFunctionNames) === false) {
176 | return;
177 | }
178 | }//end if
179 |
180 | $this->addError($phpcsFile, $stackPtr, $function, $pattern);
181 |
182 | }//end process()
183 |
184 |
185 | /**
186 | * Generates the error or warning for this sniff.
187 | *
188 | * @param File $phpcsFile The file being scanned.
189 | * @param int $stackPtr The position of the forbidden function
190 | * in the token array.
191 | * @param string $function The name of the forbidden function.
192 | * @param string $pattern The pattern used for the match.
193 | *
194 | * @return void
195 | */
196 | protected function addError($phpcsFile, $stackPtr, $function, $pattern=null)
197 | {
198 | $data = [$function];
199 | $error = 'The use of function %s() is ';
200 | if ($this->error === true) {
201 | $type = 'Found';
202 | $error .= 'forbidden';
203 | } else {
204 | $type = 'Discouraged';
205 | $error .= 'discouraged';
206 | }
207 |
208 | if ($pattern === null) {
209 | $pattern = $function;
210 | }
211 |
212 | if ($this->forbiddenFunctions[$pattern] !== null
213 | && $this->forbiddenFunctions[$pattern] !== 'null'
214 | ) {
215 | $type .= 'WithAlternative';
216 | $data[] = $this->forbiddenFunctions[$pattern];
217 | $error .= '; use %s() instead';
218 | }
219 |
220 | if ($this->error === true) {
221 | $phpcsFile->addError($error, $stackPtr, $type, $data);
222 | } else {
223 | $phpcsFile->addWarning($error, $stackPtr, $type, $data);
224 | }
225 |
226 | }//end addError()
227 |
228 |
229 | }//end class
230 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/NamingConventions/ValidVariableNameSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\NamingConventions;
12 |
13 | use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
14 | use CodeIgniter4\Util\Common;
15 | use PHP_CodeSniffer\Files\File;
16 |
17 | class ValidVariableNameSniff extends AbstractVariableSniff
18 | {
19 |
20 | /**
21 | * Tokens to ignore so that we can find a DOUBLE_COLON.
22 | *
23 | * @var array
24 | */
25 | private $ignore = [
26 | T_WHITESPACE,
27 | T_COMMENT,
28 | ];
29 |
30 |
31 | /**
32 | * Processes this test, when one of its tokens is encountered.
33 | *
34 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
35 | * @param int $stackPtr The position of the current token in the
36 | * stack passed in $tokens.
37 | *
38 | * @return void
39 | */
40 | protected function processVariable(File $phpcsFile, $stackPtr)
41 | {
42 | $tokens = $phpcsFile->getTokens();
43 | $varName = ltrim($tokens[$stackPtr]['content'], '$');
44 |
45 | $phpReservedVars = [
46 | '_SERVER',
47 | '_GET',
48 | '_POST',
49 | '_REQUEST',
50 | '_SESSION',
51 | '_ENV',
52 | '_COOKIE',
53 | '_FILES',
54 | 'GLOBALS',
55 | 'http_response_header',
56 | 'HTTP_RAW_POST_DATA',
57 | 'php_errormsg',
58 | ];
59 |
60 | // If it's a php reserved var, then its ok.
61 | if (in_array($varName, $phpReservedVars) === true) {
62 | return;
63 | }
64 |
65 | $objOperator = $phpcsFile->findNext([T_WHITESPACE], ($stackPtr + 1), null, true);
66 | if ($tokens[$objOperator]['code'] === T_OBJECT_OPERATOR) {
67 | // Check to see if we are using a variable from an object.
68 | $var = $phpcsFile->findNext([T_WHITESPACE], ($objOperator + 1), null, true);
69 | if ($tokens[$var]['code'] === T_STRING) {
70 | $bracket = $phpcsFile->findNext([T_WHITESPACE], ($var + 1), null, true);
71 | if ($tokens[$bracket]['code'] !== T_OPEN_PARENTHESIS) {
72 | $objVarName = $tokens[$var]['content'];
73 |
74 | // There is no way for us to know if the var is public or
75 | // private, so we have to ignore a leading underscore if there is
76 | // one and just check the main part of the variable name.
77 | $originalVarName = $objVarName;
78 | if (substr($objVarName, 0, 1) === '_') {
79 | $objVarName = substr($objVarName, 1);
80 | }
81 |
82 | if (Common::isCamelCaps($objVarName, false, true, false) === false) {
83 | $error = 'Object Variable "%s" must be lowerCamelCase';
84 | $data = [$originalVarName];
85 | $phpcsFile->addError($error, $var, 'ObjectVariableNotLowerCamelCase', $data);
86 | }
87 | }//end if
88 | }//end if
89 | }//end if
90 |
91 | // There is no way for us to know if the var is public or private,
92 | // so we have to ignore a leading underscore if there is one and just
93 | // check the main part of the variable name.
94 | $originalVarName = $varName;
95 | if (substr($varName, 0, 1) === '_') {
96 | $objOperator = $phpcsFile->findPrevious([T_WHITESPACE], ($stackPtr - 1), null, true);
97 | if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON) {
98 | // The variable lives within a class, and is referenced like
99 | // this: MyClass::$_variable, so we don't know its scope.
100 | $inClass = true;
101 | } else {
102 | $inClass = $phpcsFile->hasCondition($stackPtr, [T_CLASS, T_INTERFACE, T_TRAIT]);
103 | }
104 |
105 | if ($inClass === true) {
106 | $varName = substr($varName, 1);
107 | }
108 | }
109 |
110 | if (Common::isCamelCaps($varName, false, true, false) === false) {
111 | $error = 'Variable "%s" must be lowerCamelCase';
112 | $data = [$originalVarName];
113 | $phpcsFile->addError($error, $stackPtr, 'VariableNotLowerCamelCase', $data);
114 | }
115 |
116 | }//end processVariable()
117 |
118 |
119 | /**
120 | * Processes class member variables.
121 | *
122 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
123 | * @param int $stackPtr The position of the current token in the
124 | * stack passed in $tokens.
125 | *
126 | * @return void
127 | */
128 | protected function processMemberVar(File $phpcsFile, $stackPtr)
129 | {
130 | $tokens = $phpcsFile->getTokens();
131 |
132 | $varName = ltrim($tokens[$stackPtr]['content'], '$');
133 | $memberProps = $phpcsFile->getMemberProperties($stackPtr);
134 | if (empty($memberProps) === true) {
135 | // Couldn't get any info about this variable, which
136 | // generally means it is invalid or possibly has a parse
137 | // error. Any errors will be reported by the core, so
138 | // we can ignore it.
139 | return;
140 | }
141 |
142 | // Methods must not be prefixed with an underscore except those allowed in publicMethodNames.
143 | if (substr($varName, 0, 1) === '_') {
144 | if (isset(Common::$publicMethodNames[$varName]) === false) {
145 | $scope = $memberProps['scope'];
146 | $errorData = [$varName];
147 | $error = ucfirst($scope).' property "%s" must not be prefixed with an underscore';
148 | $phpcsFile->addError($error, $stackPtr, 'PropertyMustNotHaveUnderscore', $errorData);
149 | return;
150 | }
151 | }
152 |
153 | if (Common::isCamelCaps($varName, false, true, false) === false) {
154 | $errorData = [$varName];
155 | $error = 'Property "%s" is not in valid camel caps format';
156 | $phpcsFile->addError($error, $stackPtr, 'PropertyNotLowerCamelCase', $errorData);
157 | }
158 |
159 | }//end processMemberVar()
160 |
161 |
162 | /**
163 | * Processes the variable found within a double quoted string.
164 | *
165 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
166 | * @param int $stackPtr The position of the double quoted
167 | * string.
168 | *
169 | * @return void
170 | */
171 | protected function processVariableInString(File $phpcsFile, $stackPtr)
172 | {
173 | $tokens = $phpcsFile->getTokens();
174 |
175 | $phpReservedVars = [
176 | '_SERVER',
177 | '_GET',
178 | '_POST',
179 | '_REQUEST',
180 | '_SESSION',
181 | '_ENV',
182 | '_COOKIE',
183 | '_FILES',
184 | 'GLOBALS',
185 | 'http_response_header',
186 | 'HTTP_RAW_POST_DATA',
187 | 'php_errormsg',
188 | ];
189 |
190 | if (preg_match_all('|[^\\\]\${?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[$stackPtr]['content'], $matches) !== 0) {
191 | foreach ($matches[1] as $varName) {
192 | // If it's a php reserved var, then its ok.
193 | if (in_array($varName, $phpReservedVars) === true) {
194 | continue;
195 | }
196 |
197 | if (Common::isCamelCaps($varName, false, true, false) === false) {
198 | $error = 'Variable "%s" is not in valid camel caps format';
199 | $data = [$varName];
200 | $phpcsFile->addError($error, $stackPtr, 'StringNotCamelCaps', $data);
201 | }
202 | }
203 | }
204 |
205 | }//end processVariableInString()
206 |
207 |
208 | }//end class
209 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/ControlStructures/AllmanControlSignatureSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\ControlStructures;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * Allman Control Signature Sniff
18 | *
19 | * Verifies that control statements conform to their coding standards.
20 | *
21 | * @author Louis Linehan
22 | */
23 | class AllmanControlSignatureSniff implements Sniff
24 | {
25 |
26 | /**
27 | * A list of tokenizers this sniff supports.
28 | *
29 | * @var array
30 | */
31 | public $supportedTokenizers = [
32 | 'PHP',
33 | 'JS',
34 | ];
35 |
36 |
37 | /**
38 | * Returns an array of tokens this test wants to listen for.
39 | *
40 | * @return int[]
41 | */
42 | public function register()
43 | {
44 | return [
45 | T_TRY,
46 | T_CATCH,
47 | T_DO,
48 | T_WHILE,
49 | T_FOR,
50 | T_IF,
51 | T_FOREACH,
52 | T_ELSE,
53 | T_ELSEIF,
54 | ];
55 |
56 | }//end register()
57 |
58 |
59 | /**
60 | * Processes this test, when one of its tokens is encountered.
61 | *
62 | * @param File $phpcsFile The file being scanned.
63 | * @param int $stackPtr The position of the current token in the
64 | * stack passed in $tokens.
65 | *
66 | * @return void
67 | */
68 | public function process(File $phpcsFile, $stackPtr)
69 | {
70 | $tokens = $phpcsFile->getTokens();
71 |
72 | // Scope keyword should be on a new line.
73 | if (isset($tokens[$stackPtr]['scope_opener']) === true
74 | || $tokens[$stackPtr]['code'] === T_WHILE
75 | || $tokens[($stackPtr)]['code'] === T_ELSE
76 | ) {
77 | // If this is alternate syntax ":" instead of "{" then skip it.
78 | if (isset($tokens[$stackPtr]['scope_opener']) === true) {
79 | $openingBracePtr = $tokens[$stackPtr]['scope_opener'];
80 | if ($tokens[$openingBracePtr]['code'] === T_COLON) {
81 | return;
82 | }
83 | }
84 |
85 | $prevContentPtr = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
86 |
87 | // Skip if this T_IF is part of an else if.
88 | if ($tokens[($stackPtr)]['code'] !== T_IF
89 | && $tokens[($prevContentPtr)]['code'] !== T_ELSE
90 | ) {
91 | $keywordLine = $tokens[($stackPtr)]['line'];
92 | $prevContentLine = $tokens[($prevContentPtr)]['line'];
93 |
94 | if ($keywordLine === $prevContentLine) {
95 | $data = [$tokens[$stackPtr]['content']];
96 | $error = 'Scope keyword "%s" should be on a new line';
97 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'ScopeKeywordOnNewLine', $data);
98 | if ($fix === true) {
99 | $phpcsFile->fixer->beginChangeset();
100 | $phpcsFile->fixer->addNewlineBefore($stackPtr);
101 | $phpcsFile->fixer->endChangeset();
102 | }
103 | }
104 | }
105 | }//end if
106 |
107 | // Expect 1 space after keyword, Skip T_ELSE, T_DO, T_TRY.
108 | if (isset($tokens[$stackPtr]['scope_opener']) === true
109 | && $tokens[($stackPtr)]['code'] !== T_ELSE
110 | && $tokens[($stackPtr)]['code'] !== T_DO
111 | && $tokens[($stackPtr)]['code'] !== T_TRY
112 | ) {
113 | $found = 1;
114 | if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
115 | $found = 0;
116 | } else if ($tokens[($stackPtr + 1)]['content'] !== ' ') {
117 | if (strpos($tokens[($stackPtr + 1)]['content'], $phpcsFile->eolChar) !== false) {
118 | $found = 'newline';
119 | } else {
120 | $found = strlen($tokens[($stackPtr + 1)]['content']);
121 | }
122 | }
123 |
124 | if ($found !== 1) {
125 | $error = 'Expected 1 space after scope keyword "%s", found %s';
126 | $data = [
127 | strtoupper($tokens[$stackPtr]['content']),
128 | $found,
129 | ];
130 |
131 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterScopeKeyword', $data);
132 | if ($fix === true) {
133 | if ($found === 0) {
134 | $phpcsFile->fixer->addContent($stackPtr, ' ');
135 | } else {
136 | $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
137 | }
138 | }
139 | }
140 | }//end if
141 |
142 | // Opening brace should be on a new line.
143 | if (isset($tokens[$stackPtr]['scope_opener']) === true) {
144 | $openingBracePtr = $tokens[$stackPtr]['scope_opener'];
145 | $braceLine = $tokens[$openingBracePtr]['line'];
146 |
147 | // If this is alternate syntax ":" instead of "{" then skip it.
148 | if (isset($tokens[$stackPtr]['scope_opener']) === true) {
149 | $openingBracePtr = $tokens[$stackPtr]['scope_opener'];
150 | if ($tokens[$openingBracePtr]['code'] === T_COLON) {
151 | return;
152 | }
153 | }
154 |
155 | if ($tokens[($stackPtr)]['code'] === T_ELSE
156 | || $tokens[($stackPtr)]['code'] === T_TRY
157 | || $tokens[($stackPtr)]['code'] === T_DO
158 | ) {
159 | $scopeLine = $tokens[$stackPtr]['line'];
160 | // Measure from the scope opener.
161 | $lineDifference = ($braceLine - $scopeLine);
162 | } else {
163 | $closerLine = $tokens[$tokens[$stackPtr]['parenthesis_closer']]['line'];
164 | // Measure from the scope closing parenthesis.
165 | $lineDifference = ($braceLine - $closerLine);
166 | }
167 |
168 | if ($lineDifference !== 1) {
169 | $data = [
170 | $tokens[$openingBracePtr]['content'],
171 | $tokens[$stackPtr]['content'],
172 | ];
173 | if (isset($closerLine) === true) {
174 | $error = 'Opening brace "%s" should be on a new line after "%s (...)"';
175 | } else {
176 | $error = 'Opening brace "%s" should be on a new line after the keyword "%s"';
177 | }
178 |
179 | $fix = $phpcsFile->addFixableError($error, $openingBracePtr, 'ScopeOpeningBraceOnNewLine', $data);
180 | if ($fix === true) {
181 | $prevContentPtr = $phpcsFile->findPrevious(T_WHITESPACE, ($openingBracePtr - 1), null, true);
182 | $phpcsFile->fixer->beginChangeset();
183 | for ($i = ($prevContentPtr + 1); $i < $openingBracePtr; $i++) {
184 | $phpcsFile->fixer->replaceToken($i, '');
185 | }
186 |
187 | $phpcsFile->fixer->addNewlineBefore($openingBracePtr);
188 | $phpcsFile->fixer->endChangeset();
189 | }
190 | }//end if
191 | }//end if
192 |
193 | // No empty lines after opening brace.
194 | if (isset($tokens[$stackPtr]['scope_opener']) === true) {
195 | $openerPtr = $tokens[$stackPtr]['scope_opener'];
196 |
197 | $nextContentPtr = $phpcsFile->findNext(T_WHITESPACE, ($openerPtr + 1), null, true);
198 |
199 | $braceLine = $tokens[$openerPtr]['line'];
200 | $nextContentLine = $tokens[$nextContentPtr]['line'];
201 |
202 | $lineDifference = ($nextContentLine - $braceLine);
203 |
204 | if ($lineDifference === 0 || $lineDifference > 1) {
205 | $data = [$tokens[$openerPtr]['content']];
206 | $error = 'Expected content on line after "%s"';
207 | $fix = $phpcsFile->addFixableError($error, $openerPtr, 'NewlineAfterOpeningBrace', $data);
208 | if ($fix === true) {
209 | $phpcsFile->fixer->beginChangeset();
210 | for ($i = ($openerPtr + 1); $i < $nextContentPtr; $i++) {
211 | $phpcsFile->fixer->replaceToken($i, '');
212 | }
213 |
214 | $phpcsFile->fixer->addContent($openerPtr, $phpcsFile->eolChar);
215 | $phpcsFile->fixer->endChangeset();
216 | }
217 | }
218 | }//end if
219 |
220 | // Closing brace should be on a new line.
221 | if (isset($tokens[$stackPtr]['scope_closer']) === true) {
222 | $closerPtr = $tokens[$stackPtr]['scope_closer'];
223 |
224 | $prevContentPtr = $phpcsFile->findPrevious(T_WHITESPACE, ($closerPtr - 1), null, true);
225 |
226 | $braceLine = $tokens[$closerPtr]['line'];
227 | $prevContentLine = $tokens[$prevContentPtr]['line'];
228 |
229 | $lineDifference = ($braceLine - $prevContentLine);
230 |
231 | if ($lineDifference !== 1) {
232 | $data = [$tokens[$closerPtr]['content']];
233 | $error = 'Closing brace "%s" should be on a new line';
234 | $fix = $phpcsFile->addFixableError($error, $closerPtr, 'ClosingBraceOnNewLine', $data);
235 | if ($fix === true) {
236 | $phpcsFile->fixer->beginChangeset();
237 | for ($i = ($prevContentPtr + 1); $i < $closerPtr; $i++) {
238 | $phpcsFile->fixer->replaceToken($i, '');
239 | }
240 |
241 | $phpcsFile->fixer->addNewlineBefore($closerPtr);
242 | $phpcsFile->fixer->endChangeset();
243 | }
244 | }
245 | }//end if
246 |
247 | }//end process()
248 |
249 |
250 | }//end class
251 |
--------------------------------------------------------------------------------
/CodeIgniter4/ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeIgniter 4 coding standard for PHP_CodeSniffer
4 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
27 |
28 |
31 |
32 | 0
33 |
34 |
37 |
38 |
41 |
42 |
43 | 0
44 |
45 |
46 | 0
47 |
48 |
49 | 0
50 |
51 |
52 | 0
53 |
54 |
55 | 0
56 |
57 |
60 |
61 |
64 |
65 | warning
66 |
67 |
68 |
71 |
72 | Comments should not appear after statements
73 |
74 |
77 |
78 |
81 |
82 |
90 |
93 |
96 |
97 |
101 |
102 |
105 |
106 |
109 |
110 | 0
111 |
112 |
115 |
116 |
119 |
120 |
123 |
124 |
127 |
128 |
131 |
132 |
135 |
136 |
142 |
143 |
150 |
151 |
154 |
155 |
164 |
165 |
166 |
167 |
170 |
171 |
172 |
173 |
174 |
175 |
178 |
179 |
182 |
183 |
186 |
187 |
188 |
189 |
190 |
191 |
194 |
195 |
198 |
199 |
202 |
203 |
206 |
207 |
210 |
211 |
214 |
215 |
218 |
219 |
222 |
223 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
236 |
237 |
240 |
241 |
244 |
245 |
246 |
247 |
248 |
249 |
252 |
253 |
256 |
257 |
261 |
262 |
265 |
266 |
269 |
270 |
273 |
274 |
277 |
278 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
290 |
291 |
292 |
293 |
294 |
295 |
298 |
299 |
302 |
303 |
306 |
307 |
310 |
311 |
314 |
315 |
319 |
320 |
326 |
327 |
330 |
331 |
334 |
335 |
340 |
341 |
344 |
345 |
346 |
347 |
348 |
349 |
353 |
354 |
355 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Commenting/FileCommentSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Commenting;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 |
16 | /**
17 | * File Comment Sniff
18 | *
19 | * Check a doc comment exists.
20 | * Check the order of the tags.
21 | * Check the indentation of each tag.
22 | * Check required and optional tags and the format of their content.
23 | *
24 | * @author Louis Linehan
25 | */
26 | class FileCommentSniff implements Sniff
27 | {
28 |
29 | /**
30 | * Tags in correct order and related info.
31 | *
32 | * @var array
33 | */
34 | protected $tags = [
35 | '@package' => [
36 | 'required' => false,
37 | 'allow_multiple' => false,
38 | ],
39 | '@subpackage' => [
40 | 'required' => false,
41 | 'allow_multiple' => false,
42 | ],
43 | '@category' => [
44 | 'required' => false,
45 | 'allow_multiple' => false,
46 | ],
47 | '@author' => [
48 | 'required' => false,
49 | 'allow_multiple' => true,
50 | ],
51 | '@copyright' => [
52 | 'required' => false,
53 | 'allow_multiple' => true,
54 | ],
55 | '@license' => [
56 | 'required' => false,
57 | 'allow_multiple' => false,
58 | ],
59 | '@link' => [
60 | 'required' => false,
61 | 'allow_multiple' => true,
62 | ],
63 | '@since' => [
64 | 'required' => false,
65 | 'allow_multiple' => false,
66 | ],
67 | '@version' => [
68 | 'required' => false,
69 | 'allow_multiple' => false,
70 | ],
71 | '@see' => [
72 | 'required' => false,
73 | 'allow_multiple' => true,
74 | ],
75 | '@deprecated' => [
76 | 'required' => false,
77 | 'allow_multiple' => false,
78 | ],
79 | ];
80 |
81 |
82 | /**
83 | * Returns an array of tokens this test wants to listen for.
84 | *
85 | * @return array
86 | */
87 | public function register()
88 | {
89 | return [T_OPEN_TAG];
90 |
91 | }//end register()
92 |
93 |
94 | /**
95 | * Processes this test, when one of its tokens is encountered.
96 | *
97 | * @param File $phpcsFile The file being scanned.
98 | * @param int $stackPtr The position of the current token
99 | * in the stack passed in $tokens.
100 | *
101 | * @return int
102 | */
103 | public function process(File $phpcsFile, $stackPtr)
104 | {
105 | $tokens = $phpcsFile->getTokens();
106 |
107 | // Find the next non whitespace token.
108 | $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
109 |
110 | // Allow namespace at top of file.
111 | if ($tokens[$commentStart]['code'] === T_NAMESPACE) {
112 | $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($commentStart + 1));
113 | $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($semicolon + 1), null, true);
114 | }
115 |
116 | // Ignore vim header.
117 | if ($tokens[$commentStart]['code'] === T_COMMENT) {
118 | if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) {
119 | $commentStart = $phpcsFile->findNext(
120 | T_WHITESPACE,
121 | ($commentStart + 1),
122 | null,
123 | true
124 | );
125 | }
126 | }
127 |
128 | $errorToken = ($stackPtr + 1);
129 | if (isset($tokens[$errorToken]) === false) {
130 | $errorToken--;
131 | }
132 |
133 | if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) {
134 | // We are only interested if this is the first open tag.
135 | return ($phpcsFile->numTokens + 1);
136 | } else if ($tokens[$commentStart]['code'] === T_COMMENT) {
137 | $error = 'You must use "/**" style comments for a file comment';
138 | $phpcsFile->addError($error, $errorToken, 'WrongStyle');
139 | $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes');
140 | return ($phpcsFile->numTokens + 1);
141 | } else if ($commentStart === false
142 | || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG
143 | ) {
144 | $phpcsFile->addError('Missing file doc comment', $errorToken, 'Missing');
145 | $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no');
146 | return ($phpcsFile->numTokens + 1);
147 | } else {
148 | $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes');
149 | }
150 |
151 | // Check each tag.
152 | $this->processTags($phpcsFile, $stackPtr, $commentStart);
153 |
154 | // Check there is 1 empty line after.
155 | $commentCloser = $tokens[$commentStart]['comment_closer'];
156 | $nextContentPtr = $phpcsFile->findNext(T_WHITESPACE, ($commentCloser + 1), null, true);
157 | $lineDiff = ($tokens[$nextContentPtr]['line'] - $tokens[$commentCloser]['line']);
158 | if ($lineDiff === 1) {
159 | $data = [1];
160 | $error = 'Expected %s empty line after file doc comment';
161 | $fix = $phpcsFile->addFixableError($error, ($commentCloser + 1), 'NoEmptyLineAfterFileDocComment', $data);
162 | if ($fix === true) {
163 | $phpcsFile->fixer->beginChangeset();
164 | $phpcsFile->fixer->addNewlineBefore($nextContentPtr);
165 | $phpcsFile->fixer->endChangeset();
166 | }
167 | }
168 |
169 | // Ignore the rest of the file.
170 | return ($phpcsFile->numTokens + 1);
171 |
172 | }//end process()
173 |
174 |
175 | /**
176 | * Processes each required or optional tag.
177 | *
178 | * @param File $phpcsFile The file being scanned.
179 | * @param int $stackPtr The position of the current token
180 | * in the stack passed in $tokens.
181 | * @param int $commentStart Position in the stack where the comment started.
182 | *
183 | * @return void
184 | */
185 | protected function processTags(File $phpcsFile, $stackPtr, $commentStart)
186 | {
187 | $tokens = $phpcsFile->getTokens();
188 |
189 | if (get_class($this) === 'FileCommentSniff') {
190 | $docBlock = 'file';
191 | } else {
192 | $docBlock = 'class';
193 | }
194 |
195 | $commentEnd = $tokens[$commentStart]['comment_closer'];
196 |
197 | $foundTags = [];
198 | $tagTokens = [];
199 | foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
200 | $name = $tokens[$tag]['content'];
201 | if (isset($this->tags[$name]) === false) {
202 | continue;
203 | }
204 |
205 | if ($this->tags[$name]['allow_multiple'] === false && isset($tagTokens[$name]) === true) {
206 | $error = 'Only one %s tag is allowed in a %s comment';
207 | $data = [
208 | $name,
209 | $docBlock,
210 | ];
211 | $phpcsFile->addError($error, $tag, 'Duplicate'.ucfirst(substr($name, 1)).'Tag', $data);
212 | }
213 |
214 | $foundTags[] = $name;
215 | $tagTokens[$name][] = $tag;
216 |
217 | $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
218 | if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) {
219 | $error = 'Content missing for %s tag in %s comment';
220 | $data = [
221 | $name,
222 | $docBlock,
223 | ];
224 | $phpcsFile->addError($error, $tag, 'Empty'.ucfirst(substr($name, 1)).'Tag', $data);
225 | continue;
226 | }
227 | }//end foreach
228 |
229 | // Check if the tags are in the correct position.
230 | $pos = 0;
231 | foreach ($this->tags as $tag => $tagData) {
232 | if (isset($tagTokens[$tag]) === false) {
233 | if ($tagData['required'] === true) {
234 | $error = 'Missing %s tag in %s comment';
235 | $data = [
236 | $tag,
237 | $docBlock,
238 | ];
239 | $phpcsFile->addError($error, $commentEnd, 'Missing'.ucfirst(substr($tag, 1)).'Tag', $data);
240 | }
241 |
242 | continue;
243 | } else {
244 | $method = 'process'.substr($tag, 1);
245 | if (method_exists($this, $method) === true) {
246 | // Process each tag if a method is defined.
247 | call_user_func([$this, $method], $phpcsFile, $tagTokens[$tag]);
248 | }
249 | }
250 |
251 | if (isset($foundTags[$pos]) === false) {
252 | break;
253 | }
254 |
255 | if ($foundTags[$pos] !== $tag) {
256 | $error = 'The tag in position %s should be the %s tag';
257 | $data = [
258 | ($pos + 1),
259 | $tag,
260 | ];
261 | $phpcsFile->addError($error, $tokens[$commentStart]['comment_tags'][$pos], ucfirst(substr($tag, 1)).'TagOrder', $data);
262 | }
263 |
264 | // Account for multiple tags.
265 | $pos++;
266 | while (isset($foundTags[$pos]) === true && $foundTags[$pos] === $tag) {
267 | $pos++;
268 | }
269 | }//end foreach
270 |
271 | }//end processTags()
272 |
273 |
274 | /**
275 | * Process the package tag.
276 | *
277 | * @param File $phpcsFile The file being scanned.
278 | * @param array $tags The tokens for these tags.
279 | *
280 | * @return void
281 | */
282 | protected function processPackage(File $phpcsFile, array $tags)
283 | {
284 | $tokens = $phpcsFile->getTokens();
285 | foreach ($tags as $tag) {
286 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
287 | // No content.
288 | continue;
289 | }
290 |
291 | $content = $tokens[($tag + 2)]['content'];
292 |
293 | $isInvalidPackage = false;
294 |
295 | if (strpos($content, '/') !== false) {
296 | $parts = explode('/', $content);
297 | $newName = join('\\', $parts);
298 |
299 | $isInvalidPackage = true;
300 | }
301 |
302 | if ($isInvalidPackage === true) {
303 | $error = 'Package name "%s" is not valid. Use "%s" instead';
304 | $validName = trim($newName, '_');
305 | $data = [
306 | $content,
307 | $validName,
308 | ];
309 | $phpcsFile->addWarning($error, $tag, 'InvalidPackage', $data);
310 | }
311 | }//end foreach
312 |
313 | }//end processPackage()
314 |
315 |
316 | /**
317 | * Process the subpackage tag.
318 | *
319 | * @param File $phpcsFile The file being scanned.
320 | * @param array $tags The tokens for these tags.
321 | *
322 | * @return void
323 | */
324 | protected function processSubpackage(File $phpcsFile, array $tags)
325 | {
326 | $tokens = $phpcsFile->getTokens();
327 | foreach ($tags as $tag) {
328 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
329 | // No content.
330 | continue;
331 | }
332 |
333 | $warning = 'The subpackage tag is considered deprecated. It is recommended to use the @package tag instead.';
334 |
335 | $phpcsFile->addWarning($warning, $tag, 'SubpackageDepreciated');
336 | }//end foreach
337 |
338 | }//end processSubpackage()
339 |
340 |
341 | /**
342 | * Process the category tag.
343 | *
344 | * @param File $phpcsFile The file being scanned.
345 | * @param array $tags The tokens for these tags.
346 | *
347 | * @return void
348 | */
349 | protected function processCategory(File $phpcsFile, array $tags)
350 | {
351 | $tokens = $phpcsFile->getTokens();
352 | foreach ($tags as $tag) {
353 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
354 | // No content.
355 | continue;
356 | }
357 |
358 | $warning = 'The category tag is considered deprecated. It is recommended to use the @package tag instead.';
359 |
360 | $phpcsFile->addWarning($warning, $tag, 'CategoryDepreciated');
361 | }//end foreach
362 |
363 | }//end processCategory()
364 |
365 |
366 | /**
367 | * Process the author tag(s) that this header comment has.
368 | *
369 | * @param File $phpcsFile The file being scanned.
370 | * @param array $tags The tokens for these tags.
371 | *
372 | * @return void
373 | */
374 | protected function processAuthor(File $phpcsFile, array $tags)
375 | {
376 | $tokens = $phpcsFile->getTokens();
377 | foreach ($tags as $tag) {
378 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
379 | // No content.
380 | continue;
381 | }
382 |
383 | $content = $tokens[($tag + 2)]['content'];
384 |
385 | // If it has an @ it's probably contains email address.
386 | if (strrpos($content, '@') !== false) {
387 | $local = '\da-zA-Z-_+';
388 | // Dot character cannot be the first or last character in the local-part.
389 | $localMiddle = $local.'.\w';
390 | if (preg_match('/^([^<]*)\s+<(['.$local.'](['.$localMiddle.']*['.$local.'])*@[\da-zA-Z][-.\w]*[\da-zA-Z]\.[a-zA-Z]{2,7})>$/', $content) === 0) {
391 | $error = '"@author" with email must be "Display Name "';
392 | $phpcsFile->addError($error, $tag, 'InvalidAuthors');
393 | }
394 | }
395 | }
396 |
397 | }//end processAuthor()
398 |
399 |
400 | /**
401 | * Process the copyright tags.
402 | *
403 | * @param File $phpcsFile The file being scanned.
404 | * @param array $tags The tokens for these tags.
405 | *
406 | * @return void
407 | */
408 | protected function processCopyright(File $phpcsFile, array $tags)
409 | {
410 | $tokens = $phpcsFile->getTokens();
411 | foreach ($tags as $tag) {
412 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
413 | // No content.
414 | continue;
415 | }
416 |
417 | $content = $tokens[($tag + 2)]['content'];
418 | $matches = [];
419 | if (preg_match('/^([0-9]{4})((.{1})([0-9]{4}))? (.+)$/', $content, $matches) !== 0) {
420 | // Check earliest-latest year order.
421 | if ($matches[3] !== '') {
422 | if ($matches[3] !== '-') {
423 | $error = 'A hyphen must be used between the earliest and latest year';
424 | $phpcsFile->addError($error, $tag, 'CopyrightHyphen');
425 | }
426 |
427 | if ($matches[4] !== '' && $matches[4] < $matches[1]) {
428 | $error = "Invalid year span \"$matches[1]$matches[3]$matches[4]\" found; consider \"$matches[4]-$matches[1]\" instead";
429 | $phpcsFile->addWarning($error, $tag, 'InvalidCopyright');
430 | }
431 | }
432 | } else {
433 | $error = '"@copyright" must be "YYYY [- YYYY] Name of the copyright holder"';
434 | $phpcsFile->addError($error, $tag, 'IncompleteCopyright');
435 | }
436 | }//end foreach
437 |
438 | }//end processCopyright()
439 |
440 |
441 | /**
442 | * Process the license tag.
443 | *
444 | * @param File $phpcsFile The file being scanned.
445 | * @param array $tags The tokens for these tags.
446 | *
447 | * @return void
448 | */
449 | protected function processLicense(File $phpcsFile, array $tags)
450 | {
451 | $tokens = $phpcsFile->getTokens();
452 | foreach ($tags as $tag) {
453 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
454 | // No content.
455 | continue;
456 | }
457 |
458 | $content = $tokens[($tag + 2)]['content'];
459 | $matches = [];
460 | preg_match('/^([^\s]+)\s+(.*)/', $content, $matches);
461 |
462 | if (count($matches) !== 3) {
463 | $error = '@license tag must contain a URL and a license name';
464 | $phpcsFile->addError($error, $tag, 'IncompleteLicense');
465 | }
466 |
467 | // Check the url is before the text part if it's included.
468 | $parts = explode(' ', $content);
469 | if ((count($parts) > 1)) {
470 | $matches = [];
471 | preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i", $parts[0], $matches);
472 | if (count($matches) !== 1) {
473 | $error = 'The URL must come before the license name';
474 | $phpcsFile->addError($error, $tag, 'LicenseURLNotFirst');
475 | }
476 | }
477 | }//end foreach
478 |
479 | }//end processLicense()
480 |
481 |
482 | /**
483 | * Process the link tag.
484 | *
485 | * @param File $phpcsFile The file being scanned.
486 | * @param array $tags The tokens for these tags.
487 | *
488 | * @return void
489 | */
490 | protected function processLink(File $phpcsFile, array $tags)
491 | {
492 | $tokens = $phpcsFile->getTokens();
493 | foreach ($tags as $tag) {
494 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
495 | // No content.
496 | continue;
497 | }
498 |
499 | $content = $tokens[($tag + 2)]['content'];
500 | $matches = [];
501 |
502 | // Check the url is before the text part if it's included.
503 | $parts = explode(' ', $content);
504 | if ((count($parts) > 1)) {
505 | $matches = [];
506 | preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i", $parts[0], $matches);
507 | if (count($matches) !== 1) {
508 | $error = 'The URL must come before the description';
509 | $phpcsFile->addError($error, $tag, 'LinkURLNotFirst');
510 | }
511 | }
512 | }//end foreach
513 |
514 | }//end processLink()
515 |
516 |
517 | /**
518 | * Process the version tag.
519 | *
520 | * @param File $phpcsFile The file being scanned.
521 | * @param array $tags The tokens for these tags.
522 | *
523 | * @return void
524 | */
525 | protected function processVersion(File $phpcsFile, array $tags)
526 | {
527 | $tokens = $phpcsFile->getTokens();
528 | foreach ($tags as $tag) {
529 | if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) {
530 | // No content.
531 | continue;
532 | }
533 |
534 | $content = $tokens[($tag + 2)]['content'];
535 | // Split into parts if content has a space.
536 | $parts = explode(' ', $content);
537 | // Check if the first part contains a semantic version number.
538 | $matches = [];
539 | preg_match('/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/', $parts[0], $matches);
540 |
541 | if (strstr($content, 'CVS:') === false
542 | && strstr($content, 'SVN:') === false
543 | && strstr($content, 'GIT:') === false
544 | && strstr($content, 'HG:') === false
545 | && count($matches) === 0
546 | ) {
547 | $error = 'It is recommended that @version is a semantic version number or is a VCS version vector "GIT: " or "SVN: " or "HG: " or "CVS: ".';
548 | $phpcsFile->addWarning($error, $tag, 'InvalidVersion');
549 | }
550 | }//end foreach
551 |
552 | }//end processVersion()
553 |
554 |
555 | }//end class
556 |
--------------------------------------------------------------------------------
/CodeIgniter4/Sniffs/Functions/FunctionDeclarationSniff.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 British Columbia Institute of Technology
8 | * @license https://github.com/bcit-ci/CodeIgniter4-Standard/blob/master/LICENSE MIT License
9 | */
10 |
11 | namespace CodeIgniter4\Sniffs\Functions;
12 |
13 | use PHP_CodeSniffer\Sniffs\Sniff;
14 | use PHP_CodeSniffer\Files\File;
15 | use PHP_CodeSniffer\Util\Tokens;
16 | use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\OpeningFunctionBraceKernighanRitchieSniff;
17 | use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\OpeningFunctionBraceBsdAllmanSniff;
18 |
19 | /**
20 | * Function Declaration Sniff
21 | *
22 | * Ensure single and multi-line function declarations are defined correctly.
23 | *
24 | * @author Louis Linehan
25 | */
26 | class FunctionDeclarationSniff implements Sniff
27 | {
28 |
29 | /**
30 | * A list of tokenizers this sniff supports.
31 | *
32 | * @var array
33 | */
34 | public $supportedTokenizers = [
35 | 'PHP',
36 | 'JS',
37 | ];
38 |
39 | /**
40 | * The number of spaces code should be indented.
41 | *
42 | * @var integer
43 | */
44 | public $indent = 4;
45 |
46 |
47 | /**
48 | * Returns an array of tokens this test wants to listen for.
49 | *
50 | * @return array
51 | */
52 | public function register()
53 | {
54 | return [
55 | T_FUNCTION,
56 | T_CLOSURE,
57 | ];
58 |
59 | }//end register()
60 |
61 |
62 | /**
63 | * Processes this test, when one of its tokens is encountered.
64 | *
65 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
66 | * @param int $stackPtr The position of the current token
67 | * in the stack passed in $tokens.
68 | *
69 | * @return void
70 | */
71 | public function process(File $phpcsFile, $stackPtr)
72 | {
73 | $tokens = $phpcsFile->getTokens();
74 |
75 | if (isset($tokens[$stackPtr]['parenthesis_opener']) === false
76 | || isset($tokens[$stackPtr]['parenthesis_closer']) === false
77 | || $tokens[$stackPtr]['parenthesis_opener'] === null
78 | || $tokens[$stackPtr]['parenthesis_closer'] === null
79 | ) {
80 | return;
81 | }
82 |
83 | $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
84 | $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
85 |
86 | if (strtolower($tokens[$stackPtr]['content']) === 'function') {
87 | // Must be one space after the FUNCTION keyword.
88 | if ($tokens[($stackPtr + 1)]['content'] === $phpcsFile->eolChar) {
89 | $spaces = 'newline';
90 | } else if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
91 | $spaces = strlen($tokens[($stackPtr + 1)]['content']);
92 | } else {
93 | $spaces = 0;
94 | }
95 |
96 | if ($spaces !== 1) {
97 | $error = 'Expected 1 space after FUNCTION keyword; %s found';
98 | $data = [$spaces];
99 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterFunction', $data);
100 | if ($fix === true) {
101 | if ($spaces === 0) {
102 | $phpcsFile->fixer->addContent($stackPtr, ' ');
103 | } else {
104 | $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
105 | }
106 | }
107 | }
108 | }//end if
109 |
110 | // Must be one space before the opening parenthesis. For closures, this is
111 | // enforced by the first check because there is no content between the keywords
112 | // and the opening parenthesis.
113 | if ($tokens[$stackPtr]['code'] === T_FUNCTION) {
114 | if ($tokens[($openBracket - 1)]['content'] === $phpcsFile->eolChar) {
115 | $spaces = 'newline';
116 | } else if ($tokens[($openBracket - 1)]['code'] === T_WHITESPACE) {
117 | $spaces = strlen($tokens[($openBracket - 1)]['content']);
118 | } else {
119 | $spaces = 0;
120 | }
121 |
122 | if ($spaces !== 0) {
123 | $error = 'Expected 0 spaces before opening parenthesis; %s found';
124 | $data = [$spaces];
125 | $fix = $phpcsFile->addFixableError($error, $openBracket, 'SpaceBeforeOpenParenthesis', $data);
126 | if ($fix === true) {
127 | $phpcsFile->fixer->replaceToken(($openBracket - 1), '');
128 | }
129 | }
130 | }//end if
131 |
132 | // Must be one space before and after USE keyword for closures.
133 | if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
134 | $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
135 | if ($use !== false) {
136 | if ($tokens[($use + 1)]['code'] !== T_WHITESPACE) {
137 | $length = 0;
138 | } else if ($tokens[($use + 1)]['content'] === "\t") {
139 | $length = '\t';
140 | } else {
141 | $length = strlen($tokens[($use + 1)]['content']);
142 | }
143 |
144 | if ($length !== 1) {
145 | $error = 'Expected 1 space after USE keyword; found %s';
146 | $data = [$length];
147 | $fix = $phpcsFile->addFixableError($error, $use, 'SpaceAfterUse', $data);
148 | if ($fix === true) {
149 | if ($length === 0) {
150 | $phpcsFile->fixer->addContent($use, ' ');
151 | } else {
152 | $phpcsFile->fixer->replaceToken(($use + 1), ' ');
153 | }
154 | }
155 | }
156 |
157 | if ($tokens[($use - 1)]['code'] !== T_WHITESPACE) {
158 | $length = 0;
159 | } else if ($tokens[($use - 1)]['content'] === "\t") {
160 | $length = '\t';
161 | } else {
162 | $length = strlen($tokens[($use - 1)]['content']);
163 | }
164 |
165 | if ($length !== 1) {
166 | $error = 'Expected 1 space before USE keyword; found %s';
167 | $data = [$length];
168 | $fix = $phpcsFile->addFixableError($error, $use, 'SpaceBeforeUse', $data);
169 | if ($fix === true) {
170 | if ($length === 0) {
171 | $phpcsFile->fixer->addContentBefore($use, ' ');
172 | } else {
173 | $phpcsFile->fixer->replaceToken(($use - 1), ' ');
174 | }
175 | }
176 | }
177 | }//end if
178 | }//end if
179 |
180 | if ($this->isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens) === true) {
181 | $this->processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens);
182 | } else {
183 | $this->processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens);
184 | }
185 |
186 | }//end process()
187 |
188 |
189 | /**
190 | * Determine if this is a multi-line function declaration.
191 | *
192 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
193 | * @param int $stackPtr The position of the current token
194 | * in the stack passed in $tokens.
195 | * @param int $openBracket The position of the opening bracket
196 | * in the stack passed in $tokens.
197 | * @param array $tokens The stack of tokens that make up
198 | * the file.
199 | *
200 | * @return void
201 | */
202 | public function isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens)
203 | {
204 | $bracketsToCheck = [$stackPtr => $openBracket];
205 |
206 | // Closures may use the USE keyword and so be multi-line in this way.
207 | if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
208 | $use = $phpcsFile->findNext(T_USE, ($tokens[$openBracket]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
209 | if ($use !== false) {
210 | $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
211 | if ($open !== false) {
212 | $bracketsToCheck[$use] = $open;
213 | }
214 | }
215 | }
216 |
217 | foreach ($bracketsToCheck as $stackPtr => $openBracket) {
218 | // If the first argument is on a new line, this is a multi-line
219 | // function declaration, even if there is only one argument.
220 | $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
221 | if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
222 | return true;
223 | }
224 |
225 | $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
226 |
227 | $end = $phpcsFile->findEndOfStatement($openBracket + 1);
228 | while ($tokens[$end]['code'] === T_COMMA) {
229 | // If the next bit of code is not on the same line, this is a
230 | // multi-line function declaration.
231 | $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), $closeBracket, true);
232 | if ($next === false) {
233 | continue(2);
234 | }
235 |
236 | if ($tokens[$next]['line'] !== $tokens[$end]['line']) {
237 | return true;
238 | }
239 |
240 | $end = $phpcsFile->findEndOfStatement($next);
241 | }
242 |
243 | // We've reached the last argument, so see if the next content
244 | // (should be the close bracket) is also on the same line.
245 | $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), $closeBracket, true);
246 | if ($next !== false && $tokens[$next]['line'] !== $tokens[$end]['line']) {
247 | return true;
248 | }
249 | }//end foreach
250 |
251 | return false;
252 |
253 | }//end isMultiLineDeclaration()
254 |
255 |
256 | /**
257 | * Processes single-line declarations.
258 | *
259 | * Just uses the Generic BSD-Allman brace sniff.
260 | *
261 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
262 | * @param int $stackPtr The position of the current token
263 | * in the stack passed in $tokens.
264 | * @param array $tokens The stack of tokens that make up
265 | * the file.
266 | *
267 | * @return void
268 | */
269 | public function processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens)
270 | {
271 | if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
272 | $sniff = new OpeningFunctionBraceKernighanRitchieSniff();
273 | } else {
274 | $sniff = new OpeningFunctionBraceBsdAllmanSniff();
275 | }
276 |
277 | $sniff->checkClosures = true;
278 | $sniff->process($phpcsFile, $stackPtr);
279 |
280 | }//end processSingleLineDeclaration()
281 |
282 |
283 | /**
284 | * Processes multi-line declarations.
285 | *
286 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
287 | * @param int $stackPtr The position of the current token
288 | * in the stack passed in $tokens.
289 | * @param array $tokens The stack of tokens that make up
290 | * the file.
291 | *
292 | * @return void
293 | */
294 | public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens)
295 | {
296 | // We need to work out how far indented the function
297 | // declaration itself is, so we can work out how far to
298 | // indent parameters.
299 | $functionIndent = 0;
300 | for ($i = ($stackPtr - 1); $i >= 0; $i--) {
301 | if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) {
302 | $i++;
303 | break;
304 | }
305 | }
306 |
307 | if ($tokens[$i]['code'] === T_WHITESPACE) {
308 | $functionIndent = strlen($tokens[$i]['content']);
309 | }
310 |
311 | // The closing parenthesis must be on a new line, even
312 | // when checking abstract function definitions.
313 | $closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
314 | $prev = $phpcsFile->findPrevious(
315 | T_WHITESPACE,
316 | ($closeBracket - 1),
317 | null,
318 | true
319 | );
320 |
321 | if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line']) {
322 | if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
323 | $error = 'The closing parenthesis of a multi-line function declaration must be on a new line';
324 | $fix = $phpcsFile->addFixableError($error, $closeBracket, 'CloseBracketLine');
325 | if ($fix === true) {
326 | $phpcsFile->fixer->addNewlineBefore($closeBracket);
327 | }
328 | }
329 | }
330 |
331 | // If this is a closure and is using a USE statement, the closing
332 | // parenthesis we need to look at from now on is the closing parenthesis
333 | // of the USE statement.
334 | if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
335 | $use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
336 | if ($use !== false) {
337 | $open = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
338 | $closeBracket = $tokens[$open]['parenthesis_closer'];
339 |
340 | $prev = $phpcsFile->findPrevious(
341 | T_WHITESPACE,
342 | ($closeBracket - 1),
343 | null,
344 | true
345 | );
346 |
347 | if ($tokens[$closeBracket]['line'] !== $tokens[$tokens[$closeBracket]['parenthesis_opener']]['line']) {
348 | if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
349 | $error = 'The closing parenthesis of a multi-line use declaration must be on a new line';
350 | $fix = $phpcsFile->addFixableError($error, $closeBracket, 'UseCloseBracketLine');
351 | if ($fix === true) {
352 | $phpcsFile->fixer->addNewlineBefore($closeBracket);
353 | }
354 | }
355 | }
356 | }//end if
357 | }//end if
358 |
359 | // Each line between the parenthesis should be indented 4 spaces.
360 | $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
361 | $lastLine = $tokens[$openBracket]['line'];
362 | for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
363 | if ($tokens[$i]['line'] !== $lastLine) {
364 | if ($i === $tokens[$stackPtr]['parenthesis_closer']
365 | || ($tokens[$i]['code'] === T_WHITESPACE
366 | && (($i + 1) === $closeBracket
367 | || ($i + 1) === $tokens[$stackPtr]['parenthesis_closer']))
368 | ) {
369 | // Closing braces need to be indented to the same level
370 | // as the function.
371 | $expectedIndent = $functionIndent;
372 | } else {
373 | $expectedIndent = ($functionIndent + $this->indent);
374 | }
375 |
376 | // We changed lines, so this should be a whitespace indent token.
377 | if ($tokens[$i]['code'] !== T_WHITESPACE) {
378 | $foundIndent = 0;
379 | } else if ($tokens[$i]['line'] !== $tokens[($i + 1)]['line']) {
380 | // This is an empty line, so don't check the indent.
381 | $foundIndent = $expectedIndent;
382 |
383 | $error = 'Blank lines are not allowed in a multi-line function declaration';
384 | $fix = $phpcsFile->addFixableError($error, $i, 'EmptyLine');
385 | if ($fix === true) {
386 | $phpcsFile->fixer->replaceToken($i, '');
387 | }
388 | } else {
389 | $foundIndent = strlen($tokens[$i]['content']);
390 | }
391 |
392 | if ($expectedIndent !== $foundIndent) {
393 | $error = 'Multi-line function declaration not indented correctly; expected %s spaces but found %s';
394 | $data = [
395 | $expectedIndent,
396 | $foundIndent,
397 | ];
398 |
399 | $fix = $phpcsFile->addFixableError($error, $i, 'Indent', $data);
400 | if ($fix === true) {
401 | $spaces = str_repeat(' ', $expectedIndent);
402 | if ($foundIndent === 0) {
403 | $phpcsFile->fixer->addContentBefore($i, $spaces);
404 | } else {
405 | $phpcsFile->fixer->replaceToken($i, $spaces);
406 | }
407 | }
408 | }
409 |
410 | $lastLine = $tokens[$i]['line'];
411 | }//end if
412 |
413 | if ($tokens[$i]['code'] === T_ARRAY || $tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) {
414 | // Skip arrays as they have their own indentation rules.
415 | if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) {
416 | $i = $tokens[$i]['bracket_closer'];
417 | } else {
418 | $i = $tokens[$i]['parenthesis_closer'];
419 | }
420 |
421 | $lastLine = $tokens[$i]['line'];
422 | continue;
423 | }
424 | }//end for
425 |
426 | if (isset($tokens[$stackPtr]['scope_opener']) === false) {
427 | return;
428 | }
429 |
430 | $openBracket = $tokens[$stackPtr]['parenthesis_opener'];
431 | $this->processBracket($phpcsFile, $openBracket, $tokens, 'function');
432 |
433 | if ($tokens[$stackPtr]['code'] !== T_CLOSURE) {
434 | return;
435 | }
436 |
437 | $use = $phpcsFile->findNext(T_USE, ($tokens[$stackPtr]['parenthesis_closer'] + 1), $tokens[$stackPtr]['scope_opener']);
438 | if ($use === false) {
439 | return;
440 | }
441 |
442 | $openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1), null);
443 | $this->processBracket($phpcsFile, $openBracket, $tokens, 'use');
444 |
445 | // Also check spacing.
446 | if ($tokens[($use - 1)]['code'] === T_WHITESPACE) {
447 | $gap = strlen($tokens[($use - 1)]['content']);
448 | } else {
449 | $gap = 0;
450 | }
451 |
452 | }//end processMultiLineDeclaration()
453 |
454 |
455 | /**
456 | * Processes the contents of a single set of brackets.
457 | *
458 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
459 | * @param int $openBracket The position of the open bracket
460 | * in the stack passed in $tokens.
461 | * @param array $tokens The stack of tokens that make up
462 | * the file.
463 | * @param string $type The type of the token the brackets
464 | * belong to (function or use).
465 | *
466 | * @return void
467 | */
468 | public function processBracket($phpcsFile, $openBracket, $tokens, $type='function')
469 | {
470 | $errorPrefix = '';
471 | if ($type === 'use') {
472 | $errorPrefix = 'Use';
473 | }
474 |
475 | $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
476 |
477 | // The open bracket should be the last thing on the line.
478 | if ($tokens[$openBracket]['line'] !== $tokens[$closeBracket]['line']) {
479 | $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
480 | if ($tokens[$next]['line'] !== ($tokens[$openBracket]['line'] + 1)) {
481 | $error = 'The first parameter of a multi-line '.$type.' declaration must be on the line after the opening bracket';
482 | $fix = $phpcsFile->addFixableError($error, $next, $errorPrefix.'FirstParamSpacing');
483 | if ($fix === true) {
484 | $phpcsFile->fixer->addNewline($openBracket);
485 | }
486 | }
487 | }
488 |
489 | // Each line between the brackets should contain a single parameter.
490 | $lastComma = null;
491 | for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
492 | // Skip brackets, like arrays, as they can contain commas.
493 | if (isset($tokens[$i]['bracket_opener']) === true) {
494 | $i = $tokens[$i]['bracket_closer'];
495 | continue;
496 | }
497 |
498 | if (isset($tokens[$i]['parenthesis_opener']) === true) {
499 | $i = $tokens[$i]['parenthesis_closer'];
500 | continue;
501 | }
502 |
503 | if ($tokens[$i]['code'] !== T_COMMA) {
504 | continue;
505 | }
506 |
507 | $next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true);
508 | if ($tokens[$next]['line'] === $tokens[$i]['line']) {
509 | $error = 'Multi-line '.$type.' declarations must define one parameter per line';
510 | $fix = $phpcsFile->addFixableError($error, $next, $errorPrefix.'OneParamPerLine');
511 | if ($fix === true) {
512 | $phpcsFile->fixer->addNewline($i);
513 | }
514 | }
515 | }//end for
516 |
517 | }//end processBracket()
518 |
519 |
520 | }//end class
521 |
--------------------------------------------------------------------------------