Switch the code point for these below for logic reasons CLEAR_CACHE 1 => 2 (clear cache AFTER END read) READ_NEW 2 => 1 (clear cache BEFORE first read) in dbReturn cursor ext array: remove firstcall entry because it is not needed add new: - cache_flag: $cache method call number - assoc_flag: the assoc read flag from the method call - cached: if there is data cached in the cursor ext array this is true - finished: true if the last read was false - db_read_finished: if true the db read has fiinished (read_rows = num_rows) - read_finished: if true the current read (cache or db) via pos = num_rows is done - log_pos: sequential number for each call with the same query hash - log: array with current actions done in the last read Update DB IO class test with all cursor, cursor ext, read single step, read in loop, read again, etc tests
3245 lines
105 KiB
PHP
3245 lines
105 KiB
PHP
<?php
|
|
|
|
/********************************************************************
|
|
* AUTHOR: Clemens Schwaighofer
|
|
* CREATED: 2000/11/23
|
|
* VERSION: 5.0.0
|
|
* RELEASED LICENSE: GNU GPL 3
|
|
* SHORT DESCRIPTON:
|
|
* 2018/3/23, the whole class system is transformed to namespaces
|
|
* also all internal class calls are converted to camel case
|
|
*
|
|
* 2013/10/10, prepare/excute were added, including auto RETURNING primary key if
|
|
* possible for any INSERT query in exec or prepare/execute, better debugging and
|
|
* data dumping. Proper string escape wrapper, special db exec writer for complex
|
|
* array inserts in auto calls. boolean converter from postresql to php
|
|
*
|
|
* 2003/12/08, one major change: renamed db_exec_ext to db_return, as it has not
|
|
* much in common with the normal db_exec wrapper, as it was written only for
|
|
* SELECT statements and better handling of those.
|
|
*
|
|
* 2002/12/20, extended the "simple" functionality to what I wanted
|
|
* to have in first place, with db_return u execute a query and get
|
|
* automatically data return which is also cached, which means on next
|
|
* call (if not swichted of via paramter) u will no longer exec the DB
|
|
* again (save time, etc) but get the data from cache. I also started to
|
|
* test session use, but it is not yet fully tested so handle with care if
|
|
* you try it ... (session must be started AFTER this class is included, but you
|
|
* do not need to start it actually, only if u want to do some real DB calls)
|
|
*
|
|
* ~2002/12/17, simple wrapper class for mysql php functions. when one query is
|
|
* executed (db_exec) a lot of other information os retrieved too,
|
|
* like num rows, etc which is needed quite often
|
|
* some other functions return one row or one array. all functions
|
|
* have build in error support for surpressing PHP errors & warnings
|
|
*
|
|
* ~2000/11/23, just a function collection for db wrapper, so if you change DB,
|
|
* you don't have to worry about your code as long your SQL is erm ... basic ;)
|
|
*
|
|
* Wrapper functions (via $class->XX(qeury))
|
|
*
|
|
* PUBLIC VARIABLES
|
|
* $class_name
|
|
* - the name of the class
|
|
* $class_version
|
|
* - the version as an array [major, minor, patchlvl, daypatch]
|
|
* $class_last_changed
|
|
* - date (mysql format) for the last change
|
|
* $class_created
|
|
* - date this file was created (more or less a fun thing and memory user)
|
|
* $class_author
|
|
* - me
|
|
* $db_name
|
|
* - the name of the database connected to
|
|
* $db_user
|
|
* - the username
|
|
* $db_host
|
|
* - the hostname (where the DB is located)
|
|
* $db_schema
|
|
* - what schema to connect to, if not given "public" is used
|
|
* $db_encoding
|
|
* - automatic convert to this encoding on output, if not set, keeps default db encoding
|
|
* $db_port
|
|
* - the port to connect to
|
|
* $db_type
|
|
* - what kind of DB it is (pgsql, mysql, ...)
|
|
* $db_ssl
|
|
* - for postgresql, what kind of SSL we try (disable, allow, prefer, require), default is allow
|
|
* $query
|
|
* - sets the SQL query (will be set with the $query parameter from method)
|
|
* if u leave the parameter free the class will try to use this var, but this
|
|
* method is not so reccomended
|
|
* $num_rows
|
|
* - the number of rows returned by a SELECT or alterd bei UPDATE/INSERT
|
|
* $num_fields
|
|
* - the number of fields from the SELECT, is usefull if you do a SELECT *
|
|
* $field_names
|
|
* - array of field names (in the order of the return)
|
|
* $insert_id
|
|
* - for INSERT with auto_increment PK, the ID is stored here
|
|
* $error_msg
|
|
* - all error/debug messages, will be dumped to global $error_msg when db_close() is called
|
|
* $to_encoding
|
|
* - if this is set, then conversion will be done if needed. [no check yet on wrong encoding]
|
|
*
|
|
* PRIVATE VARIABLES
|
|
* $db_pwd
|
|
* - password used for connecting [var might disappear of security reasons]
|
|
* $dbh
|
|
* - the DBH handler itself. DO NOT OVERWRITE OR CHANGE THIS VARIABLE!
|
|
* $db_debug
|
|
* - debug flag set via constructor or global $DB_DEBUG var (has to be set before class create)
|
|
* $cursor_ext
|
|
* - the extended cursor for db_return calls, stores all information (for cached use)
|
|
* $cursor
|
|
* - the normal cursor (will be filled from db_exec calles)
|
|
* $error_id
|
|
* - if an error occours this var will be filled, used by _db_error to write error information
|
|
* $error_string
|
|
* - array with descriptions to error
|
|
* $nbsp
|
|
* - used for recursive function [var might disappear if I have time to recode the recursive function]
|
|
*
|
|
* PUBLIC METHODS
|
|
* $mixed db_return($query,$reset=self::USE_CACHE)
|
|
* - executes query, returns data & caches it (1 = reset/destroy, 2 = reset/cache, 3 = reset/no cache)
|
|
* 1/0 db_cache_reset($query)
|
|
* - resets the cache for one query
|
|
* _db_io()
|
|
* - pseudo deconstructor - functionality moved to db_close
|
|
* $string info($show=1)
|
|
* - returns a string various info about class (version, authoer, etc)
|
|
* - if $show set to 0, it will not be appended to the error_msgs string
|
|
* $string db_info($show=1)
|
|
* - returns a string with info about db connection, etc, if $show set to 0,
|
|
* - it will not be appended to the error_msgs string
|
|
* $string db_dump_data($query=0)
|
|
* - returns a string with all data of that query or if no query given with all data in the cursor_ext
|
|
* 0/$cursor db_exec($query=0)
|
|
* - mysql_query wrapper, writes num_rows, num_fields, etc
|
|
* $mixed db_fetch_array($cursor=0)
|
|
* - mysql_fetch_array, returns a mixed result
|
|
* $mixed db_return_row($query)
|
|
* - gibt die erste Zeile zurück (als array)
|
|
* $array_of_hashes db_return_array($query)
|
|
* - return an array of hashes with all data
|
|
* db_close()
|
|
* - closes db connection and writes error_msg to global error_msg
|
|
* db_cursor_pos($query)
|
|
* - returns the current position the db_return
|
|
* $array_of_hashes db_show_table_meta_data($table_name)
|
|
* - returns an hashed array of table column data
|
|
* function db_prepare($stm_name, $query)
|
|
* - prepares a query with the given stm name, returns false on error
|
|
* function db_execute($stm_name, $data = [])
|
|
* - execute a query that was previously prepared
|
|
* $string db_escape_string($string)
|
|
* - correctly escapes string for db insert
|
|
* $string db_boolean(string)
|
|
* - if the string value is 't' or 'f' it returns correct TRUE/FALSE for php
|
|
* $primary_key db_write_data($write_array, $not_write_array, $primary_key, $table, $data = [])
|
|
* - writes into one table based on arrays of columns to write and not write,
|
|
* - reads data from global vars or optional array
|
|
* $boolean db_set_schema(schema)
|
|
* - sets search path to a schema
|
|
* $boolean db_set_encoding(encoding)
|
|
* - sets an encoding for this database output and input
|
|
* $string db_time_format($age/datetime diff, $micro_time = false/true)
|
|
* - returns a nice formatted time string based on a age or datetime difference
|
|
* - (postgres only), micro time is default false
|
|
*
|
|
* PRIVATE METHODS
|
|
* _db_error()
|
|
* - INTERNAL ONLY!! error that occured during execution
|
|
* $string _print_array($array)
|
|
* - returns string of an array (only for interal use)
|
|
* 1/0 _connect_to_db()
|
|
* - returns 1 for successfull DB connection or 0 for none
|
|
* 1/0 _check_query_for_select($query)
|
|
* - checks if the query has select in it, and if not returns 0 (for db_return* methods)
|
|
* 1/0 _check_query_for_insert($query)
|
|
* - checks if query is INSERT, UPDATE or DELETE
|
|
* row _db_convert_encoding($row)
|
|
* - converts the array from fetch_row to the correct output encoding if set
|
|
* string _db_debug_prepare($prepare_id, $data_array)
|
|
* - returns the prepared statement with the actual data. for debug purposes only
|
|
* none _db_debug($debug_id, $string, $id, $type)
|
|
* - wrapper for normal debug, adds prefix data from id & type and strips
|
|
* - all HTML from the query data (color codes, etc) via flag to debug call
|
|
*
|
|
* HISTORY:
|
|
* 2008/10/25 (cs) add db_boolean to fix the postgres to php boolean var problem
|
|
* (TODO: implement this in any select return)
|
|
* 2008/07/03 (cs) add db_write_data function, original written for inventory tool "invSQLWriteData"
|
|
* 2008/04/16 (cs) add db_escape_string function for correct string escape
|
|
* 2007/11/14 (cs) add a prepare debug statement to replace the placeholders with the actual data in a prepared statement
|
|
* 2007/01/17 (cs) update db_prepare & db_execute error handling
|
|
* 2007/01/11 (cs) add prepare/execute pair (postgres only at the moment)
|
|
* 2006/04/03 (cs) added function to return meta data for a table
|
|
* 2005/07/19 (cs) add a function to get number for rows for a db cursor
|
|
* 2005/07/12 (cs) add named only param to db_return_array
|
|
* 2005/07/01 (cs) added db_cursor_pos to return the current pos in the db_return readout
|
|
* 2005/06/20 (cs) changed the error msg output from just writing to the var, to using the debug method
|
|
* 2005/06/17 (cs) adapted to the new error msg array format. all are to 'db' level
|
|
* 2005/03/31 (cs) added echo/print vars to define where to print out the debug messages
|
|
* 2004/11/15 (cs) error string is no longer echoed, but returned. methods _db_io changed
|
|
* 2004/09/30 (cs) fixed all old layout to new layout
|
|
* 2004/09/17 (cs) added the function to automatically convert the encoding to the correct output encoding
|
|
* 2004/08/06 (cs) two debug parameters, debug and db_debug
|
|
* 2004/07/15 (cs) changed the deconstructor to call _basic deconstructor
|
|
* 2003-06-20: added a '3' flag to db_return so NO caching is done at all (if array might get too big)
|
|
* 2003-06-19: made the error messages in DEBUG output red so they are better to see
|
|
* 2003-06-09: never started class_basic, insert this, for mobile phone detection
|
|
* 2003-04-10: moved the error handling out of the db_pgsql.inc back to db_io class
|
|
* 2003-04-09: major change as db_io does not hold any DB specific calls anymore,
|
|
* those are loaded dynamically during class start, from a include
|
|
* (db_dbname ...)
|
|
* 2003-03-24: removed/moved some basic vars away from this class to basic class and
|
|
* moved init of these vars to constructor
|
|
* 2003-02-26: adapted the error_msg var to 1x where 1 is for db_io error
|
|
* updated _db_error, moved mysql error printing into this function
|
|
* changed the "shape" of class info vars to fit into extend modell
|
|
* 2003-02-13: in db_exec the setting for the last insert id was still via the function,
|
|
* changed this to call the internal PHP mysql command.
|
|
* 2003-01-28: ugly bug within creating the field_names. The array was not reseted
|
|
* before, and so the field for the db_exec where not correct.
|
|
* 2003-01-16: fixed a "select" check in db_exec,
|
|
* added a privet method for checking query of INSERT, UPDATE, DELETE
|
|
* 2003-01-09: code cleanups and more inline documentation
|
|
* 2003-01-08: renamed db_exec_ext to db_return for obious reasons
|
|
* added a "check for select query" for all db_return* methods
|
|
* 2003-01-08: db_return gets another functionality: if u use 1 or 2 as reset value,
|
|
* the cursor will be reset BEFORE the read and no chaced data will be read.
|
|
* if you use 2, the md5 array will be kept so next read with no flag is cached,
|
|
* wheres with 1, the data gets DESTROYED at the end of the read
|
|
* (this makes the db_cache_reset function a bit obsolete)
|
|
* furthermore, the class trys to reconnect (db_exec & db_return) to the DB
|
|
* if no dbh was found (from session eg)
|
|
* 2003-01-07: fixed a small bug in return_array as he mixed up the order if you used
|
|
* SELECT * FROM ...
|
|
* 2002-12-26: changed strstr to stristr 'couse not everyone types SELECT, etc in capitals
|
|
* 2002-12-24: moved the debug output in db_return to the call if,
|
|
* so it is only printed once
|
|
* 2002-12-20: added db_dump_data function for printing out all data in
|
|
* cursor_ext (or from one query in it)
|
|
* 2002-12-20: testing and implemtenting of session storing the class (not fully tested!)
|
|
* documenting all the functions and some code cleenup
|
|
* 2002-12-19: implemented db_return which executes, returns & caches the query
|
|
* 2002-12-18: started idea of putting db_exec and db_fetch_array together
|
|
* 2002-12-17: splitted this file. basic db functions kept here, where the
|
|
* more complex (array based IO fkts) moved into a seperate file
|
|
* 2002-12-16: further reconstruction ...
|
|
* 2002-12-10: further improvment in changing db_mysql to a class
|
|
* 2002-10-18: renamed lesen to db_read, speichern to db_save and
|
|
* loeschen to db_delete
|
|
* 19.08.2002: 1 convertiert < > " & ĵ in original
|
|
* HTML zeichen zurück (für htmlspecialcharsfct)
|
|
* 09.08.2002: speichern() hat einen dritten parameter für
|
|
* addslashes (1=ja,0=nein/default)
|
|
* 04.04.2002: FK added to lesen()
|
|
* 10.07.2001: simple return row function geschrieben
|
|
* 03.07.2001: kein Thumbnail erzeugen wenn Datei nicht:
|
|
* JPG/JPEG/GIF/PNG als Endung hat
|
|
* 22.06.2001: Mozilla Fix für File upload
|
|
* 10.05.2001: alle fkt haben "db_" als pre zur identifizierung
|
|
* 10.05.2001: kleines problem mit call zu "convert_data" fkt
|
|
* 26.04.2001: umschreiben auf classen und einbiden db_io's
|
|
* 24.11.2000: erweitern um num_rows
|
|
* 23.11.2000: erster Test
|
|
*********************************************************************/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace CoreLibs\DB;
|
|
|
|
use CoreLibs\Create\Hash;
|
|
use CoreLibs\Debug\Support;
|
|
use CoreLibs\Create\Uids;
|
|
|
|
// below no ignore is needed if we want to use PgSql interface checks with PHP 8.0
|
|
// as main system. Currently all @var sets are written as object
|
|
/** @#phan-file-suppress PhanUndeclaredTypeProperty,PhanUndeclaredTypeParameter,PhanUndeclaredTypeReturnType */
|
|
|
|
class IO
|
|
{
|
|
// 0: normal read, store in cache
|
|
// 1: read new, keep at end, clean before new run
|
|
// 2: read new, clean at the end (temporary cache)
|
|
// 3: never cache
|
|
/** @var int */
|
|
public const USE_CACHE = 0;
|
|
/** @var int */
|
|
public const READ_NEW = 1;
|
|
/** @var int */
|
|
public const CLEAR_CACHE = 2;
|
|
/** @var int */
|
|
public const NO_CACHE = 3;
|
|
/** @var string */
|
|
public const ERROR_HASH_TYPE = 'adler32';
|
|
|
|
// recommend to set private/protected and only allow setting via method
|
|
// can bet set from outside
|
|
// encoding to
|
|
/** @var string */
|
|
private $to_encoding = '';
|
|
/** @var string */
|
|
private $query; // the query string at the moment
|
|
// only inside
|
|
// basic vars
|
|
/** @var object|resource|bool|int|null */ // replace object with PgSql\Connection|
|
|
private $dbh; // the dbh handler, if disconnected by command is null, bool:false/int:-1 on error,
|
|
/** @var bool */
|
|
private $db_debug = false; // DB_DEBUG ... (if set prints out debug msgs)
|
|
/** @var string */
|
|
private $db_name; // the DB connected to
|
|
/** @var string */
|
|
private $db_user; // the username used
|
|
/** @var string */
|
|
private $db_pwd; // the password used
|
|
/** @var string */
|
|
private $db_host; // the hostname
|
|
/** @var int */
|
|
private $db_port; // default db port
|
|
/** @var string */
|
|
private $db_schema; // optional DB schema, if not set uses public
|
|
/** @var string */
|
|
private $db_encoding; // optional auto encoding convert, not used if not set
|
|
/** @var string */
|
|
private $db_type; // type of db (mysql,postgres,...)
|
|
/** @var string */
|
|
private $db_ssl; // ssl flag (for postgres only), disable, allow, prefer, require
|
|
// FOR BELOW: (This should be private and only readable through some method)
|
|
// cursor array for cached readings
|
|
/** @var array<mixed,mixed> */
|
|
private $cursor_ext; // hash of hashes
|
|
// per query vars
|
|
/** @var object|resource|bool */ // replace object with PgSql\Result
|
|
private $cursor; // actual cursor (DBH)
|
|
/** @var int */
|
|
private $num_rows; // how many rows have been found
|
|
/** @var int */
|
|
private $num_fields; // how many fields has the query
|
|
/** @var array<mixed> */
|
|
private $field_names = []; // array with the field names of the current query
|
|
/** @var array<mixed> */
|
|
private $insert_id_arr = []; // always return as array, even if only one
|
|
/** @var string */
|
|
private $insert_id_pk_name; // primary key name for insert recovery from insert_id_arr
|
|
// other vars
|
|
/** @var string */
|
|
private $nbsp = ''; // used by print_array recursion function
|
|
// error & warning id
|
|
/** @var string */
|
|
private $error_id;
|
|
/** @var string */
|
|
private $warning_id;
|
|
/** @var string */
|
|
private $error_history_id;
|
|
/** @var array<mixed> Stores warning and errors combinded with detail info */
|
|
private $error_history_long = [];
|
|
// error thrown on class init if we cannot connect to db
|
|
/** @var bool */
|
|
protected $db_connection_closed = false;
|
|
// sub include with the database functions
|
|
/** @var \CoreLibs\DB\SQL\PgSQL if we have other DB types we need to add them here */
|
|
private $db_functions;
|
|
// endless loop protection
|
|
/** @var int */
|
|
private $MAX_QUERY_CALL;
|
|
/** @var int */
|
|
public const DEFAULT_MAX_QUERY_CALL = 20; // default
|
|
/** @var array<mixed> */
|
|
private $query_called = [];
|
|
// error string
|
|
/** @var array<mixed> */
|
|
protected $error_string = [];
|
|
// prepared list
|
|
/** @var array<mixed> */
|
|
private $prepare_cursor = [];
|
|
// primary key per table list
|
|
// format is 'table' => 'pk_name'
|
|
/** @var array<mixed> */
|
|
private $pk_name_table = [];
|
|
// internal primary key name, for cross calls in async
|
|
/** @var string */
|
|
private $pk_name;
|
|
// if we use RETURNING in the INSERT call
|
|
/** @var bool */
|
|
private $returning_id = false;
|
|
// if a sync is running holds the hash key of the query
|
|
/** @var string */
|
|
private $async_running;
|
|
// logging class, must be public so settings can be changed
|
|
/** @var \CoreLibs\Debug\Logging */
|
|
public $log;
|
|
|
|
/**
|
|
* main DB concstructor with auto connection to DB and failure set on failed connection
|
|
* @param array<mixed> $db_config DB configuration array
|
|
* @param \CoreLibs\Debug\Logging|null $log Logging class
|
|
* @param bool|null $db_debug_override Overrides debug settings in db_config
|
|
*/
|
|
public function __construct(
|
|
array $db_config,
|
|
?\CoreLibs\Debug\Logging $log = null,
|
|
?bool $db_debug_override = null
|
|
) {
|
|
// attach logger
|
|
$this->log = $log ?? new \CoreLibs\Debug\Logging();
|
|
// sets the names (for connect/reconnect)
|
|
$this->db_name = $db_config['db_name'] ?? '';
|
|
$this->db_user = $db_config['db_user'] ?? '';
|
|
$this->db_pwd = $db_config['db_pass'] ?? '';
|
|
$this->db_host = $db_config['db_host'] ?? '';
|
|
$this->db_port = !empty($db_config['db_port']) ? (int)$db_config['db_port'] : 5432;
|
|
// do not set to 'public' if not set, because the default is already public
|
|
$this->db_schema = !empty($db_config['db_schema']) ? $db_config['db_schema'] : '';
|
|
$this->db_encoding = !empty($db_config['db_encoding']) ? $db_config['db_encoding'] : '';
|
|
$this->db_type = $db_config['db_type'] ?? '';
|
|
$this->db_ssl = !empty($db_config['db_ssl']) ? $db_config['db_ssl'] : 'allow';
|
|
// set debug, either via global var, or from config, else set to false
|
|
$this->dbSetDebug(
|
|
// override
|
|
$db_debug_override ??
|
|
// from db config setting
|
|
$db_config['db_debug'] ??
|
|
// should be handled from outside
|
|
$_SESSION['DB_DEBUG'] ??
|
|
// globals should be deprecated
|
|
$GLOBALS['DB_DEBUG'] ??
|
|
false
|
|
);
|
|
|
|
// set loop protection max count
|
|
$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',
|
|
'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' => 'Missing Statement name',
|
|
'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.',
|
|
'43' => 'No Async query result',
|
|
'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',
|
|
'60' => 'table not found for reading meta data',
|
|
'70' => 'Trying to set an empty search path/schema',
|
|
'71' => 'Failed to set search path/schema',
|
|
'80' => 'Trying to set an empty encoding',
|
|
'81' => 'Failed to set client encoding',
|
|
];
|
|
|
|
// load the core DB functions wrapper class
|
|
if (($db_functions = $this->__loadDBFunctions()) === null) {
|
|
// abort
|
|
die('<!-- Cannot load db functions class for: ' . $this->db_type . ' -->');
|
|
}
|
|
// write to internal one, once OK
|
|
$this->db_functions = $db_functions;
|
|
|
|
// connect to DB
|
|
if (!$this->__connectToDB()) {
|
|
$this->__dbError(16);
|
|
$this->db_connection_closed = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* final desctruct method, closes the DB connection
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
$this->__closeDB();
|
|
}
|
|
|
|
// *************************************************************
|
|
// PRIVATE METHODS
|
|
// *************************************************************
|
|
|
|
/**
|
|
* based on $this->db_type
|
|
* here we need to load the db pgsql include one
|
|
* How can we do this dynamic? eg for non PgSQL
|
|
* OTOH this whole class is so PgSQL specific
|
|
* that non PgSQL doesn't make much sense anymore
|
|
*
|
|
* @return \CoreLibs\DB\SQL\PgSQL|null DB functions object or false on error
|
|
*/
|
|
private function __loadDBFunctions()
|
|
{
|
|
$db_functions = null;
|
|
switch ($this->db_type) {
|
|
// list of valid DB function objects
|
|
case 'pgsql':
|
|
$db_functions = new \CoreLibs\DB\SQL\PgSQL();
|
|
break;
|
|
// if non set or none matching abort
|
|
default:
|
|
// abort error
|
|
$this->__dbError(10);
|
|
$this->db_connection_closed = true;
|
|
break;
|
|
}
|
|
return $db_functions;
|
|
}
|
|
|
|
/**
|
|
* internal connection function. Used to connect to the DB if there is no connection done yet.
|
|
* Called before any execute
|
|
* @return bool true on successfull connect, false if failed
|
|
*/
|
|
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,
|
|
$this->db_user,
|
|
$this->db_pwd,
|
|
$this->db_name,
|
|
$this->db_port,
|
|
$this->db_ssl
|
|
);
|
|
// if no dbh here, we couldn't connect to the DB itself
|
|
if (!$this->dbh) {
|
|
$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->dbError(15));
|
|
// return false;
|
|
// }
|
|
// set search path if needed
|
|
if (!empty($this->db_schema)) {
|
|
$this->dbSetSchema($this->db_schema);
|
|
}
|
|
// set client encoding
|
|
if (!empty($this->db_encoding)) {
|
|
$this->dbSetEncoding($this->db_encoding);
|
|
}
|
|
// all okay
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* close db connection
|
|
* only used by the deconstructor
|
|
* @return void has no return
|
|
*/
|
|
private function __closeDB(): void
|
|
{
|
|
if (!empty($this->dbh) && $this->dbh !== false) {
|
|
$this->db_functions->__dbClose();
|
|
$this->dbh = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* checks if query is a SELECT, SHOW or WITH, if not error, 0 return
|
|
* NOTE:
|
|
* Query needs to start with SELECT, SHOW or WITH. if starts with "with" it is ignored
|
|
* @param string $query query to check
|
|
* @return bool true if matching, false if not
|
|
*/
|
|
private function __checkQueryForSelect(string $query): bool
|
|
{
|
|
// perhaps allow spaces before select ?!?
|
|
if (preg_match("/^(select|show|with) /i", $query)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* check for DELETE, INSERT, UPDATE
|
|
* if pure is set to true, only when INSERT is set will return true
|
|
* NOTE:
|
|
* Queries need to start with INSERT, UPDATE, DELETE. Anything else is ignored
|
|
* @param string $query query to check
|
|
* @param bool $pure pure check (only insert), default false
|
|
* @return bool true if matching, false if not
|
|
*/
|
|
private function __checkQueryForInsert(string $query, bool $pure = false): bool
|
|
{
|
|
if ($pure && preg_match("/^insert /i", $query)) {
|
|
return true;
|
|
}
|
|
if (!$pure && preg_match("/^(insert|update|delete) /i", $query)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* returns true if the query starts with UPDATE
|
|
* query NEEDS to start with UPDATE
|
|
* @param string $query query to check
|
|
* @return bool returns true if the query starts with UPDATE
|
|
*/
|
|
private function __checkQueryForUpdate(string $query): bool
|
|
{
|
|
if (preg_match("/^update /i", $query)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* internal funktion that creates the array
|
|
* NOTE:
|
|
* used in db_dump_data only
|
|
* @param array<mixed> $array array to print
|
|
* @return string string with printed and formated array
|
|
*/
|
|
private function __printArray(array $array): string
|
|
{
|
|
$string = '';
|
|
if (!is_array($array)) {
|
|
$array = [];
|
|
}
|
|
foreach ($array as $key => $value) {
|
|
$string .= $this->nbsp . '<b>' . $key . '</b> => ';
|
|
if (is_array($value)) {
|
|
$this->nbsp .= ' ';
|
|
$string .= '<br>';
|
|
$string .= $this->__printArray($value);
|
|
} else {
|
|
$string .= $value . '<br>';
|
|
}
|
|
}
|
|
$this->nbsp = substr_replace($this->nbsp, '', -18, 18);
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* calls the basic class debug with strip command
|
|
* @param string $debug_id group id for debug
|
|
* @param string $error_string error message or debug data
|
|
* @param string $id db debug group
|
|
* @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 {
|
|
// NOTE prefix allows html for echo output, will be stripped on file print
|
|
$prefix = '';
|
|
if ($id) {
|
|
$prefix .= '[<span style="color: #920069;">' . $id . '</span>] ';
|
|
}
|
|
if ($type) {
|
|
$prefix .= '{<span style="font-style: italic; color: #3f0092;">' . $type . '</span>} ';
|
|
}
|
|
switch ($id) {
|
|
case 'DB_ERROR':
|
|
$prefix .= '<span style="color: red;"><b>DB-Error</b>:</span>';
|
|
break;
|
|
case 'DB_WARNING':
|
|
$prefix .= '<span style="color: orange;"><b>DB-Warning</b>:</span>';
|
|
break;
|
|
}
|
|
if ($prefix) {
|
|
$prefix .= '- ';
|
|
}
|
|
$this->log->debug($debug_id, $error_string, true, $prefix);
|
|
}
|
|
|
|
/**
|
|
* Reset warnings and errors before run
|
|
* Is called on base queries to reset error before each run
|
|
* Recent error history can be checked with
|
|
* dbGetErrorHistory or dbGetWarningHistory
|
|
* @return void
|
|
*/
|
|
private function __dbErrorReset(): void
|
|
{
|
|
$this->error_id = '';
|
|
$this->warning_id = '';
|
|
$this->error_history_id = Uids::uniqId(self::ERROR_HASH_TYPE);
|
|
}
|
|
|
|
/**
|
|
* 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 array<mixed> Pos 0: if we could get the method where it was called
|
|
* if not found [Uknown Method]
|
|
* Pos 1: if we have the pg_error_string from last error
|
|
* if nothing then empty string
|
|
*/
|
|
private function __dbErrorPreprocessor($cursor = false): array
|
|
{
|
|
$pg_error_string = '';
|
|
// 1 = self/__dbErrorPreprocessor, 2 = __dbError, __dbWarning,
|
|
// 3+ == actual source
|
|
// loop until we get a null, build where called chain
|
|
// left to right for last called rightmost
|
|
$level = 3;
|
|
$where_called = null;
|
|
foreach (Support::getCallerMethodList($level) as $method) {
|
|
$where_called .= (!empty($where_called) ? '::' : '')
|
|
. $method;
|
|
}
|
|
if ($where_called === null) {
|
|
$where_called = '[Unknown Method]';
|
|
}
|
|
if ($cursor !== false) {
|
|
$pg_error_string = $this->db_functions->__dbPrintError($cursor);
|
|
}
|
|
if ($cursor === false && method_exists($this->db_functions, '__dbPrintError')) {
|
|
$pg_error_string = $this->db_functions->__dbPrintError();
|
|
}
|
|
if ($pg_error_string) {
|
|
$this->__dbDebug('db', $pg_error_string, 'DB_ERROR', $where_called);
|
|
}
|
|
return [$where_called, $pg_error_string];
|
|
}
|
|
|
|
/**
|
|
* Build combined error history
|
|
* contains timestamp, error/warning id and message
|
|
* error level, source as :: separated string
|
|
* additional pg error message if exists and optional msg given on error call
|
|
* all error messages are grouped by error_history_id set when errors are reset
|
|
* @param string $level
|
|
* @param string $error_id
|
|
* @param string $where_called
|
|
* @param string $pg_error_string
|
|
* @param string $msg
|
|
* @return void
|
|
*/
|
|
private function __dbErrorHistory(
|
|
string $level,
|
|
string $error_id,
|
|
string $where_called,
|
|
string $pg_error_string,
|
|
string $msg
|
|
): void {
|
|
if (empty($this->error_history_id)) {
|
|
$this->error_history_id = Uids::uniqId(self::ERROR_HASH_TYPE);
|
|
}
|
|
$this->error_history_long[$this->error_history_id][] = [
|
|
'timestamp' => \CoreLibs\Combined\DateTime::dateStringFormat(microtime(true), true, true),
|
|
'level' => $level,
|
|
'id' => $error_id,
|
|
'error' => $this->error_string[$error_id] ?? '[UNKNOWN ERROR]',
|
|
'source' => $where_called,
|
|
'pg_error' => $pg_error_string,
|
|
'msg' => $msg,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
$error_id = (string)$error_id;
|
|
[$where_called, $pg_error_string] = $this->__dbErrorPreprocessor($cursor);
|
|
// write error msg ...
|
|
$this->__dbDebug(
|
|
'db',
|
|
$error_id . ': ' . ($this->error_string[$error_id] ?? '[UNKNOWN ERROR]')
|
|
. ($msg ? ', ' . $msg : ''),
|
|
'DB_ERROR',
|
|
$where_called
|
|
);
|
|
$this->error_id = $error_id;
|
|
// keep warning history
|
|
$this->__dbErrorHistory('error', $error_id, $where_called, $pg_error_string, $msg);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
$warning_id = (string)$warning_id;
|
|
[$where_called, $pg_error_string] = $this->__dbErrorPreprocessor($cursor);
|
|
$this->__dbDebug(
|
|
'db',
|
|
$warning_id . ': ' . ($this->error_string[$warning_id] ?? '[UNKNOWN WARNING')
|
|
. ($msg ? ', ' . $msg : ''),
|
|
'DB_WARNING',
|
|
$where_called
|
|
);
|
|
$this->warning_id = $warning_id;
|
|
// keep warning history
|
|
$this->__dbErrorHistory('warning', $warning_id, $where_called, $pg_error_string, $msg);
|
|
}
|
|
|
|
/**
|
|
* if there is the 'to_encoding' var set,
|
|
* and the field is in the wrong encoding converts it to the target
|
|
* @param array<mixed>|bool|null $row Array from fetch_row
|
|
* @return array<mixed>|bool Convert fetch_row array, or false
|
|
*/
|
|
private function __dbConvertEncoding($row)
|
|
{
|
|
if ($row === null) {
|
|
return false;
|
|
}
|
|
// only do if array, else pass through row (can be false)
|
|
if (
|
|
!is_array($row) ||
|
|
empty($this->to_encoding)// || empty($this->db_encoding)
|
|
) {
|
|
return $row;
|
|
}
|
|
// go through each row and convert the encoding if needed
|
|
foreach ($row as $key => $value) {
|
|
$from_encoding = mb_detect_encoding($value);
|
|
// convert only if encoding doesn't match and source is not pure ASCII
|
|
if (
|
|
$from_encoding !== false &&
|
|
$from_encoding != $this->to_encoding &&
|
|
$from_encoding != 'ASCII'
|
|
) {
|
|
$row[$key] = mb_convert_encoding(
|
|
$value,
|
|
$this->to_encoding,
|
|
$from_encoding
|
|
);
|
|
}
|
|
}
|
|
return $row;
|
|
}
|
|
|
|
/**
|
|
* for debug purpose replaces $1, $2, etc with actual data
|
|
* @param string $stm_name prepared statement name
|
|
* @param array<mixed> $data the data array
|
|
* @return string string of query with data inside
|
|
*/
|
|
private function __dbDebugPrepare(string $stm_name, array $data = []): string
|
|
{
|
|
// get the keys from data array
|
|
$keys = array_keys($data);
|
|
// because the placeholders start with $ and at 1, we need to increase each key and prefix it with a $ char
|
|
for ($i = 0, $iMax = count($keys); $i < $iMax; $i++) {
|
|
$keys[$i] = '$' . ($keys[$i] + 1);
|
|
}
|
|
// simply replace the $1, $2, ... with the actual data and return it
|
|
return str_replace(array_reverse($keys), array_reverse($data), $this->prepare_cursor[$stm_name]['query']);
|
|
}
|
|
|
|
/**
|
|
* extracts schema and table from the query, if no schema returns just empty string
|
|
* @param string $query insert/select/update/delete query
|
|
* @return array<mixed> array with schema and table
|
|
*/
|
|
private function __dbReturnTable(string $query): array
|
|
{
|
|
$matches = [];
|
|
if (preg_match("/^SELECT /i", $query)) {
|
|
preg_match("/ (FROM) \"?(([\w_]+)\.)?([\w_]+)\"? /i", $query, $matches);
|
|
} else {
|
|
preg_match("/(INSERT INTO|DELETE FROM|UPDATE) \"?(([\w_]+)\.)?([\w_]+)\"? /i", $query, $matches);
|
|
}
|
|
return [$matches[3] ?? '', $matches[4] ?? ''];
|
|
}
|
|
|
|
/**
|
|
* check if there is another query running, or do we hang after a
|
|
* PHP error
|
|
* @param integer $timeout_seconds For complex timeout waits, default 3 seconds
|
|
* @return boolean True for connection OK, else false
|
|
*/
|
|
private function __dbCheckConnectionOk(int $timeout_seconds = 3): bool
|
|
{
|
|
// check that no other query is running right now
|
|
// below does return false after error too
|
|
// if ($this->db_functions->__dbConnectionBusy()) {
|
|
if ($this->db_functions->__dbConnectionBusySocketWait($timeout_seconds)) {
|
|
$this->__dbError(41);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* dbReturn
|
|
* Read data from previous written data cache
|
|
* @param string $query_hash The hash for the current query
|
|
* @param boolean $assoc_only Only return assoc value (key named)
|
|
* @return array<mixed> Current position query data from cache
|
|
*/
|
|
private function __dbReturnCacheRead(string $query_hash, bool $assoc_only): array
|
|
{
|
|
// unset return value ...
|
|
$return = [];
|
|
// current cursor hash element
|
|
$cursor_hash = $this->cursor_ext[$query_hash];
|
|
// position in reading for current cursor hash
|
|
$cursor_pos = $cursor_hash['pos'];
|
|
// max fields in current cursor hash
|
|
$max_fields = $cursor_hash['num_fields'] ?? 0;
|
|
// read voer each field
|
|
for ($pos = 0; $pos < $max_fields; $pos++) {
|
|
// numbered pos element data (only exists in full read)
|
|
$cursor_data_pos = $cursor_hash['data'][$cursor_pos][$pos] ?? null;
|
|
// field name position from field names
|
|
$field_name_pos = $cursor_hash['field_names'][$pos] ?? null;
|
|
// field name data
|
|
$cursor_data_name = $cursor_hash['data'][$cursor_pos][$field_name_pos] ?? null;
|
|
// create mixed return array
|
|
if (
|
|
$assoc_only === false &&
|
|
$cursor_data_pos !== null
|
|
) {
|
|
$return[$pos] = $cursor_data_pos;
|
|
}
|
|
// named part (is alreays read)
|
|
if (!empty($field_name_pos)) {
|
|
// read pos first, fallback to name if not set
|
|
if ($cursor_data_pos !== null) {
|
|
$return[$field_name_pos] = $cursor_data_pos;
|
|
} else {
|
|
$return[$field_name_pos] = $cursor_data_name;
|
|
}
|
|
}
|
|
}
|
|
$this->cursor_ext[$query_hash]['pos'] ++;
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* sub function for dbExec and dbExecAsync
|
|
* - checks query is set
|
|
* - checks there is a database handler
|
|
* - checks that here is no other query executing
|
|
* - checks for insert if returning is set/pk name
|
|
* - sets internal hash for query
|
|
* - checks multiple call count
|
|
* @param string $query query string
|
|
* @param string $pk_name primary key [if set to NULL no returning will be added]
|
|
* @return string|bool queryt hash OR boolean false on error
|
|
*/
|
|
private function __dbPrepareExec(string $query, string $pk_name)
|
|
{
|
|
// reset current cursor before exec
|
|
$this->cursor = false;
|
|
// clear matches for regex lookups
|
|
$matches = [];
|
|
// to either use the returning method
|
|
// or the guess method for getting primary keys
|
|
$this->returning_id = false;
|
|
// set the query
|
|
$this->query = $query;
|
|
// no query set
|
|
if (empty($this->query)) {
|
|
$this->__dbError(11);
|
|
return false;
|
|
}
|
|
// if no DB Handler try to reconnect
|
|
if (!$this->dbh) {
|
|
// if reconnect fails drop out
|
|
if (!$this->__connectToDB()) {
|
|
$this->__dbError(16);
|
|
return false;
|
|
}
|
|
}
|
|
// check that no other query is running right now
|
|
if (!$this->__dbCheckConnectionOk()) {
|
|
return false;
|
|
}
|
|
// if we do have an insert, check if there is no RETURNING pk_id,
|
|
// add it if I can get the PK id
|
|
if ($this->__checkQueryForInsert($this->query, true)) {
|
|
$this->pk_name = $pk_name;
|
|
if ($this->pk_name != 'NULL') {
|
|
if (!$this->pk_name) {
|
|
// TODO: get primary key from table name
|
|
list($schema, $table) = $this->__dbReturnTable($this->query);
|
|
if (!array_key_exists($table, $this->pk_name_table) || !$this->pk_name_table[$table]) {
|
|
$this->pk_name_table[$table] = $this->db_functions->__dbPrimaryKey($table, $schema);
|
|
}
|
|
$this->pk_name =
|
|
$this->pk_name_table[$table] ?
|
|
$this->pk_name_table[$table] : 'NULL';
|
|
}
|
|
if (
|
|
!preg_match("/ returning /i", $this->query) &&
|
|
$this->pk_name && $this->pk_name != 'NULL'
|
|
) {
|
|
// check if this query has a ; at the end and remove it
|
|
$__query = preg_replace("/(;\s*)$/", '', $this->query);
|
|
// must be query, if preg replace failed, use query as before
|
|
$this->query = !is_string($__query) ? $this->query : $__query;
|
|
$this->query .= " RETURNING " . $this->pk_name;
|
|
$this->returning_id = true;
|
|
} elseif (preg_match("/ returning (.*)/i", $this->query, $matches)) {
|
|
if ($this->pk_name && $this->pk_name != 'NULL') {
|
|
// add the primary key if it is not in the returning set
|
|
if (!preg_match("/$this->pk_name/", $matches[1])) {
|
|
$this->query .= " , " . $this->pk_name;
|
|
}
|
|
}
|
|
$this->returning_id = true;
|
|
}
|
|
}
|
|
}
|
|
// if we have an UPDATE and RETURNING, flag for true, but do not add anything
|
|
if (
|
|
$this->__checkQueryForUpdate($this->query) &&
|
|
preg_match("/ returning (.*)/i", $this->query, $matches)
|
|
) {
|
|
$this->returning_id = true;
|
|
}
|
|
// $this->debug('DB IO', 'Q: '.$this->query.', RETURN: '.$this->returning_id);
|
|
// for DEBUG, only on first time ;)
|
|
if ($this->db_debug) {
|
|
$this->__dbDebug('db', $this->query, '__dbPrepareExec', 'Q');
|
|
}
|
|
// import protection, hash needed
|
|
$query_hash = $this->dbGetQueryHash($this->query);
|
|
// if the array index does not exists set it 0
|
|
if (!array_key_exists($query_hash, $this->query_called)) {
|
|
$this->query_called[$query_hash] = 0;
|
|
}
|
|
// if the array index exists, but it is not a numeric one, set it to 0
|
|
if (!is_numeric($this->query_called[$query_hash])) {
|
|
$this->query_called[$query_hash] = 0;
|
|
}
|
|
// count up the run, if this is run more than the max_run then exit with error
|
|
// if set to -1, then ignore it
|
|
if (
|
|
$this->MAX_QUERY_CALL != -1 &&
|
|
$this->query_called[$query_hash] > $this->MAX_QUERY_CALL
|
|
) {
|
|
$this->__dbError(30, false, $this->query);
|
|
$this->__dbDebug('db', $this->query, 'dbExec', 'Q[nc]');
|
|
return false;
|
|
}
|
|
$this->query_called[$query_hash] ++;
|
|
// return hash
|
|
return $query_hash;
|
|
}
|
|
|
|
/**
|
|
* runs post execute for rows affected, field names, inserted primary key, etc
|
|
* @return bool true on success or false if an error occured
|
|
*/
|
|
private function __dbPostExec(): bool
|
|
{
|
|
// always reset insert array after exec
|
|
$this->insert_id_arr = [];
|
|
// if FALSE returned, set error stuff
|
|
// if either the cursor is false
|
|
if ($this->cursor === false || $this->db_functions->__dbLastErrorQuery()) {
|
|
// printout Query if debug is turned on
|
|
if ($this->db_debug) {
|
|
$this->__dbDebug('db', $this->query, 'dbExec', 'Q[nc]');
|
|
}
|
|
// internal error handling
|
|
$this->__dbError(13, $this->cursor);
|
|
return false;
|
|
} else {
|
|
// if SELECT do here ...
|
|
if ($this->__checkQueryForSelect($this->query)) {
|
|
// count the rows returned (if select)
|
|
$this->num_rows = $this->db_functions->__dbNumRows($this->cursor);
|
|
// count the fields
|
|
$this->num_fields = $this->db_functions->__dbNumFields($this->cursor);
|
|
// set field names
|
|
$this->field_names = [];
|
|
for ($i = 0; $i < $this->num_fields; $i++) {
|
|
$this->field_names[] = $this->db_functions->__dbFieldName($this->cursor, $i);
|
|
}
|
|
} elseif ($this->__checkQueryForInsert($this->query)) {
|
|
// if not select do here
|
|
// count affected rows
|
|
$this->num_rows = $this->db_functions->__dbAffectedRows($this->cursor);
|
|
if (
|
|
// ONLY insert with set pk name
|
|
($this->__checkQueryForInsert($this->query, true) && $this->pk_name != 'NULL') ||
|
|
// insert or update with returning add
|
|
($this->__checkQueryForInsert($this->query) && $this->returning_id)
|
|
) {
|
|
$this->__dbSetInsertId(
|
|
$this->returning_id,
|
|
$this->query,
|
|
$this->pk_name,
|
|
$this->cursor
|
|
);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all returning variables
|
|
* try to primary get the OK into insert_id
|
|
* - one if single
|
|
* - array if many to return
|
|
* - if many this will also hold all non pk names too
|
|
* then try to fill insert_id_arr, this is always multi level
|
|
* - fill key: value as single array or multi array
|
|
* insert_id_ext [DEPRECATED, all in insert_id_arr]
|
|
* - holds all returning as array
|
|
* TODO: Only use insert_id_arr and use functions to get ok array or single
|
|
* @param boolean $returning_id
|
|
* @param string $query
|
|
* @param string|null $pk_name
|
|
* @param object|resource|bool $cursor (PgSql\Result)
|
|
* @param string|null $stm_name If not null, is dbExecutre run
|
|
* @return void
|
|
*/
|
|
private function __dbSetInsertId(
|
|
bool $returning_id,
|
|
string $query,
|
|
?string $pk_name,
|
|
$cursor,
|
|
?string $stm_name = null
|
|
): void {
|
|
// $this->log->debug('DB SET INSERT ID', 'Ret: ' . ($returning_id ? 'Y' : 'N')
|
|
// . 'Q: ' . $query . ', PK: ' . $pk_name . ', S: ' . ($stm_name ?? '{-}'));
|
|
// as internval user only
|
|
$insert_id = null;
|
|
// reset internal array
|
|
$this->insert_id_arr = [];
|
|
// set the primary key name
|
|
$this->insert_id_pk_name = $pk_name ?? '';
|
|
// abort if cursor is empty
|
|
if ($cursor === false) {
|
|
// failed to get insert id
|
|
if ($stm_name === null) {
|
|
$this->__dbWarning(34, $cursor, '[dbExec]');
|
|
} else {
|
|
$this->__dbWarning(34, false, $stm_name . ': CURSOR is null');
|
|
}
|
|
return;
|
|
}
|
|
// set insert_id
|
|
// if we do not have a returning
|
|
// we try to get it via the primary key and another select
|
|
if (!$returning_id) {
|
|
$insert_id = $this->db_functions->__dbInsertId($query, $pk_name);
|
|
$this->insert_id_arr[] = $insert_id;
|
|
// throw warning that no pk was found
|
|
if ($insert_id === false) {
|
|
$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
|
|
// we'll need to loop this, if we have multiple insert_id returns
|
|
while (
|
|
is_array($insert_id = $this->db_functions->__dbFetchArray(
|
|
$cursor,
|
|
$this->db_functions->__dbResultType(true)
|
|
))
|
|
) {
|
|
$this->insert_id_arr[] = $insert_id;
|
|
}
|
|
// warning if we didn't get any returning data
|
|
if (count($this->insert_id_arr) == 0) {
|
|
// failed to get insert id
|
|
if ($stm_name === null) {
|
|
$this->__dbWarning(33, $cursor, '[dbExec]');
|
|
} else {
|
|
$this->__dbWarning(
|
|
33,
|
|
false,
|
|
$stm_name . ': RETURNING returned no data'
|
|
);
|
|
}
|
|
} elseif (count($this->insert_id_arr) > 1) {
|
|
// this error handling is only for INSERT (), (), ... sets
|
|
if ($stm_name === null) {
|
|
$this->__dbWarning(32, $cursor, '[dbExec]');
|
|
} else {
|
|
$this->__dbWarning(
|
|
32,
|
|
false,
|
|
$stm_name . ': RETURNING returned an array (possible multiple insert)'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// *************************************************************
|
|
// PUBLIC METHODS
|
|
// *************************************************************
|
|
|
|
// ***************************
|
|
// CLOSE, STATUS, SETTINGS VARIABLE READ
|
|
// ***************************
|
|
|
|
/**
|
|
* closes the db_connection
|
|
* normally this is not used, as the class deconstructor closes
|
|
* the connection down
|
|
* @return void has no return
|
|
*/
|
|
public function dbClose(): void
|
|
{
|
|
if ($this->dbh) {
|
|
// reset any client encodings set
|
|
$this->dbResetEncoding();
|
|
// calls db close
|
|
$this->db_functions->__dbClose();
|
|
$this->dbh = null;
|
|
$this->db_connection_closed = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* returns the db init error
|
|
* if failed to connect it is set to false
|
|
* else true
|
|
* @return bool Connection status
|
|
*/
|
|
public function dbGetConnectionStatus(): bool
|
|
{
|
|
return $this->db_connection_closed ? false : true;
|
|
}
|
|
|
|
/**
|
|
* get certain settings like username, db name
|
|
* @param string $name what setting to query
|
|
* @return mixed setting value, if not allowed name return false
|
|
*/
|
|
public function dbGetSetting(string $name)
|
|
{
|
|
$setting = '';
|
|
switch ($name) {
|
|
case 'name':
|
|
$setting = $this->db_name;
|
|
break;
|
|
case 'user':
|
|
$setting = $this->db_user;
|
|
break;
|
|
case 'encoding':
|
|
$setting = $this->db_encoding;
|
|
break;
|
|
case 'schema':
|
|
$setting = $this->db_schema;
|
|
break;
|
|
case 'host':
|
|
$setting = $this->db_host;
|
|
break;
|
|
case 'port':
|
|
$setting = $this->db_port;
|
|
break;
|
|
case 'ssl':
|
|
$setting = $this->db_ssl;
|
|
break;
|
|
case 'debug':
|
|
$setting = $this->db_debug;
|
|
break;
|
|
// we return *** and never the actual password
|
|
case 'password':
|
|
$setting = '***';
|
|
break;
|
|
default:
|
|
$setting = false;
|
|
break;
|
|
}
|
|
return $setting;
|
|
}
|
|
|
|
/**
|
|
* prints out status info from the connected DB (might be usefull for debug stuff)
|
|
* @param bool $log Show db connection info, default true
|
|
* if set to false won't write to error_msg var
|
|
* @param bool $strip Strip all HTML
|
|
* @return string db connection information string
|
|
*/
|
|
public function dbInfo(bool $log = true, bool $strip = false): string
|
|
{
|
|
$html_tags = ['{b}', '{/b}', '{br}'];
|
|
$replace_html = ['<b>', '</b>', '<br>'];
|
|
$replace_text = ['', '', ' **** '];
|
|
$string = '';
|
|
$string .= '{b}-DB-info->{/b} Connected to db {b}\'' . $this->db_name . '\'{/b} ';
|
|
$string .= 'with schema {b}\'' . $this->db_schema . '\'{/b} ';
|
|
$string .= 'as user {b}\'' . $this->db_user . '\'{/b} ';
|
|
$string .= 'at host {b}\'' . $this->db_host . '\'{/b} ';
|
|
$string .= 'on port {b}\'' . $this->db_port . '\'{/b} ';
|
|
$string .= 'with ssl mode {b}\'' . $this->db_ssl . '\'{/b}{br}';
|
|
$string .= '{b}-DB-info->{/b} DB IO Class debug output: {b}' . ($this->db_debug ? 'Yes' : 'No') . '{/b}';
|
|
if ($log === true) {
|
|
// if debug, remove / change b
|
|
$this->__dbDebug('db', str_replace(
|
|
$html_tags,
|
|
$replace_text,
|
|
$string
|
|
), 'dbInfo');
|
|
} else {
|
|
$string = $string . '{br}';
|
|
}
|
|
// for direct print, change to html or strip if flagged
|
|
return str_replace(
|
|
$html_tags,
|
|
$strip === false ? $replace_html : $replace_text,
|
|
$string
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Server version as integer value
|
|
* @return integer Version as integer
|
|
*/
|
|
public function dbVersionNumeric(): int
|
|
{
|
|
return $this->db_functions->__dbVersionNumeric();
|
|
}
|
|
|
|
/**
|
|
* return current database version (server side) as string
|
|
* @return string database version as string
|
|
*/
|
|
public function dbVersion(): string
|
|
{
|
|
return $this->db_functions->__dbVersion();
|
|
}
|
|
|
|
/**
|
|
* extended version info, can access all additional information data
|
|
* @param string $parameter Array parameter name, if not valid returns
|
|
* empty string
|
|
* @param boolean $strip Strip extended server info string, default true
|
|
* eg nn.n (other info) will only return nn.n
|
|
* @return string Parameter value
|
|
*/
|
|
public function dbVersionInfo(string $parameter, bool $strip = true): string
|
|
{
|
|
return $this->db_functions->__dbVersionInfo($parameter, $strip);
|
|
}
|
|
|
|
/**
|
|
* All possible parameter names for dbVersionInfo
|
|
* @return array<mixed> List of all parameter names
|
|
*/
|
|
public function dbVersionInfoParameters(): array
|
|
{
|
|
return $this->db_functions->__dbVersionInfoParameterList();
|
|
}
|
|
|
|
/**
|
|
* returns boolean true or false if the string matches the database version
|
|
* @param string $compare string to match in type =X.Y, >X.Y, <X.Y, <=X.Y, >=X.Y
|
|
* @return bool true for ok, false on not ok
|
|
*/
|
|
public function dbCompareVersion(string $compare): bool
|
|
{
|
|
$matches = [];
|
|
// compare has =, >, < prefix, and gets stripped, if the rest is not X.Y format then error
|
|
preg_match("/^([<>=]{1,})(\d{1,})\.(\d{1,})/", $compare, $matches);
|
|
$compare = $matches[1];
|
|
$to_master = $matches[2];
|
|
$to_minor = $matches[3];
|
|
$to_version = '';
|
|
if (!$compare || !strlen($to_master) || !strlen($to_minor)) {
|
|
return false;
|
|
} else {
|
|
$to_version = $to_master . ($to_minor < 10 ? '0' : '') . $to_minor;
|
|
}
|
|
// db_version can return X.Y.Z
|
|
// we only compare the first two
|
|
preg_match(
|
|
"/^(\d{1,})\.(\d{1,})\.?(\d{1,})?/",
|
|
$this->dbVersion(),
|
|
$matches
|
|
);
|
|
$master = $matches[1];
|
|
$minor = $matches[2];
|
|
$version = $master . ($minor < 10 ? '0' : '') . $minor;
|
|
$return = false;
|
|
// compare
|
|
switch ($compare) {
|
|
case '=':
|
|
if ($to_version == $version) {
|
|
$return = true;
|
|
}
|
|
break;
|
|
case '<':
|
|
if ($version < $to_version) {
|
|
$return = true;
|
|
}
|
|
break;
|
|
case '<=':
|
|
if ($version <= $to_version) {
|
|
$return = true;
|
|
}
|
|
break;
|
|
case '>':
|
|
if ($version > $to_version) {
|
|
$return = true;
|
|
}
|
|
break;
|
|
case '>=':
|
|
if ($version >= $to_version) {
|
|
$return = true;
|
|
}
|
|
break;
|
|
default:
|
|
$return = false;
|
|
}
|
|
return $return;
|
|
}
|
|
|
|
// ***************************
|
|
// DEBUG DATA DUMP
|
|
// ***************************
|
|
|
|
/**
|
|
* dumps ALL data for this query, OR if no query given all in cursor_ext array
|
|
* @param string $query Query, if given, only from this quey (if found)
|
|
* else current cursor
|
|
* @return string Formated string with all the data in the array
|
|
*/
|
|
public function dbDumpData($query = ''): string
|
|
{
|
|
// set start array
|
|
if ($query) {
|
|
$array = $this->cursor_ext[$this->dbGetQueryHash($query)] ?? [];
|
|
} else {
|
|
$array = $this->cursor_ext;
|
|
}
|
|
$string = '';
|
|
if (is_array($array)) {
|
|
$this->nbsp = '';
|
|
$string .= $this->__printArray($array);
|
|
$this->__dbDebug('db', $string, 'dbDumpData');
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
// ***************************
|
|
// DATA WRITE CONVERSION
|
|
// ***************************
|
|
|
|
/**
|
|
* neutral function to escape a string for DB writing
|
|
* @param string|int|float|bool $string string to escape
|
|
* @return string escaped string
|
|
*/
|
|
public function dbEscapeString($string): string
|
|
{
|
|
return $this->db_functions->__dbEscapeString($string);
|
|
}
|
|
|
|
/**
|
|
* neutral function to escape a string for DB writing
|
|
* this one adds '' quotes around the string
|
|
* @param string|int|float|bool $string string to escape
|
|
* @return string escaped string
|
|
*/
|
|
public function dbEscapeLiteral($string): string
|
|
{
|
|
return $this->db_functions->__dbEscapeLiteral($string);
|
|
}
|
|
|
|
/**
|
|
* string escape for column and table names
|
|
* @param string $string string to escape
|
|
* @return string escaped string
|
|
*/
|
|
public function dbEscapeIdentifier($string): string
|
|
{
|
|
return $this->db_functions->__dbEscapeIdentifier($string);
|
|
}
|
|
|
|
/**
|
|
* escape data for writing to bytea type column field
|
|
* @param string $bytea bytea to escape
|
|
* @return string escaped bytea
|
|
*/
|
|
public function dbEscapeBytea($bytea)
|
|
{
|
|
return $this->db_functions->__dbEscapeBytea($bytea);
|
|
}
|
|
|
|
/**
|
|
* clear up any data for valid DB insert
|
|
* @param int|float|string|null $value to escape data
|
|
* @param string $kbn escape trigger type
|
|
* @return string escaped value
|
|
*/
|
|
public function dbSqlEscape($value, string $kbn = '')
|
|
{
|
|
switch ($kbn) {
|
|
case 'i':
|
|
$value = empty($value) ? 'NULL' : intval($value);
|
|
break;
|
|
case 'f':
|
|
$value = empty($value) ? 'NULL' : floatval($value);
|
|
break;
|
|
case 't':
|
|
$value = $value === null ?
|
|
'NULL' :
|
|
"'" . $this->dbEscapeString($value) . "'";
|
|
break;
|
|
case 'tl':
|
|
$value = $value === null ?
|
|
'NULL' :
|
|
$this->dbEscapeLiteral($value);
|
|
break;
|
|
// what is d?
|
|
case 'd':
|
|
$value = empty($value) ? 'NULL' : "'" . $this->dbEscapeString($value) . "'";
|
|
break;
|
|
// bytea data
|
|
case 'by':
|
|
$value = empty($value) ? 'NULL' : $this->dbEscapeBytea((string)$value);
|
|
break;
|
|
case 'b':
|
|
if (is_float($value)) {
|
|
$value = (int)$value;
|
|
}
|
|
$value = $value === '' || $value === null ?
|
|
'NULL' :
|
|
"'" . $this->dbBoolean($value, true) . "'";
|
|
break;
|
|
case 'i2':
|
|
$value = empty($value) ? 0 : intval($value);
|
|
break;
|
|
}
|
|
return (string)$value;
|
|
}
|
|
|
|
// ***************************
|
|
// DATA READ/WRITE CONVERSION
|
|
// ***************************
|
|
|
|
/**
|
|
* if the input is a single char 't' or 'f
|
|
* it will return the boolean value instead
|
|
* also converts smallint 1/0 to true false
|
|
* @param string|bool|int $string 't' / 'f' or any string, or bool true/false
|
|
* @param boolean $rev do reverse (bool to string)
|
|
* @return bool|string correct php boolean true/false
|
|
* or postgresql 't'/'f'
|
|
*/
|
|
public function dbBoolean($string, $rev = false)
|
|
{
|
|
if (!$rev) {
|
|
if ($string == 't' || $string == 'true') {
|
|
return true;
|
|
}
|
|
if ($string == 'f' || $string == 'false') {
|
|
return false;
|
|
}
|
|
// fallback in case top is not t/f, default on set unset
|
|
if ($string) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
if ($string) {
|
|
return 't';
|
|
} else {
|
|
return 'f';
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************
|
|
// DATA READ CONVERSION
|
|
// ***************************
|
|
|
|
/**
|
|
* only for postgres. pretty formats an age or datetime difference string
|
|
* @param string $interval Age or interval/datetime difference
|
|
* @param bool $show_micro micro on off (default false)
|
|
* @return string Y/M/D/h/m/s formatted string (like timeStringFormat)
|
|
*/
|
|
public function dbTimeFormat(string $interval, bool $show_micro = false): string
|
|
{
|
|
$matches = [];
|
|
// in string (datetime diff): 1786 days 22:11:52.87418
|
|
// or (age): 4 years 10 mons 21 days 12:31:11.87418
|
|
// also -09:43:54.781021 or without - prefix
|
|
// can have missing parts, but day/time must be fully set
|
|
preg_match(
|
|
"/^(-)?((\d+) year[s]? ?)?((\d+) mon[s]? ?)?((\d+) day[s]? ?)?"
|
|
. "((\d{1,2}):(\d{1,2}):(\d{1,2}))?(\.(\d+))?$/",
|
|
$interval,
|
|
$matches
|
|
);
|
|
|
|
// prefix (-)
|
|
$prefix = $matches[1] ?? '';
|
|
// date, years (2, 3), month (4, 5), days (6, 7)
|
|
$years = $matches[2] ?? '';
|
|
$months = $matches[4] ?? '';
|
|
$days = $matches[6] ?? '';
|
|
// time (8), hour (9), min (10), sec (11)
|
|
$hour = $matches[9] ?? '';
|
|
$minutes = $matches[10] ?? '';
|
|
$seconds = $matches[11] ?? '';
|
|
// micro second block (12), ms (13)
|
|
$milliseconds = $matches[13] ?? '';
|
|
|
|
// clean up, hide entries that have 00 in the time group
|
|
$hour = $hour != '00' ? preg_replace('/^0/', '', $hour) : '';
|
|
$minutes = $minutes != '00' ? preg_replace('/^0/', '', $minutes) : '';
|
|
$seconds = $seconds != '00' ? preg_replace('/^0/', '', $seconds) : '';
|
|
|
|
// strip any leading or trailing spaces
|
|
$time_string = trim(
|
|
$prefix . $years . $months . $days
|
|
. (!empty($hour) && is_string($hour) ? $hour . 'h ' : '')
|
|
. (!empty($minutes) && is_string($minutes) ? $minutes . 'm ' : '')
|
|
. (!empty($seconds) && is_string($seconds) ? $seconds . 's ' : '')
|
|
. ($show_micro && !empty($milliseconds) ? $milliseconds . 'ms' : '')
|
|
);
|
|
// if the return string is empty, return 0s instead
|
|
return empty($time_string) ? '0s' : $time_string;
|
|
}
|
|
|
|
/**
|
|
* this is only needed for Postgresql. Converts postgresql arrays to PHP
|
|
* Recommended to rather user 'array_to_json' instead and convet JSON in PHP
|
|
* @param string $text input text to parse to an array
|
|
* @return array<mixed> PHP array of the parsed data
|
|
* @deprecated Recommended to use 'array_to_json' in PostgreSQL instead
|
|
*/
|
|
public function dbArrayParse(string $text): array
|
|
{
|
|
$__db_array_parse = $this->db_functions->__dbArrayParse($text);
|
|
return is_array($__db_array_parse) ? $__db_array_parse : [];
|
|
}
|
|
|
|
// ***************************
|
|
// TABLE META DATA READ
|
|
// ***************************
|
|
|
|
/**
|
|
* returns an array of the table with columns and values. FALSE on no table found
|
|
* @param string $table table name
|
|
* @param string $schema optional schema name
|
|
* @return array<mixed>|bool array of table data, false on error (table not found)
|
|
*/
|
|
public function dbShowTableMetaData(string $table, string $schema = '')
|
|
{
|
|
$this->__dbErrorReset();
|
|
$table = (!empty($schema) ? $schema . '.' : '') . $table;
|
|
$array = $this->db_functions->__dbMetaData($table);
|
|
if (!is_array($array)) {
|
|
$this->__dbError(60);
|
|
$array = false;
|
|
}
|
|
return $array;
|
|
}
|
|
|
|
// ***************************
|
|
// QUERY EXECUSION AND DATA READ
|
|
// ***************************
|
|
|
|
/**
|
|
* single running function, if called creates hash from
|
|
* query string and so can itself call exec/return calls
|
|
* caches data, so next time called with IDENTICAL (!!!!)
|
|
* [this means 1:1 bit to bit identical query] returns cached
|
|
* data, or with reset flag set calls data from DB again
|
|
* NOTE on $cache param:
|
|
* - if set to 0, if same query run again, will read from cache
|
|
* - if set to 1, the data will be read new and cached, cache reset a new run
|
|
* (wheres 1 reads cache AND destroys at end of read)
|
|
* - if set to 2, at the end of the query (last row returned),
|
|
* the stored array will be deleted ...
|
|
* - if set to 3, after EACH row, the data will be reset,
|
|
* no caching is done except for basic (count, etc)
|
|
* @param string $query Query string
|
|
* @param int $cache reset status: default: USE_CACHE
|
|
* USE_CACHE/0: normal read from cache on second run
|
|
* READ_NEW/1: write to cache, clean before new run
|
|
* CLEAR_CACHE/2: write cache, clean after finished
|
|
* NO_CACHE/3: don't write cache
|
|
* @param bool $assoc_only True to only returned the named and not
|
|
* index position ones
|
|
* @return array<mixed>|bool return array data or false on error/end
|
|
* @#suppress PhanTypeMismatchDimFetch
|
|
*/
|
|
public function dbReturn(
|
|
string $query,
|
|
int $cache = self::USE_CACHE,
|
|
bool $assoc_only = false
|
|
) {
|
|
$this->__dbErrorReset();
|
|
if (!$query) {
|
|
$this->__dbError(11);
|
|
return false;
|
|
}
|
|
// create hash from query ...
|
|
$query_hash = $this->dbGetQueryHash($query);
|
|
// pre declare array
|
|
if (!isset($this->cursor_ext[$query_hash])) {
|
|
$this->cursor_ext[$query_hash] = [
|
|
// cursor, null: unset, 1: finished read/cache, 2: object/resource reading
|
|
'cursor' => null,
|
|
// cached data
|
|
'data' => [],
|
|
// field names as array
|
|
'field_names' => [],
|
|
// number of fields (field names)
|
|
'num_fields' => 0,
|
|
// number of rows that will be maximum returned
|
|
'num_rows' => 0,
|
|
// how many rows have been read from db
|
|
'read_rows' => 0,
|
|
// current read pos (db/cache), 0 on last read (finished)
|
|
'pos' => 0,
|
|
// the query used in this call
|
|
'query' => '',
|
|
// cache flag from method call
|
|
'cache_flag' => $cache,
|
|
// flag if we only have assoc data
|
|
'assoc_flag' => $assoc_only,
|
|
// flag if we have cache data stored at the moment
|
|
'cached' => false,
|
|
// when fetch array or cache read returns false
|
|
// in loop read that means dbReturn retuns false without erro
|
|
'finished' => false,
|
|
// read from cache/db (pos == rows)
|
|
'read_finished' => false,
|
|
// read from db only (read == rows)
|
|
'db_read_finished' => false,
|
|
// for debug
|
|
'log_pos' => 1, // how many times called overall
|
|
'log' => [], // current run log
|
|
];
|
|
} else {
|
|
$this->cursor_ext[$query_hash]['log_pos'] ++;
|
|
}
|
|
// reset log for each read
|
|
$this->cursor_ext[$query_hash]['log'] = [];
|
|
// set the query
|
|
$this->cursor_ext[$query_hash]['query'] = $query;
|
|
// before doing ANYTHING check if query is "SELECT ..." everything else does not work
|
|
if (!$this->__checkQueryForSelect($this->cursor_ext[$query_hash]['query'])) {
|
|
$this->__dbError(17, false, $this->cursor_ext[$query_hash]['query']);
|
|
return false;
|
|
}
|
|
// set first call to false
|
|
$first_call = false;
|
|
// init return als false
|
|
$return = false;
|
|
// if it is a call with reset in it we reset the cursor,
|
|
// so we get an uncached return
|
|
// but only for the FIRST call (pos == 0)
|
|
if ($cache > self::USE_CACHE && !$this->cursor_ext[$query_hash]['pos']) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'Reset cursor';
|
|
$this->cursor_ext[$query_hash]['cursor'] = null;
|
|
if ($cache == self::READ_NEW) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'Cache reset';
|
|
$this->cursor_ext[$query_hash]['data'] = [];
|
|
}
|
|
}
|
|
|
|
// if no cursor yet, execute
|
|
if (!$this->cursor_ext[$query_hash]['cursor']) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'No cursor';
|
|
// for DEBUG, print out each query executed
|
|
if ($this->db_debug) {
|
|
$this->__dbDebug('db', $this->cursor_ext[$query_hash]['query'], 'dbReturn', 'Q');
|
|
}
|
|
// if no DB Handler try to reconnect
|
|
if (!$this->dbh) {
|
|
// if reconnect fails drop out
|
|
if (!$this->__connectToDB()) {
|
|
$this->__dbError(16);
|
|
return false;
|
|
}
|
|
}
|
|
// check that no other query is running right now
|
|
if (!$this->__dbCheckConnectionOk()) {
|
|
return false;
|
|
}
|
|
$this->cursor_ext[$query_hash]['cursor'] =
|
|
$this->db_functions->__dbQuery($this->cursor_ext[$query_hash]['query']);
|
|
// if still no cursor ...
|
|
if (!$this->cursor_ext[$query_hash]['cursor']) {
|
|
if ($this->db_debug) {
|
|
$this->__dbDebug('db', $this->cursor_ext[$query_hash]['query'], 'dbReturn', 'Q');
|
|
}
|
|
// internal error handling
|
|
$this->__dbError(13, $this->cursor_ext[$query_hash]['cursor']);
|
|
return false;
|
|
} else {
|
|
$first_call = true;
|
|
}
|
|
} // only go if NO cursor exists
|
|
|
|
// if cursor exists ...
|
|
if ($this->cursor_ext[$query_hash]['cursor']) {
|
|
if ($first_call === true) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'First call';
|
|
// count the rows returned (if select)
|
|
$this->cursor_ext[$query_hash]['num_rows'] =
|
|
$this->db_functions->__dbNumRows($this->cursor_ext[$query_hash]['cursor']);
|
|
// also set last return
|
|
$this->num_rows = $this->cursor_ext[$query_hash]['num_rows'];
|
|
// count the fields
|
|
$this->cursor_ext[$query_hash]['num_fields'] =
|
|
$this->db_functions->__dbNumFields($this->cursor_ext[$query_hash]['cursor']);
|
|
$this->num_fields = $this->cursor_ext[$query_hash]['num_fields'];
|
|
// set field names
|
|
$this->cursor_ext[$query_hash]['field_names'] = [];
|
|
for ($i = 0; $i < $this->cursor_ext[$query_hash]['num_fields']; $i++) {
|
|
$this->cursor_ext[$query_hash]['field_names'][] =
|
|
$this->db_functions->__dbFieldName(
|
|
$this->cursor_ext[$query_hash]['cursor'],
|
|
$i
|
|
);
|
|
}
|
|
$this->field_names = $this->cursor_ext[$query_hash]['field_names'];
|
|
// reset first call var
|
|
$first_call = false;
|
|
// reset the internal pos counter
|
|
$this->cursor_ext[$query_hash]['pos'] = 0;
|
|
// reset the global (for cache) read counter
|
|
$this->cursor_ext[$query_hash]['read_rows'] = 0;
|
|
// reset read finished flag
|
|
$this->cursor_ext[$query_hash]['finished'] = false;
|
|
$this->cursor_ext[$query_hash]['read_finished'] = false;
|
|
$this->cursor_ext[$query_hash]['db_read_finished'] = false;
|
|
// set cursor ccached flag based on cache flag
|
|
if ($cache < self::NO_CACHE) {
|
|
$this->cursor_ext[$query_hash]['cached'] = true;
|
|
}
|
|
}
|
|
// main database read if not all read and we have an active cursor
|
|
if (
|
|
$this->cursor_ext[$query_hash]['read_rows'] !=
|
|
$this->cursor_ext[$query_hash]['num_rows'] &&
|
|
!is_int($this->cursor_ext[$query_hash]['cursor'])
|
|
) {
|
|
$return = $this->__dbConvertEncoding(
|
|
$this->db_functions->__dbFetchArray(
|
|
$this->cursor_ext[$query_hash]['cursor'],
|
|
$this->db_functions->__dbResultType($assoc_only)
|
|
)
|
|
);
|
|
$this->cursor_ext[$query_hash]['log'][] = 'DB Reading data: '
|
|
. (is_bool($return) ? 'EOF' : 'DATA');
|
|
// if returned is NOT an array, abort to false
|
|
if (!is_array($return)) {
|
|
$return = false;
|
|
}
|
|
}
|
|
// read from cache, or do partial cache set and caching
|
|
if (!$return && $cache == self::USE_CACHE) {
|
|
// check if end of output ...
|
|
if (
|
|
$this->cursor_ext[$query_hash]['pos'] >=
|
|
$this->cursor_ext[$query_hash]['num_rows']
|
|
) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'USE CACHE, end';
|
|
// finish read
|
|
$this->cursor_ext[$query_hash]['finished'] = true;
|
|
// reset pos for next read
|
|
$this->cursor_ext[$query_hash]['pos'] = 0;
|
|
// if not reset given, set the cursor to true, so in a cached
|
|
// call on a different page we don't get problems from
|
|
// DB connection (as those will be LOST)
|
|
$this->cursor_ext[$query_hash]['cursor'] = 1;
|
|
$return = false;
|
|
} else {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'USE CACHE, read data';
|
|
$this->cursor_ext[$query_hash]['read_finished'] = false;
|
|
$this->cursor_ext[$query_hash]['finished'] = false;
|
|
// cached data read
|
|
$return = $this->__dbReturnCacheRead($query_hash, $assoc_only);
|
|
if (
|
|
$this->cursor_ext[$query_hash]['pos'] ==
|
|
$this->cursor_ext[$query_hash]['num_rows']
|
|
) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'USE CACHE, all cache rows read';
|
|
$this->cursor_ext[$query_hash]['read_finished'] = true;
|
|
}
|
|
}
|
|
} else {
|
|
// return row, if last && reset, then unset the whole hash array
|
|
if (!$return && $this->cursor_ext[$query_hash]['pos']) {
|
|
$this->cursor_ext[$query_hash]['pos'] = 0;
|
|
$this->cursor_ext[$query_hash]['cursor'] = 1;
|
|
$this->cursor_ext[$query_hash]['finished'] = true;
|
|
// for clear cache, clear cache, else only write log info
|
|
if ($cache == self::CLEAR_CACHE) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'CLEAR CACHE, end';
|
|
// unset data block only
|
|
$this->cursor_ext[$query_hash]['data'] = [];
|
|
$this->cursor_ext[$query_hash]['cached'] = false;
|
|
} elseif ($cache == self::READ_NEW) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'READ NEW, end';
|
|
} elseif ($cache == self::NO_CACHE) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'NO CACHE, end';
|
|
}
|
|
}
|
|
// if something found, write data into hash array
|
|
if ($return) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'Return Data';
|
|
// internal position counter
|
|
$this->cursor_ext[$query_hash]['pos'] ++;
|
|
$this->cursor_ext[$query_hash]['read_rows'] ++;
|
|
// read is finished
|
|
if (
|
|
$this->cursor_ext[$query_hash]['read_rows'] ==
|
|
$this->cursor_ext[$query_hash]['num_rows']
|
|
) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'Return data all db rows read';
|
|
$this->cursor_ext[$query_hash]['db_read_finished'] = true;
|
|
$this->cursor_ext[$query_hash]['read_finished'] = true;
|
|
}
|
|
// if reset is < NO_CACHE level caching is done, else no
|
|
if ($cache < self::NO_CACHE) {
|
|
$this->cursor_ext[$query_hash]['log'][] = 'Cache Data';
|
|
// why was this here?
|
|
// $temp = [];
|
|
// foreach ($return as $field_name => $data) {
|
|
// $temp[$field_name] = $data;
|
|
// }
|
|
$this->cursor_ext[$query_hash]['data'][] = $return;
|
|
}
|
|
} // cached data if
|
|
} // cached or not if
|
|
} // cursor exists
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* executes the query and returns & sets the internal cursor
|
|
* furthermore this functions also sets varios other vars
|
|
* like num_rows, num_fields, etc depending on query
|
|
* for INSERT INTO queries it is highly recommended to set the pk_name to avoid an
|
|
* additional read from the database for the PK NAME
|
|
* @param string $query the query, if not given,
|
|
* the query class var will be used
|
|
* if this was not set, method will quit with a 0 (failure)
|
|
* @param string $pk_name optional primary key name, for insert id
|
|
* return if the pk name is very different
|
|
* if pk name is table name and _id, pk_name
|
|
* is not needed to be set
|
|
* if NULL is given here, no RETURNING will be auto added
|
|
* @return object|resource|bool cursor for this query or false on error (PgSql\Result)
|
|
*/
|
|
public function dbExec(string $query = '', string $pk_name = '')
|
|
{
|
|
$this->__dbErrorReset();
|
|
// prepare and check if we can actually run it
|
|
if ($this->__dbPrepareExec($query, $pk_name) === false) {
|
|
// bail if no query hash set
|
|
return false;
|
|
}
|
|
// ** 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) {
|
|
$this->__dbError(13);
|
|
return false;
|
|
}
|
|
// if FALSE returned, set error stuff
|
|
// run the post exec processing
|
|
if (!$this->__dbPostExec()) {
|
|
return false;
|
|
} else {
|
|
return $this->cursor;
|
|
}
|
|
}
|
|
|
|
// add adbExecParams(string $query = '', array $params = [], string $pk_name = ')
|
|
|
|
/**
|
|
* executes a cursor and returns the data, if no more data 0 will be returned
|
|
* @param object|resource|bool $cursor the cursor from db_exec or
|
|
* pg_query/pg_exec/mysql_query
|
|
* if not set will use internal cursor,
|
|
* if not found, stops with 0 (error)
|
|
* (PgSql\Result)
|
|
* @param bool $assoc_only false is default,
|
|
* if true only named rows,
|
|
* not numbered index rows
|
|
* @return array<mixed>|bool row array or false on error
|
|
*/
|
|
public function dbFetchArray($cursor = false, bool $assoc_only = false)
|
|
{
|
|
$this->__dbErrorReset();
|
|
// set last available cursor if none set or false
|
|
if ($cursor === false) {
|
|
$cursor = $this->cursor;
|
|
}
|
|
if ($cursor === false) {
|
|
$this->__dbError(12);
|
|
return false;
|
|
}
|
|
return $this->__dbConvertEncoding(
|
|
$this->db_functions->__dbFetchArray(
|
|
$cursor,
|
|
$this->db_functions->__dbResultType($assoc_only)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* returns the FIRST row of the given query
|
|
* @param string $query the query to be executed
|
|
* @param bool $assoc_only if true, only return assoc entry (default false)
|
|
* @return array<mixed>|bool row array or false on error
|
|
*/
|
|
public function dbReturnRow(string $query, bool $assoc_only = false)
|
|
{
|
|
$this->__dbErrorReset();
|
|
if (!$query) {
|
|
$this->__dbError(11);
|
|
return false;
|
|
}
|
|
// before doing ANYTHING check if query is
|
|
// "SELECT ..." everything else does not work
|
|
if (!$this->__checkQueryForSelect($query)) {
|
|
$this->__dbError(17, false, $query);
|
|
return false;
|
|
}
|
|
$cursor = $this->dbExec($query);
|
|
if ($cursor === false) {
|
|
return false;
|
|
}
|
|
$result = $this->dbFetchArray($cursor, $assoc_only);
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* createds an array of hashes of the query (all data)
|
|
* @param string $query the query to be executed
|
|
* @param bool $assoc_only if true, only name ref are returned (default true)
|
|
* @return array<mixed>|bool array of hashes (row -> fields), false on error
|
|
*/
|
|
public function dbReturnArray(string $query, bool $assoc_only = true)
|
|
{
|
|
$this->__dbErrorReset();
|
|
if (!$query) {
|
|
$this->__dbError(11);
|
|
return false;
|
|
}
|
|
// before doing ANYTHING check if query is "SELECT ..." everything else does not work
|
|
if (!$this->__checkQueryForSelect($query)) {
|
|
$this->__dbError(17, false, $query);
|
|
return false;
|
|
}
|
|
$cursor = $this->dbExec($query);
|
|
if ($cursor === false) {
|
|
return false;
|
|
}
|
|
$rows = [];
|
|
while (is_array($res = $this->dbFetchArray($cursor, $assoc_only))) {
|
|
// $data = [];
|
|
// for ($i = 0; $i < $this->num_fields; $i++) {
|
|
// $data[$this->field_names[$i]] = $res[$this->field_names[$i]] ?? null;
|
|
// }
|
|
// $rows[] = $data;
|
|
$rows[] = $res;
|
|
}
|
|
return $rows;
|
|
}
|
|
|
|
// ***************************
|
|
// CURSOR EXT CACHE RESET
|
|
// ***************************
|
|
|
|
/**
|
|
* resets all data stored to this query
|
|
* @param string $query The Query whose cache should be cleaned
|
|
* @return bool false if query not found, true if success
|
|
*/
|
|
public function dbCacheReset(string $query): bool
|
|
{
|
|
$this->__dbErrorReset();
|
|
$query_hash = $this->dbGetQueryHash($query);
|
|
// clears cache for this query
|
|
if (empty($this->cursor_ext[$query_hash]['query'])) {
|
|
$this->__dbError(18);
|
|
return false;
|
|
}
|
|
unset($this->cursor_ext[$query_hash]);
|
|
return true;
|
|
}
|
|
|
|
// ***************************
|
|
// CURSOR EXT DATA CHECK
|
|
// ***************************
|
|
|
|
/**
|
|
* returns the full array for cursor ext
|
|
* or cursor for one query
|
|
* or detail data fonr one query cursor data
|
|
* @param string|null $query Query string, if not null convert to hash
|
|
* and return set cursor ext for only this
|
|
* if not found or null return null
|
|
* @return array<mixed>|string|int|resource|object|null
|
|
* Cursor Extended array full if no parameter
|
|
* Key is hash string from query run
|
|
* Or cursor data entry if query field is set
|
|
* If nothing found return null
|
|
*/
|
|
public function dbGetCursorExt($query = null, string $query_field = '')
|
|
{
|
|
if ($query !== null) {
|
|
$query_hash = $this->dbGetQueryHash($query);
|
|
if (
|
|
is_array($this->cursor_ext) &&
|
|
isset($this->cursor_ext[$query_hash])
|
|
) {
|
|
if (empty($query_field)) {
|
|
return $this->cursor_ext[$query_hash];
|
|
} else {
|
|
return $this->cursor_ext[$query_hash][$query_field] ?? null;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return $this->cursor_ext;
|
|
}
|
|
|
|
/**
|
|
* returns the current position the read out
|
|
* @param string $query query to find in cursor_ext
|
|
* @return int|bool query position (row pos), false on error
|
|
*/
|
|
public function dbGetCursorPos(string $query)
|
|
{
|
|
$this->__dbErrorReset();
|
|
if (!$query) {
|
|
$this->__dbError(11);
|
|
return false;
|
|
}
|
|
$query_hash = $this->dbGetQueryHash($query);
|
|
return (int)$this->cursor_ext[$query_hash]['pos'];
|
|
}
|
|
|
|
/**
|
|
* returns the number of rows for the current select query
|
|
* @param string $query query to find in cursor_ext
|
|
* @return int|bool query position (row pos), false on error
|
|
*/
|
|
public function dbGetCursorNumRows(string $query)
|
|
{
|
|
$this->__dbErrorReset();
|
|
if (!$query) {
|
|
$this->__dbError(11);
|
|
return false;
|
|
}
|
|
$query_hash = $this->dbGetQueryHash($query);
|
|
return (int)$this->cursor_ext[$query_hash]['num_rows'];
|
|
}
|
|
|
|
// ***************************
|
|
// MAXIMUM QUERY EXECUTION CHECK HELPERS
|
|
// ***************************
|
|
|
|
/**
|
|
* resets the call times for the max query called to 0
|
|
* USE CAREFULLY: rather make the query prepare -> execute
|
|
* @param string $query query string
|
|
* @return void has no return
|
|
*/
|
|
public function dbResetQueryCalled(string $query): void
|
|
{
|
|
$this->query_called[$this->dbGetQueryHash($query)] = 0;
|
|
}
|
|
|
|
/**
|
|
* gets how often a query was called already
|
|
* @param string $query query string
|
|
* @return int count of times the query was executed
|
|
*/
|
|
public function dbGetQueryCalled(string $query): int
|
|
{
|
|
$query_hash = $this->dbGetQueryHash($query);
|
|
if ($this->query_called[$query_hash]) {
|
|
return $this->query_called[$query_hash];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// ***************************
|
|
// PREPARED QUERY WORK
|
|
// ***************************
|
|
|
|
/**
|
|
* prepares a query
|
|
* for INSERT INTO queries it is highly recommended to set the pk_name to avoid an additional
|
|
* read from the database for the PK NAME
|
|
* @param string $stm_name statement name
|
|
* @param string $query queryt string to run
|
|
* @param string $pk_name optional primary key
|
|
* @return bool|object|resource false on error, true on warning or
|
|
* result on full ok (PgSql\Result)
|
|
*/
|
|
public function dbPrepare(string $stm_name, string $query, string $pk_name = '')
|
|
{
|
|
$this->__dbErrorReset();
|
|
$matches = [];
|
|
if (!$query) {
|
|
$this->__dbError(11);
|
|
return false;
|
|
}
|
|
// if no DB Handler drop out
|
|
if (!$this->dbh) {
|
|
// if reconnect fails drop out
|
|
if (!$this->__connectToDB()) {
|
|
$this->__dbError(16);
|
|
return false;
|
|
}
|
|
}
|
|
// check that no other query is running right now
|
|
if (!$this->__dbCheckConnectionOk()) {
|
|
return false;
|
|
}
|
|
// no statement name
|
|
if (empty($stm_name)) {
|
|
$this->__dbError(25);
|
|
return false;
|
|
}
|
|
// check if this was already prepared
|
|
if (
|
|
!array_key_exists($stm_name, $this->prepare_cursor) ||
|
|
!is_array($this->prepare_cursor[$stm_name])
|
|
) {
|
|
// init cursor
|
|
$this->prepare_cursor[$stm_name] = [
|
|
'pk_name' => '',
|
|
'count' => 0,
|
|
'query' => '',
|
|
'result' => null,
|
|
'returning_id' => false
|
|
];
|
|
// if this is an insert query, check if we can add a return
|
|
if ($this->__checkQueryForInsert($query, true)) {
|
|
if ($pk_name != 'NULL') {
|
|
// set primary key name
|
|
// current: only via parameter
|
|
if (!$pk_name) {
|
|
// read the primary key from the table,
|
|
// if we do not have one, we get nothing in return
|
|
list($schema, $table) = $this->__dbReturnTable($query);
|
|
if (empty($this->pk_name_table[$table])) {
|
|
$this->pk_name_table[$table] = $this->db_functions->__dbPrimaryKey($table, $schema);
|
|
}
|
|
$pk_name = $this->pk_name_table[$table];
|
|
}
|
|
if ($pk_name) {
|
|
$this->prepare_cursor[$stm_name]['pk_name'] = $pk_name;
|
|
}
|
|
// if no returning, then add it
|
|
if (!preg_match("/ returning /i", $query) && $this->prepare_cursor[$stm_name]['pk_name']) {
|
|
$query .= " RETURNING " . $this->prepare_cursor[$stm_name]['pk_name'];
|
|
$this->prepare_cursor[$stm_name]['returning_id'] = true;
|
|
} elseif (
|
|
preg_match("/ returning (.*)/i", $query, $matches) &&
|
|
$this->prepare_cursor[$stm_name]['pk_name']
|
|
) {
|
|
// if returning exists but not pk_name, add it
|
|
if (!preg_match("/{$this->prepare_cursor[$stm_name]['pk_name']}/", $matches[1])) {
|
|
$query .= " , " . $this->prepare_cursor[$stm_name]['pk_name'];
|
|
}
|
|
$this->prepare_cursor[$stm_name]['returning_id'] = true;
|
|
}
|
|
} else {
|
|
$this->prepare_cursor[$stm_name]['pk_name'] = $pk_name;
|
|
}
|
|
}
|
|
$match = [];
|
|
// search for $1, $2, in the query and push it into the control array
|
|
// skip counts for same eg $1, $1, $2 = 2 and not 3
|
|
preg_match_all('/(\$[0-9]{1,})/', $query, $match);
|
|
$this->prepare_cursor[$stm_name]['count'] = count(array_unique($match[1]));
|
|
$this->prepare_cursor[$stm_name]['query'] = $query;
|
|
$result = $this->db_functions->__dbPrepare($stm_name, $query);
|
|
if ($result) {
|
|
$this->prepare_cursor[$stm_name]['result'] = $result;
|
|
return $result;
|
|
} else {
|
|
$this->__dbError(
|
|
21,
|
|
false,
|
|
$stm_name . ': Prepare field with: ' . $stm_name . ' | ' . $query
|
|
);
|
|
return $result;
|
|
}
|
|
} else {
|
|
// thrown warning
|
|
$this->__dbWarning(20, false, $stm_name);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* runs a prepare query
|
|
* @param string $stm_name statement name for the query to run
|
|
* @param array<mixed> $data data to run for this query, empty array for none
|
|
* @return mixed false on error, or result on OK
|
|
*/
|
|
public function dbExecute(string $stm_name, array $data = [])
|
|
{
|
|
$this->__dbErrorReset();
|
|
// if no DB Handler drop out
|
|
if (!$this->dbh) {
|
|
// if reconnect fails drop out
|
|
if (!$this->__connectToDB()) {
|
|
$this->__dbError(16);
|
|
return false;
|
|
}
|
|
}
|
|
// check that no other query is running right now
|
|
if (!$this->__dbCheckConnectionOk()) {
|
|
return false;
|
|
}
|
|
// no statement name
|
|
if (empty($stm_name)) {
|
|
$this->__dbError(25);
|
|
return false;
|
|
}
|
|
// if we do not have no prepare cursor array entry
|
|
// for this statement name, abort
|
|
if (
|
|
empty($this->prepare_cursor[$stm_name]) ||
|
|
!is_array($this->prepare_cursor[$stm_name])
|
|
) {
|
|
$this->__dbError(
|
|
24,
|
|
false,
|
|
$stm_name . ': We do not have a prepared query entry for this statement name.'
|
|
);
|
|
return false;
|
|
}
|
|
// if the count does not match
|
|
if ($this->prepare_cursor[$stm_name]['count'] != count($data)) {
|
|
$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;
|
|
}
|
|
if ($this->db_debug) {
|
|
$this->__dbDebug('db', $this->__dbDebugPrepare($stm_name, $data), 'dbExecPrep', 'Q');
|
|
}
|
|
$result = $this->db_functions->__dbExecute($stm_name, $data);
|
|
if ($result === false) {
|
|
$this->log->debug('ExecuteData', 'ERROR in STM[' . $stm_name . '|'
|
|
. $this->prepare_cursor[$stm_name]['result'] . ']: '
|
|
. $this->log->prAr($data));
|
|
$this->__dbError(
|
|
22,
|
|
$this->prepare_cursor[$stm_name]['result'],
|
|
$stm_name . ': Execution failed'
|
|
);
|
|
return false;
|
|
}
|
|
if (
|
|
// pure insert wth pk name
|
|
($this->__checkQueryForInsert($this->prepare_cursor[$stm_name]['query'], true) &&
|
|
$this->prepare_cursor[$stm_name]['pk_name'] != 'NULL') ||
|
|
// insert or update with returning set
|
|
($this->__checkQueryForInsert($this->prepare_cursor[$stm_name]['query']) &&
|
|
$this->prepare_cursor[$stm_name]['returning_id'] === true
|
|
)
|
|
) {
|
|
$this->__dbSetInsertId(
|
|
$this->prepare_cursor[$stm_name]['returning_id'],
|
|
$this->prepare_cursor[$stm_name]['query'],
|
|
$this->prepare_cursor[$stm_name]['pk_name'],
|
|
$result,
|
|
$stm_name
|
|
);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
// ***************************
|
|
// ASYNCHRONUS EXECUTION/CHECK
|
|
// ***************************
|
|
|
|
/**
|
|
* executres the query async so other methods can be run during this
|
|
* for INSERT INTO queries it is highly recommended to set the pk_name
|
|
* to avoid an additional read from the database for the PK NAME
|
|
* NEEDS : dbCheckAsync
|
|
* @param string $query query to run
|
|
* @param string $pk_name optional primary key name, only used with
|
|
* insert for returning call
|
|
* @return bool true if async query was sent ok,
|
|
* false if error happened
|
|
*/
|
|
public function dbExecAsync(string $query, string $pk_name = ''): bool
|
|
{
|
|
$this->__dbErrorReset();
|
|
// prepare and check if we can actually run the query
|
|
if (($query_hash = $this->__dbPrepareExec($query, $pk_name)) === false) {
|
|
// bail if no hash set
|
|
return false;
|
|
}
|
|
// run the async query, this just returns true or false
|
|
// the actually result is in dbCheckAsync
|
|
if (!$this->db_functions->__dbSendQuery($this->query)) {
|
|
// if failed, process here
|
|
$this->__dbError(40);
|
|
return false;
|
|
} else {
|
|
$this->async_running = (string)$query_hash;
|
|
// all ok, we return true
|
|
// (as would be from the original send query function)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* checks a previous async query and returns data if finished
|
|
* NEEDS : dbExecAsync
|
|
* @return bool|object|resource cursor resource if the query is still running,
|
|
* false if an error occured or cursor of that query
|
|
* (PgSql\Result)
|
|
*/
|
|
public function dbCheckAsync()
|
|
{
|
|
$this->__dbErrorReset();
|
|
// if there is actually a async query there
|
|
if (!empty($this->async_running)) {
|
|
// alternative try __dbConnectionBusySocketWait
|
|
if ($this->db_functions->__dbConnectionBusy()) {
|
|
return true;
|
|
} else {
|
|
$cursor = $this->db_functions->__dbGetResult();
|
|
if ($cursor === false) {
|
|
$this->__dbError(43);
|
|
return false;
|
|
}
|
|
// get the result/or error
|
|
$this->cursor = $cursor;
|
|
$this->async_running = '';
|
|
// run the post exec processing
|
|
if (!$this->__dbPostExec()) {
|
|
return false;
|
|
} else {
|
|
return $this->cursor;
|
|
}
|
|
}
|
|
} else {
|
|
// if no async running print error
|
|
$this->__dbError(
|
|
42,
|
|
false,
|
|
'No async query has been started yet.'
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the current async running query hash
|
|
* @return string Current async running query hash
|
|
*/
|
|
public function dbGetAsyncRunning(): string
|
|
{
|
|
return $this->async_running;
|
|
}
|
|
|
|
// ***************************
|
|
// COMPLEX WRITE WITH CONFIG ARRAYS
|
|
// ***************************
|
|
|
|
// ** REMARK **
|
|
// db_write_data is the old without separate update no write list
|
|
// db_write_data_ext is the extended with additional array
|
|
// for no write list for update
|
|
|
|
/**
|
|
* writes into one table based on array of table columns
|
|
* @param array<mixed> $write_array list of elements to write
|
|
* @param array<mixed> $not_write_array list of elements not to write
|
|
* @param int $primary_key id key to decide if we write insert or update
|
|
* @param string $table name for the target table
|
|
* @param array<mixed> $data data array to override _POST data
|
|
* @return int|bool primary key
|
|
*/
|
|
public function dbWriteData(
|
|
array $write_array,
|
|
array $not_write_array,
|
|
int $primary_key,
|
|
string $table,
|
|
array $data = []
|
|
) {
|
|
if (!is_array($write_array)) {
|
|
$write_array = [];
|
|
}
|
|
if (!is_array($not_write_array)) {
|
|
$not_write_array = [];
|
|
}
|
|
$not_write_update_array = [];
|
|
return $this->dbWriteDataExt(
|
|
$write_array,
|
|
$primary_key,
|
|
$table,
|
|
$not_write_array,
|
|
$not_write_update_array,
|
|
$data
|
|
);
|
|
}
|
|
|
|
/**
|
|
* writes into one table based on array of table columns
|
|
* PARAM INFO: $primary key
|
|
* this can be a plain string/int and will be internal transformed into the array form
|
|
* or it takes the array form of array [row => column, value => pk value]
|
|
* @param array<mixed> $write_array list of elements to write
|
|
* @param int|string|array<mixed> $primary_key primary key string or array set
|
|
* @param string $table name for the target table
|
|
* @param array<mixed> $not_write_array list of elements not to write (optional)
|
|
* @param array<mixed> $not_write_update_array list of elements not
|
|
* to write during update (optional)
|
|
* @param array<mixed> $data optional array with data
|
|
* if not _POST vars are used
|
|
* @return int|bool primary key
|
|
*/
|
|
public function dbWriteDataExt(
|
|
array $write_array,
|
|
$primary_key,
|
|
string $table,
|
|
array $not_write_array = [],
|
|
array $not_write_update_array = [],
|
|
array $data = []
|
|
) {
|
|
if (!is_array($primary_key)) {
|
|
$primary_key = [
|
|
'row' => $table . '_id',
|
|
'value' => $primary_key
|
|
];
|
|
} else {
|
|
if (!isset($primary_key['row'])) {
|
|
$primary_key['row'] = '';
|
|
}
|
|
if (!isset($primary_key['value'])) {
|
|
$primary_key['value'] = '';
|
|
}
|
|
}
|
|
// var set for strings
|
|
$q_sub_value = '';
|
|
$q_sub_data = '';
|
|
// get the table layout and row types
|
|
$table_data = $this->dbShowTableMetaData(($this->db_schema ? $this->db_schema . '.' : '') . $table);
|
|
if (!is_array($table_data)) {
|
|
return false;
|
|
}
|
|
// @phan HACK
|
|
$primary_key['value'] = $primary_key['value'] ?? '';
|
|
$primary_key['row'] = $primary_key['row'] ?? '';
|
|
// loop through the write array and each field to build the query
|
|
foreach ($write_array as $field) {
|
|
if (
|
|
(empty($primary_key['value']) ||
|
|
(!empty($primary_key['value']) &&
|
|
!in_array($field, $not_write_update_array))
|
|
) &&
|
|
!in_array($field, $not_write_array)
|
|
) {
|
|
// data from external or data field
|
|
$_data = null;
|
|
if (count($data) >= 1 && array_key_exists($field, $data)) {
|
|
$_data = $data[$field];
|
|
} elseif (array_key_exists($field, $GLOBALS)) {
|
|
$_data = $GLOBALS[$field];
|
|
}
|
|
$has_default = $table_data[$field]['has default'];
|
|
$not_null = $table_data[$field]['not null'];
|
|
// if not null and string => '', if not null and int or numeric => 0, if bool => skip, all others skip
|
|
if ($not_null && $_data == null) {
|
|
if (strstr($table_data[$field]['type'], 'int') || strstr($table_data[$field]['type'], 'numeric')) {
|
|
$_data = 0;
|
|
} else {
|
|
$_data = '';
|
|
}
|
|
}
|
|
// we detect bool, so we can force a write on "false"
|
|
$is_bool = $table_data[$field]['type'] == 'bool' ? true : false;
|
|
// write if the field has to be not null, or if there is
|
|
// no data and the field has no default values or if there
|
|
// is data or if this is an update and there is no data (set null)
|
|
if (
|
|
($not_null && $_data) ||
|
|
(!$has_default && !$_data) ||
|
|
(is_numeric($_data) && $_data) ||
|
|
($primary_key['value'] && !$_data) ||
|
|
$_data
|
|
) {
|
|
if ($q_sub_value && !$primary_key['value']) {
|
|
$q_sub_value .= ', ';
|
|
}
|
|
if ($q_sub_data) {
|
|
// && (!$primary_key ||
|
|
// ($primary_key && !in_array($field, $not_write_array))))
|
|
$q_sub_data .= ', ';
|
|
}
|
|
if ($primary_key['value']) {
|
|
$q_sub_data .= $field . ' = ';
|
|
} else {
|
|
$q_sub_value .= $field;
|
|
}
|
|
// if field is "date" and -- -> reset
|
|
if ($_data == '--' && strstr($table_data[$field]['type'], 'date')) {
|
|
$_data = '';
|
|
}
|
|
// write data into sql string
|
|
if (strstr($table_data[$field]['type'], 'int')) {
|
|
$q_sub_data .= is_numeric($_data) ? $_data : 'NULL';
|
|
} else {
|
|
// if bool -> set bool, else write data
|
|
$q_sub_data .= isset($_data) ?
|
|
"'" . (
|
|
$is_bool ?
|
|
$this->dbBoolean($_data, true) :
|
|
$this->dbEscapeString($_data)
|
|
) . "'" :
|
|
'NULL';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// first work contact itself (we need contact id for everything else)
|
|
if ($primary_key['value'] && $primary_key['row']) {
|
|
$q = 'UPDATE ' . $table . ' SET ';
|
|
$q .= $q_sub_data . ' ';
|
|
$q .= 'WHERE ' . $primary_key['row'] . ' = ' . $primary_key['value'];
|
|
} else {
|
|
$q = 'INSERT INTO ' . $table . ' (';
|
|
$q .= $q_sub_value;
|
|
$q .= ') VALUES (';
|
|
$q .= $q_sub_data;
|
|
$q .= ')';
|
|
}
|
|
if (!$this->dbExec($q)) {
|
|
return false;
|
|
}
|
|
if (!$primary_key['value']) {
|
|
$primary_key['value'] = $this->dbGetInsertPK();
|
|
}
|
|
// if there is not priamry key value field return false
|
|
return $primary_key['value'] ?? false;
|
|
}
|
|
|
|
// ***************************
|
|
// INTERNAL SETTINGS READ/CHANGE
|
|
// ***************************
|
|
|
|
/**
|
|
* switches the debug flag on or off
|
|
* if none given, then return current set only
|
|
* @param bool|null $debug true/false or null for just getting current set
|
|
* @return bool Current debug flag as set
|
|
*/
|
|
public function dbSetDebug($debug = null): bool
|
|
{
|
|
if ($debug !== null) {
|
|
$this->db_debug = $debug;
|
|
}
|
|
return $this->db_debug;
|
|
}
|
|
|
|
/**
|
|
* Switches db debug flag on or off
|
|
* OR
|
|
* with the optional parameter fix sets debug
|
|
* returns current set stats
|
|
* @param bool|null $debug Flag to turn debug on off or null for toggle
|
|
* @return bool Current debug status
|
|
* True for debug is on, False for off
|
|
*/
|
|
public function dbToggleDebug(?bool $debug = null): bool
|
|
{
|
|
if ($debug !== null) {
|
|
$this->db_debug = $debug;
|
|
} else {
|
|
$this->db_debug = $this->db_debug ? false : true;
|
|
}
|
|
return $this->db_debug;
|
|
}
|
|
|
|
/**
|
|
* Return current set db debug flag status
|
|
* @return bool Current debug status
|
|
*/
|
|
public function dbGetDebug(): bool
|
|
{
|
|
return $this->db_debug;
|
|
}
|
|
|
|
/**
|
|
* set max query calls, set to -1 to disable loop
|
|
* protection. this will generate a warning
|
|
* empty call (null) will reset to default
|
|
* @param int|null $max_calls Set the max loops allowed
|
|
* @return bool True for succesfull set
|
|
*/
|
|
public function dbSetMaxQueryCall(?int $max_calls = null): bool
|
|
{
|
|
$this->__dbErrorReset();
|
|
// if null then reset to default
|
|
if ($max_calls === null) {
|
|
$max_calls = self::DEFAULT_MAX_QUERY_CALL;
|
|
}
|
|
// if -1 then disable loop check
|
|
// DANGEROUS, WARN USER
|
|
if ($max_calls == -1) {
|
|
$this->__dbWarning(50);
|
|
}
|
|
// negative or 0
|
|
if ($max_calls < -1 || $max_calls == 0) {
|
|
$this->__dbError(51);
|
|
// early abort
|
|
return false;
|
|
}
|
|
// ok entry, set
|
|
$this->MAX_QUERY_CALL = $max_calls;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* returns current set max query calls for loop avoidance
|
|
* @return int Integer number, if -1 the loop check is disabled
|
|
*/
|
|
public function dbGetMaxQueryCall(): int
|
|
{
|
|
return $this->MAX_QUERY_CALL;
|
|
}
|
|
|
|
/**
|
|
* sets new db schema
|
|
* @param string $db_schema Schema name
|
|
* @return bool False on failure to find schema value or set schema,
|
|
* True on successful set
|
|
*/
|
|
public function dbSetSchema(string $db_schema)
|
|
{
|
|
$this->__dbErrorReset();
|
|
if (empty($db_schema)) {
|
|
$this->__dbError(70);
|
|
return false;
|
|
}
|
|
$status = false;
|
|
$set_value = $this->db_functions->__dbSetSchema($db_schema);
|
|
switch ($set_value) {
|
|
// no problem
|
|
case 0:
|
|
$this->db_schema = $db_schema;
|
|
$status = true;
|
|
break;
|
|
// cursor failed (1) or schema does not exists (2)
|
|
case 1:
|
|
case 2:
|
|
// setting schema failed (3)
|
|
case 3:
|
|
$this->__dbError(71);
|
|
$status = false;
|
|
break;
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* returns the current set db schema
|
|
* @param bool $class_var If set to true, will return class var and
|
|
* not DB setting
|
|
* @return string DB schema name or schema string
|
|
*/
|
|
public function dbGetSchema(bool $class_var = false): string
|
|
{
|
|
// return $this->db_schema;
|
|
if ($class_var === true) {
|
|
return $this->db_schema;
|
|
}
|
|
return $this->db_functions->__dbGetSchema();
|
|
}
|
|
|
|
/**
|
|
* sets the client encoding in the postgres database
|
|
* @param string $db_encoding Valid encoding name,
|
|
* so the the data gets converted to this encoding
|
|
* @return bool false, or true of db exec encoding set
|
|
*/
|
|
public function dbSetEncoding(string $db_encoding): bool
|
|
{
|
|
$this->__dbErrorReset();
|
|
// no encding, abort
|
|
if (empty($db_encoding)) {
|
|
$this->__dbError(80);
|
|
return false;
|
|
}
|
|
// set client encoding on database side
|
|
$status = false;
|
|
$set_value = $this->db_functions->__dbSetEncoding($db_encoding);
|
|
switch ($set_value) {
|
|
// no problem
|
|
case 0:
|
|
$this->db_encoding = $db_encoding;
|
|
$status = true;
|
|
break;
|
|
// 1 & 2 are for encoding not found
|
|
case 1:
|
|
case 2:
|
|
// 3 is set failed
|
|
case 3:
|
|
$this->__dbError(81);
|
|
$status = false;
|
|
break;
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* returns the current set client encoding from the connected DB
|
|
* @param bool $class_var If set to true, will return class var and
|
|
* not DB setting
|
|
* @return string current client encoding
|
|
*/
|
|
public function dbGetEncoding(bool $class_var = false): string
|
|
{
|
|
if ($class_var === true) {
|
|
return $this->db_encoding;
|
|
}
|
|
return $this->db_functions->__dbGetEncoding();
|
|
}
|
|
|
|
/**
|
|
* Resets client encodig back to databse encoding
|
|
* @return void
|
|
*/
|
|
public function dbResetEncoding(): void
|
|
{
|
|
$this->dbExec('RESET client_encoding');
|
|
}
|
|
|
|
/**
|
|
* Set the to_encoding var that will trigger on return of selected data
|
|
* on the fly PHP encoding conversion
|
|
* Alternative use dbSetEcnoding to trigger encoding change on the DB side
|
|
* Set to empty string to turn off
|
|
* @param string $encoding PHP Valid encoding to set
|
|
* @return string Current set encoding
|
|
*/
|
|
public function dbSetToEncoding(string $encoding): string
|
|
{
|
|
$this->to_encoding = $encoding;
|
|
return $this->to_encoding;
|
|
}
|
|
|
|
/**
|
|
* Returns current set to encoding
|
|
* @return string Current set encoding
|
|
*/
|
|
public function dbGetToEncoding(): string
|
|
{
|
|
return $this->to_encoding;
|
|
}
|
|
|
|
// ***************************
|
|
// QUERY DATA AND DB HANDLER
|
|
// ***************************
|
|
|
|
/**
|
|
* Return current database handler
|
|
* @return object|resource|bool|int|null
|
|
*/
|
|
public function dbGetDbh()
|
|
{
|
|
return $this->dbh;
|
|
}
|
|
|
|
/**
|
|
* Returns hash for query
|
|
* Hash is used in all internal storage systems for return data
|
|
* @param string $query The query to create the hash from
|
|
* @return string Hash, as set by hash lpng
|
|
*/
|
|
public function dbGetQueryHash(string $query): string
|
|
{
|
|
return Hash::__hashLong($query);
|
|
}
|
|
|
|
/**
|
|
* Get current set query
|
|
* @return string Current set query string
|
|
*/
|
|
public function dbGetQuery(): string
|
|
{
|
|
return $this->query;
|
|
}
|
|
|
|
/**
|
|
* Clear current query
|
|
* @return void
|
|
*/
|
|
public function dbResetQuery(): void
|
|
{
|
|
$this->query = '';
|
|
}
|
|
|
|
// ***************************
|
|
// INTERNAL VARIABLES READ POST QUERY RUN
|
|
// ***************************
|
|
|
|
/**
|
|
* returns current set primary key name for last run query
|
|
* Is empty string if not setable
|
|
*
|
|
* @return string Primary key name
|
|
*/
|
|
public function dbGetInsertPKName(): string
|
|
{
|
|
return (string)$this->insert_id_pk_name;
|
|
}
|
|
|
|
/**
|
|
* Returns current primary key for inserted row.
|
|
* Either a single element for a single insert or an array
|
|
* if multiple insert values where used.
|
|
*
|
|
* @return array<mixed>|string|int|null Current insert query primary key
|
|
*/
|
|
public function dbGetInsertPK()
|
|
{
|
|
if (empty($this->insert_id_pk_name)) {
|
|
return null;
|
|
}
|
|
return $this->dbGetReturningExt($this->insert_id_pk_name);
|
|
}
|
|
|
|
/**
|
|
* Returns the full RETURNING array
|
|
* If no parameter given returns as is:
|
|
* Either as single array level for single insert
|
|
* Or nested array for multiple insert values
|
|
*
|
|
* If key was set only returns tghose values directly or as array
|
|
*
|
|
* On multiple insert return the position for which to return can be set too
|
|
*
|
|
* Replacement for insert_id_ext array access before
|
|
*
|
|
* @param string|null $key
|
|
* @param integer|null $pos
|
|
* @return array<mixed>|string|int|null
|
|
*/
|
|
public function dbGetReturningExt(?string $key = null, ?int $pos = null)
|
|
{
|
|
// return as is if key is null
|
|
if ($key === null) {
|
|
if (count($this->insert_id_arr) == 1) {
|
|
// return as nul if not found
|
|
return $this->insert_id_arr[0] ?? null;
|
|
} else {
|
|
return $this->insert_id_arr;
|
|
}
|
|
}
|
|
// no key string set
|
|
if (empty($key)) {
|
|
return null;
|
|
}
|
|
if (
|
|
count($this->insert_id_arr) == 1 &&
|
|
isset($this->insert_id_arr[0][$key])
|
|
) {
|
|
return $this->insert_id_arr[0][$key];
|
|
} elseif (count($this->insert_id_arr) > 1) {
|
|
// do we try to find at one position
|
|
if ($pos !== null) {
|
|
if (isset($this->insert_id_arr[$pos][$key])) {
|
|
return $this->insert_id_arr[$pos][$key];
|
|
} else {
|
|
return null;
|
|
}
|
|
} else {
|
|
// find in all inside the array
|
|
$__arr = array_column($this->insert_id_arr, $key);
|
|
/** @phpstan-ignore-next-line [Why is this always true?] */
|
|
if (count($__arr)) {
|
|
return $__arr;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
} else {
|
|
// not found
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Always returns the returning block as an array
|
|
* @return array<mixed> All returning data as array. even if one row only
|
|
*/
|
|
public function dbGetReturningArray(): array
|
|
{
|
|
return $this->insert_id_arr;
|
|
}
|
|
|
|
/**
|
|
* returns current number of rows that where
|
|
* affected by UPDATE/SELECT, etc
|
|
* null on empty
|
|
* @return int|null Number of rows or null if not set
|
|
*/
|
|
public function dbGetNumRows(): ?int
|
|
{
|
|
return $this->num_rows;
|
|
}
|
|
|
|
/**
|
|
* Number of fields in select query
|
|
* @return integer|null Number of fields in select or null if not set
|
|
*/
|
|
public function dbGetNumFields(): ?int
|
|
{
|
|
return $this->num_fields;
|
|
}
|
|
|
|
/**
|
|
* Return field names from query
|
|
* @return array<mixed> Field names as array
|
|
*/
|
|
public function dbGetFieldNames(): array
|
|
{
|
|
return $this->field_names;
|
|
}
|
|
|
|
// ***************************
|
|
// ERROR AND WARNING DATA
|
|
// ***************************
|
|
|
|
/**
|
|
* Sets error number that was last
|
|
* 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 dbGetLastError(bool $transform = false): string
|
|
{
|
|
// if no error, return empty
|
|
if (empty($this->error_id)) {
|
|
return '';
|
|
}
|
|
// either error number or error detail string
|
|
if (!$transform) {
|
|
return $this->error_id;
|
|
} else {
|
|
return $this->error_id . ': '
|
|
. ($this->error_string[$this->error_id] ?? '[NO ERROR MESSAGE]');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets warning number that was last
|
|
* So we always have the last warning number stored even if a new one is created
|
|
* @param boolean $transform Set to true to transform into id + warning message
|
|
* @return string Last Warning number as string or warning message
|
|
*/
|
|
public function dbGetLastWarning(bool $transform = false)
|
|
{
|
|
// if no warning, return empty
|
|
if (empty($this->warning_id)) {
|
|
return '';
|
|
}
|
|
// either warning number or warning detail string
|
|
if (!$transform) {
|
|
return $this->warning_id;
|
|
} else {
|
|
return $this->warning_id . ': '
|
|
. ($this->error_string[$this->warning_id] ?? '[NO WARNING MESSAGE]');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the combined warning and error history
|
|
* TODO: add options to return only needed (Eg error, time based)
|
|
* @return array<mixed> Complete long error history string
|
|
*/
|
|
public function dbGetCombinedErrorHistory(): array
|
|
{
|
|
return $this->error_history_long;
|
|
}
|
|
|
|
// ***************************
|
|
// DEPEREACTED CALLS
|
|
// all call below are no longer in use and throw deprecated errors
|
|
// ***************************
|
|
|
|
/**
|
|
* return current set insert_id as is
|
|
* @return array<mixed>|string|int|bool|null Primary key value, most likely int
|
|
* Array for multiple return set
|
|
* Empty string for unset
|
|
* Null for error
|
|
* @deprecated Use ->dbGetInsertPK();
|
|
*/
|
|
public function dbGetReturning()
|
|
{
|
|
return $this->dbGetInsertPK();
|
|
}
|
|
|
|
/**
|
|
* returns the db init error
|
|
* if failed to connect it is set to true
|
|
* else false
|
|
* @return bool connection failure status
|
|
* @deprecated Use dbGetConnectionStatus() and True means correct connection
|
|
*/
|
|
public function getConnectionStatus(): bool
|
|
{
|
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, '
|
|
. 'use dbGetConnectionStatus() with True for successful connection', E_USER_DEPRECATED);
|
|
// reverse because before it was reverse
|
|
return $this->dbGetConnectionStatus() ? false : true;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* @return array<mixed>|string|int|bool|null See above
|
|
* @deprecated use getReturningExt($key = null) instead
|
|
*/
|
|
public function getInsertReturn($key = null)
|
|
{
|
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use getReturningExt($key = null)', E_USER_DEPRECATED);
|
|
return $this->dbGetReturningExt($key);
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: getReturning
|
|
* @return array<mixed>|string|int|bool|null [DEPRECATED]
|
|
* @deprecated use dbGetReturning() instead
|
|
*/
|
|
public function getReturning()
|
|
{
|
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetReturning()', E_USER_DEPRECATED);
|
|
return $this->dbGetInsertPK();
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: getInsertPK
|
|
* @return array<mixed>|string|int|bool|null [DEPRECATED]
|
|
* @deprecated use dbGetInsertPK() instead
|
|
*/
|
|
public function getInsertPK()
|
|
{
|
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetInsertPK()', E_USER_DEPRECATED);
|
|
return $this->dbGetInsertPK();
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: getReturningExt
|
|
* @param string|null $key [DEPRECATED]
|
|
* @return array<mixed>|string|bool|int|null [DEPRECATED]
|
|
* @deprecated use dbGetReturningExt($key = null) instead
|
|
*/
|
|
public function getReturningExt($key = null)
|
|
{
|
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetReturningExt($key = null)', E_USER_DEPRECATED);
|
|
return $this->dbGetReturningExt($key);
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: getCursorExt
|
|
* @param string|null $q [DEPRECATED]
|
|
* @return array<mixed>|string|int|resource|object|null [DEPRECATED]
|
|
* @deprecated use dbGetCursorExt($q = null) instead
|
|
*/
|
|
public function getCursorExt($q = null)
|
|
{
|
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetCursorExt($q = null)', E_USER_DEPRECATED);
|
|
return $this->dbGetCursorExt($q);
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: getNumRows
|
|
* @return int|null [DEPRECATED]
|
|
* @deprecated use dbGetNumRows() instead
|
|
*/
|
|
public function getNumRows()
|
|
{
|
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetNumRows()', E_USER_DEPRECATED);
|
|
return $this->dbGetNumRows();
|
|
}
|
|
|
|
// end if db class
|
|
}
|
|
|
|
// __END__
|