getExtension() !== 'php') { continue; } $relativeFile = ltrim(str_replace($pluginRoot, '', $file->getPathname()), DIRECTORY_SEPARATOR); $relativeFile = str_replace(DIRECTORY_SEPARATOR, '/', $relativeFile); if (strpos($relativeFile, 'tests/') === 0 || strpos($relativeFile, 'vendor/') === 0 || strpos($relativeFile, 'include/vendor/') === 0) { continue; } $files[] = $relativeFile; } sort($files); function plugin_test_read_source_file($relativeFile) { $path = realpath(__DIR__ . '/../../' . $relativeFile); if ($path === false) { throw new RuntimeException("Unable to resolve required plugin source: {$relativeFile}"); } $contents = file_get_contents($path); if ($contents === false) { throw new RuntimeException("Unable to read required plugin source: {$relativeFile}"); } return $contents; } it('does not use asymmetric visibility (PHP 8.4)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/\bprivate\s*\(\s*set\s*\)|\bprotected\s*\(\s*set\s*\)/', $contents))->toBe(0, "{$relativeFile} uses asymmetric visibility which requires PHP 8.4" ); } }); it('does not use the #[Override] attribute (PHP 8.3)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/#\[\s*\\\\?Override\s*\]/i', $contents))->toBe(0, "{$relativeFile} uses #[Override] which requires PHP 8.3" ); } }); it('does not use the #[Deprecated] attribute (PHP 8.4)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/#\[\s*\\\\?Deprecated\b/i', $contents))->toBe(0, "{$relativeFile} uses #[Deprecated] which requires PHP 8.4" ); } }); it('does not use json_validate() (PHP 8.3)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/\bjson_validate\s*\(/', $contents))->toBe(0, "{$relativeFile} uses json_validate() which requires PHP 8.3" ); } }); it('does not use array_find()/array_any()/array_all() (PHP 8.4)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/\barray_(find|any|all)\s*\(/', $contents))->toBe(0, "{$relativeFile} uses array_find()/array_any()/array_all() which requires PHP 8.4" ); } }); it('does not use typed class constants (PHP 8.3)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/\bconst\s+(?:\?\s*)?[A-Za-z_\\\\][A-Za-z0-9_\\\\]*(?:\s*\|\s*(?:\?\s*)?[A-Za-z_\\\\][A-Za-z0-9_\\\\]*)*\s+[A-Za-z_][A-Za-z0-9_]*\s*=/', $contents))->toBe(0, "{$relativeFile} uses a typed class constant which requires PHP 8.3" ); } }); it('does not use a dynamic class constant fetch (PHP 8.3)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/::\s*\{\s*\$/', $contents))->toBe(0, "{$relativeFile} uses a dynamic class constant fetch (Foo::" . '{$bar}' . ") which requires PHP 8.3" ); } }); it('does not use each() (removed in PHP 8.0)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/(?])\beach\s*\(/', $contents))->toBe(0, "{$relativeFile} uses each() which was removed in PHP 8.0" ); } }); it('does not use create_function() (removed in PHP 8.0)', function () use ($files) { foreach ($files as $relativeFile) { $contents = plugin_test_read_source_file($relativeFile); expect(preg_match('/(?])\bcreate_function\s*\(/', $contents))->toBe(0, "{$relativeFile} uses create_function() which was removed in PHP 8.0" ); } });