Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DeclaredClassCasingFilter #3969

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.rst
Expand Up @@ -431,6 +431,10 @@ Choose from the list of available rules:

*Risky rule: forcing strict types will stop non strict code from working.*

* **declared_class_casing** [@Symfony, @PhpCsFixer]

Classes should be referred to using the correct casing.

* **dir_constant** [@Symfony:risky, @PhpCsFixer:risky]

Replaces ``dirname(__FILE__)`` expression with equivalent ``__DIR__``
Expand Down
116 changes: 116 additions & 0 deletions src/Fixer/Casing/DeclaredClassCasingFixer.php
@@ -0,0 +1,116 @@
<?php

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Fixer\Casing;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
* @author siad007
*/
final class DeclaredClassCasingFixer extends AbstractFixer
{
/**
* @var string[]
*/
private static $declaredClassNames;
SpacePossum marked this conversation as resolved.
Show resolved Hide resolved

/**
* {@inheritdoc}
*/
public function getDefinition()
{
return new FixerDefinition(
'Classes should be referred to using the correct casing.',
[new CodeSample("<?php\nnew STDCLASS();\n")]
);
}

/**
* {@inheritdoc}
*/
public function isCandidate(Tokens $tokens)
{
return $tokens->isTokenKindFound(T_STRING);
}

/**
* @param \SplFileInfo $file
* @param Tokens $tokens
*/
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
{
if (null === self::$declaredClassNames) {
foreach (get_declared_classes() as $class) {
self::$declaredClassNames[strtolower($class)] = $class;
}
}

foreach ($tokens as $index => $token) {
$name = $this->declaredClass($index, $token, $tokens);

if (null !== $name) {
$tokens[$index] = new Token([T_STRING, self::$declaredClassNames[$name]]);
}
}
}

/**
* Get the lower case name of the declared class or null.
*
* @param int $index
* @param Token $token
* @param Tokens $tokens
*
* @return null|string
*/
private function declaredClass($index, Token $token, Tokens $tokens)
{
$lower = strtolower($token->getContent());

if (!\array_key_exists($lower, self::$declaredClassNames)) {
return null;
}

$notBeforeTypes = [
T_CLASS,
T_AS,
T_DOUBLE_COLON,
T_OBJECT_OPERATOR,
T_FUNCTION,
T_CONST,
T_TRAIT,
T_USE,
CT::T_USE_TRAIT,
];

$beforeClassName = $tokens->getPrevMeaningfulToken($index);

if ($tokens[$beforeClassName]->isGivenKind($notBeforeTypes)) {
return null;
}

if (
$tokens[$beforeClassName]->isGivenKind(T_NS_SEPARATOR)
&& $tokens[$tokens->getPrevMeaningfulToken($beforeClassName)]->isGivenKind([T_STRING])
) {
return null;
}

return $lower;
}
}
1 change: 1 addition & 0 deletions src/RuleSet.php
Expand Up @@ -70,6 +70,7 @@ final class RuleSet implements RuleSetInterface
'class_definition' => ['single_line' => true],
'concat_space' => ['spacing' => 'none'],
'declare_equal_normalize' => true,
'declared_class_casing' => true,
'function_typehint_space' => true,
'include' => true,
'increment_style' => true,
Expand Down
139 changes: 139 additions & 0 deletions tests/Fixer/Casing/DeclaredClassCasingFixerTest.php
@@ -0,0 +1,139 @@
<?php

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Tests\Fixer\Casing;

use PhpCsFixer\Console\Application;
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;

/**
* @author siad007
*
* @internal
*
* @covers \PhpCsFixer\Fixer\Casing\DeclaredClassCasingFixer
*/
final class DeclaredClassCasingFixerTest extends AbstractFixerTestCase
{
/**
* @param string $expected
* @param null|string $input
*
* @dataProvider provideFixCases
*/
public function testFix($expected, $input = null)
{
$this->doTest($expected, $input);
}

public function provideFixCases()
{
return [
[
'<?php
$stdclass = new \stdClass();
$stdclass = new stdClass();
',
'<?php
$stdclass = new \STDCLASS();
$stdclass = new STDCLASS();
',
],
[
'<?php

namespace Foo;

class exception extends \Exception
{
}
',
'<?php

namespace Foo;

class exception extends \EXCEPTION
{
}
',
],
[
'<?php

echo \Exception::class;
echo Exception::class;
',
'<?php

echo \ExCePTion::class;
echo ExCePTion::class;
',
],
[
'<?php

$a::exception();
',
],
[
'<?php

$a->exception();
',
],
[
'<?php

function exception() {};
',
],
[
'<?php

echo "This is an " . "exception";
',
],
[
'<?php

namespace Foo;

trait stdclass
{
}

use Foo\stdclass as exception;

class test
{
use exception;
}
',
],
[
'<?php
use stdClass as exception;
',
],
[
'<?php
class Foo {
const stdclass = 1;
}
',
],
'imported constant '.Application::VERSION => [
sprintf('<?php echo %s::VERSION; new PhpCsFixer\Console\APPLICATION();', strtoupper(Application::class)),
],
];
}
}