Add src files for CoreLibs composer all package
This commit is contained in:
655
src/DB/Extended/ArrayIO.php
Normal file
655
src/DB/Extended/ArrayIO.php
Normal file
@@ -0,0 +1,655 @@
|
||||
<?php
|
||||
|
||||
/*********************************************************************
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2002/12/17
|
||||
* VERSION: 1.0.0
|
||||
* RELEASED LICENSE: GNU GPL 3
|
||||
* SHORT DESC :RIPTION:
|
||||
* DB Array IO Class:
|
||||
* writes, reads or deletes a complete array (one data set) in/out a
|
||||
* table from the connected DB.
|
||||
* you don't have to write any SQL queries, worry over update/insert
|
||||
*
|
||||
* HISTORY:
|
||||
* 2019/9/11 (cs) error string 21->1021, 22->1022 for not overlapping with IO
|
||||
* 2005/07/07 (cs) updated array class for postgres: set 0 & NULL if int field given, insert uses () values () syntax
|
||||
* 2005/03/31 (cs) fixed the class call with all debug vars
|
||||
* 2003-03-10: error_ids where still wrong chagned 11->21 and 12->22
|
||||
* 2003-02-26: db_array_io is no longer single class but extens db_io,
|
||||
* as it needs it anyway
|
||||
* moved the class info vars into class_info array into
|
||||
* the constructor, removed info function
|
||||
* 2003-02-24: in db_delete moved query build to top, or pk_name/value
|
||||
* will be reset before delete is done
|
||||
* 2002-12-20: just added info() method
|
||||
* 2002-12-17: splitted the class from other file (with main db wrapper)
|
||||
*********************************************************************/
|
||||
|
||||
// picture upload should be taken out from here and out in media_class
|
||||
// as it actually has nothing to do with this one here ? (or at least
|
||||
// put into separete function in this class)
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DB\Extended;
|
||||
|
||||
// subclass for one array handling
|
||||
class ArrayIO extends \CoreLibs\DB\IO
|
||||
{
|
||||
// main calss variables
|
||||
/** @var array<mixed> */
|
||||
public $table_array; // the array from the table to work on
|
||||
/** @var string */
|
||||
public $table_name; // the table_name
|
||||
/** @var string */
|
||||
public $pk_name = ''; // the primary key from this table
|
||||
/** @var int|string|null */
|
||||
public $pk_id; // the PK id
|
||||
// security values
|
||||
/** @var int base acl for current page */
|
||||
private $base_acl_level = 0;
|
||||
|
||||
/**
|
||||
* constructor for the array io class, set the
|
||||
* primary key name automatically (from array)
|
||||
*
|
||||
* @param array<mixed> $db_config db connection config
|
||||
* @param array<mixed> $table_array table array config
|
||||
* @param string $table_name table name string
|
||||
* @param \CoreLibs\Debug\Logging|null $log Logging class, default set if not set
|
||||
* @param int $base_acl_level Set base acl level, if needed
|
||||
* @param int $acl_admin Flag if this is an admin ACL access level
|
||||
*/
|
||||
public function __construct(
|
||||
array $db_config,
|
||||
array $table_array,
|
||||
string $table_name,
|
||||
\CoreLibs\Debug\Logging $log = null,
|
||||
int $base_acl_level = 0,
|
||||
int $acl_admin = 0
|
||||
) {
|
||||
// instance db_io class
|
||||
parent::__construct($db_config, $log ?? new \CoreLibs\Debug\Logging());
|
||||
// more error vars for this class
|
||||
$this->error_string['1999'] = 'No table array or table name set';
|
||||
$this->error_string['1021'] = 'No Primary Key given';
|
||||
$this->error_string['1022'] = 'Could not run Array Query';
|
||||
|
||||
$this->table_array = $table_array;
|
||||
$this->table_name = $table_name;
|
||||
|
||||
// error abort if no table array or no table name
|
||||
if (empty($table_array) || empty($table_name)) {
|
||||
$this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing');
|
||||
}
|
||||
|
||||
// set primary key for given table_array
|
||||
foreach ($this->table_array as $key => $value) {
|
||||
if (!empty($value['pk'])) {
|
||||
$this->pk_name = $key;
|
||||
}
|
||||
}
|
||||
$this->dbArrayIOSetAcl($base_acl_level, $acl_admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* class deconstructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
parent::__destruct();
|
||||
}
|
||||
|
||||
/**
|
||||
* set the base acl level and admin acl flag
|
||||
* This is needed for table array ACL checks
|
||||
* if not set I assume 0 (non write/non read/non admin)
|
||||
*
|
||||
* @param int $base_acl_level ACL Level from 0 to 100, -1 is not allowed
|
||||
* Will sett 0 if invalid
|
||||
* @param int $acl_admin 0 for non admin, 1 for admin (base acl is 100)
|
||||
* @return void
|
||||
*/
|
||||
public function dbArrayIOSetAcl(int $base_acl_level, int $acl_admin): void
|
||||
{
|
||||
// default not allowed, must be 0 at least
|
||||
if ($base_acl_level < 0) {
|
||||
$base_acl_level = 0;
|
||||
}
|
||||
// only 0 or 1 allowed
|
||||
if (!in_array($acl_admin, [0, 1])) {
|
||||
$acl_admin = 0;
|
||||
}
|
||||
// if the user is admin flagged, auto set to 100, if not already set to 100
|
||||
if ($acl_admin == 1) {
|
||||
$base_acl_level = 100;
|
||||
}
|
||||
$this->base_acl_level = $base_acl_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* changes all previously alterd HTML code into visible one,
|
||||
* works for <b>,<i>, and <a> (thought <a> can be / or should
|
||||
* be handled with the magic links functions
|
||||
* used with the read function
|
||||
*
|
||||
* @param string $text any html encoded string
|
||||
* @return string decoded html string
|
||||
*/
|
||||
public function convertData($text): string
|
||||
{
|
||||
$text = str_replace('<b>', '<b>', $text);
|
||||
$text = str_replace('</b>', '</b>', $text);
|
||||
$text = str_replace('<i>', '<i>', $text);
|
||||
$text = str_replace('</i>', '</i>', $text);
|
||||
// my need a change
|
||||
$text = str_replace('<a href="', '<a target="_blank" href="', $text);
|
||||
$text = str_replace('">', '">', $text);
|
||||
$text = str_replace('</a>', '</a>', $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* changeds all HTML entities into non HTML ones
|
||||
*
|
||||
* @param string $text encoded html string
|
||||
* @return string decoded html string
|
||||
*/
|
||||
public function convertEntities($text): string
|
||||
{
|
||||
$text = str_replace('<', '<', $text);
|
||||
$text = str_replace('>', '>', $text);
|
||||
$text = str_replace('&', '&', $text);
|
||||
$text = str_replace('"', '"', $text);
|
||||
$text = str_replace(''', "'", $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* dumps the current data
|
||||
*
|
||||
* @param bool $write write to error message, default false
|
||||
* @return string the array data as html string entry
|
||||
*/
|
||||
public function dbDumpArray($write = false): string
|
||||
{
|
||||
reset($this->table_array);
|
||||
$string = '';
|
||||
foreach ($this->table_array as $column => $data_array) {
|
||||
$string .= '<b>' . $column . '</b> -> ' . $data_array['value'] . '<br>';
|
||||
}
|
||||
// add output to internal error_msg
|
||||
if ($write === true) {
|
||||
$this->__dbDebug('dbArray', $string);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if pk is set and if not, set from pk_id and
|
||||
* if this also not set return 0
|
||||
*
|
||||
* @return bool true if pk value is set, else false
|
||||
*/
|
||||
public function dbCheckPkSet()
|
||||
{
|
||||
// if pk_id is set, overrule ...
|
||||
if ($this->pk_id) {
|
||||
$this->table_array[$this->pk_name]['value'] = $this->pk_id;
|
||||
}
|
||||
// if not set ... produce error
|
||||
if (!$this->table_array[$this->pk_name]['value']) {
|
||||
// if no PK found, error ...
|
||||
$this->__dbError(1021);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resets the whole array values
|
||||
* @param boolean $reset_pk true if we want to reset the pk too
|
||||
* @return void has no return
|
||||
*/
|
||||
public function dbResetArray($reset_pk = false): void
|
||||
{
|
||||
reset($this->table_array);
|
||||
foreach (array_keys($this->table_array) as $column) {
|
||||
if (!$this->table_array[$column]['pk']) {
|
||||
unset($this->table_array[$column]['value']);
|
||||
} elseif ($reset_pk) {
|
||||
unset($this->table_array[$column]['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes one dataset
|
||||
*
|
||||
* @param array<mixed> $table_array optional override for table array set
|
||||
* set this as new table array too
|
||||
* @param boolean $acl_limit [false], if set to true, well do ACL limit check
|
||||
* @return array<mixed> returns the table array that was deleted
|
||||
*/
|
||||
public function dbDelete($table_array = [], $acl_limit = false)
|
||||
{
|
||||
// is array and has values, override set and set new
|
||||
if (is_array($table_array) && count($table_array)) {
|
||||
$this->table_array = $table_array;
|
||||
}
|
||||
if (!$this->dbCheckPkSet()) {
|
||||
return $this->table_array;
|
||||
}
|
||||
if ($acl_limit === true && $this->base_acl_level < 100) {
|
||||
$this->log->debug('DB DELETE ERROR', 'ACL Limit on, Delete, '
|
||||
. 'but base ACL level of 100 not met: ' . $this->base_acl_level);
|
||||
return $this->table_array;
|
||||
}
|
||||
// delete query
|
||||
$q = 'DELETE FROM ' . $this->table_name . ' WHERE ';
|
||||
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
|
||||
// delete files and build FK query
|
||||
reset($this->table_array);
|
||||
$q_where = '';
|
||||
foreach (array_keys($this->table_array) as $column) {
|
||||
// suchen nach bildern und lschen ...
|
||||
if (
|
||||
!empty($this->table_array[$column]['file']) &&
|
||||
file_exists($this->table_array[$column]['url'] . $this->table_array[$column]['value'])
|
||||
) {
|
||||
if (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
|
||||
unlink($this->table_array[$column]['path'] . $this->table_array[$column]['value']);
|
||||
}
|
||||
$file_name = str_replace('_tn', '', $this->table_array[$column]['value']);
|
||||
if (file_exists($this->table_array[$column]['path'] . $file_name)) {
|
||||
unlink($this->table_array[$column]['path'] . $file_name);
|
||||
}
|
||||
}
|
||||
// if we have a foreign key
|
||||
if (!empty($this->table_array[$column]['fk'])) {
|
||||
// create FK constraint checks
|
||||
if ($q_where) {
|
||||
$q_where .= ' AND ';
|
||||
}
|
||||
$q_where .= $column . ' = ' . $this->table_array[$column]['value'];
|
||||
}
|
||||
// allgemeines zurcksetzen des arrays
|
||||
unset($this->table_array[$column]['value']);
|
||||
}
|
||||
|
||||
// attach fk row if there ...
|
||||
if ($q_where) {
|
||||
$q .= ' AND ' . $q_where;
|
||||
}
|
||||
// if 0, error
|
||||
$this->pk_id = null;
|
||||
if (!$this->dbExec($q)) {
|
||||
$this->__dbError(1022);
|
||||
}
|
||||
return $this->table_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads one row into the array
|
||||
*
|
||||
* @param boolean $edit on true convert data, else as is
|
||||
* @param array<mixed> $table_array optional table array, overwrites
|
||||
* internal set array
|
||||
* @return array<mixed> set table array with values
|
||||
*/
|
||||
public function dbRead($edit = false, $table_array = [])
|
||||
{
|
||||
// if array give, overrules internal array
|
||||
if (is_array($table_array) && count($table_array)) {
|
||||
$this->table_array = $table_array;
|
||||
}
|
||||
if (!$this->dbCheckPkSet()) {
|
||||
return $this->table_array;
|
||||
}
|
||||
reset($this->table_array);
|
||||
$q_select = '';
|
||||
$q_where = '';
|
||||
// create select part & addition FK part
|
||||
foreach ($this->table_array as $column => $data_array) {
|
||||
if ($q_select) {
|
||||
$q_select .= ', ';
|
||||
}
|
||||
if (
|
||||
!empty($data_array['type']) && $data_array['type'] == 'datetime' &&
|
||||
!empty($data_array['sql_read'])
|
||||
) {
|
||||
// convert tom different timestamp type
|
||||
$q_select .= "TO_CHAR($column, '" . $data_array['sql_read'] . "') AS $column";
|
||||
} else {
|
||||
$q_select .= $column;
|
||||
}
|
||||
|
||||
// check FK ...
|
||||
if (
|
||||
isset($this->table_array[$column]['fk']) &&
|
||||
isset($this->table_array[$column]['value'])
|
||||
) {
|
||||
if (!empty($q_where)) {
|
||||
$q_where .= ' AND ';
|
||||
}
|
||||
$q_where .= $column .= ' = ' . $this->table_array[$column]['value'];
|
||||
}
|
||||
}
|
||||
|
||||
$q = 'SELECT ';
|
||||
$q .= $q_select;
|
||||
$q .= ' FROM ' . $this->table_name . ' WHERE ';
|
||||
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
|
||||
if ($q_where) {
|
||||
$q .= ' AND ' . $q_where;
|
||||
}
|
||||
|
||||
// if query was executed okay, else set error
|
||||
if ($this->dbExec($q)) {
|
||||
if (is_array($res = $this->dbFetchArray())) {
|
||||
reset($this->table_array);
|
||||
foreach ($this->table_array as $column => $data_array) {
|
||||
// wenn "edit" dann gib daten wie in DB zurück, ansonten aufbereiten fr ausgabe
|
||||
// ?? sollte das nicht drauen ??? man weis ja net was da drin steht --> is noch zu berlegen
|
||||
// $this->log->debug('DB READ', 'EDIT: $edit | Spalte: $column | type: '
|
||||
// .$this->table_array[$column]['type'].' | Res: '.$res[$column]);
|
||||
if ($edit) {
|
||||
$this->table_array[$column]['value'] = $res[$column];
|
||||
// if password, also write to hidden
|
||||
if (
|
||||
isset($this->table_array[$column]['type']) &&
|
||||
$this->table_array[$column]['type'] == 'password'
|
||||
) {
|
||||
$this->table_array[$column]['HIDDEN_value'] = $res[$column];
|
||||
}
|
||||
} else {
|
||||
$this->table_array[$column]['value'] = $this->convertData(nl2br($res[$column]));
|
||||
// had to put out the htmlentities from the line above as it breaks japanese characters
|
||||
}
|
||||
}
|
||||
}
|
||||
// possible dbFetchArray errors ...
|
||||
$this->pk_id = $this->table_array[$this->pk_name]['value'];
|
||||
} else {
|
||||
$this->__dbError(1022);
|
||||
}
|
||||
return $this->table_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes one set into DB or updates one set (if PK exists)
|
||||
*
|
||||
* @param boolean $addslashes old convert entities and set set escape
|
||||
* @param array<mixed> $table_array optional table array, overwrites internal one
|
||||
* @param boolean $acl_limit [false], if set to true, well do ACL limit check
|
||||
* @return array<mixed> table array or null
|
||||
*/
|
||||
public function dbWrite(
|
||||
bool $addslashes = false,
|
||||
array $table_array = [],
|
||||
bool $acl_limit = false
|
||||
): array {
|
||||
if (is_array($table_array) && count($table_array)) {
|
||||
$this->table_array = $table_array;
|
||||
}
|
||||
// PK ID check
|
||||
// if ($this->pk_id && !$this->table_array[$this->pk_name]["value"]) {
|
||||
// $this->table_array[$this->pk_name]["value"]=$this->pk_id;
|
||||
// }
|
||||
// checken ob PKs gesetzt, wenn alle -> update, wenn keiner -> insert, wenn ein paar -> ERROR!
|
||||
if (!$this->table_array[$this->pk_name]['value']) {
|
||||
$insert = 1;
|
||||
} else {
|
||||
$insert = 0;
|
||||
}
|
||||
// early abort for new write with not enough ACL level
|
||||
if ($insert && $acl_limit === true && $this->base_acl_level < 100) {
|
||||
$this->log->debug('DB WRITE ERROR', 'ACL Limit on, Insert, '
|
||||
. 'but base ACL level of 100 not met: ' . $this->base_acl_level);
|
||||
return $this->table_array;
|
||||
}
|
||||
|
||||
reset($this->table_array);
|
||||
$q_data = '';
|
||||
$q_vars = '';
|
||||
$q_where = '';
|
||||
foreach ($this->table_array as $column => $data_array) {
|
||||
/********************************* START FILE *************************************/
|
||||
// file upload
|
||||
if (isset($this->table_array[$column]['file'])) {
|
||||
// falls was im tmp drinnen, sprich ein upload, datei kopieren, Dateinamen in db schreiben
|
||||
// falls datei schon am server (physischer pfad), dann einfach url in db schreiben (update)
|
||||
// falls in 'delete' 'ja' dann loeschen (und gibts eh nur beim update)
|
||||
if ($this->table_array[$column]['delete']) {
|
||||
unset($this->table_array[$column]['delete']);
|
||||
if (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
|
||||
unlink($this->table_array[$column]['path'] . $this->table_array[$column]['value']);
|
||||
}
|
||||
$file_name = str_replace('_tn', '', $this->table_array[$column]['value']);
|
||||
if (file_exists($this->table_array[$column]['path'] . $file_name)) {
|
||||
unlink($this->table_array[$column]['path'] . $file_name);
|
||||
}
|
||||
$this->table_array[$column]['value'] = '';
|
||||
} else {
|
||||
if ($this->table_array[$column]['tmp'] != 'none' && $this->table_array[$column]['tmp']) {
|
||||
// mozilla, patch
|
||||
$fn_name = explode('/', $this->table_array[$column]['dn']);
|
||||
$this->table_array[$column]['dn'] = $fn_name[count($fn_name) - 1];
|
||||
$filename_parts = explode('.', $this->table_array[$column]['dn']);
|
||||
$ext = end($filename_parts);
|
||||
array_splice($filename_parts, -1, 1);
|
||||
$name = str_replace(' ', '_', implode('.', $filename_parts));
|
||||
$file_name = $name . '.' . $ext;
|
||||
//echo 'Dn: $file_name';
|
||||
copy($this->table_array[$column]['tmp'], $this->table_array[$column]['path'] . $file_name);
|
||||
// automatisch thumbnail generieren, geht nur mit convert (ImageMagic!!!), aber nur bei bild ..
|
||||
if (in_array(strtolower($ext), ['jpeg', 'jpg', 'gif', 'png'])) {
|
||||
$file_name_tn = $name . '_tn.' . $ext;
|
||||
$input = $this->table_array[$column]['path'] . $file_name;
|
||||
$output = $this->table_array[$column]['path'] . $file_name_tn;
|
||||
$com = 'convert -geometry 115 ' . $input . ' ' . $output;
|
||||
exec($com);
|
||||
$this->table_array[$column]['value'] = $file_name_tn;
|
||||
} else {
|
||||
$this->table_array[$column]['value'] = $file_name;
|
||||
}
|
||||
} elseif (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
|
||||
// mach gar nix, wenn bild schon da ???
|
||||
}
|
||||
} // delete or upload
|
||||
} // file IF
|
||||
/********************************* END FILE **************************************/
|
||||
|
||||
// do not write 'pk' (primary key) or 'view' values
|
||||
// also do not write UPDATE for elements that are
|
||||
// acl flagged, not if we have an ACL limiter, don't insert
|
||||
// $this->log->debug('DB WRITE', 'C: ' . $column . ', '
|
||||
// . 'ACL Level ' . $this->log->prBl($acl_limit) . ', '
|
||||
// . 'TA ACL: ' . ($this->table_array[$column]['min_edit_acl'] ?? 100) . ', '
|
||||
// . 'Base ACL: ' . $this->base_acl_level);
|
||||
if (
|
||||
!isset($this->table_array[$column]['pk']) &&
|
||||
isset($this->table_array[$column]['type']) &&
|
||||
$this->table_array[$column]['type'] != 'view' &&
|
||||
strlen($column) > 0 &&
|
||||
// no acl limiter
|
||||
($acl_limit === false ||
|
||||
(
|
||||
// acl limit is true, min edit must be at larger than set
|
||||
$acl_limit === true &&
|
||||
$this->base_acl_level >=
|
||||
($this->table_array[$column]['min_edit_acl'] ?? 100)
|
||||
))
|
||||
) {
|
||||
// for password use hidden value if main is not set
|
||||
if (
|
||||
isset($this->table_array[$column]['type']) &&
|
||||
$this->table_array[$column]['type'] == 'password' &&
|
||||
empty($this->table_array[$column]['value'])
|
||||
) {
|
||||
$this->table_array[$column]['value'] = $this->table_array[$column]['HIDDEN_value'];
|
||||
}
|
||||
if (!$insert) {
|
||||
if (strlen($q_data)) {
|
||||
$q_data .= ', ';
|
||||
}
|
||||
$q_data .= $column . ' = ';
|
||||
} else {
|
||||
// this is insert
|
||||
if (strlen($q_data)) {
|
||||
$q_data .= ', ';
|
||||
}
|
||||
if (strlen($q_vars)) {
|
||||
$q_vars .= ', ';
|
||||
}
|
||||
$q_vars .= $column;
|
||||
}
|
||||
// integer is different
|
||||
if (isset($this->table_array[$column]['int']) || isset($this->table_array[$column]['int_null'])) {
|
||||
$this->log->debug('WRITE CHECK', '[' . $column . '][' . $this->table_array[$column]['value'] . ']'
|
||||
. '[' . $this->table_array[$column]['type'] . '] '
|
||||
. 'VALUE SET: ' . (string)isset($this->table_array[$column]['value'])
|
||||
. ' | INT NULL: ' . (string)isset($this->table_array[$column]['int_null']));
|
||||
if (
|
||||
isset($this->table_array[$column]['value']) &&
|
||||
!$this->table_array[$column]['value'] &&
|
||||
isset($this->table_array[$column]['int_null'])
|
||||
) {
|
||||
$_value = 'NULL';
|
||||
} elseif (
|
||||
!isset($this->table_array[$column]['value']) ||
|
||||
(isset($this->table_array[$column]['value']) && !$this->table_array[$column]['value'])
|
||||
) {
|
||||
$_value = 0;
|
||||
} else {
|
||||
$_value = $this->table_array[$column]['value'];
|
||||
}
|
||||
$q_data .= $_value;
|
||||
} elseif (isset($this->table_array[$column]['bool'])) {
|
||||
// boolean storeage (reverse check on ifset)
|
||||
$q_data .= "'" . $this->dbBoolean($this->table_array[$column]['value'], true) . "'";
|
||||
} elseif (
|
||||
isset($this->table_array[$column]['interval']) ||
|
||||
isset($this->table_array[$column]['date']) ||
|
||||
isset($this->table_array[$column]['datetime']) ||
|
||||
isset($this->table_array[$column]['emptynull'])
|
||||
) {
|
||||
// for interval we check if no value, then we set null
|
||||
if (
|
||||
!isset($this->table_array[$column]['value']) ||
|
||||
(isset($this->table_array[$column]['value']) && !$this->table_array[$column]['value'])
|
||||
) {
|
||||
$_value = 'NULL';
|
||||
} elseif (isset($this->table_array[$column]['value'])) {
|
||||
$_value = $this->dbEscapeLiteral($this->table_array[$column]['value']);
|
||||
} else {
|
||||
// fallback
|
||||
$_value = 'NULL';
|
||||
}
|
||||
$q_data .= $_value;
|
||||
} else {
|
||||
// if the error check is json, we set field to null if NOT set
|
||||
// else normal string write
|
||||
if (
|
||||
isset($this->table_array[$column]['error_check']) &&
|
||||
$this->table_array[$column]['error_check'] == 'json' &&
|
||||
(
|
||||
!isset($this->table_array[$column]['value']) ||
|
||||
(isset($this->table_array[$column]['value']) &&
|
||||
!$this->table_array[$column]['value'])
|
||||
)
|
||||
) {
|
||||
$q_data .= 'NULL';
|
||||
} else {
|
||||
// normal string
|
||||
$q_data .= "'";
|
||||
// if add slashes do convert & add slashes else write AS is
|
||||
if ($addslashes) {
|
||||
$q_data .= $this->dbEscapeString(
|
||||
$this->convertEntities($this->table_array[$column]['value'])
|
||||
);
|
||||
} else {
|
||||
$q_data .= $this->dbEscapeString($this->table_array[$column]['value']);
|
||||
}
|
||||
$q_data .= "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
} // while ...
|
||||
|
||||
if (empty($q_data)) {
|
||||
$this->log->debug('DB WRITE ERROR', 'No data to write, possible through ACL');
|
||||
return $this->table_array;
|
||||
}
|
||||
|
||||
// NOW get PK, and FK settings (FK only for update query)
|
||||
// get it at the end, cause now we can be more sure of no double IDs, etc
|
||||
reset($this->table_array);
|
||||
// create select part & addition FK part
|
||||
foreach ($this->table_array as $column => $data_array) {
|
||||
// check FK ...
|
||||
if (
|
||||
isset($this->table_array[$column]['fk']) &&
|
||||
isset($this->table_array[$column]['value'])
|
||||
) {
|
||||
if (!empty($q_where)) {
|
||||
$q_where .= ' AND ';
|
||||
}
|
||||
$q_where .= $column .= ' = ' . $this->table_array[$column]['value'];
|
||||
}
|
||||
}
|
||||
|
||||
// if no PK set, then get max ID from DB
|
||||
if (!$this->table_array[$this->pk_name]['value']) {
|
||||
// max id, falls INSERT
|
||||
$q = 'SELECT MAX(' . $this->pk_name . ') + 1 AS pk_id FROM ' . $this->table_name;
|
||||
if (is_array($res = $this->dbReturnRow($q))) {
|
||||
$pk_id = $res['pk_id'];
|
||||
} else {
|
||||
$pk_id = 1;
|
||||
}
|
||||
$this->table_array[$this->pk_name]['value'] = $pk_id;
|
||||
}
|
||||
|
||||
if (!$insert) {
|
||||
$q = 'UPDATE ' . $this->table_name . ' SET ';
|
||||
$q .= $q_data;
|
||||
$q .= ' WHERE ';
|
||||
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
|
||||
if (!empty($q_where)) {
|
||||
$q .= ' AND ' . $q_where;
|
||||
}
|
||||
// set pk_id ... if it has changed or so
|
||||
$this->pk_id = $this->table_array[$this->pk_name]['value'];
|
||||
} else {
|
||||
$q = 'INSERT INTO ' . $this->table_name . ' ';
|
||||
$q .= '(' . $q_vars . ') ';
|
||||
$q .= 'VALUES (' . $q_data . ')';
|
||||
// write primary key too
|
||||
// if ($q_data)
|
||||
// $q .= ", ";
|
||||
// $q .= $this->pk_name." = ".$this->table_array[$this->pk_name]['value']." ";
|
||||
// $this->pk_id = $this->table_array[$this->pk_name]['value'];
|
||||
}
|
||||
// return success or not
|
||||
if (!$this->dbExec($q)) {
|
||||
$this->__dbError(1022);
|
||||
}
|
||||
// set primary key
|
||||
if ($insert) {
|
||||
$insert_id = $this->dbGetInsertPK();
|
||||
if (is_array($insert_id)) {
|
||||
$insert_id = 0;
|
||||
}
|
||||
$this->table_array[$this->pk_name]['value'] = $insert_id;
|
||||
$this->pk_id = $insert_id;
|
||||
}
|
||||
// return the table if needed
|
||||
return $this->table_array;
|
||||
}
|
||||
// end of class
|
||||
}
|
||||
|
||||
// __END__
|
||||
3342
src/DB/IO.php
Normal file
3342
src/DB/IO.php
Normal file
File diff suppressed because it is too large
Load Diff
883
src/DB/SQL/PgSQL.php
Normal file
883
src/DB/SQL/PgSQL.php
Normal file
@@ -0,0 +1,883 @@
|
||||
<?php
|
||||
|
||||
/*********************************************************************
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2003/04/09
|
||||
* SHORT DESCRIPTION:
|
||||
* 2018/3/23, the whole class system is transformed to namespaces
|
||||
* also all internal class calls are converted to camel case
|
||||
*
|
||||
* pgsql wrapper calls
|
||||
*
|
||||
* HISTORY:
|
||||
* 2008/04/16 (cs) wrapper for pg escape string
|
||||
* 2007/01/11 (cs) add prepare/execute for postgres
|
||||
* 2006/09/12 (cs) in case db_query retuns false, save the query and
|
||||
* run the query through the send/get procedure to get
|
||||
* correct error data from the db
|
||||
* 2006/06/26 (cs) added port for db connection
|
||||
* 2006/04/03 (cs) added meta data for table
|
||||
* 2005/07/25 (cs) removed the plural s remove, not needed and not 100% working
|
||||
* 2005/07/07 (cs) the default it is table_name _ id
|
||||
* 2005/01/19 (cs) changed the pgsql connect, so it dies if it can't connect to the DB
|
||||
* 2004/09/30 (cs) layout cleanup
|
||||
*
|
||||
*
|
||||
* collection of PostgreSQL wrappers
|
||||
*
|
||||
* pg_prepare
|
||||
* pg_execute
|
||||
* pg_num_rows
|
||||
* pg_num_fields
|
||||
* pg_field_name
|
||||
* pg_affected_rows (*)
|
||||
* pg_fetch_array
|
||||
* pg_query
|
||||
* pg_send_query
|
||||
* pg_get_result
|
||||
* pg_connection_busy
|
||||
* pg_close
|
||||
* pg_connect (*)
|
||||
* pg_meta_data
|
||||
* pg_escape_string
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DB\SQL;
|
||||
|
||||
// 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 PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
|
||||
{
|
||||
/** @var string */
|
||||
private $last_error_query;
|
||||
// NOTE for PHP 8.1 this is no longer a resource
|
||||
/** @var object|resource|bool */ // replace object with PgSql\Connection
|
||||
private $dbh;
|
||||
|
||||
/**
|
||||
* queries last error query and returns true or false if error was set
|
||||
*
|
||||
* @return bool true/false if last error is set
|
||||
*/
|
||||
public function __dbLastErrorQuery(): bool
|
||||
{
|
||||
if ($this->last_error_query) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_query, catches error and stores it in class var
|
||||
*
|
||||
* @param string $query Query string
|
||||
* @return object|resource|bool query result (PgSql\Result)
|
||||
*/
|
||||
public function __dbQuery(string $query)
|
||||
{
|
||||
$this->last_error_query = '';
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
// read out the query status and save the query if needed
|
||||
$result = pg_query($this->dbh, $query);
|
||||
if ($result === false) {
|
||||
$this->last_error_query = $query;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proposed
|
||||
* wrapperf or pg_query_params for queries in the style of
|
||||
* SELECT foo FROM bar WHERE foobar = $1
|
||||
*
|
||||
* @param string $query Query string with placeholders $1, ..
|
||||
* @param array<mixed> $params Matching parameters for each placerhold
|
||||
* @return object|resource|bool Query result (PgSql\Result)
|
||||
*/
|
||||
public function __dbQueryParams(string $query, array $params)
|
||||
{
|
||||
$this->last_error_query = '';
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
// parse query and get all $n entries
|
||||
// TODO count of $n must match params
|
||||
// read out the query status and save the query if needed
|
||||
$result = pg_query_params($this->dbh, $query, $params);
|
||||
if ($result === false) {
|
||||
$this->last_error_query = $query;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends an async query to the server
|
||||
*
|
||||
* @param string $query query string
|
||||
* @return bool true/false if query was sent successful
|
||||
*/
|
||||
public function __dbSendQuery(string $query): bool
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$result = pg_send_query($this->dbh, $query);
|
||||
return $result ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_get_result
|
||||
*
|
||||
* @return object|resource|bool resource handler or false for error (PgSql\Result)
|
||||
*/
|
||||
public function __dbGetResult()
|
||||
{
|
||||
$this->last_error_query = '';
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$result = pg_get_result($this->dbh);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
if ($error = pg_result_error($result)) {
|
||||
$this->last_error_query = $error;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_close
|
||||
*
|
||||
* @return void has no return
|
||||
*/
|
||||
public function __dbClose(): void
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return;
|
||||
}
|
||||
if (pg_connection_status($this->dbh) === PGSQL_CONNECTION_OK) {
|
||||
// in 8.1 this throws an error, and we don't need that anyway
|
||||
// pg_close($this->dbh);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_prepare
|
||||
*
|
||||
* @param string $name statement name
|
||||
* @param string $query query string
|
||||
* @return object|resource|bool prepare statement handler or
|
||||
* false for error (PgSql\Result)
|
||||
*/
|
||||
public function __dbPrepare(string $name, string $query)
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$result = pg_prepare($this->dbh, $name, $query);
|
||||
if (!$result) {
|
||||
$this->last_error_query = $query;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_execute for running a prepared statement
|
||||
*
|
||||
* @param string $name statement name
|
||||
* @param array<mixed> $data data array
|
||||
* @return object|resource|bool returns status or false for error (PgSql\Result)
|
||||
*/
|
||||
public function __dbExecute(string $name, array $data)
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$result = pg_execute($this->dbh, $name, $data);
|
||||
if (!$result) {
|
||||
$this->last_error_query = $name;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_num_rows
|
||||
*
|
||||
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
|
||||
* @return int number of rows, -1 on error
|
||||
*/
|
||||
public function __dbNumRows($cursor): int
|
||||
{
|
||||
if ($cursor === false || is_bool($cursor)) {
|
||||
return -1;
|
||||
}
|
||||
return pg_num_rows($cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_num_fields
|
||||
*
|
||||
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
|
||||
* @return int number for fields in result, -1 on error
|
||||
*/
|
||||
public function __dbNumFields($cursor): int
|
||||
{
|
||||
if ($cursor === false || is_bool($cursor)) {
|
||||
return -1;
|
||||
}
|
||||
return pg_num_fields($cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_field_name
|
||||
*
|
||||
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
|
||||
* @param int $i field position
|
||||
* @return string|bool name or false on error
|
||||
*/
|
||||
public function __dbFieldName($cursor, int $i)
|
||||
{
|
||||
if ($cursor === false || is_bool($cursor)) {
|
||||
return false;
|
||||
}
|
||||
return pg_field_name($cursor, $i);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_fetch_array
|
||||
* if through/true false, use __dbResultType(true)
|
||||
*
|
||||
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
|
||||
* @param int $result_type result type as int number
|
||||
* @return array<mixed>|bool array result data or false on end/error
|
||||
*/
|
||||
public function __dbFetchArray($cursor, int $result_type = PGSQL_BOTH)
|
||||
{
|
||||
if ($cursor === false || is_bool($cursor)) {
|
||||
return false;
|
||||
}
|
||||
// result type is passed on as is [should be checked]
|
||||
return pg_fetch_array($cursor, null, $result_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* simple match up between assoc true/false
|
||||
*
|
||||
* @param bool $assoc_type true (default) for PGSQL_ASSOC, false for PGSQL_BOTH
|
||||
* @return int valid result type for fetch array
|
||||
*/
|
||||
public function __dbResultType(bool $assoc_type = true): int
|
||||
{
|
||||
if ($assoc_type == true) {
|
||||
return PGSQL_ASSOC;
|
||||
}
|
||||
// fallback to default
|
||||
return PGSQL_BOTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_fetch_all
|
||||
*
|
||||
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
|
||||
* @return array<mixed>|bool data array or false for end/error
|
||||
*/
|
||||
public function __dbFetchAll($cursor)
|
||||
{
|
||||
if ($cursor === false || is_bool($cursor)) {
|
||||
return false;
|
||||
}
|
||||
return pg_fetch_all($cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_affected_rows
|
||||
*
|
||||
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
|
||||
* @return int affected rows, 0 for none, -1 for error
|
||||
*/
|
||||
public function __dbAffectedRows($cursor): int
|
||||
{
|
||||
if ($cursor === false || is_bool($cursor)) {
|
||||
return -1;
|
||||
}
|
||||
return pg_affected_rows($cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads the last inserted primary key for the query
|
||||
* if there is no pk_name tries to auto built it from the table name
|
||||
* this only works if db schema is after no plural names
|
||||
* and pk name is table name + _id
|
||||
*
|
||||
* detects schema prefix in table name
|
||||
* @param string $query query string
|
||||
* @param string|null $pk_name primary key name, if '' then auto detect
|
||||
* @return string|int|false primary key value
|
||||
*/
|
||||
public function __dbInsertId(string $query, ?string $pk_name)
|
||||
{
|
||||
// only if an insert has been done
|
||||
if (preg_match("/^insert /i", $query)) {
|
||||
$schema = '';
|
||||
// get table name from insert
|
||||
$array = explode(' ', $query);
|
||||
$_table = $array[2];
|
||||
// if there is a dot inside, we need to split
|
||||
if (strstr($_table, '.')) {
|
||||
list($schema, $table) = explode('.', $_table);
|
||||
} else {
|
||||
$table = $_table;
|
||||
}
|
||||
// no PK name given at all
|
||||
if (empty($pk_name)) {
|
||||
// if name is plurar, make it singular
|
||||
// if (preg_match("/.*s$/i", $table))
|
||||
// $table = substr($table, 0, -1);
|
||||
// set pk_name to "id"
|
||||
$pk_name = $table . "_id";
|
||||
}
|
||||
$seq = ($schema ? $schema . '.' : '') . $table . "_" . $pk_name . "_seq";
|
||||
$q = "SELECT CURRVAL('$seq') AS insert_id";
|
||||
// I have to do manually or I overwrite the original insert internal vars ...
|
||||
if ($q = $this->__dbQuery($q)) {
|
||||
if (is_array($res = $this->__dbFetchArray($q))) {
|
||||
list($id) = $res;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$id = [-1, $q];
|
||||
}
|
||||
return $id;
|
||||
} else {
|
||||
//if not insert, return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* queries database for the primary key name to this table in the selected schema
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param string $schema optional schema name, '' for default
|
||||
* @return string|bool primary key name or false if not found
|
||||
*/
|
||||
public function __dbPrimaryKey(string $table, string $schema = '')
|
||||
{
|
||||
if ($table) {
|
||||
// check if schema set is different from schema given,
|
||||
// only needed if schema is not empty
|
||||
$table_prefix = '';
|
||||
if ($schema) {
|
||||
$search_path = $this->__dbGetSchema();
|
||||
if ($search_path != $schema) {
|
||||
$table_prefix = $schema . '.';
|
||||
}
|
||||
}
|
||||
// read from table the PK name
|
||||
// faster primary key get
|
||||
$q = "SELECT pg_attribute.attname AS column_name, "
|
||||
. "format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS type "
|
||||
. "FROM pg_index, pg_class, pg_attribute ";
|
||||
if ($schema) {
|
||||
$q .= ", pg_namespace ";
|
||||
}
|
||||
$q .= "WHERE "
|
||||
// regclass translates the OID to the name
|
||||
. "pg_class.oid = '" . $table_prefix . $table . "'::regclass AND "
|
||||
. "indrelid = pg_class.oid AND ";
|
||||
if ($schema) {
|
||||
$q .= "nspname = '" . $schema . "' AND "
|
||||
. "pg_class.relnamespace = pg_namespace.oid AND ";
|
||||
}
|
||||
$q .= "pg_attribute.attrelid = pg_class.oid AND "
|
||||
. "pg_attribute.attnum = any(pg_index.indkey) "
|
||||
. "AND indisprimary";
|
||||
$cursor = $this->__dbQuery($q);
|
||||
if ($cursor !== false) {
|
||||
$__db_fetch_array = $this->__dbFetchArray($cursor);
|
||||
if (!is_array($__db_fetch_array)) {
|
||||
return false;
|
||||
}
|
||||
return $__db_fetch_array['column_name'] ?? false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_connect, writes out failure to screen if error occurs (hidden var)
|
||||
*
|
||||
* @param string $db_host host name
|
||||
* @param string $db_user user name
|
||||
* @param string $db_pass password
|
||||
* @param string $db_name databse name
|
||||
* @param integer $db_port port (int, 5432 is default)
|
||||
* @param string $db_ssl SSL (allow is default)
|
||||
* @return object|resource|bool db handler PgSql\Connection or false on error
|
||||
*/
|
||||
public function __dbConnect(
|
||||
string $db_host,
|
||||
string $db_user,
|
||||
string $db_pass,
|
||||
string $db_name,
|
||||
int $db_port,
|
||||
string $db_ssl = 'allow'
|
||||
) {
|
||||
if (empty($db_name)) {
|
||||
return false;
|
||||
}
|
||||
// if there is no host, leave it empty, this will try default unix path
|
||||
// same for port (defaults to 5432 if not set)
|
||||
// must set is db name
|
||||
// if no user name, db name is used
|
||||
$connection_string = [];
|
||||
if (!empty($db_host)) {
|
||||
$connection_string[] = 'host=' . $db_host;
|
||||
}
|
||||
if (!empty($db_port)) {
|
||||
$connection_string[] = 'port=' . $db_port;
|
||||
}
|
||||
if (!empty($db_user)) {
|
||||
$connection_string[] = 'user=' . $db_user;
|
||||
}
|
||||
if (!empty($db_pass)) {
|
||||
$connection_string[] = 'password=' . $db_pass;
|
||||
}
|
||||
// we must have at least db name set
|
||||
$connection_string[] = 'dbname=' . $db_name;
|
||||
if (!empty($db_ssl)) {
|
||||
$connection_string[] = 'sslmode=' . $db_ssl;
|
||||
}
|
||||
// connect
|
||||
$this->dbh = pg_connect(join(' ', $connection_string));
|
||||
return $this->dbh;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads the last error for this cursor and returns
|
||||
* html formatted string with error name
|
||||
*
|
||||
* @param bool|object|resource $cursor cursor PgSql\Result (former resource)
|
||||
* or null
|
||||
* @return string error string
|
||||
*/
|
||||
public function __dbPrintError($cursor = false): string
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return '';
|
||||
}
|
||||
// run the query again for the error result here
|
||||
if (($cursor === false || is_bool($cursor)) && $this->last_error_query) {
|
||||
pg_send_query($this->dbh, $this->last_error_query);
|
||||
$this->last_error_query = '';
|
||||
$cursor = pg_get_result($this->dbh);
|
||||
}
|
||||
if ($cursor && !is_bool($cursor) && $error_str = pg_result_error($cursor)) {
|
||||
return '-PostgreSQL-Error- '
|
||||
. $error_str;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_meta_data
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param bool $extended show extended info (default true)
|
||||
* @return array<mixed>|bool array data for the table info or false on error
|
||||
*/
|
||||
public function __dbMetaData(string $table, $extended = true)
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
// needs to prefixed with @ or it throws a warning on not existing table
|
||||
return @pg_meta_data($this->dbh, $table, $extended);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_escape_string
|
||||
*
|
||||
* @param string|int|float|bool $string any string/int/float/bool
|
||||
* @return string excaped string
|
||||
*/
|
||||
public function __dbEscapeString($string): string
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return '';
|
||||
}
|
||||
return pg_escape_string($this->dbh, (string)$string);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_escape_literal
|
||||
* difference to escape string is that this one adds quotes ('') around
|
||||
* the string too
|
||||
*
|
||||
* @param string|int|float|bool $string any string/int/float/bool
|
||||
* @return string excaped string including quites
|
||||
*/
|
||||
public function __dbEscapeLiteral($string): string
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return (string)'';
|
||||
}
|
||||
// for phpstan, thinks this is string|false?
|
||||
return (string)pg_escape_literal($this->dbh, (string)$string);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_escape_identifier
|
||||
* Only used for table names, column names
|
||||
*
|
||||
* @param string $string any string
|
||||
* @return string escaped string
|
||||
*/
|
||||
public function __dbEscapeIdentifier(string $string): string
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return '';
|
||||
}
|
||||
// for phpstan, thinks this is string|false?
|
||||
return (string)pg_escape_identifier($this->dbh, (string)$string);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_escape_byte
|
||||
*
|
||||
* @param string $data data stream
|
||||
* @return string escaped bytea string
|
||||
*/
|
||||
public function __dbEscapeBytea(string $data): string
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return '';
|
||||
}
|
||||
return pg_escape_bytea($this->dbh, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* unescape bytea data from postgesql
|
||||
*
|
||||
* @param string $bytea Bytea data stream
|
||||
* @return string Unescaped bytea data
|
||||
*/
|
||||
public function __dbUnescapeBytea(string $bytea): string
|
||||
{
|
||||
return pg_unescape_bytea($bytea);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_connection_busy
|
||||
*
|
||||
* @return bool True if connection is busy, False if not or no db connection at all
|
||||
*/
|
||||
public function __dbConnectionBusy(): bool
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
return pg_connection_busy($this->dbh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Experimental wrapper with scoket timetout
|
||||
*
|
||||
* @param integer $timeout_seconds Wait how many seconds on timeout
|
||||
* @return boolean True if connection is busy, or false on
|
||||
* not busy or no db connection at all
|
||||
*/
|
||||
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$busy = pg_connection_busy($this->dbh);
|
||||
/** @var array<resource>|null */
|
||||
$socket = [pg_socket($this->dbh)];
|
||||
$null = null;
|
||||
while ($busy) {
|
||||
// Will wait on that socket until that happens or the timeout is reached
|
||||
stream_select($socket, $null, $null, $timeout_seconds);
|
||||
$busy = pg_connection_busy($this->dbh);
|
||||
}
|
||||
return $busy;
|
||||
}
|
||||
|
||||
/**
|
||||
* extended wrapper for pg_version
|
||||
* can return any setting in this array block
|
||||
* If no connection, return empty string,
|
||||
* if not in array return empty string
|
||||
* On default 'version' will be stripped of any space attached info
|
||||
* eg 13.5 (other info) will return only 13.5
|
||||
*
|
||||
* @param string $parameter Parameter string to extract from array
|
||||
* @param boolean $strip If parameter is server strip out on default
|
||||
* Set to false to get original string AS is
|
||||
* @return string The parameter value
|
||||
*/
|
||||
public function __dbVersionInfo(string $parameter, bool $strip = true): string
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return '';
|
||||
}
|
||||
// extract element
|
||||
$return_string = (string)(pg_version($this->dbh)[$parameter] ?? '');
|
||||
// for version, strip if requested
|
||||
if (
|
||||
in_array($parameter, ['server']) &&
|
||||
$strip === true
|
||||
) {
|
||||
$return_string = explode(' ', $return_string, 2)[0] ?? '';
|
||||
}
|
||||
return $return_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all parameters that are possible from the db_version
|
||||
*
|
||||
* @return array<mixed> Parameter key names from pg_version
|
||||
*/
|
||||
public function __dbVersionInfoParameterList(): array
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return [];
|
||||
}
|
||||
return array_keys(pg_version($this->dbh));
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_version
|
||||
* Note: this only returns server version
|
||||
* not connection version OR client version
|
||||
*
|
||||
* @return string version string
|
||||
*/
|
||||
public function __dbVersion(): string
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return '';
|
||||
}
|
||||
// array has client, protocol, server, we just return server stripped
|
||||
return $this->__dbVersionInfo('server', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a numeric version eg 90506 or 130006, etc
|
||||
* Note that this calls a show command on the server
|
||||
* Note:
|
||||
* Old version is 9.5.6 where 9.5 is the major version
|
||||
* Newer Postgresql (10 on) have only one major version so eg 13.5
|
||||
* is returned as 130005
|
||||
*
|
||||
* @return integer Server version
|
||||
*/
|
||||
public function __dbVersionNumeric(): int
|
||||
{
|
||||
return (int)$this->__dbShow('server_version_num');
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: it is recommended to convert arrays to json and parse the json in
|
||||
* PHP itself, array_to_json(array)
|
||||
* This is a fallback for old PostgreSQL versions
|
||||
* postgresql array to php array
|
||||
* https://stackoverflow.com/a/27964420
|
||||
*
|
||||
* @param string $array_text Array text from PostgreSQL
|
||||
* @param int $start Start string position
|
||||
* @param int|null $end End string position from recursive call
|
||||
* @return array<mixed>|null PHP type array
|
||||
*/
|
||||
public function __dbArrayParse(
|
||||
string $array_text,
|
||||
int $start = 0,
|
||||
?int &$end = null
|
||||
): ?array {
|
||||
if (empty($array_text) || $array_text[0] != '{') {
|
||||
return null;
|
||||
}
|
||||
$return = [];
|
||||
$string = false;
|
||||
$quote = '';
|
||||
$len = strlen($array_text);
|
||||
$v = '';
|
||||
// start from offset
|
||||
for ($array_pos = $start + 1; $array_pos < $len; $array_pos += 1) {
|
||||
$ch = $array_text[$array_pos];
|
||||
// check wher ein the string are we
|
||||
// end, one down
|
||||
if (!$string && $ch == '}') {
|
||||
if ($v !== '' || !empty($return)) {
|
||||
$return[] = $v;
|
||||
}
|
||||
$end = $array_pos;
|
||||
break;
|
||||
// open new array, jump recusrive up
|
||||
} elseif (!$string && $ch == '{') {
|
||||
// full string + poff set and end
|
||||
$v = $this->__dbArrayParse($array_text, $array_pos, $array_pos);
|
||||
// next array element
|
||||
} elseif (!$string && $ch == ',') {
|
||||
$return[] = $v;
|
||||
$v = '';
|
||||
// flag that this is a string
|
||||
} elseif (!$string && ($ch == '"' || $ch == "'")) {
|
||||
$string = true;
|
||||
$quote = $ch;
|
||||
// quoted string
|
||||
} elseif ($string && $ch == $quote && $array_text[$array_pos - 1] == "\\") {
|
||||
$v = substr($v, 0, -1) . $ch;
|
||||
} elseif ($string && $ch == $quote && $array_text[$array_pos - 1] != "\\") {
|
||||
$string = false;
|
||||
} else {
|
||||
// build string
|
||||
$v .= $ch;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any server setting
|
||||
* if no connection or empty parameter or other error returns false
|
||||
* else returns a string
|
||||
*
|
||||
* @param string $parameter Parameter to query
|
||||
* @return string|bool Settings value as string
|
||||
*/
|
||||
public function __dbParameter(string $parameter)
|
||||
{
|
||||
if ($this->dbh === false || is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
if (empty($parameter)) {
|
||||
return false;
|
||||
}
|
||||
return pg_parameter_status($this->dbh, $parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for any SHOW data blocks
|
||||
* eg search_path or client_encoding
|
||||
*
|
||||
* @param string $show_string Part to show, if invalid will return empty string
|
||||
* @return string Found part as is
|
||||
*/
|
||||
public function __dbShow(string $show_string): string
|
||||
{
|
||||
// get search path
|
||||
$cursor = $this->__dbQuery("SHOW " . $this->__dbEscapeIdentifier($show_string));
|
||||
// abort on failure and return empty
|
||||
if ($cursor === false) {
|
||||
return '';
|
||||
}
|
||||
// get result
|
||||
$db_schema = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
|
||||
return $db_schema[$show_string] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new database schema/search_path
|
||||
* Checks if schema exits and if not aborts with error code 2
|
||||
*
|
||||
* @param string $db_schema Schema to set
|
||||
* @return int Returns 0 if no error
|
||||
* 1 for check query failed
|
||||
* 2 for schema not found in database
|
||||
*/
|
||||
public function __dbSetSchema(string $db_schema): int
|
||||
{
|
||||
// check if schema actually exists
|
||||
$query = "SELECT EXISTS("
|
||||
. "SELECT 1 FROM information_schema.schemata "
|
||||
. "WHERE schema_name = " . $this->__dbEscapeLiteral($db_schema)
|
||||
. ")";
|
||||
$cursor = $this->__dbQuery($query);
|
||||
// abort if execution fails
|
||||
if ($cursor === false) {
|
||||
return 1;
|
||||
}
|
||||
// check if schema does not exists
|
||||
$row = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
|
||||
if (empty($row['exists']) || $row['exists'] == 'f') {
|
||||
return 2;
|
||||
}
|
||||
$query = "SET search_path TO " . $this->__dbEscapeLiteral($db_schema);
|
||||
$this->__dbQuery($query);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current set schema/search_path
|
||||
*
|
||||
* @return string Search Path as currently set in DB
|
||||
*/
|
||||
public function __dbGetSchema(): string
|
||||
{
|
||||
return $this->__dbShow('search_path');
|
||||
}
|
||||
|
||||
/**
|
||||
* set the client encoding
|
||||
* Returns 0 on set ok, or 3 if the client encoding could not be set
|
||||
*
|
||||
* @param string $db_encoding
|
||||
* @return int Returns 0 for no error
|
||||
* [not used] 1 for check query failed
|
||||
* [not used] 2 for invalid client encoding
|
||||
* 3 client encoding could not be set
|
||||
*/
|
||||
public function __dbSetEncoding(string $db_encoding): int
|
||||
{
|
||||
// check if ecnoding is valid first
|
||||
// does not take into account aliases
|
||||
// TODO lookup with alisaes so eg ShiftJIS does not get a false
|
||||
// $query = "SELECT EXISTS("
|
||||
// . "SELECT pg_catalog.pg_encoding_to_char(conforencoding) "
|
||||
// . "FROM pg_catalog.pg_conversion "
|
||||
// . "WHERE pg_catalog.pg_encoding_to_char(conforencoding) = "
|
||||
// . $this->dbEscapeLiteral($db_encoding)
|
||||
// . ")";
|
||||
// $cursor = $this->__dbQuery($query);
|
||||
// if ($cursor === false) {
|
||||
// return 1;
|
||||
// }
|
||||
// $row = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
|
||||
// if ($row['exists'] == 'f') {
|
||||
// return 2;
|
||||
// }
|
||||
$query = "SET client_encoding TO " . $this->__dbEscapeLiteral($db_encoding);
|
||||
if ($this->__dbQuery($query) === false) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current set client encoding
|
||||
* @return string Client encoding string, empty if not set
|
||||
*/
|
||||
public function __dbGetEncoding(): string
|
||||
{
|
||||
return $this->__dbShow('client_encoding');
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
305
src/DB/SQL/SqlInterface/SqlFunctions.php
Normal file
305
src/DB/SQL/SqlInterface/SqlFunctions.php
Normal file
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Intrface for all SQL\* functions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DB\SQL\SqlInterface;
|
||||
|
||||
interface SqlFunctions
|
||||
{
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __dbLastErrorQuery(): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $query
|
||||
* @return object|resource|bool
|
||||
*/
|
||||
public function __dbQuery(string $query);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $query
|
||||
* @param array<mixed> $params
|
||||
* @return object|resource|bool
|
||||
*/
|
||||
public function __dbQueryParams(string $query, array $params);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $query
|
||||
* @return boolean
|
||||
*/
|
||||
public function __dbSendQuery(string $query): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return object|resource|bool
|
||||
*/
|
||||
public function __dbGetResult();
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __dbClose(): void;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $query
|
||||
* @return object|resource|bool
|
||||
*/
|
||||
public function __dbPrepare(string $name, string $query);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $name
|
||||
* @param array<mixed> $data
|
||||
* @return object|resource|bool
|
||||
*/
|
||||
public function __dbExecute(string $name, array $data);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param object|resource|bool $cursor
|
||||
* @return integer
|
||||
*/
|
||||
public function __dbNumRows($cursor): int;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param object|resource|bool $cursor
|
||||
* @return integer
|
||||
*/
|
||||
public function __dbNumFields($cursor): int;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param object|resource|bool $cursor
|
||||
* @param int $i
|
||||
* @return string|bool
|
||||
*/
|
||||
public function __dbFieldName($cursor, int $i);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param object|resource|bool $cursor
|
||||
* @param int $result_type
|
||||
* @return array<mixed>|bool
|
||||
*/
|
||||
public function __dbFetchArray($cursor, int $result_type = PGSQL_BOTH);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param boolean $assoc_type
|
||||
* @return integer
|
||||
*/
|
||||
public function __dbResultType(bool $assoc_type = true): int;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param object|resource|bool $cursor
|
||||
* @return array<mixed>|bool
|
||||
*/
|
||||
public function __dbFetchAll($cursor);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param object|resource|bool $cursor
|
||||
* @return integer
|
||||
*/
|
||||
public function __dbAffectedRows($cursor): int;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $query
|
||||
* @param string|null $pk_name
|
||||
* @return string|integer|false
|
||||
*/
|
||||
public function __dbInsertId(string $query, ?string $pk_name);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $schema
|
||||
* @return string|bool
|
||||
*/
|
||||
public function __dbPrimaryKey(string $table, string $schema = '');
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $db_host
|
||||
* @param string $db_user
|
||||
* @param string $db_pass
|
||||
* @param string $db_name
|
||||
* @param integer $db_port
|
||||
* @param string $db_ssl
|
||||
* @return object|resource|bool
|
||||
*/
|
||||
public function __dbConnect(
|
||||
string $db_host,
|
||||
string $db_user,
|
||||
string $db_pass,
|
||||
string $db_name,
|
||||
int $db_port,
|
||||
string $db_ssl = 'allow'
|
||||
);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param object|resource|bool $cursor
|
||||
* @return string
|
||||
*/
|
||||
public function __dbPrintError($cursor = false): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $table
|
||||
* @param boolean $extended
|
||||
* @return array<mixed>|bool
|
||||
*/
|
||||
public function __dbMetaData(string $table, $extended = true);
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string|int|float|bool $string
|
||||
* @return string
|
||||
*/
|
||||
public function __dbEscapeString($string): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string|int|float|bool $string
|
||||
* @return string
|
||||
*/
|
||||
public function __dbEscapeLiteral($string): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public function __dbEscapeIdentifier(string $string): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
public function __dbEscapeBytea(string $data): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $bytea
|
||||
* @return string
|
||||
*/
|
||||
public function __dbUnescapeBytea(string $bytea): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function __dbConnectionBusy(): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param integer $timeout_seconds
|
||||
* @return boolean
|
||||
*/
|
||||
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __dbVersion(): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $array_text
|
||||
* @param integer $start
|
||||
* @param integer|null $end
|
||||
* @return array<mixed>|null
|
||||
*/
|
||||
public function __dbArrayParse(
|
||||
string $array_text,
|
||||
int $start = 0,
|
||||
?int &$end = null
|
||||
): ?array;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $show_string
|
||||
* @return string
|
||||
*/
|
||||
public function __dbShow(string $show_string): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $db_schema
|
||||
* @return integer
|
||||
*/
|
||||
public function __dbSetSchema(string $db_schema): int;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __dbGetSchema(): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $db_encoding
|
||||
* @return integer
|
||||
*/
|
||||
public function __dbSetEncoding(string $db_encoding): int;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __dbGetEncoding(): string;
|
||||
}
|
||||
|
||||
// __END__
|
||||
Reference in New Issue
Block a user