Files
xxx/tests/php/Support/FakeDb.php
T
tim ed959888b4 Add PHP unit tests and page tests.
Run "composer install", then "composer test".
2026-09-26 03:48:09 +02:00

101 lines
3.1 KiB
PHP

<?php
namespace Xxx\Tests\Support;
/*
* A stand-in for mysqli. open_db() makes one when the global $db_class
* names this class. All connections share the static state below, so a
* test sees the queries of every connection that the code opened.
*
* Handlers answer queries: FakeDb::on('/pattern/', $answer), where the
* answer is a list of rows, TRUE, FALSE (a failed query) or a function
* that takes ($sql, $params) and returns one of these. The handler added
* last wins. A query that no handler matches gets no rows (SELECT) or
* TRUE (anything else).
*/
class FakeDb {
public static array $log = array();
public static array $handlers = array();
public static array $connections = array();
public static ?string $connectError = NULL;
// When set, every query is also appended to this file as a JSON line
public static ?string $logFile = NULL;
public $connect_error = NULL;
public $connect_errno = 0;
public $error = '';
public bool $closed = FALSE;
public static function reset(): void {
self::$log = array();
self::$handlers = array();
self::$connections = array();
self::$connectError = NULL;
self::$logFile = NULL;
}
public static function on(string $pattern, $answer): void {
self::$handlers[] = array($pattern, $answer);
}
// The SQL of every query so far
public static function queries(): array {
return array_column(self::$log, 'sql');
}
// The queries whose SQL matches $pattern, each as array('sql' => ..., 'params' => ...)
public static function matching(string $pattern): array {
return array_values(array_filter(self::$log, function ($entry) use ($pattern) {
return preg_match($pattern, $entry['sql']);
}));
}
public function __construct(...$args) {
self::$connections[] = $args;
if (self::$connectError !== NULL) {
$this->connect_errno = 1045;
$this->connect_error = self::$connectError;
}
}
public function real_escape_string($string) {
return addslashes((string)$string);
}
public function execute_query($sql, $params = array()) {
if (substr_count($sql, '?') !== count($params))
throw new \LogicException("Placeholder count does not match the parameters: $sql");
return $this->run($sql, array_values($params));
}
public function query($sql) {
return $this->run($sql, array());
}
public function close() {
$this->closed = TRUE;
return TRUE;
}
private function run(string $sql, array $params) {
$entry = array('sql' => $sql, 'params' => $params);
self::$log[] = $entry;
if (self::$logFile !== NULL)
file_put_contents(self::$logFile, json_encode($entry) . "\n", FILE_APPEND | LOCK_EX);
$answer = NULL;
foreach (array_reverse(self::$handlers) as list($pattern, $handler)) {
if (preg_match($pattern, $sql)) {
$answer = is_callable($handler) ? $handler($sql, $params) : $handler;
break;
}
}
if ($answer === NULL)
$answer = preg_match('/^\s*SELECT/i', $sql) ? array() : TRUE;
if ($answer === FALSE) {
$this->error = 'Fake failure for: ' . $sql;
return FALSE;
}
return is_array($answer) ? new FakeResult($answer) : $answer;
}
}