diff --git a/4dev/tests/CoreLibsDBIOTest.php b/4dev/tests/CoreLibsDBIOTest.php index f75b6b0f..89d8cb8e 100644 --- a/4dev/tests/CoreLibsDBIOTest.php +++ b/4dev/tests/CoreLibsDBIOTest.php @@ -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 * diff --git a/4dev/tests/log/.gitignore b/4dev/tests/log/.gitignore new file mode 100644 index 00000000..2a50fc00 --- /dev/null +++ b/4dev/tests/log/.gitignore @@ -0,0 +1,3 @@ +*log +*LOG +!.gitignore diff --git a/www/admin/class_test.db.php b/www/admin/class_test.db.php index 23341663..811d9b03 100644 --- a/www/admin/class_test.db.php +++ b/www/admin/class_test.db.php @@ -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:
" . print_r($data, true) . "

"; +// trigger a warning +print "WARNING NEXT
"; +// trigger an error +print "ERROR NEXT
"; +$query = "INSERT invalid FROM invalid"; +$data = $db->dbReturnArray($query); +print "ERROR (INS ON dbExec):
" . print_r($db->dbGetErrorHistory(true), true) . "

"; +$query = "SELECT invalid FROM invalid"; +$data = $db->dbReturnArray($query); +print "ERROR (HARD ERROR):
" . print_r($db->dbGetErrorHistory(true), true) . "

"; + +// 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) diff --git a/www/admin/class_test.php b/www/admin/class_test.php index b0bf55d5..871c428f 100644 --- a/www/admin/class_test.php +++ b/www/admin/class_test.php @@ -67,6 +67,7 @@ print '
Class Test: ARRAY HANDLER
'; print '
Class Test: FILE
'; print '
Class Test: RANDOM KEY
'; print '
Class Test: SYSTEM
'; +print '
Class Test: READ ENV FILE
'; print '
Class Test: RUNNING TIME
'; print '
Class Test: DEBUG
'; print '
Class Test: OUTPUT FORM
'; diff --git a/www/admin/class_test.readenvfile.php b/www/admin/class_test.readenvfile.php new file mode 100644 index 00000000..1bd32f0f --- /dev/null +++ b/www/admin/class_test.readenvfile.php @@ -0,0 +1,50 @@ + 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 "TEST CLASS: READ ENV FILE"; +print ""; +print '
Class Test Master
'; + +print "ALREADY from config.php: " . \CoreLibs\Debug\Support::printAr($_ENV) . "
"; + +// test .env in local + +// error message +print $log->printErrorMsg(); + +print ""; + +// __END__ diff --git a/www/configs/config.master.php b/www/configs/config.master.php index ff3bab0d..b920c7f7 100644 --- a/www/configs/config.master.php +++ b/www/configs/config.master.php @@ -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__ diff --git a/www/configs/config.php b/www/configs/config.php index def6a7a3..803b43e0 100755 --- a/www/configs/config.php +++ b/www/configs/config.php @@ -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; } diff --git a/www/configs/read_env_file.php b/www/configs/read_env_file.php index 22f3e28c..c3c6006d 100644 --- a/www/configs/read_env_file.php +++ b/www/configs/read_env_file.php @@ -1,5 +1,10 @@ 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) { diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index ca0d8b12..4823efd2 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -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 */ + private $error_history = []; /** @var int */ private $warning_id; /** @var int */ - private $had_warning; + // private $had_warning = 0; + /** @var array */ + 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 .= '[' . $id . '] '; @@ -625,7 +639,7 @@ class IO } switch ($id) { case 'DB_ERROR': - $prefix .= 'DB-Error:'; + $prefix .= 'DB-Error:'; break; case 'DB_WARNING': $prefix .= 'DB-Warning:'; @@ -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', 'DB-Error ' . $stm_name . ': Prepare field with: ' . $stm_name . ' | ' . $query . '', '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', - 'DB-Error ' . $stm_name - . ': We do not have a prepared query entry for this statement name.', - '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', - 'DB-Error ' . $stm_name - . ': Prepared query Data has to be given in array form.', - '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', - 'DB-Error ' . $stm_name - . ': Array data count does not match prepared fields. Need: ' - . $this->prepare_cursor[$stm_name]['count'] . ', has: ' - . count($data) . '', - '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', - 'DB-Error ' . $stm_name - . ': Execution failed', - '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', - 'DB-Error No async query ' - . 'has beedbFetchArrayn started yet.', - '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 diff --git a/www/lib/CoreLibs/DB/SQL/PgSQL.php b/www/lib/CoreLibs/DB/SQL/PgSQL.php index abd041a3..077d7124 100644 --- a/www/lib/CoreLibs/DB/SQL/PgSQL.php +++ b/www/lib/CoreLibs/DB/SQL/PgSQL.php @@ -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(""); // } @@ -438,8 +483,8 @@ class PgSQL $cursor = pg_get_result($this->dbh); } if ($cursor && !is_bool($cursor) && $error_str = pg_result_error($cursor)) { - return "-PostgreSQL-Error-> " - . $error_str . "
"; + return '-PostgreSQL-Error- ' + . $error_str; } else { return ''; } diff --git a/www/lib/CoreLibs/Get/ReadEnvFile.php b/www/lib/CoreLibs/Get/ReadEnvFile.php new file mode 100644 index 00000000..ae759eb7 --- /dev/null +++ b/www/lib/CoreLibs/Get/ReadEnvFile.php @@ -0,0 +1,94 @@ + 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__ diff --git a/www/vendor/composer/autoload_classmap.php b/www/vendor/composer/autoload_classmap.php index 31f9c715..a3b194cb 100644 --- a/www/vendor/composer/autoload_classmap.php +++ b/www/vendor/composer/autoload_classmap.php @@ -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', diff --git a/www/vendor/composer/autoload_static.php b/www/vendor/composer/autoload_static.php index 81db1480..8857d916 100644 --- a/www/vendor/composer/autoload_static.php +++ b/www/vendor/composer/autoload_static.php @@ -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',