Browse Source

minor #2354 Enhancement: Allow to specify minimum and maximum PHP versions for code samples (localheinz)

This PR was squashed before being merged into the 2.0-dev branch (closes #2354).

Discussion
----------

Enhancement: Allow to specify minimum and maximum PHP versions for code samples

This PR

* [x] allows to compose a `CodeSample` and a `VersionSpecification` into a `VersionSpecificCodeSample`, which will then be ignored in the `DescribeCommand` when the `VersionSpecification` isn't satisfied by the current PHP version

Related to https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/2351#issuecomment-264530922.

Commits
-------

54c35a87 Enhancement: Allow to specify minimum and maximum PHP versions for code samples
Dariusz Ruminski 8 years ago
parent
commit
ceb66dbb19

+ 5 - 0
src/Console/Command/DescribeCommand.php

@@ -19,6 +19,7 @@ use PhpCsFixer\Fixer\DefinedFixerInterface;
 use PhpCsFixer\Fixer\FixerInterface;
 use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
 use PhpCsFixer\FixerDefinition\ShortFixerDefinition;
+use PhpCsFixer\FixerDefinition\VersionSpecificCodeSampleInterface;
 use PhpCsFixer\FixerFactory;
 use PhpCsFixer\RuleSet;
 use PhpCsFixer\StdinFileInfo;
@@ -149,6 +150,10 @@ final class DescribeCommand extends Command
             ));
 
             foreach ($definition->getCodeSamples() as $index => $codeSample) {
+                if ($codeSample instanceof VersionSpecificCodeSampleInterface && !$codeSample->isSuitableFor(PHP_VERSION_ID)) {
+                    continue;
+                }
+
                 $old = $codeSample->getCode();
                 $tokens = Tokens::fromCode($old);
                 if ($fixer instanceof ConfigurableFixerInterface) {

+ 9 - 9
src/Fixer/Alias/PowToExponentiationFixer.php

@@ -13,8 +13,9 @@
 namespace PhpCsFixer\Fixer\Alias;
 
 use PhpCsFixer\AbstractFunctionReferenceFixer;
-use PhpCsFixer\FixerDefinition\CodeSample;
-use PhpCsFixer\FixerDefinition\ShortFixerDefinition;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\FixerDefinition\VersionSpecification;
+use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
 use PhpCsFixer\Tokenizer\CT;
 use PhpCsFixer\Tokenizer\Token;
 use PhpCsFixer\Tokenizer\Tokens;
@@ -74,20 +75,19 @@ final class PowToExponentiationFixer extends AbstractFunctionReferenceFixer
      */
     public function getDefinition()
     {
-        /* @TODO That code should be in use, but for now it will fail on lower PHP version...
         return new FixerDefinition(
            'Converts \'pow()\' to \'**\' operator. Requires PHP >= 5.6.',
-            array(new CodeSample("<?php\n pow(\$a, 1);")),
+            array(
+                new VersionSpecificCodeSample(
+                    "<?php\n pow(\$a, 1);",
+                    new VersionSpecification(50600)
+                ),
+            ),
             null,
             null,
             null,
             'Risky when the function \'pow()\' function is overridden.'
         );
-        */
-
-        return new ShortFixerDefinition(
-            'Converts \'pow()\' to \'**\' operator. Requires PHP >= 5.6.'
-        );
     }
 
     /**

+ 4 - 3
src/Fixer/ArrayNotation/ArraySyntaxFixer.php

@@ -17,6 +17,8 @@ use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
 use PhpCsFixer\Fixer\ConfigurableFixerInterface;
 use PhpCsFixer\FixerDefinition\CodeSample;
 use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\FixerDefinition\VersionSpecification;
+use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
 use PhpCsFixer\Tokenizer\CT;
 use PhpCsFixer\Tokenizer\Token;
 use PhpCsFixer\Tokenizer\Tokens;
@@ -91,12 +93,11 @@ final class ArraySyntaxFixer extends AbstractFixer implements ConfigurableFixerI
                     "<?php\n[1,2];",
                     array('syntax' => 'long')
                 ),
-                /* @TODO That code should be in use, but for now it will fail on lower PHP version...
-                new CodeSample(
+                new VersionSpecificCodeSample(
                     "<?php\narray(1,2);",
+                    new VersionSpecification(50400),
                     array('syntax' => 'short')
                 ),
-                */
             ),
             null,
             'Configure to use "long" or "short" array declaration syntax.',

+ 67 - 0
src/FixerDefinition/VersionSpecificCodeSample.php

@@ -0,0 +1,67 @@
+<?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\FixerDefinition;
+
+/**
+ * @author Andreas Möller <am@localheinz.com>
+ */
+final class VersionSpecificCodeSample implements VersionSpecificCodeSampleInterface
+{
+    /**
+     * @var CodeSampleInterface
+     */
+    private $codeSample;
+
+    /**
+     * @var VersionSpecificationInterface
+     */
+    private $versionSpecification;
+
+    /**
+     * @param string                        $code
+     * @param VersionSpecificationInterface $versionSpecification
+     * @param null|array                    $configuration
+     */
+    public function __construct(
+        $code,
+        VersionSpecificationInterface $versionSpecification,
+        array $configuration = null
+    ) {
+        $this->codeSample = new CodeSample($code, $configuration);
+        $this->versionSpecification = $versionSpecification;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCode()
+    {
+        return $this->codeSample->getCode();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfiguration()
+    {
+        return $this->codeSample->getConfiguration();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isSuitableFor($version)
+    {
+        return $this->versionSpecification->isSatisfiedBy($version);
+    }
+}

+ 26 - 0
src/FixerDefinition/VersionSpecificCodeSampleInterface.php

@@ -0,0 +1,26 @@
+<?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\FixerDefinition;
+
+/**
+ * @author Andreas Moeller <am@localheinz.com>
+ */
+interface VersionSpecificCodeSampleInterface extends CodeSampleInterface
+{
+    /**
+     * @param int $version
+     *
+     * @return bool
+     */
+    public function isSuitableFor($version);
+}

+ 72 - 0
src/FixerDefinition/VersionSpecification.php

@@ -0,0 +1,72 @@
+<?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\FixerDefinition;
+
+/**
+ * @author Andreas Möller <am@localheinz.com>
+ */
+final class VersionSpecification implements VersionSpecificationInterface
+{
+    /**
+     * @var int|null
+     */
+    private $minimum;
+
+    /**
+     * @var int|null
+     */
+    private $maximum;
+
+    /**
+     * @param int|null $minimum
+     * @param int|null $maximum
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct($minimum = null, $maximum = null)
+    {
+        if (null === $minimum && null === $maximum) {
+            throw new \InvalidArgumentException('Either minimum or maximum need to be specified');
+        }
+
+        if (null !== $minimum && (!is_int($minimum) || 1 > $minimum)) {
+            throw new \InvalidArgumentException('Minimum needs to be either null or an integer greater than 0');
+        }
+
+        if (null !== $maximum && (!is_int($maximum) || 1 > $maximum)) {
+            throw new \InvalidArgumentException('Minimum needs to be either null or an integer greater than 0');
+        }
+
+        if (null !== $maximum && null !== $minimum && $maximum < $minimum) {
+            throw new \InvalidArgumentException('Maximum should not be less than the minimum');
+        }
+
+        $this->minimum = $minimum;
+        $this->maximum = $maximum;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isSatisfiedBy($version)
+    {
+        if (null !== $this->minimum && $version < $this->minimum) {
+            return false;
+        }
+        if (null !== $this->maximum && $version > $this->maximum) {
+            return false;
+        }
+
+        return true;
+    }
+}

+ 26 - 0
src/FixerDefinition/VersionSpecificationInterface.php

@@ -0,0 +1,26 @@
+<?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\FixerDefinition;
+
+/**
+ * @author Andreas Möller <am@localheinz.com>
+ */
+interface VersionSpecificationInterface
+{
+    /**
+     * @param int $version
+     *
+     * @return bool
+     */
+    public function isSatisfiedBy($version);
+}

+ 46 - 0
tests/FixerDefinition/CodeSampleTest.php

@@ -0,0 +1,46 @@
+<?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\FixerDefinition;
+
+use PhpCsFixer\FixerDefinition\CodeSample;
+
+/**
+ * @author Andreas Möller <am@localheinz.com>
+ *
+ * @internal
+ */
+final class CodeSampleTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConstructorSetsValues()
+    {
+        $code = '<php echo $foo;';
+        $configuration = array(
+            'foo' => 'bar',
+        );
+
+        $codeSample = new CodeSample(
+            $code,
+            $configuration
+        );
+
+        $this->assertSame($code, $codeSample->getCode());
+        $this->assertSame($configuration, $codeSample->getConfiguration());
+    }
+
+    public function testConfigurationDefaultsToNull()
+    {
+        $codeSample = new CodeSample('<php echo $foo;');
+
+        $this->assertNull($codeSample->getConfiguration());
+    }
+}

+ 93 - 0
tests/FixerDefinition/VersionSpecificCodeSampleTest.php

@@ -0,0 +1,93 @@
+<?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\FixerDefinition;
+
+use PhpCsFixer\FixerDefinition\VersionSpecificationInterface;
+use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
+use Prophecy\Prophecy;
+
+/**
+ * @author Andreas Möller <am@localheinz.com>
+ *
+ * @internal
+ */
+final class VersionSpecificCodeSampleTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConstructorSetsValues()
+    {
+        $code = '<php echo $foo;';
+        $configuration = array(
+            'foo' => 'bar',
+        );
+
+        $codeSample = new VersionSpecificCodeSample(
+            $code,
+            $this->createVersionSpecificationMock()->reveal(),
+            $configuration
+        );
+
+        $this->assertSame($code, $codeSample->getCode());
+        $this->assertSame($configuration, $codeSample->getConfiguration());
+    }
+
+    public function testConfigurationDefaultsToNull()
+    {
+        $codeSample = new VersionSpecificCodeSample(
+            '<php echo $foo;',
+            $this->createVersionSpecificationMock()->reveal()
+        );
+
+        $this->assertNull($codeSample->getConfiguration());
+    }
+
+    /**
+     * @dataProvider providerIsSuitableForVersionUsesVersionSpecification
+     *
+     * @param int  $version
+     * @param bool $isSatisfied
+     */
+    public function testIsSuitableForUsesVersionSpecification($version, $isSatisfied)
+    {
+        $versionSpecification = $this->createVersionSpecificationMock();
+
+        $versionSpecification
+            ->isSatisfiedBy($version)
+            ->willReturn($isSatisfied);
+
+        $codeSample = new VersionSpecificCodeSample(
+            '<php echo $foo;',
+            $versionSpecification->reveal()
+        );
+
+        $this->assertSame($isSatisfied, $codeSample->isSuitableFor($version));
+    }
+
+    /**
+     * @return array
+     */
+    public function providerIsSuitableForVersionUsesVersionSpecification()
+    {
+        return array(
+            'is-satisfied' => array(PHP_VERSION_ID, true),
+            'is-not-satisfied' => array(PHP_VERSION_ID, false),
+        );
+    }
+
+    /**
+     * @return Prophecy\ObjectProphecy|VersionSpecificationInterface
+     */
+    private function createVersionSpecificationMock()
+    {
+        return $this->prophesize('PhpCsFixer\FixerDefinition\VersionSpecificationInterface');
+    }
+}

+ 141 - 0
tests/FixerDefinition/VersionSpecificationTest.php

@@ -0,0 +1,141 @@
+<?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\FixerDefinition;
+
+use PhpCsFixer\FixerDefinition\VersionSpecification;
+
+/**
+ * @author Andreas Möller <am@localheinz.com>
+ *
+ * @internal
+ */
+final class VersionSpecificationTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConstructorRequiresEitherMinimumOrMaximum()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+
+        new VersionSpecification();
+    }
+
+    /**
+     * @dataProvider providerInvalidVersion
+     *
+     * @param mixed $minimum
+     */
+    public function testConstructorRejectsInvalidMinimum($minimum)
+    {
+        $this->setExpectedException('InvalidArgumentException');
+
+        new VersionSpecification($minimum);
+    }
+
+    /**
+     * @dataProvider providerInvalidVersion
+     *
+     * @param mixed $maximum
+     */
+    public function testConstructorRejectsInvalidMaximum($maximum)
+    {
+        $this->setExpectedException('InvalidArgumentException');
+
+        new VersionSpecification(
+            PHP_VERSION_ID,
+            $maximum
+        );
+    }
+
+    /**
+     * @return array
+     */
+    public function providerInvalidVersion()
+    {
+        return array(
+            'negative' => array(-1),
+            'zero' => array(0),
+            'float' => array(3.14),
+            'string' => array('foo'),
+            'integerish' => array('9000'),
+            'array' => array(array()),
+            'object' => array(new \stdClass()),
+        );
+    }
+
+    public function testConstructorRejectsMaximumLessThanMinimum()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+
+        new VersionSpecification(
+            PHP_VERSION_ID,
+            PHP_VERSION_ID - 1
+        );
+    }
+
+    /**
+     * @dataProvider providerIsSatisfiedByReturnsTrue
+     *
+     * @param null|int $minimum
+     * @param null|int $maximum
+     * @param int      $actual
+     */
+    public function testIsSatisfiedByReturnsTrue($minimum, $maximum, $actual)
+    {
+        $versionSpecification = new VersionSpecification(
+            $minimum,
+            $maximum
+        );
+
+        $this->assertTrue($versionSpecification->isSatisfiedBy($actual));
+    }
+
+    /**
+     * @return array
+     */
+    public function providerIsSatisfiedByReturnsTrue()
+    {
+        return array(
+            'version-same-as-maximum' => array(null, PHP_VERSION_ID, PHP_VERSION_ID),
+            'version-same-as-minimum' => array(PHP_VERSION_ID, null, PHP_VERSION_ID),
+            'version-between-minimum-and-maximum' => array(PHP_VERSION_ID - 1, PHP_VERSION_ID + 1, PHP_VERSION_ID),
+            'version-same-as-minimum-and-maximum' => array(PHP_VERSION_ID, PHP_VERSION_ID, PHP_VERSION_ID),
+        );
+    }
+
+    /**
+     * @dataProvider providerIsSatisfiedByReturnsFalse
+     *
+     * @param null|int $minimum
+     * @param null|int $maximum
+     * @param int      $actual
+     */
+    public function testIsSatisfiedByReturnsFalse($minimum, $maximum, $actual)
+    {
+        $versionSpecification = new VersionSpecification(
+            $minimum,
+            $maximum
+        );
+
+        $this->assertFalse($versionSpecification->isSatisfiedBy($actual));
+    }
+
+    /**
+     * @return array
+     */
+    public function providerIsSatisfiedByReturnsFalse()
+    {
+        return array(
+            'version-greater-than-maximum' => array(null, PHP_VERSION_ID, PHP_VERSION_ID + 1),
+            'version-less-than-minimum' => array(PHP_VERSION_ID, null, PHP_VERSION_ID - 1),
+        );
+    }
+}

Some files were not shown because too many files changed in this diff