'[static] ' . $callable, is_string($callable) => '[function] ' . $callable, is_array($callable) && is_object($callable[0]) => '[method] ' . get_class($callable[0]) . '->' . $callable[1], is_array($callable) => '[static] ' . $callable[0] . '::' . $callable[1], $callable instanceof Closure => '[closure]', is_object($callable) => '[invokable] ' . get_class($callable), default => '[unknown]', }; } } class TestBootstrap { public function __construct(protected array $configuration) {} public function run(bool $debug = false): void { if (!Benchmark::execute([$this, 'isMigrated'])) { Benchmark::execute([$this, 'migrate']); } $output = Benchmark::execute([$this, 'seedTables']); if ($debug) { var_dump($output); } } protected string $baseCommand = "bin/phinx"; public function isMigrated(): bool { $cmd = "{$this->baseCommand} status -e testing -f json --no-info"; $output = shell_exec($cmd); $status = json_decode($output, true); return $status['missing_count'] === 0 and $status['pending_count'] === 0; } public function migrate(): false|null|string { $cmd = "{$this->baseCommand} migrate -e testing"; return shell_exec($cmd); } public function resetDatabase(): void { $tables = $this->getTables(); if ($this->connect()->beginTransaction()) { try { $this->connect()->query("SET FOREIGN_KEY_CHECKS=0;"); foreach ($tables as $table) { $this->connect()->query("DROP TABLE IF EXISTS `{$table}`"); } $this->connect()->query("SET FOREIGN_KEY_CHECKS=1;"); if ($this->connect()->inTransaction()) { $this->connect()->commit(); } } catch (PDOException $exception) { if ($this->connect()->inTransaction()) { $this->connect()->rollBack(); } throw $exception; } } } public function truncateTables(): void { $tables = $this->getTables(); if ($this->connect()->beginTransaction()) { try { $this->connect()->query("SET FOREIGN_KEY_CHECKS=0;"); foreach ($tables as $table) { $this->connect()->query("TRUNCATE TABLE `{$table}`"); } $this->connect()->query("SET FOREIGN_KEY_CHECKS=1;"); if ($this->connect()->inTransaction()) { $this->connect()->commit(); } } catch (PDOException $exception) { if ($this->connect()->inTransaction()) { $this->connect()->rollBack(); } throw $exception; } } } public function seedTables(): false|null|string { $cmd = "{$this->baseCommand} seed:run -e testing"; $output = shell_exec($cmd); $testSeeder = new Tests\Extension\TestSeeder($this->connect()); $testSeeder->run(); return $output; } protected PDO $connection; protected function connect(): PDO { if (!isset($this->connection)) { $dsn = "mysql:host={$this->configuration['DB_HOST']};dbname={$this->configuration['DB_DATABASE']}"; $this->connection = new PDO( $dsn, $this->configuration['DB_USER'], $this->configuration['DB_PASSWORD'] ); } return $this->connection; } protected array $tables; protected function getTables(): array { if (!isset($this->tables)) { $this->tables = $this->connect()->query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN); } return $this->tables; } } spl_autoload_register(function($className) { $baseTestPath = __DIR__ . "/tests"; $namespaceMap = [ "Tests\\Extension\\" => "{$baseTestPath}/extension", "Tests\\Integration\\" => "{$baseTestPath}/integration", "Tests\\Unit\\" => "{$baseTestPath}/unit/src", "Tests\\Performance\\" => "{$baseTestPath}/performance", ]; foreach ($namespaceMap as $namespace => $path) { if (str_starts_with($className, $namespace)) { require str_replace([$namespace, '\\'], ["{$path}/", DIRECTORY_SEPARATOR], $className) . ".php"; return; } } }); $bootstrap = new TestBootstrap($_ENV); $resetDatabase = (array_key_exists('RESET_DATABASE', $_ENV) and $_ENV['RESET_DATABASE'] === 'true') ?? false; $debug = (array_key_exists('DEBUG', $_ENV) and $_ENV['DEBUG'] === 'true') ?? false; Benchmark::execute([$bootstrap, 'run'], ['debug' => $debug]); Benchmark::print(); register_shutdown_function(function() use ($bootstrap, $resetDatabase) { $method = 'truncateTables'; if ($resetDatabase) { $method = 'resetDatabase'; } Benchmark::execute([$bootstrap, $method]); Benchmark::print(); });