Browse Source

Added Psr4Fixer

Graham Campbell 8 years ago
parent
commit
927350e9e4

+ 1 - 0
.php_cs.dist

@@ -23,6 +23,7 @@ return PhpCsFixer\Config::create()
         'php_unit_construct' => true,
         'php_unit_dedicate_assert' => true,
         'php_unit_strict' => true,
+        'psr4' => true,
         'strict_comparison' => true,
         'strict_param' => true,
         'no_extra_consecutive_blank_lines' => array('break', 'continue', 'extra', 'return', 'throw', 'use', 'curly_brace_open'),

+ 4 - 0
README.rst

@@ -666,6 +666,10 @@ Choose from the list of available fixers:
                          should match the file name.
                          (Risky fixer!)
 
+* **psr4**
+                         Class names should match the
+                         file name. (Risky fixer!)
+
 * **random_api_migration**
                          Replaces rand, srand,
                          getrandmax functions calls

+ 78 - 0
src/AbstractPsrFixer.php

@@ -0,0 +1,78 @@
+<?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;
+
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ * @author Bram Gotink <bram@gotink.me>
+ * @author Graham Campbell <graham@mineuk.com>
+ */
+abstract class AbstractPsrFixer extends AbstractFixer
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens)
+    {
+        return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isRisky()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        return -10;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports(\SplFileInfo $file)
+    {
+        if ($file instanceof StdinFileInfo) {
+            return false;
+        }
+
+        $filenameParts = explode('.', $file->getBasename(), 2);
+
+        if (
+            // ignore file with extension other than php
+            (!isset($filenameParts[1]) || 'php' !== $filenameParts[1])
+            // ignore file with name that cannot be a class name
+            || 0 === preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $filenameParts[0])
+        ) {
+            return false;
+        }
+
+        $tokens = Tokens::fromCode(sprintf('<?php %s {}', $filenameParts[0]));
+        if ($tokens[1]->isKeyword() || $tokens[1]->isMagicConstant()) {
+            return false;
+        }
+
+        // ignore stubs/fixtures, since they are typically containing invalid files for various reasons
+        return !preg_match('{[/\\\\](stub|fixture)s?[/\\\\]}i', $file->getRealPath());
+    }
+}

+ 3 - 57
src/Fixer/Basic/Psr0Fixer.php

@@ -12,17 +12,16 @@
 
 namespace PhpCsFixer\Fixer\Basic;
 
-use PhpCsFixer\AbstractFixer;
-use PhpCsFixer\StdinFileInfo;
-use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\AbstractPsrFixer;
 use PhpCsFixer\Tokenizer\Tokens;
 
 /**
  * @author Jordi Boggiano <j.boggiano@seld.be>
  * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  * @author Bram Gotink <bram@gotink.me>
+ * @author Graham Campbell <graham@mineuk.com>
  */
-final class Psr0Fixer extends AbstractFixer
+final class Psr0Fixer extends AbstractPsrFixer
 {
     private $configuration = array();
 
@@ -40,22 +39,6 @@ final class Psr0Fixer extends AbstractFixer
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function isCandidate(Tokens $tokens)
-    {
-        return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function isRisky()
-    {
-        return true;
-    }
-
     /**
      * {@inheritdoc}
      */
@@ -144,14 +127,6 @@ final class Psr0Fixer extends AbstractFixer
         }
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getPriority()
-    {
-        return -10;
-    }
-
     /**
      * {@inheritdoc}
      */
@@ -159,33 +134,4 @@ final class Psr0Fixer extends AbstractFixer
     {
         return 'Classes must be in a path that matches their namespace, be at least one namespace deep and the class name should match the file name.';
     }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function supports(\SplFileInfo $file)
-    {
-        if ($file instanceof StdinFileInfo) {
-            return false;
-        }
-
-        $filenameParts = explode('.', $file->getBasename(), 2);
-
-        if (
-            // ignore file with extension other than php
-            (!isset($filenameParts[1]) || 'php' !== $filenameParts[1])
-            // ignore file with name that cannot be a class name
-            || 0 === preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $filenameParts[0])
-        ) {
-            return false;
-        }
-
-        $tokens = Tokens::fromCode(sprintf('<?php %s {}', $filenameParts[0]));
-        if ($tokens[1]->isKeyword() || $tokens[1]->isMagicConstant()) {
-            return false;
-        }
-
-        // ignore stubs/fixtures, since they are typically containing invalid files for various reasons
-        return !preg_match('{[/\\\\](stub|fixture)s?[/\\\\]}i', $file->getRealPath());
-    }
 }

+ 79 - 0
src/Fixer/Basic/Psr4Fixer.php

@@ -0,0 +1,79 @@
+<?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\Basic;
+
+use PhpCsFixer\AbstractPsrFixer;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ * @author Bram Gotink <bram@gotink.me>
+ * @author Graham Campbell <graham@mineuk.com>
+ */
+final class Psr4Fixer extends AbstractPsrFixer
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function fix(\SplFileInfo $file, Tokens $tokens)
+    {
+        $namespace = false;
+        $classyName = null;
+        $classyIndex = 0;
+
+        foreach ($tokens as $index => $token) {
+            if ($token->isGivenKind(T_NAMESPACE)) {
+                if (false !== $namespace) {
+                    return;
+                }
+
+                $namespace = true;
+            } elseif ($token->isClassy()) {
+                if (null !== $classyName) {
+                    return;
+                }
+
+                $classyIndex = $tokens->getNextMeaningfulToken($index);
+                $classyName = $tokens[$classyIndex]->getContent();
+            }
+        }
+
+        if (null === $classyName) {
+            return;
+        }
+
+        if (false !== $namespace) {
+            $filename = basename(str_replace('\\', '/', $file->getRealPath()), '.php');
+
+            if ($classyName !== $filename) {
+                $tokens[$classyIndex]->setContent($filename);
+            }
+        } else {
+            $normClass = str_replace('_', '/', $classyName);
+            $filename = substr(str_replace('\\', '/', $file->getRealPath()), -strlen($normClass) - 4, -4);
+
+            if ($normClass !== $filename && strtolower($normClass) === strtolower($filename)) {
+                $tokens[$classyIndex]->setContent(str_replace('/', '_', $filename));
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDescription()
+    {
+        return 'Class names should match the file name.';
+    }
+}

+ 213 - 0
tests/Fixer/Basic/Psr4FixerTest.php

@@ -0,0 +1,213 @@
+<?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\Basic;
+
+use PhpCsFixer\Test\AbstractFixerTestCase;
+
+/**
+ * @author Graham Campbell <graham@mineuk.com>
+ *
+ * @internal
+ */
+final class Psr4FixerTest extends AbstractFixerTestCase
+{
+    public function testFixCase()
+    {
+        $fixer = $this->getFixer();
+
+        $file = $this->getMock('SplFileInfo', array('getRealPath'), array(__DIR__.'/Psr4/Foo/Bar.php'));
+        $file->expects($this->any())->method('getRealPath')->willReturn(__DIR__.'/Psr4/Foo/Bar.php');
+
+        $expected = <<<'EOF'
+<?php
+namespace Psr4\foo;
+class Bar {}
+EOF;
+        $input = <<<'EOF'
+<?php
+namespace Psr4\foo;
+class bar {}
+EOF;
+
+        $this->doTest($expected, $input, $file, $fixer);
+
+        $expected = <<<'EOF'
+<?php
+class Psr4_Foo_Bar {}
+EOF;
+        $input = <<<'EOF'
+<?php
+class Psr4_fOo_bAr {}
+EOF;
+
+        $this->doTest($expected, $input, $file);
+    }
+
+    public function testFixClassName()
+    {
+        $file = $this->getTestFile(__FILE__);
+
+        $expected = <<<'EOF'
+<?php
+namespace PhpCsFixer\Tests\Fixer\Basic;
+class Psr4FixerTest {}
+/* class foo */
+EOF;
+        $input = <<<'EOF'
+<?php
+namespace PhpCsFixer\Tests\Fixer\Basic;
+class blah {}
+/* class foo */
+EOF;
+
+        $this->doTest($expected, $input, $file);
+    }
+
+    public function testFixAbstractClassName()
+    {
+        $file = $this->getTestFile(__FILE__);
+
+        $expected = <<<'EOF'
+<?php
+namespace PhpCsFixer\Tests\Fixer\Basic;
+abstract class Psr4FixerTest {}
+/* class foo */
+EOF;
+        $input = <<<'EOF'
+<?php
+namespace PhpCsFixer\Tests\Fixer\Basic;
+abstract class blah {}
+/* class foo */
+EOF;
+
+        $this->doTest($expected, $input, $file);
+    }
+
+    public function testFixFinalClassName()
+    {
+        $file = $this->getTestFile(__FILE__);
+
+        $expected = <<<'EOF'
+<?php
+namespace PhpCsFixer\Tests\Fixer\Basic;
+final class Psr4FixerTest {}
+/* class foo */
+EOF;
+        $input = <<<'EOF'
+<?php
+namespace PhpCsFixer\Tests\Fixer\Basic;
+final class blah {}
+/* class foo */
+EOF;
+
+        $this->doTest($expected, $input, $file);
+    }
+
+    public function testFixClassNameWithComment()
+    {
+        $file = $this->getTestFile(__FILE__);
+
+        $expected = <<<'EOF'
+<?php
+namespace /* namespace here */ PhpCsFixer\Fixer\Psr4;
+class /* hi there */ Psr4FixerTest /* why hello */ {}
+/* class foo */
+EOF;
+        $input = <<<'EOF'
+<?php
+namespace /* namespace here */ PhpCsFixer\Fixer\Psr4;
+class /* hi there */ blah /* why hello */ {}
+/* class foo */
+EOF;
+
+        $this->doTest($expected, $input, $file);
+    }
+
+    public function testHandlePartialNamespaces()
+    {
+        $fixer = $this->getFixer();
+
+        $file = $this->getTestFile(__DIR__.'/../../../src/Fixer/Basic/Psr4Fixer.php');
+
+        $expected = <<<'EOF'
+<?php
+namespace Foo\Bar\Baz\FIXER\Basic;
+class Psr4Fixer {}
+EOF;
+        $this->doTest($expected, null, $file, $fixer);
+
+        $expected = <<<'EOF'
+<?php
+namespace /* hi there */ Foo\Bar\Baz\FIXER\Basic;
+class /* hi there */ Psr4Fixer {}
+EOF;
+        $this->doTest($expected, null, $file, $fixer);
+
+        $expected = <<<'EOF'
+<?php
+namespace Foo\Bar\Baz;
+class Psr4Fixer {}
+EOF;
+        $this->doTest($expected, null, $file, $fixer);
+    }
+
+    /**
+     * @dataProvider provideIgnoredCases
+     */
+    public function testIgnoreWrongNames($filename)
+    {
+        $file = $this->getTestFile($filename);
+
+        $expected = <<<'EOF'
+<?php
+namespace Aaa;
+class Bar {}
+EOF;
+
+        $this->doTest($expected, null, $file);
+    }
+
+    public function provideIgnoredCases()
+    {
+        $ignoreCases = array(
+            array('.php'),
+            array('Foo.class.php'),
+            array('4Foo.php'),
+            array('$#.php'),
+        );
+
+        foreach (array('__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'try', 'unset', 'use', 'var', 'while', 'xor') as $keyword) {
+            $ignoreCases[] = array($keyword.'.php');
+        }
+
+        foreach (array('__CLASS__', '__DIR__', '__FILE__', '__FUNCTION__', '__LINE__', '__METHOD__', '__NAMESPACE__') as $magicConstant) {
+            $ignoreCases[] = array($magicConstant.'.php');
+            $ignoreCases[] = array(strtolower($magicConstant).'.php');
+        }
+
+        foreach (array(
+            'T_CALLABLE' => 'callable',
+            'T_FINALLY' => 'finally',
+            'T_INSTEADOF' => 'insteadof',
+            'T_TRAIT' => 'trait',
+            'T_TRAIT_C' => '__TRAIT__',
+        ) as $tokenType => $tokenValue) {
+            if (defined($tokenType)) {
+                $ignoreCases[] = array($tokenValue.'.php');
+                $ignoreCases[] = array(strtolower($tokenValue).'.php');
+            }
+        }
+
+        return $ignoreCases;
+    }
+}

+ 1 - 1
tests/Runner/RunnerTest.php

@@ -25,7 +25,7 @@ use Symfony\Component\Finder\Finder;
 /**
  * @internal
  */
-final class FixerTest extends \PHPUnit_Framework_TestCase
+final class RunnerTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * @covers PhpCsFixer\Runner\Runner::fix