2018-11-23 00:55:54 +00:00
< ? php
/** @ license MIT
* Copyright 2017 J . King , Dustin Wilson et al .
* See LICENSE and AUTHORS files for details */
declare ( strict_types = 1 );
namespace JKingWeb\Arsse\Test ;
use JKingWeb\Arsse\Arsse ;
2018-11-25 04:18:17 +00:00
use JKingWeb\Arsse\Db\Driver ;
2018-11-23 00:55:54 +00:00
class DatabaseInformation {
public $name ;
public $backend ;
public $pdo ;
public $resultClass ;
public $statementClass ;
public $driverClass ;
public $stringOutput ;
public $interfaceConstructor ;
2018-11-25 04:18:17 +00:00
public $truncateFunction ;
public $razeFunction ;
2018-11-23 00:55:54 +00:00
protected static $data ;
public function __construct ( string $name ) {
if ( ! isset ( self :: $data )) {
self :: $data = self :: getData ();
}
2018-12-20 23:06:28 +00:00
if ( ! array_key_exists ( $name , self :: $data )) {
2018-11-23 00:55:54 +00:00
throw new \Exception ( " Invalid database driver name " );
}
$this -> name = $name ;
foreach ( self :: $data [ $name ] as $key => $value ) {
$this -> $key = $value ;
}
}
public static function list () : array {
if ( ! isset ( self :: $data )) {
self :: $data = self :: getData ();
}
return array_keys ( self :: $data );
}
public static function listPDO () : array {
if ( ! isset ( self :: $data )) {
self :: $data = self :: getData ();
}
return array_values ( array_filter ( array_keys ( self :: $data ), function ( $k ) {
return self :: $data [ $k ][ 'pdo' ];
}));
}
protected static function getData () {
2018-11-25 04:18:17 +00:00
$sqlite3TableList = function ( $db ) : array {
$listTables = " SELECT name from sqlite_master where type = 'table' and name like 'arsse_%' " ;
if ( $db instanceof Driver ) {
$tables = $db -> query ( $listTables ) -> getAll ();
$tables = sizeof ( $tables ) ? array_column ( $tables , " name " ) : [];
} elseif ( $db instanceof \PDO ) {
2018-11-27 19:26:33 +00:00
retry :
try {
$tables = $db -> query ( $listTables ) -> fetchAll ( \PDO :: FETCH_ASSOC );
} catch ( \PDOException $e ) {
goto retry ;
}
2018-11-25 04:18:17 +00:00
$tables = sizeof ( $tables ) ? array_column ( $tables , " name " ) : [];
} else {
$tables = [];
$result = $db -> query ( $listTables );
while ( $r = $result -> fetchArray ( \SQLITE3_ASSOC )) {
$tables [] = $r [ 'name' ];
}
$result -> finalize ();
}
return $tables ;
};
$sqlite3TruncateFunction = function ( $db , array $afterStatements = []) use ( $sqlite3TableList ) {
2018-11-27 19:26:33 +00:00
// rollback any pending transaction
try {
$db -> exec ( " ROLLBACK " );
2018-12-05 22:28:11 +00:00
} catch ( \Throwable $e ) {
2018-11-27 19:26:33 +00:00
}
2018-11-25 04:18:17 +00:00
foreach ( $sqlite3TableList ( $db ) as $table ) {
if ( $table == " arsse_meta " ) {
$db -> exec ( " DELETE FROM $table where key <> 'schema_version' " );
} else {
$db -> exec ( " DELETE FROM $table " );
}
}
foreach ( $afterStatements as $st ) {
$db -> exec ( $st );
}
};
$sqlite3RazeFunction = function ( $db , array $afterStatements = []) use ( $sqlite3TableList ) {
2018-11-27 19:26:33 +00:00
// rollback any pending transaction
try {
$db -> exec ( " ROLLBACK " );
2018-12-05 22:28:11 +00:00
} catch ( \Throwable $e ) {
2018-11-27 19:26:33 +00:00
}
2018-11-25 04:18:17 +00:00
$db -> exec ( " PRAGMA foreign_keys=0 " );
foreach ( $sqlite3TableList ( $db ) as $table ) {
$db -> exec ( " DROP TABLE IF EXISTS $table " );
}
$db -> exec ( " PRAGMA user_version=0 " );
$db -> exec ( " PRAGMA foreign_keys=1 " );
foreach ( $afterStatements as $st ) {
$db -> exec ( $st );
}
};
$pgObjectList = function ( $db ) : array {
$listObjects = " SELECT table_name as name, 'TABLE' as type from information_schema.tables where table_schema = current_schema() and table_name like 'arsse_%' union SELECT collation_name as name, 'COLLATION' as type from information_schema.collations where collation_schema = current_schema() " ;
if ( $db instanceof Driver ) {
return $db -> query ( $listObjects ) -> getAll ();
} elseif ( $db instanceof \PDO ) {
return $db -> query ( $listObjects ) -> fetchAll ( \PDO :: FETCH_ASSOC );
} else {
2018-12-14 00:47:51 +00:00
$r = @ pg_query ( $db , $listObjects );
$out = $r ? pg_fetch_all ( $r ) : false ;
return $out ? $out : [];
}
};
$pgExecFunction = function ( $db , $q ) {
if ( $db instanceof Driver ) {
$db -> exec ( $q );
} elseif ( $db instanceof \PDO ) {
$db -> exec ( $q );
} else {
pg_query ( $db , $q );
}
};
$pgTruncateFunction = function ( $db , array $afterStatements = []) use ( $pgObjectList , $pgExecFunction ) {
// rollback any pending transaction
try {
@ $pgExecFunction ( $db , " ROLLBACK " );
} catch ( \Throwable $e ) {
}
foreach ( $pgObjectList ( $db ) as $obj ) {
if ( $obj [ 'type' ] != " TABLE " ) {
continue ;
} elseif ( $obj [ 'name' ] == " arsse_meta " ) {
$pgExecFunction ( $db , " DELETE FROM { $obj [ 'name' ] } where key <> 'schema_version' " );
} else {
$pgExecFunction ( $db , " TRUNCATE TABLE { $obj [ 'name' ] } restart identity cascade " );
}
}
foreach ( $afterStatements as $st ) {
$pgExecFunction ( $db , $st );
}
};
$pgRazeFunction = function ( $db , array $afterStatements = []) use ( $pgObjectList , $pgExecFunction ) {
// rollback any pending transaction
try {
$pgExecFunction ( $db , " ROLLBACK " );
} catch ( \Throwable $e ) {
}
foreach ( $pgObjectList ( $db ) as $obj ) {
$pgExecFunction ( $db , " DROP { $obj [ 'type' ] } IF EXISTS { $obj [ 'name' ] } cascade " );
}
foreach ( $afterStatements as $st ) {
$pgExecFunction ( $db , $st );
2018-11-25 04:18:17 +00:00
}
};
2018-12-20 23:06:28 +00:00
$mysqlTableList = function ( $db ) : array {
$listTables = " SELECT table_name as name from information_schema.tables where table_schema = database() and table_name like 'arsse_%' " ;
if ( $db instanceof Driver ) {
$tables = $db -> query ( $listTables ) -> getAll ();
} elseif ( $db instanceof \PDO ) {
$tables = $db -> query ( $listTables ) -> fetchAll ( \PDO :: FETCH_ASSOC );
} else {
$tables = $db -> query ( $listTables ) -> fetch_all ( \MYSQLI_ASSOC );
}
$tables = sizeof ( $tables ) ? array_column ( $tables , " name " ) : [];
return $tables ;
};
$mysqlTruncateFunction = function ( $db , array $afterStatements = []) use ( $mysqlTableList ) {
// rollback any pending transaction
try {
$db -> query ( " UNLOCK TABLES; ROLLBACK " );
} catch ( \Throwable $e ) {
}
foreach ( $mysqlTableList ( $db ) as $table ) {
if ( $table == " arsse_meta " ) {
$db -> query ( " DELETE FROM $table where `key` <> 'schema_version' " );
} else {
$db -> query ( " DELETE FROM $table " );
}
2018-12-21 15:14:26 +00:00
$db -> query ( " ALTER TABLE $table auto_increment = 1 " );
2018-12-20 23:06:28 +00:00
}
foreach ( $afterStatements as $st ) {
$db -> query ( $st );
}
};
$mysqlRazeFunction = function ( $db , array $afterStatements = []) use ( $mysqlTableList ) {
// rollback any pending transaction
try {
$db -> query ( " UNLOCK TABLES; ROLLBACK " );
} catch ( \Throwable $e ) {
}
foreach ( $mysqlTableList ( $db ) as $table ) {
$db -> query ( " DROP TABLE IF EXISTS $table " );
}
foreach ( $afterStatements as $st ) {
$db -> query ( $st );
}
};
2018-11-23 00:55:54 +00:00
return [
'SQLite 3' => [
'pdo' => false ,
'backend' => " SQLite 3 " ,
'statementClass' => \JKingWeb\Arsse\Db\SQLite3\Statement :: class ,
'resultClass' => \JKingWeb\Arsse\Db\SQLite3\Result :: class ,
'driverClass' => \JKingWeb\Arsse\Db\SQLite3\Driver :: class ,
'stringOutput' => false ,
'interfaceConstructor' => function () {
try {
$d = new \SQLite3 ( Arsse :: $conf -> dbSQLite3File );
} catch ( \Throwable $e ) {
return ;
}
$d -> enableExceptions ( true );
return $d ;
},
2018-11-25 04:18:17 +00:00
'truncateFunction' => $sqlite3TruncateFunction ,
'razeFunction' => $sqlite3RazeFunction ,
2018-11-23 00:55:54 +00:00
],
'PDO SQLite 3' => [
'pdo' => true ,
'backend' => " SQLite 3 " ,
2019-01-11 00:01:32 +00:00
'statementClass' => \JKingWeb\Arsse\Db\SQLite3\PDOStatement :: class ,
2018-11-23 00:55:54 +00:00
'resultClass' => \JKingWeb\Arsse\Db\PDOResult :: class ,
'driverClass' => \JKingWeb\Arsse\Db\SQLite3\PDODriver :: class ,
'stringOutput' => true ,
'interfaceConstructor' => function () {
try {
$d = new \PDO ( " sqlite: " . Arsse :: $conf -> dbSQLite3File , " " , " " , [ \PDO :: ATTR_ERRMODE => \PDO :: ERRMODE_EXCEPTION ]);
$d -> exec ( " PRAGMA busy_timeout=0 " );
return $d ;
} catch ( \Throwable $e ) {
return ;
}
},
2018-11-25 04:18:17 +00:00
'truncateFunction' => $sqlite3TruncateFunction ,
'razeFunction' => $sqlite3RazeFunction ,
2018-11-23 00:55:54 +00:00
],
2018-12-14 00:47:51 +00:00
'PostgreSQL' => [
'pdo' => false ,
'backend' => " PostgreSQL " ,
'statementClass' => \JKingWeb\Arsse\Db\PostgreSQL\Statement :: class ,
'resultClass' => \JKingWeb\Arsse\Db\PostgreSQL\Result :: class ,
'driverClass' => \JKingWeb\Arsse\Db\PostgreSQL\Driver :: class ,
'stringOutput' => true ,
'interfaceConstructor' => function () {
$connString = \JKingWeb\Arsse\Db\PostgreSQL\Driver :: makeConnectionString ( false , Arsse :: $conf -> dbPostgreSQLUser , Arsse :: $conf -> dbPostgreSQLPass , Arsse :: $conf -> dbPostgreSQLDb , Arsse :: $conf -> dbPostgreSQLHost , Arsse :: $conf -> dbPostgreSQLPort , " " );
if ( $d = @ pg_connect ( $connString , \PGSQL_CONNECT_FORCE_NEW )) {
foreach ( \JKingWeb\Arsse\Db\PostgreSQL\Driver :: makeSetupQueries ( Arsse :: $conf -> dbPostgreSQLSchema ) as $q ) {
pg_query ( $d , $q );
}
return $d ;
} else {
return ;
}
},
'truncateFunction' => $pgTruncateFunction ,
'razeFunction' => $pgRazeFunction ,
],
2018-11-23 00:55:54 +00:00
'PDO PostgreSQL' => [
'pdo' => true ,
'backend' => " PostgreSQL " ,
2018-11-29 18:45:37 +00:00
'statementClass' => \JKingWeb\Arsse\Db\PostgreSQL\PDOStatement :: class ,
2018-11-23 00:55:54 +00:00
'resultClass' => \JKingWeb\Arsse\Db\PDOResult :: class ,
'driverClass' => \JKingWeb\Arsse\Db\PostgreSQL\PDODriver :: class ,
2018-11-29 18:45:37 +00:00
'stringOutput' => false ,
2018-11-23 00:55:54 +00:00
'interfaceConstructor' => function () {
$connString = \JKingWeb\Arsse\Db\PostgreSQL\Driver :: makeConnectionString ( true , Arsse :: $conf -> dbPostgreSQLUser , Arsse :: $conf -> dbPostgreSQLPass , Arsse :: $conf -> dbPostgreSQLDb , Arsse :: $conf -> dbPostgreSQLHost , Arsse :: $conf -> dbPostgreSQLPort , " " );
try {
$d = new \PDO ( " pgsql: " . $connString , Arsse :: $conf -> dbPostgreSQLUser , Arsse :: $conf -> dbPostgreSQLPass , [ \PDO :: ATTR_ERRMODE => \PDO :: ERRMODE_EXCEPTION ]);
} catch ( \Throwable $e ) {
return ;
}
foreach ( \JKingWeb\Arsse\Db\PostgreSQL\PDODriver :: makeSetupQueries ( Arsse :: $conf -> dbPostgreSQLSchema ) as $q ) {
$d -> exec ( $q );
}
return $d ;
},
2018-12-14 00:47:51 +00:00
'truncateFunction' => $pgTruncateFunction ,
'razeFunction' => $pgRazeFunction ,
2018-11-23 00:55:54 +00:00
],
2018-12-20 23:06:28 +00:00
'PDO MySQL' => [
'pdo' => true ,
'backend' => " MySQL " ,
2018-12-21 17:35:10 +00:00
'statementClass' => \JKingWeb\Arsse\Db\MySQL\PDOStatement :: class ,
2018-12-20 23:06:28 +00:00
'resultClass' => \JKingWeb\Arsse\Db\PDOResult :: class ,
'driverClass' => \JKingWeb\Arsse\Db\MySQL\PDODriver :: class ,
'stringOutput' => true ,
'interfaceConstructor' => function () {
try {
$dsn = [];
$params = [
'charset' => " utf8mb4 " ,
'host' => Arsse :: $conf -> dbMySQLHost ,
'port' => Arsse :: $conf -> dbMySQLPort ,
'dbname' => Arsse :: $conf -> dbMySQLDb ,
];
foreach ( $params as $k => $v ) {
$dsn [] = " $k = $v " ;
}
$dsn = " mysql: " . implode ( " ; " , $dsn );
return new \PDO ( $dsn , Arsse :: $conf -> dbMySQLUser , Arsse :: $conf -> dbMySQLPass , [ \PDO :: ATTR_ERRMODE => \PDO :: ERRMODE_EXCEPTION , \PDO :: MYSQL_ATTR_MULTI_STATEMENTS => false , \PDO :: MYSQL_ATTR_INIT_COMMAND => " SET sql_mode = ' " . \JKingWeb\Arsse\Db\MySQL\PDODriver :: SQL_MODE . " ' " ,]);
} catch ( \Throwable $e ) {
return ;
}
},
'truncateFunction' => $mysqlTruncateFunction ,
'razeFunction' => $mysqlRazeFunction ,
],
2018-11-23 00:55:54 +00:00
];
}
}