Skip to content

Commit

Permalink
New sniff to check class instantiation for PSR12 + package.xml entrie…
Browse files Browse the repository at this point in the history
…s for new sniffs (ref #750)
  • Loading branch information
gsherwood committed Apr 19, 2018
1 parent c61bba3 commit f3accfb
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 8 deletions.
47 changes: 45 additions & 2 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,11 @@ http://pear.php.net/dtd/package-2.0.xsd">
-- Allows the required spacing to be set using the spacing sniff property (default is 0)
-- Allows newlines to be used by setting the ignoreNewlines sniff property (default is false)
-- Thanks to Juliette Reinders Folmer for the contribution
- Added New PSR12.Keywords.ShortFormTypeKeywords sniff
- Added new PSR12.Classes.ClassInstantiation sniff
-- Ensures parenthesis are used when instantiating a new class
- Added new PSR12.Keywords.ShortFormTypeKeywords sniff
-- Ensures the short form of PHP types is used when type casting
- Added New PSR12.Namespaces.CompundNamespaceDepth sniff
- Added new PSR12.Namespaces.CompundNamespaceDepth sniff
-- Ensures compund namespace use statements have a max depth of 2 levels
-- The max depth can be changed by setting the 'maxDepth' sniff property in a ruleset.xml file
- Improved core support for grouped property declarations
Expand Down Expand Up @@ -1041,6 +1043,47 @@ http://pear.php.net/dtd/package-2.0.xsd">
</dir>
<file baseinstalldir="PHP/CodeSniffer" name="ruleset.xml" role="php" />
</dir>
<dir name="PSR12">
<dir name="Docs">
<dir name="Classes">
<file baseinstalldir="PHP/CodeSniffer" name="ClassInstantiationStandard.xml" role="php" />
</dir>
<dir name="Keywords">
<file baseinstalldir="PHP/CodeSniffer" name="ShortFormTypeKeywordsStandard.xml" role="php" />
</dir>
<dir name="Namespaces">
<file baseinstalldir="PHP/CodeSniffer" name="CompoundNamespaceDepthStandard.xml" role="php" />
</dir>
</dir>
<dir name="Sniffs">
<dir name="Classes">
<file baseinstalldir="PHP/CodeSniffer" name="ClassInstantiationSniff.php" role="php" />
</dir>
<dir name="Keywords">
<file baseinstalldir="PHP/CodeSniffer" name="ShortFormTypeKeywordsSniff.php" role="php" />
</dir>
<dir name="Namespaces">
<file baseinstalldir="PHP/CodeSniffer" name="CompoundNamespaceDepthSniff.php" role="php" />
</dir>
</dir>
<dir name="Tests">
<dir name="Classes">
<file baseinstalldir="PHP/CodeSniffer" name="ClassInstantiationUnitTest.inc" role="test" />
<file baseinstalldir="PHP/CodeSniffer" name="ClassInstantiationUnitTest.inc.fixed" role="test" />
<file baseinstalldir="PHP/CodeSniffer" name="ClassInstantiationUnitTest.php" role="test" />
</dir>
<dir name="Keywords">
<file baseinstalldir="PHP/CodeSniffer" name="ShortFormTypeKeywordsUnitTest.inc" role="test" />
<file baseinstalldir="PHP/CodeSniffer" name="ShortFormTypeKeywordsUnitTest.inc.fixed" role="test" />
<file baseinstalldir="PHP/CodeSniffer" name="ShortFormTypeKeywordsUnitTest.php" role="test" />
</dir>
<dir name="Namespaces">
<file baseinstalldir="PHP/CodeSniffer" name="CompoundNamespaceDepthUnitTest.inc" role="test" />
<file baseinstalldir="PHP/CodeSniffer" name="CompoundNamespaceDepthUnitTest.php" role="test" />
</dir>
</dir>
<file baseinstalldir="PHP/CodeSniffer" name="ruleset.xml" role="php" />
</dir>
<dir name="PSR2">
<dir name="Docs">
<dir name="Classes">
Expand Down
19 changes: 19 additions & 0 deletions src/Standards/PSR12/Docs/Classes/ClassInstantiationStandard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<documentation title="Class Instantiation">
<standard>
<![CDATA[
When instantiating a new class, parenthesis MUST always be present even when there are no arguments passed to the constructor.
]]>
</standard>
<code_comparison>
<code title="Valid: Parenthesis used.">
<![CDATA[
new Foo();
]]>
</code>
<code title="Invalid: Parenthesis not used.">
<![CDATA[
new Foo;
]]>
</code>
</code_comparison>
</documentation>
82 changes: 82 additions & 0 deletions src/Standards/PSR12/Sniffs/Classes/ClassInstantiationSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
/**
* Verifies that classes are instantiated with parenthesis.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/

namespace PHP_CodeSniffer\Standards\PSR12\Sniffs\Classes;

use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;

class ClassInstantiationSniff implements Sniff
{


/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_NEW];

}//end register()


/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in the
* stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();

// Find the class name.
$allowed = [
T_STRING,
T_NS_SEPARATOR,
T_SELF,
T_STATIC,
T_VARIABLE,
T_DOLLAR,
];

$allowed += Tokens::$emptyTokens;

$classNameEnd = $phpcsFile->findNext($allowed, ($stackPtr + 1), null, true);
if ($classNameEnd === false) {
return;
}

if ($tokens[$classNameEnd]['code'] === T_ANON_CLASS) {
// Ignore anon classes.
return;
}

if ($tokens[$classNameEnd]['code'] === T_OPEN_PARENTHESIS) {
// Using parenthesis.
return;
}

$error = 'Parenthesis must be used when instantiating a new class';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'MissingParenthesis');
if ($fix === true) {
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($classNameEnd - 1), null, true);
$phpcsFile->fixer->addContent($prev, '()');
}

}//end process()


}//end class
25 changes: 25 additions & 0 deletions src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
$foo = new Foo();
$foo = new Foo;
$foo = new Foo\Bar;
$foo = new Foo\Bar();
$foo = new Foo /* comment */ ();

$foo = new $foo();
$foo = new $foo;
$foo = new $$foo();
$foo = new $$foo;

$foo = new self();
$foo = new self;
$foo = new static();
$foo = new static;

foo(new class {});
echo (new Foo())->bar();
echo (new Foo)->bar();
echo (new Foo((new Bar)->getBaz()))->bar();
$foo = (new Foo)::$bar;

echo (new Foo((new Bar//comment
)->getBaz(new Baz /* comment */)))->bar();
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
$foo = new Foo();
$foo = new Foo();
$foo = new Foo\Bar();
$foo = new Foo\Bar();
$foo = new Foo /* comment */ ();

$foo = new $foo();
$foo = new $foo();
$foo = new $$foo();
$foo = new $$foo();

$foo = new self();
$foo = new self();
$foo = new static();
$foo = new static();

foo(new class {});
echo (new Foo())->bar();
echo (new Foo())->bar();
echo (new Foo((new Bar())->getBaz()))->bar();
$foo = (new Foo())::$bar;

echo (new Foo((new Bar()//comment
)->getBaz(new Baz() /* comment */)))->bar();
60 changes: 60 additions & 0 deletions src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
/**
* Unit test class for the ClassInstantiation sniff.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/

namespace PHP_CodeSniffer\Standards\PSR12\Tests\Classes;

use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;

class ClassInstantiationUnitTest extends AbstractSniffUnitTest
{


/**
* Returns the lines where errors should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of errors that should occur on that line.
*
* @return array<int, int>
*/
public function getErrorList()
{
return [
3 => 1,
4 => 1,
9 => 1,
11 => 1,
14 => 1,
16 => 1,
20 => 1,
21 => 1,
22 => 1,
24 => 1,
25 => 1,
];

}//end getErrorList()


/**
* Returns the lines where warnings should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of warnings that should occur on that line.
*
* @return array<int, int>
*/
public function getWarningList()
{
return [];

}//end getWarningList()


}//end class
9 changes: 3 additions & 6 deletions src/Standards/PSR12/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@

<!-- The header of a PHP file may consist of a number of different blocks. If present, each of the blocks below MUST be separated by a single blank line, and MUST NOT contain a blank line. Each block MUST be in the order listed below, although blocks that are not relevant may be omitted.
Opening <?php tag.
Opening PHP tag.
File-level docblock.
One or more declare statements.
The namespace declaration of the file.
Expand All @@ -92,7 +92,7 @@
The remainder of the code in the file.
When a file contains a mix of HTML and PHP, any of the above sections may still be used. If so, they MUST be present at the top of the file, even if the remainder of the code consists a closing PHP tag and then a mixture of HTML and PHP. -->

<!-- When the opening <?php tag is on the first line of the file, it MUST be on its own line with no other statements unless it is a file containing markup outside of PHP opening and closing tags. -->
<!-- When the opening PHP tag is on the first line of the file, it MUST be on its own line with no other statements unless it is a file containing markup outside of PHP opening and closing tags. -->

<!-- Import statements MUST never begin with a leading backslash as they must always be fully qualified. -->

Expand All @@ -110,17 +110,14 @@
<!-- Any closing brace MUST NOT be followed by any comment or statement on the same line. -->

<!-- When instantiating a new class, parenthesis MUST always be present even when there are no arguments passed to the constructor. -->
<!-- checked by PSR12.Classes.ClassInstantiation -->

<!-- 4.1 Extends and Implements -->

<!-- The extends and implements keywords MUST be declared on the same line as the class name. -->

<!-- The opening brace for the class MUST go on its own line; the closing brace for the class MUST go on the next line after the body. -->

<!-- Opening braces MUST be on their own line and MUST NOT be preceded or followed by a blank line. -->

<!-- Closing braces MUST be on their own line and MUST NOT be preceded by a blank line. -->

<!-- Lists of implements and extends MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one interface per line. -->
<rule ref="PSR2.Classes.ClassDeclaration"/>

Expand Down

0 comments on commit f3accfb

Please sign in to comment.