Autoloader updates, read env to class, DB IO update

Move autolaoder loading from config.master.php to config.php and before
we read config.master.php
The read env function has moved into a class and is launched after the
auto loader has been loaded

DB IO class update with better error reporting with last error set and
error history of all errors in order.
TODO: per query or per action error grouping
This commit is contained in:
Clemens Schwaighofer
2022-02-24 19:59:47 +09:00
parent 0dc57564c5
commit 51a0276268
14 changed files with 621 additions and 219 deletions

View File

@@ -15,8 +15,51 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsDBIOTest extends TestCase
{
private static $db_config = [];
private static $log;
public static function setUpBeforeClass(): void
{
// define basic connection set valid and one invalid
self::$db_config = [
// self localhost/ip connection
'valid' => [
'db_name' => 'corelibs_db_io_test',
'db_user' => 'corelibs_db_io_test',
'db_pass' => 'corelibs_db_io_test',
'db_host' => 'localhost',
'db_port' => 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
'invalid' => [
'db_name' => '',
'db_user' => '',
'db_pass' => '',
'db_host' => '',
'db_port' => 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
];
self::$log = new \CoreLibs\Debug\Logging([
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-DB-IO-Test',
'debug_all' => false,
'echo_all' => false,
'print_all' => false,
]);
}
/**
* Undocumented function
* Check that we can actually do these tests
*
* @return void
*/
@@ -27,10 +70,55 @@ final class CoreLibsDBIOTest extends TestCase
'The PgSQL extension is not available.'
);
}
// print_r(self::$db_config);
}
// - connect to DB test (getConnectionStatus)
// - connected get dbInfo data check (show true, false)
// - disconnect: dbClose
// - connected get all default settings via get
// dbGetDebug, dbGetMaxQueryCall, dbGetSchema, dbGetEncoding,
// dbVerions, dbCompareVersion
// dbGetSetting (name, user, ecnoding, schema, host, port, ssl, debug, password)
// - connected set
// dbSetMaxQueryCall, dbSetDebug, dbToggleDebug, dbSetSchema, dbSetEncoding
// - db execution tests
// dbReturn, dbDumpData, dbCacheReset, dbExec, dbExecAsync, dbCheckAsync
// dbFetchArray, dbReturnRow, dbReturnArray, dbCursorPos, dbCursorNumRows,
// dbShowTableMetaData, dbPrepare, dbExecute
// dbEscapeString, dbEscapeLiteral, dbEscapeBytea, dbSqlEscape, dbArrayParse
// - complex write sets
// dbWriteData, dbWriteDataExt
// - non connection tests
// dbBoolean, dbTimeFormat
// - internal read data (post exec)
// dbGetReturning, dbGetInsertPKName, dbGetInsertPK, dbGetReturningExt,
// dbGetReturningArray, dbGetCursorExt, dbGetNumRows,
// getHadError, getHadWarning,
// dbResetQueryCalled, dbGetQueryCalled
// - deprecated tests
// getInsertReturn, getReturning, getInsertPK, getReturningExt,
// getCursorExt, getNumRows
public function testConnection()
{
//
$db = new \CoreLibs\DB\IO(
self::$db_config['invalid'],
self::$log
);
print "INIT ERROR INVALID: " . $db->getConnectionStatus() . "\n";
print "LAST ERROR: " . $db->dbGetLastError(true) . "\n";
print "ERRORS: " . print_r($db->dbGetErrorHistory(true), true) . "\n";
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
print "INIT ERROR VALID: " . $db->getConnectionStatus() . "\n";
}
/**
* Undocumented function
* grouped DB IO test
*
* @testdox DB\IO Class tests
*

3
4dev/tests/log/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*log
*LOG
!.gitignore

View File

@@ -92,7 +92,7 @@ print "DIRECT INSERT PREVIOUS INSERTED: "
// PREPARED INSERT
$db->dbPrepare("ins_test_foo", "INSERT INTO test_foo (test) VALUES ($1) RETURNING test");
$status = $db->dbExecute("ins_test_foo", array('BAR TEST ' . time()));
$status = $db->dbExecute("ins_test_foo", ['BAR TEST ' . time()]);
print "PREPARE INSERT[ins_test_foo] STATUS: " . Support::printToString($status) . " | "
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
@@ -245,11 +245,24 @@ $query = "SELECT type, sdate, integer FROM foobar";
$data = $db->dbReturnArray($query, true);
print "Full foobar list: <br><pre>" . print_r($data, true) . "</pre><br>";
// trigger a warning
print "<b>WARNING NEXT</b><br>";
// trigger an error
print "<b>ERROR NEXT</b><br>";
$query = "INSERT invalid FROM invalid";
$data = $db->dbReturnArray($query);
print "ERROR (INS ON dbExec): <pre>" . print_r($db->dbGetErrorHistory(true), true) . "</pre><br>";
$query = "SELECT invalid FROM invalid";
$data = $db->dbReturnArray($query);
print "ERROR (HARD ERROR): <pre>" . print_r($db->dbGetErrorHistory(true), true) . "</pre><br>";
// how to handle HARD errors
# async test queries
/*
$db->dbExecAsync("SELECT test FROM test_foo, (SELECT pg_sleep(10)) as sub WHERE test_foo_id IN (27, 50, 67, 44, 10)");
echo "WAITING FOR ASYNC: ";
$chars = array('|', '/', '-', '\\');
$chars = ['|', '/', '-', '\\'];
while (($ret = $db->dbCheckAsync()) === true)
{
if ((list($_, $char) = each($chars)) === FALSE)

View File

@@ -67,6 +67,7 @@ print '<div><a href="class_test.array.php">Class Test: ARRAY HANDLER</a></div>';
print '<div><a href="class_test.file.php">Class Test: FILE</a></div>';
print '<div><a href="class_test.randomkey.php">Class Test: RANDOM KEY</a></div>';
print '<div><a href="class_test.system.php">Class Test: SYSTEM</a></div>';
print '<div><a href="class_test.readenvfile.php">Class Test: READ ENV FILE</a></div>';
print '<div><a href="class_test.runningtime.php">Class Test: RUNNING TIME</a></div>';
print '<div><a href="class_test.debug.php">Class Test: DEBUG</a></div>';
print '<div><a href="class_test.output.form.php">Class Test: OUTPUT FORM</a></div>';

View File

@@ -0,0 +1,50 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
// basic class test file
define('USE_DATABASE', false);
// sample config
require 'config.php';
// set session name
if (!defined('SET_SESSION_NAME')) {
define('SET_SESSION_NAME', EDIT_SESSION_NAME);
}
// define log file id
$LOG_FILE_ID = 'classTest-readEnvFile';
ob_end_flush();
$log = new CoreLibs\Debug\Logging([
'log_folder' => BASE . LOG,
'file_id' => $LOG_FILE_ID,
// add file date
'print_file_date' => true,
// set debug and print flags
'debug_all' => $DEBUG_ALL ?? false,
'echo_all' => $ECHO_ALL ?? false,
'print_all' => $PRINT_ALL ?? false,
]);
$ref_class = 'CoreLibs\Get\ReadEnvFile';
print "<html><head><title>TEST CLASS: READ ENV FILE</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "<br>";
// test .env in local
// error message
print $log->printErrorMsg();
print "</body></html>";
// __END__

View File

@@ -303,8 +303,11 @@ if (defined('DEBUG') && DEBUG == false) {
}
/************* AUTO LOADER *******************/
// **DEPRECATED** MOVED TO config.php
// read auto loader for lib only
// require BASE . LIB . 'autoloader.php';
// if (is_file(BASE . LIB . 'autoloader.php')) {
// require BASE . LIB . 'autoloader.php';
// }
// composer auto loader, IF composer.json file includes classmap for lib/:
// "autoload": {
// "classmap": [
@@ -313,6 +316,8 @@ if (defined('DEBUG') && DEBUG == false) {
// },
// NOTE: MUST RUN composer dump-autoload if file/class names are changed or added
// NOTE BASE: __DIR__ . DIRECTORY_SEPARATOR . '..' DIRECTORY_SEPARATOR;
require BASE . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
// if (is_file(BASE . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php')) {
// require BASE . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
// }
// __END__

View File

@@ -13,16 +13,36 @@ declare(strict_types=1);
define('CONFIG_PATH', 'configs' . DIRECTORY_SEPARATOR);
// config path prefix search, start with 0, got down each level __DIR__ has, if nothing found -> bail
$CONFIG_PATH_PREFIX = '';
$end_autoload = false;
for ($dir_pos = 0, $dir_max = count(explode(DIRECTORY_SEPARATOR, __DIR__)); $dir_pos <= $dir_max; $dir_pos++) {
$CONFIG_PATH_PREFIX .= '..' . DIRECTORY_SEPARATOR;
if (file_exists($CONFIG_PATH_PREFIX . CONFIG_PATH . 'config.master.php')) {
// check if there is an read env file, load it
if (file_exists($CONFIG_PATH_PREFIX . CONFIG_PATH . 'read_env_file.php')) {
require $CONFIG_PATH_PREFIX . CONFIG_PATH . 'read_env_file.php';
// load env variables first
readEnvFile($CONFIG_PATH_PREFIX . CONFIG_PATH);
if ($end_autoload === false) {
/************* AUTO LOADER *******************/
// read auto loader for lib only
// It is recommended to setup basic composer and use just one auto loader
// if (is_file($CONFIG_PATH_PREFIX . 'lib' . DIRECTORY_SEPARATOR . 'autoloader.php')) {
// require $CONFIG_PATH_PREFIX . 'lib' . DIRECTORY_SEPARATOR . 'autoloader.php';
// $end_autoload = true;
// }
// composer auto loader, IF composer.json file includes classmap for lib/:
// "autoload": {
// "classmap": [
// "lib/"
// ]
// },
// NOTE: MUST RUN composer dump-autoload if file/class names are changed or added
// NOTE BASE: __DIR__ . DIRECTORY_SEPARATOR . '..' DIRECTORY_SEPARATOR;
// load auto loader
if (is_file($CONFIG_PATH_PREFIX . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php')) {
require $CONFIG_PATH_PREFIX . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
$end_autoload = true;
}
// then load master config file that loads all other config files
// load enviorment file if it exists
\CoreLibs\Get\ReadEnvFile::readEnvFile($CONFIG_PATH_PREFIX . CONFIG_PATH);
}
/************* MASTER CONFIG *******************/
if (is_file($CONFIG_PATH_PREFIX . CONFIG_PATH . 'config.master.php')) {
// load master config file that loads all other config files
require $CONFIG_PATH_PREFIX . CONFIG_PATH . 'config.master.php';
break;
}

View File

@@ -1,5 +1,10 @@
<?php
// config.php reads auto loader and uses CoreLibs version of this
// DEPRECATED for here
declare(strict_types=1);
/**
* parses .env file
*

View File

@@ -156,8 +156,7 @@ class ArrayIO extends \CoreLibs\DB\IO
// if not set ... produce error
if (!$this->table_array[$this->pk_name]['value']) {
// if no PK found, error ...
$this->error_id = 91;
$this->__dbError();
$this->dbError(91);
return false;
} else {
return true;
@@ -235,8 +234,7 @@ class ArrayIO extends \CoreLibs\DB\IO
// if 0, error
$this->pk_id = null;
if (!$this->dbExec($q)) {
$this->error_id = 92;
$this->__dbError();
$this->dbError(92);
}
return $this->table_array;
}
@@ -310,8 +308,7 @@ class ArrayIO extends \CoreLibs\DB\IO
// possible dbFetchArray errors ...
$this->pk_id = $this->table_array[$this->pk_name]['value'];
} else {
$this->error_id = 92;
$this->__dbError();
$this->dbError(92);
}
return $this->table_array;
}
@@ -535,8 +532,7 @@ class ArrayIO extends \CoreLibs\DB\IO
}
// return success or not
if (!$this->dbExec($q)) {
$this->error_id = 92;
$this->__dbError();
$this->dbError(92);
}
// set primary key
if ($insert) {

View File

@@ -324,13 +324,17 @@ class IO
private $nbsp = ''; // used by print_array recursion function
// error & warning id
/** @var int */
protected $error_id;
private $error_id;
/** @var int */
private $had_error = 0;
// private $had_error = 0;
/** @var array<int> */
private $error_history = [];
/** @var int */
private $warning_id;
/** @var int */
private $had_warning;
// private $had_warning = 0;
/** @var array<int> */
private $warning_history = [];
// error thrown on class init if we cannot connect to db
/** @var bool */
protected $db_init_error = false;
@@ -411,35 +415,39 @@ class IO
$this->MAX_QUERY_CALL = self::DEFAULT_MAX_QUERY_CALL;
// error & debug stuff, error & warning ids are the same, its just in which var they get written
$this->error_string['10'] = 'Could not load DB interface functions';
$this->error_string['11'] = 'No Querystring given';
$this->error_string['12'] = 'No Cursor given, no correct query perhaps?';
$this->error_string['13'] = 'Query could not be executed without errors';
$this->error_string['14'] = 'Can\'t connect to DB server';
$this->error_string['15'] = 'Can\'t select DB';
$this->error_string['16'] = 'No DB Handler found / connect or reconnect failed';
$this->error_string['17'] = 'All dbReturn* methods work only with SELECT statements, '
. 'please use dbExec for everything else';
$this->error_string['18'] = 'Query not found in cache. Nothing has been reset';
$this->error_string['19'] = 'Wrong PK name given or no PK name given at all, can\'t get Insert ID';
$this->error_string['20'] = 'Found given Prepare Statement Name in array, '
. 'Query not prepared, will use existing one';
$this->error_string['21'] = 'Query Prepare failed';
$this->error_string['22'] = 'Query Execute failed';
$this->error_string['23'] = 'Query Execute failed, data array does not match placeholders';
$this->error_string['24'] = 'Missing prepared query entry for execute.';
$this->error_string['25'] = 'Prepare query data is not in array format.';
$this->error_string['30'] = 'Query call in a possible endless loop. '
. 'Was called more than ' . $this->MAX_QUERY_CALL . ' times';
$this->error_string['31'] = 'Could not fetch PK after query insert';
$this->error_string['32'] = 'Multiple PK return as array';
$this->error_string['33'] = 'Returning PK was not found';
$this->error_string['34'] = 'Cursor invalid for fetch PK after query insert';
$this->error_string['40'] = 'Query async call failed.';
$this->error_string['41'] = 'Connection is busy with a different query. Cannot execute.';
$this->error_string['42'] = 'Cannot check for async query, none has been started yet.';
$this->error_string['50'] = 'Setting max query call to -1 will disable loop protection for all subsequent runs';
$this->error_string['51'] = 'Max query call needs to be set to at least 1';
$this->error_string = [
'10' => 'Could not load DB interface functions',
'11' => 'No Querystring given',
'12' => 'No Cursor given, no correct query perhaps?',
'13' => 'Query could not be executed without errors',
'14' => 'Can\'t connect to DB server',
'15' => 'Can\'t select DB or no db name given',
'16' => 'No DB Handler found / connect or reconnect failed',
'17' => 'All dbReturn* methods work only with SELECT statements, '
. 'please use dbExec for everything else',
'18' => 'Query not found in cache. Nothing has been reset',
'19' => 'Wrong PK name given or no PK name given at all, can\'t '
. 'get Insert ID',
'20' => 'Found given Prepare Statement Name in array, '
. 'Query not prepared, will use existing one',
'21' => 'Query Prepare failed',
'22' => 'Query Execute failed',
'23' => 'Query Execute failed, data array does not match placeholders',
'24' => 'Missing prepared query entry for execute.',
'25' => 'Prepare query data is not in array format.',
'30' => 'Query call in a possible endless loop. '
. 'Was called more than ' . $this->MAX_QUERY_CALL . ' times',
'31' => 'Could not fetch PK after query insert',
'32' => 'Multiple PK return as array',
'33' => 'Returning PK was not found',
'34' => 'Cursor invalid for fetch PK after query insert',
'40' => 'Query async call failed.',
'41' => 'Connection is busy with a different query. Cannot execute.',
'42' => 'Cannot check for async query, none has been started yet.',
'50' => 'Setting max query call to -1 will disable loop protection '
. 'for all subsequent runs',
'51' => 'Max query call needs to be set to at least 1',
];
// based on $this->db_type
// here we need to load the db pgsql include one
@@ -450,15 +458,13 @@ class IO
$this->db_functions = new \CoreLibs\DB\SQL\PgSQL();
} else {
// abort error
$this->error_id = 10;
$this->__dbError();
$this->dbError(10);
$this->db_init_error = true;
}
// connect to DB
if (!$this->__connectToDB()) {
$this->error_id = 16;
$this->__dbError();
$this->dbError(16);
$this->db_init_error = true;
}
}
@@ -482,6 +488,11 @@ class IO
*/
private function __connectToDB(): bool
{
// no DB name set, abort
if (empty($this->db_name)) {
$this->dbError(15);
return false;
}
// generate connect string
$this->dbh = $this->db_functions->__dbConnect(
$this->db_host,
@@ -493,17 +504,15 @@ class IO
);
// if no dbh here, we couldn't connect to the DB itself
if (!$this->dbh) {
$this->error_id = 14;
$this->__dbError();
$this->dbError(14);
return false;
}
// 15 error (cant select to DB is not valid in postgres, as connect is different)
// if returns 0 we couldn't select the DB
if ($this->dbh == -1) {
$this->error_id = 15;
$this->__dbError();
return false;
}
// if ($this->dbh == -1) {;
// $this->dbError(15));
// return false;
// }
// set search path if needed
if ($this->db_schema) {
$this->dbSetSchema();
@@ -614,8 +623,13 @@ class IO
* @param string $type query identifier (Q, I, etc)
* @return void has no return
*/
protected function __dbDebug(string $debug_id, string $error_string, string $id = '', string $type = ''): void
{
protected function __dbDebug(
string $debug_id,
string $error_string,
string $id = '',
string $type = ''
): void {
// NOTE prefix allows html for echo output, will be stripped on file print
$prefix = '';
if ($id) {
$prefix .= '[<span style="color: #920069;">' . $id . '</span>] ';
@@ -625,7 +639,7 @@ class IO
}
switch ($id) {
case 'DB_ERROR':
$prefix .= '<span style="color: d;"><b>DB-Error</b>:</span>';
$prefix .= '<span style="color: red;"><b>DB-Error</b>:</span>';
break;
case 'DB_WARNING':
$prefix .= '<span style="color: orange;"><b>DB-Warning</b>:</span>';
@@ -638,16 +652,13 @@ class IO
}
/**
* if error_id set, writes long error string into error_msg
* NOTE:
* needed to make public so it can be called from DB.Array.IO too
* @param object|resource|bool $cursor current cursor for pg_result_error,
* pg_last_error too, but pg_result_error
* is more accurate (PgSql\Result)
* @param string $msg optional message
* @return void has no return
* Check if there is a cursor and write this cursors error info
* @param object|resource|bool $cursor current cursor for pg_result_error,
* pg_last_error too, but pg_result_error
* is more accurate (PgSql\Result)
* @return string|null If we could get the method where it was called
*/
public function __dbError($cursor = false, string $msg = ''): void
private function __dbErrorPreprocessor($cursor = false): ?string
{
$pg_error_string = '';
$where_called = (string)\CoreLibs\Debug\Support::getCallerMethod();
@@ -660,32 +671,52 @@ class IO
if ($pg_error_string) {
$this->__dbDebug('db', $pg_error_string, 'DB_ERROR', $where_called);
}
// okay, an error occured
if ($this->error_id) {
// write error msg ...
$this->__dbDebug(
'db',
$this->error_id . ': ' . $this->error_string[$this->error_id]
. ($msg ? ', ' . $msg : ''),
'DB_ERROR',
$where_called
);
$this->had_error = $this->error_id;
// write detailed error log
}
if ($this->warning_id) {
$this->__dbDebug(
'db',
$this->warning_id . ': ' . $this->error_string[$this->warning_id]
. ($msg ? ', ' . $msg : ''),
'DB_WARNING',
$where_called
);
$this->had_warning = $this->warning_id;
}
// unset the error/warning vars
$this->error_id = 0;
$this->warning_id = 0;
return $where_called;
}
/**
* write an error
* @param integer $error_id Any Error ID, used in debug message string
* @param object|resource|bool $cursor Optional cursor, passed on to preprocessor
* @param string $msg optional message added to debug
* @return void
*/
protected function dbError(int $error_id, $cursor = false, string $msg = ''): void
{
$where_called = $this->__dbErrorPreprocessor($cursor);
// write error msg ...
$this->__dbDebug(
'db',
$error_id . ': ' . $this->error_string[$error_id]
. ($msg ? ', ' . $msg : ''),
'DB_ERROR',
$where_called
);
$this->error_id = $error_id;
// keep warning history
$this->error_history[] = $error_id;
}
/**
* write a warning
* @param integer $warning_id Integer warning id added to debug
* @param object|resource|bool $cursor Optional cursor, passed on to preprocessor
* @param string $msg optional message added to debug
* @return void
*/
protected function dbWarning(int $warning_id, $cursor = false, string $msg = ''): void
{
$where_called = $this->__dbErrorPreprocessor($cursor);
$this->__dbDebug(
'db',
$warning_id . ': ' . $this->error_string[$warning_id]
. ($msg ? ', ' . $msg : ''),
'DB_WARNING',
$where_called
);
$this->warning_id = $warning_id;
// keep warning history
$this->warning_history[] = $warning_id;
}
/**
@@ -775,23 +806,20 @@ class IO
$this->query = $query;
}
if (!$this->query) {
$this->error_id = 11;
$this->__dbError();
$this->dbError(11);
return false;
}
// if no DB Handler drop out
if (!$this->dbh) {
// if reconnect fails drop out
if (!$this->__connectToDB()) {
$this->error_id = 16;
$this->__dbError();
$this->dbError(16);
return false;
}
}
// check that no other query is running right now
if ($this->db_functions->__dbConnectionBusy()) {
$this->error_id = 41;
$this->__dbError();
$this->dbError(41);
return false;
}
// if we do have an insert, check if there is no RETURNING pk_id,
@@ -850,8 +878,7 @@ class IO
$this->MAX_QUERY_CALL != -1 &&
$this->query_called[$md5] > $this->MAX_QUERY_CALL
) {
$this->error_id = 30;
$this->__dbError();
$this->dbError(30);
$this->__dbDebug('db', $this->query, 'dbExec', 'Q[nc]');
return false;
}
@@ -874,8 +901,7 @@ class IO
$this->__dbDebug('db', $this->query, 'dbExec', 'Q[nc]');
}
// internal error handling
$this->error_id = 13;
$this->__dbError($this->cursor);
$this->dbError(13, $this->cursor);
return false;
} else {
// if SELECT do here ...
@@ -947,11 +973,10 @@ class IO
// abort if cursor is empty
if ($cursor === false) {
// failed to get insert id
$this->warning_id = 34;
if ($stm_name === null) {
$this->__dbError($cursor, '[dbExec]');
$this->dbWarning(34, $cursor, '[dbExec]');
} else {
$this->__dbError();
$this->dbWarning(34);
$this->__dbDebug(
'db',
$stm_name . ': CURSOR is null',
@@ -968,8 +993,7 @@ class IO
$this->insert_id_arr[] = $insert_id;
// throw warning that no pk was found
if ($insert_id === false) {
$this->warning_id = 31;
$this->__dbError($cursor, '[dbExec]');
$this->dbWarning(31, $cursor, '[dbExec]');
}
} else { // was stm_name null or not null and cursor
// we have returning, now we need to check if we get one or many returned
@@ -985,11 +1009,10 @@ class IO
// warning if we didn't get any returning data
if (count($this->insert_id_arr) == 0) {
// failed to get insert id
$this->warning_id = 33;
if ($stm_name === null) {
$this->__dbError($cursor, '[dbExec]');
$this->dbWarning(33, $cursor, '[dbExec]');
} else {
$this->__dbError();
$this->dbWarning(33);
$this->__dbDebug(
'db',
$stm_name . ': RETURNING returned no data',
@@ -1000,9 +1023,9 @@ class IO
// this error handling is only for INSERT (), (), ... sets
$this->warning_id = 32;
if ($stm_name === null) {
$this->__dbError($cursor, '[dbExec]');
$this->dbWarning(32, $cursor, '[dbExec]');
} else {
$this->__dbError();
$this->dbWarning(32);
$this->__dbDebug(
'db',
$stm_name . ': RETURNING returned an array (possible multiple insert)',
@@ -1393,8 +1416,7 @@ class IO
public function dbReturn(string $query, int $reset = self::USE_CACHE, bool $assoc_only = false)
{
if (!$query) {
$this->error_id = 11;
$this->__dbError();
$this->dbError(11);
return false;
}
// create MD5 from query ...
@@ -1416,8 +1438,7 @@ class IO
$this->cursor_ext[$md5]['query'] = $query;
// before doing ANYTHING check if query is "SELECT ..." everything else does not work
if (!$this->__checkQueryForSelect($this->cursor_ext[$md5]['query'])) {
$this->error_id = 17;
$this->__dbError(false, $this->cursor_ext[$md5]['query']);
$this->dbError(17, false, $this->cursor_ext[$md5]['query']);
return false;
}
// init return als false
@@ -1441,15 +1462,13 @@ class IO
if (!$this->dbh) {
// if reconnect fails drop out
if (!$this->__connectToDB()) {
$this->error_id = 16;
$this->__dbError();
$this->dbError(16);
return false;
}
}
// check that no other query is running right now
if ($this->db_functions->__dbConnectionBusy()) {
$this->error_id = 41;
$this->__dbError();
$this->dbError(41);
return false;
}
$this->cursor_ext[$md5]['cursor'] = $this->db_functions->__dbQuery($this->cursor_ext[$md5]['query']);
@@ -1459,8 +1478,7 @@ class IO
$this->__dbDebug('db', $this->cursor_ext[$md5]['query'], 'dbReturn', 'Q');
}
// internal error handling
$this->error_id = 13;
$this->__dbError($this->cursor_ext[$md5]['cursor']);
$this->dbError(13, $this->cursor_ext[$md5]['cursor']);
return false;
} else {
$this->cursor_ext[$md5]['firstcall'] = 1;
@@ -1613,10 +1631,11 @@ class IO
}
// ** actual db exec call
$cursor = $this->db_functions->__dbQuery($this->query);
// if we faield, just set the master cursors to false too
$this->cursor = $cursor;
if ($cursor === false) {
return false;
}
$this->cursor = $cursor;
// if FALSE returned, set error stuff
// run the post exec processing
if (!$this->__dbPostExec()) {
@@ -1645,8 +1664,7 @@ class IO
$cursor = $this->cursor;
}
if ($cursor === false) {
$this->error_id = 12;
$this->__dbError();
$this->dbError(12);
return false;
}
return $this->__dbConvertEncoding(
@@ -1666,17 +1684,18 @@ class IO
public function dbReturnRow(string $query, bool $assoc_only = false)
{
if (!$query) {
$this->error_id = 11;
$this->__dbError();
$this->dbError(11);
return false;
}
// before doing ANYTHING check if query is "SELECT ..." everything else does not work
if (!$this->__checkQueryForSelect($query)) {
$this->error_id = 17;
$this->__dbError(false, $query);
$this->dbError(17, false, $query);
return false;
}
$cursor = $this->dbExec($query);
if ($cursor === false) {
$this->dbError(13);
}
$result = $this->dbFetchArray($cursor, $assoc_only);
return $result;
}
@@ -1690,17 +1709,19 @@ class IO
public function dbReturnArray(string $query, bool $assoc_only = false)
{
if (!$query) {
$this->error_id = 11;
$this->__dbError();
$this->dbError(11);
return false;
}
// before doing ANYTHING check if query is "SELECT ..." everything else does not work
if (!$this->__checkQueryForSelect($query)) {
$this->error_id = 17;
$this->__dbError(false, $query);
$this->dbError(17, false, $query);
return false;
}
$cursor = $this->dbExec($query);
if ($cursor === false) {
$this->dbError(13);
return false;
}
$rows = [];
while (is_array($res = $this->dbFetchArray($cursor, $assoc_only))) {
$data = [];
@@ -1726,8 +1747,7 @@ class IO
$md5 = md5($query);
// clears cache for this query
if (!$this->cursor_ext[$md5]['query']) {
$this->error_id = 18;
$this->__dbError();
$this->dbError(18);
return false;
}
unset($this->cursor_ext[$md5]);
@@ -1746,8 +1766,7 @@ class IO
public function dbCursorPos(string $query)
{
if (!$query) {
$this->error_id = 11;
$this->__dbError();
$this->dbError(11);
return false;
}
$md5 = md5($query);
@@ -1762,8 +1781,7 @@ class IO
public function dbCursorNumRows(string $query)
{
if (!$query) {
$this->error_id = 11;
$this->__dbError();
$this->dbError(11);
return false;
}
$md5 = md5($query);
@@ -1818,23 +1836,20 @@ class IO
{
$matches = [];
if (!$query) {
$this->error_id = 11;
$this->__dbError();
$this->dbError(11);
return false;
}
// if no DB Handler drop out
if (!$this->dbh) {
// if reconnect fails drop out
if (!$this->__connectToDB()) {
$this->error_id = 16;
$this->__dbError();
$this->dbError(16);
return false;
}
}
// check that no other query is running right now
if ($this->db_functions->__dbConnectionBusy()) {
$this->error_id = 41;
$this->__dbError();
$this->dbError(41);
return false;
}
// check if this was already prepared
@@ -1884,15 +1899,18 @@ class IO
$this->prepare_cursor[$stm_name]['result'] = $result;
return $result;
} else {
$this->error_id = 21;
$this->__dbError();
$this->__dbDebug(
$this->dbError(
21,
null,
$stm_name . ': Prepare field with: ' . $stm_name . ' | ' . $query
);
/* $this->__dbDebug(
'db',
'<span style="color: red;"><b>DB-Error</b> ' . $stm_name
. ': Prepare field with: ' . $stm_name . ' | '
. $query . '</span>',
'DB_ERROR'
);
); */
return $result;
}
} else {
@@ -1912,34 +1930,29 @@ class IO
{
// if we do not have no prepare cursor array entry for this statement name, abort
if (!is_array($this->prepare_cursor[$stm_name])) {
$this->error_id = 24;
$this->__dbDebug(
'db',
'<span style="color: red;"><b>DB-Error</b> ' . $stm_name
. ': We do not have a prepared query entry for this statement name.</span>',
'DB_ERROR'
$this->dbError(
24,
false,
$stm_name . ': We do not have a prepared query entry for this statement name.'
);
return false;
}
if (!is_array($data)) {
$this->error_id = 25;
$this->__dbDebug(
'db',
'<span style="color: red;"><b>DB-Error</b> ' . $stm_name
. ': Prepared query Data has to be given in array form.</span>',
'DB_ERROR'
$this->dbError(
25,
false,
$stm_name . ': Prepared query Data has to be given in array form.'
);
return false;
}
if ($this->prepare_cursor[$stm_name]['count'] != count($data)) {
$this->error_id = 23;
$this->__dbDebug(
'db',
'<span style="color: red;"><b>DB-Error</b> ' . $stm_name
. ': Array data count does not match prepared fields. Need: '
. $this->prepare_cursor[$stm_name]['count'] . ', has: '
. count($data) . '</span>',
'DB_ERROR'
$this->dbError(
23,
false,
$stm_name
. ': Array data count does not match prepared fields. Need: '
. $this->prepare_cursor[$stm_name]['count'] . ', has: '
. count($data)
);
return false;
}
@@ -1951,13 +1964,10 @@ class IO
$this->log->debug('ExecuteData', 'ERROR in STM[' . $stm_name . '|'
. $this->prepare_cursor[$stm_name]['result'] . ']: '
. $this->log->prAr($data));
$this->error_id = 22;
$this->__dbError($this->prepare_cursor[$stm_name]['result']);
$this->__dbDebug(
'db',
'<span style="color: red;"><b>DB-Error</b> ' . $stm_name
. ': Execution failed</span>',
'DB_ERROR'
$this->dbError(
22,
$this->prepare_cursor[$stm_name]['result'],
$stm_name . ': Execution failed'
);
return false;
}
@@ -2006,8 +2016,7 @@ class IO
// run the async query
if (!$this->db_functions->__dbSendQuery($this->query)) {
// if failed, process here
$this->error_id = 40;
$this->__dbError();
$this->dbError(40);
return false;
} else {
$this->async_running = (string)$md5;
@@ -2046,12 +2055,10 @@ class IO
}
} else {
// if no async running print error
$this->error_id = 42;
$this->__dbDebug(
'db',
'<span style="color: red;"><b>DB-Error</b> No async query '
. 'has beedbFetchArrayn started yet.</span>',
'DB_ERROR'
$this->dbError(
42,
false,
'No async query has been started yet.'
);
return false;
}
@@ -2301,13 +2308,11 @@ class IO
// if -1 then disable loop check
// DANGEROUS, WARN USER
if ($max_calls == -1) {
$this->warning_id = 50;
$this->__dbError();
$this->dbWarning(50);
}
// negative or 0
if ($max_calls < -1 || $max_calls == 0) {
$this->error_id = 51;
$this->__dbError();
$this->dbError(51);
// early abort
return false;
}
@@ -2521,37 +2526,112 @@ class IO
* null on empty
* @return int|null Number of rows
*/
public function dbGetNumRows()
public function dbGetNumRows(): ?int
{
return $this->num_rows;
}
/**
* Sets error number that was last
* So we always have the last error number stored even if a new one is created
*
* @return int last error number
* So we always have the last error number stored even if a new
* one is created
* @param boolean $transform Set to true to transform into id + error message
* @return string Last error number as string or error message
*/
public function getHadError()
public function dbGetLastError(bool $transform = false): string
{
return $this->had_error;
if (!$transform) {
return (string)$this->error_id;
} else {
return $this->error_id . ': '
. ($this->error_string[$this->error_id] ?? '[NO ERROR MESSAGE]');
}
}
/**
* Return the error history as is or map it to the error messages
* @param boolean $transform Set to true to transform into id + error message
* @return array Either as is, or id + error message array
*/
public function dbGetErrorHistory(bool $transform = false): array
{
if (!$transform) {
return $this->error_history;
} else {
return array_map(
function ($id) {
return $id . ': '
. ($this->error_string[$id] ?? '[NO ERROR MESSAGE]');
},
$this->error_history
);
}
}
/**
* Sets warning number that was last
* So we always have the last warning number stored even if a new one is created
*
* @return int last error number
* @param boolean $transform Set to true to transform into id + warning message
* @return string Last Warning number as string or warning message
*/
public function getHadWarning()
public function dbGetLastWarning(bool $transform = false)
{
return $this->had_warning;
if (!$transform) {
return (string)$this->warning_id;
} else {
return $this->warning_id . ': '
. ($this->error_string[$this->warning_id] ?? '[NO WARNING MESSAGE]');
}
}
/**
* Return the warning history as is or map it to the error messages
* @param boolean $transform Set to true to transform into id + warning message
* @return array Either as is, or id + warning message array
*/
public function dbGetWarningHistory(bool $transform = false): array
{
if (!$transform) {
return $this->warning_history;
} else {
return array_map(
function ($id) {
return $id . ': '
. ($this->error_string[$id] ?? '[NO WARNING MESSAGE]');
},
$this->warning_history
);
}
}
/******************************** */
// DEPEREACTED CALLS
/******************************** */
/**
* Sets error number that was last
* So we always have the last error number stored even if a new one is created
* @return int last error number
* @deprecated Use dbGetLastError()
*/
public function getHadError(): int
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetLastError()', E_USER_DEPRECATED);
return (int)$this->dbGetLastError();
}
/**
* Sets warning number that was last
* So we always have the last warning number stored even if a new one is created
* @return int last error number
* @deprecated Use dbGetLastWarning()
*/
public function getHadWarning(): int
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetLastWarning()', E_USER_DEPRECATED);
return (int)$this->dbGetLastWarning();
}
/**
* old call for getInserReturnExt
* @param string|null $key See above

View File

@@ -80,8 +80,8 @@ class PgSQL
}
/**
* wrapper for gp_query, catches error and stores it in class var
* @param string $query query string
* wrapper for pg_query, catches error and stores it in class var
* @param string $query Query string
* @return object|resource|bool query result (PgSql\Result)
*/
public function __dbQuery(string $query)
@@ -98,6 +98,30 @@ class PgSQL
return $result;
}
/**
* Proposed
* wrapperf or pg_query_params for queries in the style of
* SELECT foo FROM bar WHERE foobar = $1
* @param string $query Query string with placeholders $1, ..
* @param array $params matching parameters for each placerhold
* @return object|resource|bool query result (PgSql\Result)
*/
public function __dbQueryParams(string $query, array $params)
{
$this->last_error_query = '';
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
// parse query and get all $n entries
// TODO count of $n must match params
// read out the query status and save the query if needed
$result = pg_query_params($this->dbh, $query, $params);
if ($result === false) {
$this->last_error_query = $query;
}
return $result;
}
/**
* sends an async query to the server
* @param string $query query string
@@ -397,22 +421,43 @@ class PgSQL
* @param string $db_name databse name
* @param integer $db_port port (int, 5432 is default)
* @param string $db_ssl SSL (allow is default)
* @return object|resource|bool db handler PgSql\Connection or false on error
* @return object|resource|bool db handler PgSql\Connection or false on error
*/
public function __dbConnect(
string $db_host,
string $db_user,
string $db_pass,
string $db_name,
int $db_port = 5432,
int $db_port,
string $db_ssl = 'allow'
) {
// to avoid empty db_port
if (!$db_port) {
$db_port = 5432;
if (empty($db_name)) {
return false;
}
$this->dbh = pg_connect("host=" . $db_host . " port=" . $db_port . " user="
. $db_user . " password=" . $db_pass . " dbname=" . $db_name . " sslmode=" . $db_ssl);
// if there is no host, leave it empty, this will try default unix path
// same for port (defaults to 5432 if not set)
// must set is db name
// if no user name, db name is used
$connection_string = [];
if (!empty($db_host)) {
$connection_string[] = 'host=' . $db_host;
}
if (!empty($db_port)) {
$connection_string[] = 'port=' . $db_port;
}
if (!empty($db_user)) {
$connection_string[] = 'user=' . $db_user;
}
if (!empty($db_pass)) {
$connection_string[] = 'password=' . $db_pass;
}
// we must have at least db name set
$connection_string[] = 'dbname=' . $db_name;
if (!empty($db_ssl)) {
$connection_string[] = 'sslmode=' . $db_ssl;
}
// connect
$this->dbh = pg_connect(join(' ', $connection_string));
// if (!$this->dbh) {
// die("<!-- Can't connect to database //-->");
// }
@@ -438,8 +483,8 @@ class PgSQL
$cursor = pg_get_result($this->dbh);
}
if ($cursor && !is_bool($cursor) && $error_str = pg_result_error($cursor)) {
return "<span style=\"color: red;\"><b>-PostgreSQL-Error-></b> "
. $error_str . "</span><br>";
return '-PostgreSQL-Error- '
. $error_str;
} else {
return '';
}

View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Get;
class ReadEnvFile
{
/**
* parses .env file
*
* Rules for .env file
* variable is any alphanumeric string followed by = on the same line
* content starts with the first non space part
* strings can be contained in "
* strings MUST be contained in " if they are multiline
* if string starts with " it will match until another " is found
* anything AFTER " is ignored
* if there are two variables with the same name only the first is used
* variables are case sensitive
*
* @param string $path Folder to file, default is __DIR__
* @param string $env_file What file to load, default is .env
* @return int -1 other error
* 0 for success full load
* 1 for file loadable, but no data inside
* 2 for file not readable
* 3 for file not found
*/
public static function readEnvFile(
string $path = __DIR__,
string $env_file = '.env'
): int {
// default -1;
$status = -1;
$env_file_target = $path . DIRECTORY_SEPARATOR . $env_file;
// this is not a file -> abort
if (!is_file($env_file_target)) {
$status = 3;
return $status;
}
// cannot open file -> abort
if (($fp = fopen($env_file_target, 'r')) === false) {
$status = 2;
return $status;
}
// set to readable but not yet any data loaded
$status = 1;
$block = false;
$var = '';
while ($line = fgets($fp)) {
// main match for variable = value part
if (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) {
$var = $matches[1];
$value = $matches[2];
$quotes = $matches[3];
// wirte only if env is not set yet, and write only the first time
if (empty($_ENV[$var])) {
if (!empty($quotes)) {
// match greedy for first to last so we move any " if there are
if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) {
$value = $matches[1];
} else {
// this is a multi line
$block = true;
// first " in string remove
// add removed new line back because this is a multi line
$value = ltrim($value, '"') . PHP_EOL;
}
}
// if block is set, we strip line of slashes
$_ENV[$var] = $block === true ? stripslashes($value) : $value;
// set successful load
$status = 0;
}
} elseif ($block === true) {
// read line until there is a unescaped "
// this also strips everything after the last "
if (preg_match("/(.*[^\\\])\"/", $line, $matches)) {
$block = false;
// strip ending " and EVERYTHING that follows after that
$line = $matches[1];
}
// strip line of slashes
$_ENV[$var] .= stripslashes($line);
}
}
fclose($fp);
return $status;
}
}
// __END__

View File

@@ -35,6 +35,7 @@ return array(
'CoreLibs\\Debug\\Logging' => $baseDir . '/lib/CoreLibs/Debug/Logging.php',
'CoreLibs\\Debug\\RunningTime' => $baseDir . '/lib/CoreLibs/Debug/RunningTime.php',
'CoreLibs\\Debug\\Support' => $baseDir . '/lib/CoreLibs/Debug/Support.php',
'CoreLibs\\Get\\ReadEnvFile' => $baseDir . '/lib/CoreLibs/Get/ReadEnvFile.php',
'CoreLibs\\Get\\System' => $baseDir . '/lib/CoreLibs/Get/System.php',
'CoreLibs\\Language\\Core\\CachedFileReader' => $baseDir . '/lib/CoreLibs/Language/Core/CachedFileReader.php',
'CoreLibs\\Language\\Core\\FileReader' => $baseDir . '/lib/CoreLibs/Language/Core/FileReader.php',

View File

@@ -100,6 +100,7 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9
'CoreLibs\\Debug\\Logging' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/Logging.php',
'CoreLibs\\Debug\\RunningTime' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/RunningTime.php',
'CoreLibs\\Debug\\Support' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/Support.php',
'CoreLibs\\Get\\ReadEnvFile' => __DIR__ . '/../..' . '/lib/CoreLibs/Get/ReadEnvFile.php',
'CoreLibs\\Get\\System' => __DIR__ . '/../..' . '/lib/CoreLibs/Get/System.php',
'CoreLibs\\Language\\Core\\CachedFileReader' => __DIR__ . '/../..' . '/lib/CoreLibs/Language/Core/CachedFileReader.php',
'CoreLibs\\Language\\Core\\FileReader' => __DIR__ . '/../..' . '/lib/CoreLibs/Language/Core/FileReader.php',