declare strict types in db wrappers

This commit is contained in:
maxpozdeev 2022-08-27 21:07:31 +03:00
parent 550055a58c
commit 2980fcb949
6 changed files with 196 additions and 157 deletions

View file

@ -350,7 +350,7 @@ class TasksController extends ApiController {
if (!$r || $listId == $r['list_id']) return false;
// Check target list exists
if (!$db->sq("SELECT COUNT(*) FROM {$db->prefix}lists WHERE id=?", $listId))
if (!$db->sq("SELECT COUNT(*) FROM {$db->prefix}lists WHERE id=?", [$listId]))
return false;
$ow = 1 + (int)$db->sq("SELECT MAX(ow) FROM {$db->prefix}todolist WHERE list_id=? AND compl=?", array($listId, $r['compl']?1:0));

View file

@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
/*
This file is a part of myTinyTodo.
@ -11,14 +11,16 @@ class DatabaseResult_Mysql extends DatabaseResult_Abstract
{
/** @var PDOStatement */
protected $q;
/** @var int */
protected $affected;
function __construct($dbh, $query, $resultless = 0)
function __construct(PDO $dbh, string $query, bool $resultless = false)
{
// use with DELETE, INSERT, UPDATE
if ($resultless)
{
$this->affected = $dbh->exec($query); //throws PDOException
$this->affected = (int) $dbh->exec($query); //throws PDOException
}
// SELECT
else
@ -28,17 +30,25 @@ class DatabaseResult_Mysql extends DatabaseResult_Abstract
}
}
function fetchRow()
function fetchRow(): ?array
{
return $this->q->fetch(PDO::FETCH_NUM);
$res = $this->q->fetch(PDO::FETCH_NUM);
if ($res === false || !is_array($res)) {
return null;
}
return $res;
}
function fetchAssoc()
function fetchAssoc(): ?array
{
return $this->q->fetch(PDO::FETCH_ASSOC);
$res = $this->q->fetch(PDO::FETCH_ASSOC);
if ($res === false || !is_array($res)) {
return null;
}
return $res;
}
function rowsAffected()
function rowsAffected(): int
{
return $this->affected;
}
@ -49,14 +59,17 @@ class Database_Mysql extends Database_Abstract
{
/** @var PDO */
protected $dbh;
protected $affected = null;
/** @var int */
protected $affected = 0;
protected $dbname;
function __construct()
{
}
function connect($params)
function connect(array $params): void
{
$host = $params['host'];
$user = $params['user'];
@ -67,9 +80,7 @@ class Database_Mysql extends Database_Abstract
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->dbname = $db;
$this->dbh = new PDO("mysql:host=$host;dbname=$db", $user, $pass, $options);
return true;
}
@ -78,12 +89,14 @@ class Database_Mysql extends Database_Abstract
Returns single row of SELECT query as indexed array (FETCH_NUM).
Returns single field value if resulting array has only one field.
*/
function sq($query, $p = NULL)
function sq(string $query, ?array $values = null)
{
$q = $this->_dq($query, $p);
$q = $this->_dq($query, $values);
$res = $q->fetchRow();
if ($res === false) return NULL;
if ($res === false || !is_array($res)) {
return null;
}
if (sizeof($res) > 1) return $res;
else return $res[0];
@ -91,51 +104,44 @@ class Database_Mysql extends Database_Abstract
/*
Returns single row of SELECT query as dictionary array (FETCH_ASSOC).
Returns single field value if resulting array has only one field.
*/
function sqa($query, $p = NULL)
function sqa(string $query, ?array $values = null): ?array
{
$q = $this->_dq($query, $p);
$q = $this->_dq($query, $values);
$res = $q->fetchAssoc();
if ($res === false) return NULL;
if (sizeof($res) > 1) return $res;
else return $res[0];
if ($res === false || !is_array($res)){
return null;
}
return $res;
}
function dq($query, $p = NULL) : DatabaseResult_Abstract
function dq(string $query, ?array $values = null) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
return $this->_dq($query, $values);
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
function ex(string $query, ?array $values = null): void
{
$dbr = $this->_dq($query, $p, true);
return $this->affected();
$this->_dq($query, $values, true);
}
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
private function _dq(string $query, ?array $values = null, bool $resultless = false) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
$m = explode('?', $query);
if (sizeof($p) > 0)
if (null !== $values && sizeof($values) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
$m = explode('?', $query);
if (sizeof($m) < sizeof($values)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
if (sizeof($m) > sizeof($values)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i<sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
$query .= $m[$i]. $this->quote($values[$i]);
}
$query .= $m[$i];
}
@ -145,28 +151,35 @@ class Database_Mysql extends Database_Abstract
return $dbr;
}
function affected()
function affected(): int
{
return $this->affected;
}
function quote($s)
function quote($value): string
{
return '\''. addslashes($s). '\'';
if (null === $value) {
return 'null';
}
return '\''. addslashes( (string) $value). '\'';
}
function quoteForLike($format, $s)
function quoteForLike(string $format, string $string): string
{
$s = str_replace(array('%','_'), array('\%','\_'), addslashes($s));
return '\''. sprintf($format, $s). '\'';
$string = str_replace(array('%','_'), array('\%','\_'), addslashes($string));
return '\''. sprintf($format, $string). '\'';
}
function lastInsertId($name = null)
function lastInsertId(?string $name = null): ?string
{
return $this->dbh->lastInsertId();
$ret = $this->dbh->lastInsertId();
if (false === $ret) {
return null;
}
return (string) $ret;
}
function tableExists($table)
function tableExists(string $table): bool
{
$r = $this->sq("SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
array($this->dbname, $table) );
@ -174,7 +187,7 @@ class Database_Mysql extends Database_Abstract
return true;
}
function tableFieldExists($table, $field): bool
function tableFieldExists(string $table, string $field): bool
{
$table = str_replace('`', '\\`', addslashes($table));
$q = $this->dq("DESCRIBE `$table`");

View file

@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
/*
This file is a part of myTinyTodo.
@ -12,19 +12,27 @@ class DatabaseResult_Mysqli extends DatabaseResult_Abstract
/** @var mysqli_result */
protected $q;
function __construct(mysqli $dbh, $query, $resultless = 0)
function __construct(mysqli $dbh, string $query, bool $resultless = false)
{
$this->q = $dbh->query($query); //throws mysqli_sql_exception
}
function fetchRow()
function fetchRow(): ?array
{
return $this->q->fetch_row();
$res = $this->q->fetch_row();
if ($res === null || $res === false || !is_array($res)) {
return null;
}
return $res;
}
function fetchAssoc()
function fetchAssoc(): ?array
{
return $this->q->fetch_assoc();
$res = $this->q->fetch_assoc();
if ($res === null || $res === false || !is_array($res)) {
return null;
}
return $res;
}
}
@ -41,7 +49,7 @@ class Database_Mysqli extends Database_Abstract
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
}
function connect($params)
function connect(array $params): void
{
$host = $params['host'];
$user = $params['user'];
@ -49,67 +57,66 @@ class Database_Mysqli extends Database_Abstract
$db = $params['db'];
$this->dbname = $db;
$this->dbh = new mysqli($host, $user, $pass, $db); //throws mysqli_sql_exception
return true;
}
function lastInsertId($name = null)
function lastInsertId(?string $name = null): ?string
{
return $this->dbh->insert_id;
return (string) $this->dbh->insert_id;
}
function sq($query, $p = NULL)
function sq(string $query, ?array $values = null)
{
$q = $this->_dq($query, $p);
$q = $this->_dq($query, $values);
$res = $q->fetchRow();
if ($res === false || $res === null) return NULL;
if ($res === false || !is_array($res)) {
return null;
}
if (sizeof($res) > 1) return $res;
else return $res[0];
}
function sqa($query, $p = NULL)
/*
Returns single row of SELECT query as dictionary array (fetch_assoc()).
*/
function sqa(string $query, ?array $values = null): ?array
{
$q = $this->_dq($query, $p);
$q = $this->_dq($query, $values);
$res = $q->fetchAssoc();
if ($res === false || $res === null) return NULL;
if ($res === false || !is_array($res)){
return null;
}
return $res;
}
function dq($query, $p = NULL) : DatabaseResult_Abstract
function dq(string $query, ?array $values = null) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
return $this->_dq($query, $values);
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
function ex(string $query, ?array $values = null): void
{
$dbr = $this->_dq($query, $p, 1);
return $this->affected();
$this->_dq($query, $values, true);
}
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
private function _dq(string $query, ?array $values = null, bool $resultless = false) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
$m = explode('?', $query);
if (sizeof($p) > 0)
if (null !== $values && sizeof($values) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
$m = explode('?', $query);
if (sizeof($m) < sizeof($values)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
if (sizeof($m) > sizeof($values)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i < sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
$query .= $m[$i]. $this->quote($values[$i]);
}
$query .= $m[$i];
}
@ -117,23 +124,26 @@ class Database_Mysqli extends Database_Abstract
return new DatabaseResult_Mysqli($this->dbh, $query, $resultless);
}
function affected()
function affected(): int
{
return $this->dbh->affected_rows;
return max( (int)$this->dbh->affected_rows, 0 );
}
function quote($s)
function quote($value): string
{
return '\''. addslashes($s). '\'';
if (null === $value) {
return 'null';
}
return '\''. addslashes( (string) $value). '\'';
}
function quoteForLike($format, $s)
function quoteForLike(string $format, string $string): string
{
$s = str_replace(array('%','_'), array('\%','\_'), addslashes($s));
return '\''. sprintf($format, $s). '\'';
$string = str_replace(array('%','_'), array('\%','\_'), addslashes($string));
return '\''. sprintf($format, $string). '\'';
}
function tableExists($table)
function tableExists(string $table): bool
{
$r = $this->sq("SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
array($this->dbname, $table) );
@ -141,7 +151,7 @@ class Database_Mysqli extends Database_Abstract
return true;
}
function tableFieldExists($table, $field): bool
function tableFieldExists(string $table, string $field): bool
{
$table = str_replace('`', '\\`', addslashes($table));
$q = $this->dq("DESCRIBE `$table`");

View file

@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
/*
This file is a part of myTinyTodo.
@ -10,14 +10,16 @@ class DatabaseResult_Sqlite3 extends DatabaseResult_Abstract
{
/** @var PDOStatement */
protected $q;
/** @var int */
protected $affected;
function __construct($dbh, $query, $resultless = 0)
function __construct(PDO $dbh, string $query, bool $resultless = false)
{
// use with DELETE, INSERT, UPDATE
if ($resultless)
{
$this->affected = $dbh->exec($query); //throws PDOException
$this->affected = (int) $dbh->exec($query); //throws PDOException
}
// SELECT
else
@ -27,17 +29,25 @@ class DatabaseResult_Sqlite3 extends DatabaseResult_Abstract
}
}
function fetchRow()
function fetchRow(): ?array
{
return $this->q->fetch(PDO::FETCH_NUM);
$res = $this->q->fetch(PDO::FETCH_NUM);
if ($res === false || !is_array($res)) {
return null;
}
return $res;
}
function fetchAssoc()
function fetchAssoc(): ?array
{
return $this->q->fetch(PDO::FETCH_ASSOC);
$res = $this->q->fetch(PDO::FETCH_ASSOC);
if ($res === false || !is_array($res)) {
return null;
}
return $res;
}
function rowsAffected()
function rowsAffected(): int
{
return $this->affected;
}
@ -48,83 +58,82 @@ class Database_Sqlite3 extends Database_Abstract
{
/** @var PDO */
protected $dbh;
protected $affected = null;
/** @var int */
protected $affected = 0;
function __construct()
{
}
function connect($params)
function connect(array $params): void
{
$filename = $params['filename'];
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->dbh = new PDO("sqlite:$filename", null, null, $options); //throws PDOException
return true;
}
/*
SELECT queries for single row
*/
function sq($query, $p = NULL)
function sq(string $query, ?array $values = null)
{
$q = $this->_dq($query, $p);
$q = $this->_dq($query, $values);
$res = $q->fetchRow();
if ($res === false) return NULL;
if ($res === false || !is_array($res)) {
return null;
}
if (sizeof($res) > 1) return $res;
else return $res[0];
}
/*
SELECT queries for single row
Returns single row of SELECT query as dictionary array (FETCH_ASSOC).
*/
function sqa($query, $p = NULL)
function sqa(string $query, ?array $values = null): ?array
{
$q = $this->_dq($query, $p);
$q = $this->_dq($query, $values);
$res = $q->fetchAssoc();
if ($res === false) return NULL;
if ($res === false || !is_array($res)) {
return null;
}
return $res;
}
/*
SELECT queries for multiple rows
*/
function dq($query, $p = NULL) : DatabaseResult_Abstract
function dq(string $query, ?array $values = null) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
return $this->_dq($query, $values);
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
function ex(string $query, ?array $values = null): void
{
$dbr = $this->_dq($query, $p, 1);
return $this->affected();
$this->_dq($query, $values, true);
}
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
private function _dq(string $query, ?array $values = null, bool $resultless = false) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
$m = explode('?', $query);
if (sizeof($p) > 0)
if (null !== $values && sizeof($values) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
$m = explode('?', $query);
if (sizeof($m) < sizeof($values)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
if (sizeof($m) > sizeof($values)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i<sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
$query .= $m[$i]. $this->quote($values[$i]);
}
$query .= $m[$i];
}
@ -134,41 +143,48 @@ class Database_Sqlite3 extends Database_Abstract
return $dbr;
}
function affected()
function affected(): int
{
return $this->affected;
}
function quote($s)
function quote($value): string
{
return $this->dbh->quote($s);
if (null === $value) {
return 'null';
}
return $this->dbh->quote( (string) $value);
}
function quoteForLike($format, $s)
function quoteForLike(string $format, string $string): string
{
$s = str_replace(array('\\','%','_'), array('\\\\','\%','\_'), $s);
return $this->dbh->quote(sprintf($format, $s)). " ESCAPE '\'";
$string = str_replace(array('\\','%','_'), array('\\\\','\%','\_'), $string);
return $this->dbh->quote(sprintf($format, $string)). " ESCAPE '\'";
}
function lastInsertId($name = null)
function lastInsertId(?string $name = null): ?string
{
return $this->dbh->lastInsertId();
$ret = $this->dbh->lastInsertId();
if (false === $ret) {
return null;
}
return (string) $ret;
}
function tableExists($table)
function tableExists(string $table): bool
{
$exists = $this->sq("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", $table);
$exists = $this->sq("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", [$table]);
if ($exists == "1") {
return true;
}
$exists = $this->sq("SELECT 1 FROM sqlite_temp_master WHERE type='table' AND name=?", $table);
$exists = $this->sq("SELECT 1 FROM sqlite_temp_master WHERE type='table' AND name=?", [$table]);
if ($exists == "1") {
return true;
}
return false;
}
function tableFieldExists($table, $field): bool
function tableFieldExists(string $table, string $field): bool
{
$q = $this->dq("PRAGMA table_info(". $this->quote($table). ")");
while ($r = $q->fetchRow()) {

View file

@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
/*
This file is a part of myTinyTodo.
@ -41,17 +41,17 @@ abstract class Database_Abstract
/** @var string */
protected $lastQuery = '';
abstract function connect($params);
abstract function sq($query, $p = NULL);
abstract function sqa($query, $p = NULL);
abstract function dq($query, $p = NULL) : DatabaseResult_Abstract;
abstract function ex($query, $p = NULL);
abstract function affected();
abstract function quote($s);
abstract function quoteForLike($format, $s);
abstract function lastInsertId($name = null);
abstract function tableExists($table);
abstract function tableFieldExists($table, $field): bool;
abstract function connect(array $params): void;
abstract function sq(string $query, ?array $values = null);
abstract function sqa(string $query, ?array $values = null): ?array;
abstract function dq(string $query, ?array $values = null): DatabaseResult_Abstract;
abstract function ex(string $query, ?array $values = null): void;
abstract function affected(): int;
abstract function quote($value): string;
abstract function quoteForLike(string $format, string $string): string;
abstract function lastInsertId(?string $name = null): ?string;
abstract function tableExists(string $table): bool;
abstract function tableFieldExists(string $table, string $field): bool;
function __get(string $propName) {
if ( in_array($propName, self::$readonlyProps) ) {
@ -60,7 +60,7 @@ abstract class Database_Abstract
throw new Error("Attempt to read undefined property ". get_class($this). "::\$$propName");
}
function setPrefix(string $prefix) {
function setPrefix(string $prefix): void {
if ($prefix != '' && !preg_match("/^[a-zA-Z0-9_]+$/", $prefix)) {
throw new Exception("Incorrect table prefix");
}
@ -70,7 +70,7 @@ abstract class Database_Abstract
abstract class DatabaseResult_Abstract
{
abstract function fetchRow();
abstract function fetchAssoc();
abstract function fetchRow(): ?array;
abstract function fetchAssoc(): ?array;
}

View file

@ -42,7 +42,7 @@ class MTTSessionHandler implements SessionHandlerInterface
// read session data if not expired
$time = time();
$expire = $time;
$r = $this->db->sq("SELECT data,last_access FROM {$this->db->prefix}sessions WHERE id = ? AND expires >= $expire", $id);
$r = $this->db->sq("SELECT data,last_access FROM {$this->db->prefix}sessions WHERE id = ? AND expires >= $expire", [$id]);
if ( is_null($r) ) return '';
// update last access time and set expires in 14 days
@ -63,7 +63,7 @@ class MTTSessionHandler implements SessionHandlerInterface
*/
public function write($id, $data): bool
{
$exists = $this->db->sq("SELECT COUNT(*) FROM {$this->db->prefix}sessions WHERE id = ?", $id);
$exists = $this->db->sq("SELECT COUNT(*) FROM {$this->db->prefix}sessions WHERE id = ?", [$id]);
if (!$exists) {
// Create new session with 14 days lifetime
$expire = time() + 14 * 86400;
@ -85,7 +85,7 @@ class MTTSessionHandler implements SessionHandlerInterface
*/
public function destroy($id): bool
{
$this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE id = ?", $id);
$this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE id = ?", [$id]);
return true;
}
@ -98,8 +98,8 @@ class MTTSessionHandler implements SessionHandlerInterface
{
// We ignore php runtime 'session.gc_maxlifetime'
$expire = time();
$affected = $this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE expires < $expire");
return $affected;
$this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE expires < $expire");
return $this->db->affected();
}
}