diff --git a/src/Bartlett/CompatInfo/Analyser/CompatibilityAnalyser.php b/src/Bartlett/CompatInfo/Analyser/CompatibilityAnalyser.php index ee30e7e6..61755a35 100644 --- a/src/Bartlett/CompatInfo/Analyser/CompatibilityAnalyser.php +++ b/src/Bartlett/CompatInfo/Analyser/CompatibilityAnalyser.php @@ -11,6 +11,7 @@ namespace Bartlett\CompatInfo\Analyser; +use Bartlett\CompatInfo\Sniffs\PHP\DeclareSniff; use Bartlett\CompatInfo\Util\Database; use Bartlett\CompatInfo\Collection\ReferenceCollection; use Bartlett\CompatInfo\PhpParser\ConditionalCodeNodeProcessor; @@ -70,7 +71,7 @@ public function __construct() $this->references = new ReferenceCollection(array(), $pdo); $this->sniffs = [ - + new DeclareSniff(), ]; } @@ -195,6 +196,19 @@ public function afterTraverse(array $nodes) parent::afterTraverse($nodes); $this->computeNamespaceVersions(); + + foreach ($this->metrics as $key => $values) { + if (strpos($key, '\\') === false) { + // not a sniff service result + continue; + } + + foreach ($values as $value => $rawData) { + if (isset($rawData['version'])) { + $this->updateGlobalVersion($rawData['version'], '', $rawData['version']); + } + } + } } /** diff --git a/src/Bartlett/CompatInfo/Sniffs/PHP/DeclareSniff.php b/src/Bartlett/CompatInfo/Sniffs/PHP/DeclareSniff.php new file mode 100644 index 00000000..5a97fa7e --- /dev/null +++ b/src/Bartlett/CompatInfo/Sniffs/PHP/DeclareSniff.php @@ -0,0 +1,67 @@ +directives = []; + } + + public function leaveSniff(): void + { + parent::leaveSniff(); + + if (!empty($this->directives)) { + // inform analyser that few sniffs were found + $this->visitor->setMetrics( + array(DeclareSniff::class => $this->directives) + ); + } + } + + public function enterNode(Node $node) + { + parent::enterNode($node); + + if (!$node instanceof Node\Stmt\Declare_) { + return; + } + + foreach ($node->declares as $declare) { + $id = (string) $declare->key; + + if (strcasecmp($id, 'strict_types') === 0) { + $version = '7.0.0'; + } elseif (strcasecmp($id, 'ticks') === 0) { + $version = '7.0.0'; + } elseif (strcasecmp($id, 'encoding') === 0) { + $version = '5.3.0'; + } else { + $version = '4.0.0'; + } + + if (!isset($this->directives[$id])) { + $this->directives[$id] = array( + 'severity' => $this->getCurrentSeverity($version), + 'version' => $version, + 'spots' => [] + ); + } + $this->directives[$id]['spots'][] = $this->getCurrentSpot($node); + } + } +} diff --git a/tests/DeclareSniffTest.php b/tests/DeclareSniffTest.php new file mode 100644 index 00000000..90485486 --- /dev/null +++ b/tests/DeclareSniffTest.php @@ -0,0 +1,115 @@ + + * @license https://opensource.org/licenses/BSD-3-Clause The 3-Clause BSD License + * @since Class available since Release 5.4.0 + */ + +namespace Bartlett\Tests\CompatInfo; + +use Bartlett\CompatInfo\Analyser\CompatibilityAnalyser; +use Bartlett\Reflect\Client; + +/** + * @link https://www.php.net/manual/en/control-structures.declare.php + */ +class DeclareSniffTest extends \PHPUnit\Framework\TestCase +{ + protected static $fixtures; + protected static $analyserId; + protected static $api; + + /** + * Sets up the shared fixture. + * + * @return void + */ + public static function setUpBeforeClass(): void + { + self::$fixtures = __DIR__ . DIRECTORY_SEPARATOR + . 'fixtures' . DIRECTORY_SEPARATOR + . 'sniffs' . DIRECTORY_SEPARATOR + ; + + self::$analyserId = CompatibilityAnalyser::class; + + $client = new Client(); + + // request for a Bartlett\Reflect\Api\Analyser + self::$api = $client->api('analyser'); + } + + /** + * Regression test for declare control structure detection GH#268 + * + * @link https://github.com/llaville/php-compat-info/issues/268 + * CompatInfo 5.3.0 is detected as PHP 5.6 + * @group features, regression + * @return void + */ + public function testStructureTypesDirective() + { + $dataSource = self::$fixtures . 'declare_strict_types.php'; + $analysers = ['compatibility']; + $metrics = self::$api->run($dataSource, $analysers); + $versions = $metrics[self::$analyserId]['versions']; + + $this->assertEquals( + [ + 'php.min' => '7.0.0', + 'php.max' => '', + 'php.all' => '7.0.0', + ], + $versions + ); + } + + /** + * @group features + * @return void + */ + public function testTicksDirective() + { + $dataSource = self::$fixtures . 'declare_ticks.php'; + $analysers = ['compatibility']; + $metrics = self::$api->run($dataSource, $analysers); + $versions = $metrics[self::$analyserId]['versions']; + + $this->assertEquals( + [ + 'php.min' => '7.0.0', + 'php.max' => '', + 'php.all' => '7.0.0', + ], + $versions + ); + } + + /** + * @group features + * @return void + */ + public function testEncodingDirective() + { + $dataSource = self::$fixtures . 'declare_encoding.php'; + $analysers = ['compatibility']; + $metrics = self::$api->run($dataSource, $analysers); + $versions = $metrics[self::$analyserId]['versions']; + + $this->assertEquals( + [ + 'php.min' => '5.3.0', + 'php.max' => '', + 'php.all' => '5.3.0', + ], + $versions + ); + } +} diff --git a/tests/fixtures/sniffs/declare_encoding.php b/tests/fixtures/sniffs/declare_encoding.php new file mode 100644 index 00000000..44943644 --- /dev/null +++ b/tests/fixtures/sniffs/declare_encoding.php @@ -0,0 +1,2 @@ +