<?php
namespace PHPFUI\PHPUnitSyntaxCoverage;
class Extensions extends \PHPUnit\Framework\TestCase
{
private ?\PHPFUI\PHPUnitSyntaxCoverage\ClassFinder $classFinder = null;
private static \PhpParser\Parser $parser;
private array $skipDirectories = [];
private array $skipNamespaces = [];
private bool $skipNamespaceTest = false;
private ?\PhpParser\NodeTraverser $traverser = null;
public static function setUpBeforeClass() : void
{
$factory = new \PhpParser\ParserFactory();
self::$parser = $factory->createForHostVersion();
}
protected function setUp() : void
{
$this->traverser = new \PhpParser\NodeTraverser();
$this->classFinder = new \PHPFUI\PHPUnitSyntaxCoverage\ClassFinder();
}
protected function tearDown() : void
{
$this->traverser = null;
$this->classFinder = null;
}
public function addSkipDirectory(string $directory) : self
{
$this->skipDirectories[] = $directory;
return $this;
}
public function addSkipNamespace(string $namespace) : self
{
$this->skipNamespaces[] = $namespace;
return $this;
}
public function assertValidPHP(string $code, string $message = '') : void
{
$this->assertNotEmpty($code, 'Empty PHP file. ' . $message);
try
{
$ast = self::$parser->parse($code);
}
catch (\Throwable $e)
{
throw new \PHPFUI\PHPUnitSyntaxCoverage\Exception($message . "\n" . $e->getMessage());
}
$this->assertNotEmpty($ast, 'Empty Abstract Syntax tree. ' . $message);
$this->traverser->addVisitor($this->classFinder);
$this->traverser->traverse($ast);
foreach ($this->classFinder->getClasses() as $class)
{
try
{
$reflection = new \ReflectionClass($class);
}
catch (\Throwable $e)
{
throw new \PHPFUI\PHPUnitSyntaxCoverage\Exception($message . "\n" . $e->getMessage() . ": ReflectionClass({$class}) failed to load");
}
}
}
public function assertValidPHPDirectory(string $directory, string $message = '', bool $recurseSubdirectories = true, array $extensions = ['.php']) : void
{
if ($recurseSubdirectories)
{
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
);
}
else
{
$iterator = new \DirectoryIterator($directory);
}
$exts = \array_flip($extensions);
foreach ($iterator as $item)
{
$type = $item->getType();
if ('file' == $type)
{
$file = $item->getPathname();
$ext = \strrchr($file, '.');
if ($ext && isset($exts[$ext]))
{
$skip = false;
foreach ($this->skipDirectories as $directory)
{
if (false !== \stripos($file, $directory))
{
$skip = true;
break;
}
}
if (! $skip)
{
$this->setup();
$this->assertValidPHPFile($file, $message . "\nFile: " . $file);
$this->tearDown();
}
}
}
}
}
public function assertValidPHPFile(string $fileName, string $message = '') : void
{
$this->assertFileExists($fileName, $message . ': File does not exist');
$code = \file_get_contents($fileName);
$this->assertValidPHP($code, $message . ': Not valid PHP code');
if (! $this->skipNamespaceTest)
{
$namespace = $this->classFinder->getNamespace();
if (! \in_array($namespace, $this->skipNamespaces))
{
$fileName = \str_replace('/', '\\', $fileName);
$this->assertStringContainsString($namespace . '\\', $fileName, "Namespace {$namespace} not found in file path {$fileName}");
}
}
}
public function skipNamespaceTesting() : self
{
$this->skipNamespaceTest = true;
return $this;
}
}