'[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 $resetDatabase = false): void { if ($resetDatabase) { if (Benchmark::execute([$this, 'isMigrated'])) { Benchmark::execute([$this, 'resetDatabase']); } } if (!Benchmark::execute([$this, 'isMigrated'])) { Benchmark::execute([$this, 'migrate']); } Benchmark::execute([$this, 'seedTables']); } 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(): void { $cmd = "{$this->baseCommand} migrate -e testing"; 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(): void { $cmd = "{$this->baseCommand} seed:run -e testing"; shell_exec($cmd); } 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}/", $className) . ".php"; return; } } }); $bootstrap = new TestBootstrap($_ENV); $resetDatabase = $_ENV['RESET_DATABASE'] ?? false; Benchmark::execute([$bootstrap, 'run'], ['resetDatabase' => $resetDatabase]); Benchmark::print(); register_shutdown_function(function() use ($bootstrap) { Benchmark::execute([$bootstrap, 'resetDatabase']); Benchmark::print(); });