= 0; $i--) { $arg_val = $args[$i]; if (is_null($arg_val)) { $v = 'NULL'; } else { $v = '\''.$this->escape($arg_val) . '\''; } $sql = substr_replace($sql, $v, $positions[$i], 1); } } if (!empty($config['mysql']['log'])) logDebug(__METHOD__.': ', $sql); return $sql; } function insert(string $table, array $fields) { return $this->performInsert('INSERT', $table, $fields); } function replace(string $table, array $fields) { return $this->performInsert('REPLACE', $table, $fields); } protected function performInsert(string $command, string $table, array $fields) { $names = []; $values = []; $count = 0; foreach ($fields as $k => $v) { $names[] = $k; $values[] = $v; $count++; } $sql = "{$command} INTO `{$table}` (`" . implode('`, `', $names) . "`) VALUES (" . implode(', ', array_fill(0, $count, '?')) . ")"; array_unshift($values, $sql); return $this->query(...$values); } function update(string $table, array $rows, ...$cond) { $fields = []; $args = []; foreach ($rows as $row_name => $row_value) { $fields[] = "`{$row_name}`=?"; $args[] = $row_value; } $sql = "UPDATE `$table` SET ".implode(', ', $fields); if (!empty($cond)) { $sql .= " WHERE ".$cond[0]; if (count($cond) > 1) $args = array_merge($args, array_slice($cond, 1)); } return $this->query($sql, ...$args); } function multipleInsert(string $table, array $rows) { list($names, $values) = $this->getMultipleInsertValues($rows); $sql = "INSERT INTO `{$table}` (`".implode('`, `', $names)."`) VALUES ".$values; return $this->query($sql); } function multipleReplace(string $table, array $rows) { list($names, $values) = $this->getMultipleInsertValues($rows); $sql = "REPLACE INTO `{$table}` (`".implode('`, `', $names)."`) VALUES ".$values; return $this->query($sql); } protected function getMultipleInsertValues(array $rows): array { $names = []; $sql_rows = []; foreach ($rows as $i => $fields) { $row_values = []; foreach ($fields as $field_name => $field_val) { if ($i == 0) { $names[] = $field_name; } $row_values[] = $this->escape($field_val); } $sql_rows[] = "('".implode("', '", $row_values)."')"; } return [$names, implode(', ', $sql_rows)]; } function __destruct() { if ($this->link) $this->link->close(); } function connect(): bool { $this->link = new mysqli(); $result = $this->link->real_connect($this->host, $this->user, $this->password, $this->database); if ($result) $this->link->set_charset('utf8mb4'); return !!$result; } function query(string $sql, ...$args): mysqli_result|bool { $sql = $this->prepareQuery($sql, ...$args); $q = $this->link->query($sql); if (!$q) logError(__METHOD__.': '.$this->link->error."\n$sql\n".backtrace_as_string(1)); return $q; } function fetch($q): ?array { $row = $q->fetch_assoc(); if (!$row) { $q->free(); return null; } return $row; } function fetchAll($q): ?array { if (!$q) return null; $list = []; while ($f = $q->fetch_assoc()) { $list[] = $f; } $q->free(); return $list; } function fetchRow($q): ?array { return $q?->fetch_row(); } function result($q, $field = 0) { return $q?->fetch_row()[$field]; } function insertId(): int { return $this->link->insert_id; } function numRows($q): ?int { return $q?->num_rows; } function affectedRows(): ?int { return $this->link?->affected_rows; } function foundRows(): int { return (int)$this->fetch($this->query("SELECT FOUND_ROWS() AS `count`"))['count']; } function escape(string $s): string { return $this->link->real_escape_string($s); } } class mysql_bitfield { private GMP $value; private int $size; public function __construct($value, int $size = 64) { $this->value = gmp_init($value); $this->size = $size; } public function has(int $bit): bool { $this->validateBit($bit); return gmp_testbit($this->value, $bit); } public function set(int $bit): void { $this->validateBit($bit); gmp_setbit($this->value, $bit); } public function clear(int $bit): void { $this->validateBit($bit); gmp_clrbit($this->value, $bit); } public function isEmpty(): bool { return !gmp_cmp($this->value, 0); } public function __toString(): string { $buf = ''; for ($bit = $this->size-1; $bit >= 0; --$bit) $buf .= gmp_testbit($this->value, $bit) ? '1' : '0'; if (($pos = strpos($buf, '1')) !== false) { $buf = substr($buf, $pos); } else { $buf = '0'; } return $buf; } private function validateBit(int $bit): void { if ($bit < 0 || $bit >= $this->size) throw new Exception('invalid bit '.$bit.', allowed range: [0..'.$this->size.')'); } } function DB(): mysql|null { global $config; /** @var ?mysql $link */ static $link = null; if (!is_null($link)) return $link; $link = new mysql( $config['mysql']['host'], $config['mysql']['user'], $config['mysql']['password'], $config['mysql']['database']); if (!$link->connect()) { if (!is_cli()) { header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 300'); die('database connection failed'); } else { fwrite(STDERR, 'database connection failed'); exit(1); } } return $link; } function MC(): Memcached { static $mc = null; if ($mc === null) { $mc = new Memcached(); $mc->addServer("127.0.0.1", 11211); } return $mc; }