Compare commits

..

10 Commits

Author SHA1 Message Date
Clemens Schwaighofer
7748b83a6b DB\IO add stack trace to debug/error/warning calls
To add the actuall call reference for DB IO debug calls we add the
call trace as context options
2023-06-16 13:17:49 +09:00
Clemens Schwaighofer
f83293ff1a Info comments for DB\IO convert options 2023-06-13 11:47:38 +09:00
Clemens Schwaighofer
9c3be2942e phan and phpstan fixes, also add a convert flag reset to original
dbResetConvertFlag resets to the settings given on init of class
2023-06-09 18:23:28 +09:00
Clemens Schwaighofer
ee62bd98ee Add auto type convert for DB\IO
set via db options "db_convert_type" as array with "on", "json", "numeric",
"bytea"

"on" only converts know good types: "bool", "int"

"json" will convert json/jsonb to array
"bytea" will decode escaped bytea to string (note: this might change to resource)

"numeric" will convert to float.
NOTE: if a numeric number is too large a covnersion might drop data.
Use with care.

Convert flags can be chagned with dbSetConvertFlag and dbUnsetConvertFlag

All convert flags are in "DB\Options\Convert" as enum.
2023-06-09 17:01:03 +09:00
Clemens Schwaighofer
02e9610fad Add a general log method to Logger class
the params order is the actual correct one:
log level, message, context, group_id, prefix

not that group_id and prefix are only used if log level is debug

Switched debug params order for context and prefix so prefix is last
2023-06-09 16:59:22 +09:00
Clemens Schwaighofer
8a41db4649 Output\Image cleanup for phpstan 2023-06-09 12:16:49 +09:00
Clemens Schwaighofer
e27ea3dc9f DB\IO phpdoc update 2023-06-09 10:24:26 +09:00
Clemens Schwaighofer
ec4bf54d81 DB\IO phpdoc layout update
Add line between params and method description
2023-06-09 10:21:02 +09:00
Clemens Schwaighofer
ec3ca787fa Logging: prepare message only if log level is high enough
Also some clean ups on internal method call parameters
2023-06-05 09:30:26 +09:00
Clemens Schwaighofer
86acbbb85b Per run logging was not set if flag changed
if setFlag was set for per_run or per_date the init values where not set

Fixed that on setFlag it is checked if we have per_date or per_run and
then set if not set.

Not that for date, set the flag will set a new date, for per run no.
2023-06-02 17:38:09 +09:00
18 changed files with 1115 additions and 127 deletions

View File

@@ -38,6 +38,7 @@ namespace tests;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use CoreLibs\Logging\Logger\Level;
use CoreLibs\DB\Options\Convert;
/**
* Test class for DB\IO + DB\SQL\PgSQL
@@ -4557,6 +4558,176 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbClose();
}
// testing auto convert
/**
* Undocumented function
*
* @covers ::dbSetConvertFlag
* @testdox Check convert type works
*
* @return void
*/
public function testConvertType(): void
{
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$bytea_data = $db->dbEscapeBytea(
file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'CoreLibsDBIOTest.php') ?: ''
);
$query_insert = <<<SQL
INSERT INTO table_with_primary_key (
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
) VALUES (
$1,
$2, $3, $4, $5,
$6, $7, $8, $9,
$10, $11, $12, $13
)
SQL;
$db->dbExecParams(
$query_insert,
[
'CONVERT_TYPE_TEST',
1, 1.5, 'varchar', 'varchar literla',
json_encode(['json', 'a', 1, true, 'sub' => ['b', 'c']]),
json_encode(['jsonb', 'a', 1, true, 'sub' => ['b', 'c']]),
$bytea_data, date('Y-m-d H:i:s'), date('Y-m-d'), date('H:m:s'),
'{1,2,3}', '{"a","b","c"}'
]
);
$type_layout = [
'uid' => 'string',
'row_int' => 'int',
'row_numeric' => 'float',
'row_varchar' => 'string',
'row_varchar_literal' => 'string',
'row_json' => 'json',
'row_jsonb' => 'json',
'row_bytea' => 'bytea',
'row_timestamp' => 'string',
'row_date' => 'string',
'row_interval' => 'string',
'row_array_int' => 'string',
'row_array_varchar' => 'string'
];
$query_select = <<<SQL
SELECT
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
FROM
table_with_primary_key
WHERE
uid = $1
SQL;
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
// all hast to be string
foreach ($res as $key => $value) {
$this->assertIsString($value, 'Aseert string for column: ' . $key);
}
// convert base only
$db->dbSetConvertFlag(Convert::on);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::numeric);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::json);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::bytea);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
case 'bytea':
// for hex types it must not start with \x
$this->assertStringStartsNotWith(
'\x',
$value,
'Aseert bytes not starts with \x for column: ' . $key . '/' . $name
);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
}
// - internal read data (post exec)
// dbGetNumRows, dbGetNumFields, dbGetFieldNames,
// dbGetQuery, dbGetQueryHash, dbGetDbh
@@ -4588,7 +4759,7 @@ final class CoreLibsDBIOTest extends TestCase
. "('Foxtrott', 'Tango', 789, '1982-10-15') ",
null,
//
2,
3,
4,
['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'],
['varchar', 'varchar', 'int4', 'date'],
@@ -4705,9 +4876,16 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbExecParams($query, $params);
}
$this->assertInstanceOf(
'PgSql\Result',
$db->dbGetCursor(),
'Failed assert dbGetCursor'
);
$this->assertEquals(
$compare_query ?? $query,
$db->dbGetQuery()
$db->dbGetQuery(),
'Failed assert dbGetQuery'
);
$this->assertEquals(
// perhaps move that somewhere else?
@@ -4720,7 +4898,8 @@ final class CoreLibsDBIOTest extends TestCase
($params === null ?
$db->dbGetQueryHash($query) :
$db->dbGetQueryHash($query, $params)
)
),
'Failed assertdbGetQueryHash '
);
$this->assertEquals(
$expected_rows,
@@ -4742,6 +4921,35 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbGetFieldTypes(),
'Failed assert dbGetFieldTypes'
);
// check FieldNameTypes matches
$this->assertEquals(
array_combine(
$expected_col_names,
$expected_col_types
),
$db->dbGetFieldNameTypes(),
'Failed assert dbGetFieldNameTypes'
);
// check pos matches name
// name matches type
// pos matches type
foreach ($expected_col_names as $pos => $name) {
$this->assertEquals(
$name,
$db->dbGetFieldName($pos),
'Failed assert dbGetFieldName: ' . $pos . ' => ' . $name
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($name),
'Failed assert dbGetFieldType: ' . $name . ' => ' . $expected_col_types[$pos]
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($pos),
'Failed assert dbGetFieldType: ' . $pos . ' => ' . $expected_col_types[$pos]
);
}
$dbh = $db->dbGetDbh();
$this->assertIsObject(
$dbh

View File

@@ -691,6 +691,11 @@ final class CoreLibsLoggingLoggingTest extends TestCase
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerLoggingLevelWrite(): array
{
return [
@@ -796,6 +801,46 @@ final class CoreLibsLoggingLoggingTest extends TestCase
);
}
// check log level that writer writes in correct level
// also that non debug ignores prefix/group
/**
* Undocumented function
*
* @covers ::log
* @testdox log() general call test
*
* @return void
*/
public function testLoggingLog(): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLoggingLog',
'log_folder' => self::LOG_FOLDER,
'log_per_level' => true,
]);
$log_ok = $log->log(Level::Debug, 'DEBUG', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_DEBUG.log'
);
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (info) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_INFO.log'
);
}
// must test flow:
// init normal
// log -> check file name
// set per date
// log -> check file name
// and same for per_run
// deprecated calls check?
}

View File

@@ -38,7 +38,9 @@ print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><a href="class_test.db.type.php">Class Test DB Types</a></div>';
print '<div><a href="class_test.db.dbReturn.php">Class Test DB dbReturn</a></div>';
print '<div><a href="class_test.db.single.php">Class Test DB Single Aciont</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "LOGFILE NAME: " . $db->log->getLogFile() . "<br>";
@@ -170,10 +172,10 @@ print "EOM READ OF PREVIOUS INSERTED: " . print_r($db->dbReturnRow($query), true
print "LAST ERROR: " . $db->dbGetLastError() . "<br>";
print "<br>";
$query = <<<SQL
SELECT
test_foo_id, test
FROM test_foo
WHERE test_foo_id = $1;
SELECT
test_foo_id, test
FROM test_foo
WHERE test_foo_id = $1;
SQL;
print "RETURN ROW PARAMS: " . print_r(
$db->dbReturnRowParams(
@@ -334,10 +336,10 @@ $status = $db->dbExecParams($query_insert, $query_params);
echo "<b>*</b><br>";
echo "EOM STRING WITH MORE THAN 10 PARAMETERS: "
. Support::printToString($query_params) . " |<br>"
. " |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true)
. "QUERY: " . $db->dbGetQuery() . " |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |<br>"
. "RETURNING EXT: <pre>" . print_r($db->dbGetReturningExt(), true) . "</pre> |<br>"
. "RETURNING RETURN: <pre>" . print_r($db->dbGetReturningArray(), true) . "</pre> |<br>"
. "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<hr>";
// binary insert tests
@@ -355,10 +357,10 @@ $status = $db->dbExec($query);
$__last_insert_id = $db->dbGetInsertPK();
print "BINARY DATA INSERT: "
. Support::printToString($status) . " |<br>"
. " |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " | "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true)
. "QUERY: " . $db->dbGetQuery() . " |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |<br>"
. "RETURNING EXT: <pre>" . print_r($db->dbGetReturningExt(), true) . "</pre> |<br>"
. "RETURNING RETURN: <pre>" . print_r($db->dbGetReturningArray(), true) . "</pre> |<br>"
. "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<b>*</b><br>";
@@ -369,7 +371,11 @@ INSERT INTO binary_test (
$1, $2, $3
)
SQL;
$status = $db->dbExecParams($query, [$filename, $rand_bin_uid, $binary_data]);
// $binary_data is dbEscapedBytea!
$uniqid = \CoreLibs\Create\Uids::uniqIdShort();
$status = $db->dbExecParams($query, [
'class_test.db.php', $uniqid, $binary_data
]);
$__last_insert_id = $db->dbGetInsertPK();
print "BINARY DATA INSERT PARAMS: "
. Support::printToString($status) . " |<br>"
@@ -378,7 +384,23 @@ print "BINARY DATA INSERT PARAMS: "
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
. "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true)
. "ERROR: " . $db->dbGetLastError(true) . "<br>";
print "BINARY READER: ";
$query = <<<SQL
SELECT
filename, uid, binary_data
FROM
binary_test
WHERE
uid = $1
SQL;
$res = $db->dbReturnRowParams($query, [$uniqid]);
if (is_array($res)) {
var_dump($res);
$file = $db->dbUnescapeBytea($res['binary_data']);
// var_dump($file);
} else {
print "Execute error";
}
echo "<hr>";
// returning test with multiple entries
@@ -578,6 +600,44 @@ while (
}
echo "<hr>";
print "DB RETURN PARAMS LIKE<br>";
$q = <<<SQL
SELECT
test_foo_id, test, some_bool, string_a, number_a, number_a_numeric
FROM test_foo
WHERE string_a LIKE $1;
SQL;
while (
is_array($res = $db->dbReturnParams($q, ['%string%']))
) {
print "ROW: <pre>" . print_r($res, true) . "</pre><br>";
}
echo "<hr>";
print "DB RETURN PARAMS ANY<br>";
$q = <<<SQL
SELECT
test_foo_id, test, some_bool, string_a, number_a, number_a_numeric
FROM test_foo
WHERE string_a = ANY($1);
SQL;
$query_value = '{'
. join(',', ['string a'])
. '}';
while (
is_array($res = $db->dbReturnParams($q, [$query_value]))
) {
print "ROW: <pre>" . print_r($res, true) . "</pre><br>";
}
echo "<hr>";
print "COMPOSITE ELEMENT READ<br>";
$res = $db->dbReturnRow("SELECT item, count, (item).name, (item).price, (item).supplier_id FROM on_hand");
print "ROW: <pre>" . print_r($res) . "</pre>";
var_dump($res);
print "Field Name/Types: <pre>" . print_r($db->dbGetFieldNameTypes(), true) . "</pre>";
echo "<hr>";
// NOTE: try to replacate connection still exists if script is run a second time
// open pg bouncer connection
$db_pgb = new CoreLibs\DB\IO($DB_CONFIG['test_pgbouncer'] ?? [], $log);

View File

@@ -35,7 +35,6 @@ print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><a href="class_test.db.dbReturn.php">Class Test DB dbReturn</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "LOGFILE NAME: " . $db->log->getLogFile() . "<br>";
@@ -54,38 +53,76 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
print "NO DB HANDLER<br>";
}
// params > 10 for debug
// error catcher
$query_insert = <<<SQL
INSERT INTO many_columns (
col_01_int,
col_01, col_02, col_03, col_04, col_05, col_06, col_07, col_08, col_09,
col_10, col_11, col_12, col_02_int
) VALUES (
1,
$1, $2, $3, $4, $5, $6, $7, $8, $9,
$10, $11, $12, $13
)
RETURNING
many_columns_id,
col_01_int,
col_01, col_02, col_03, col_04, col_05, col_06, col_07, col_08, col_09,
col_10, col_11, col_12, col_02_int
SQL;
/**
* Undocumented function
*
* @param \CoreLibs\DB\IO $dbc
* @return void
*/
function testDBS(\CoreLibs\DB\IO $dbc): void
{
echo "Int call<br>";
$dbc->dbReturnRow("SELECT test FROM test_foo LIMIT 1");
}
$uniqid = \CoreLibs\Create\Uids::uniqIdShort();
$binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: '');
$query_params = [
'col 1', 'col 2', 'col 3', 'col 4', 'col 5', 'col 6', 'col 7', 'col 8',
'col 9', 'col 10', 'col 11', 'col 12', null
$uniqid,
true,
'STRING A',
2,
2.5,
1,
date('H:m:s'),
date('Y-m-d H:m:s'),
json_encode(['a' => 'string', 'b' => 1, 'c' => 1.5, 'f' => true, 'g' => ['a', 1, 1.5]]),
null,
'{"a", "b"}',
'{1,2}',
'{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}',
'("Text", 4, 6.3)',
$binary_data
];
$query_insert = <<<SQL
INSERT INTO test_foo (
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_int_1,
array_composite,
composite_item,
some_binary
) VALUES (
$1, $2, $3, $4, $5, $6,
$7, $8, $9, $10,
$11, $12,
$13,
$14,
$15
)
SQL;
$status = $db->dbExecParams($query_insert, $query_params);
echo "<b>*</b><br>";
echo "EOM STRING WITH MORE THAN 10 PARAMETERS: "
. Support::printToString($query_params) . " |<br>"
. " |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " | "
// . "RETURNING EXT: " . Support::printToString($db->dbGetReturningExt()) . " | "
. "RETURNING RETURN: " . Support::printToString($db->dbGetReturningArray())
. "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<hr>";
$query_select = <<<SQL
SELECT
test_foo_id,
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
number_real, number_double, number_serial,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_char_2, array_int_1, array_int_2, array_composite,
composite_item, (composite_item).*
some_binary
FROM
test_foo
WHERE
test = $1;
SQL;
$res = $db->dbReturnRowParams($query_select, [$uniqid]);
if (is_array($res)) {
var_dump($res);
}
testDBS($db);
print "</body></html>";

View File

@@ -0,0 +1,171 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
// basic class test file
define('USE_DATABASE', true);
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-db-types';
ob_end_flush();
use CoreLibs\Debug\Support;
use CoreLibs\DB\Options\Convert;
$log = new CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
// db connection and attach logger
$db = new CoreLibs\DB\IO(DB_CONFIG, $log);
$db->log->debug('START', '=============================>');
$PAGE_NAME = 'TEST CLASS: DB COLUMN TYPES';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "LOGFILE NAME: " . $db->log->getLogFile() . "<br>";
print "LOGFILE ID: " . $db->log->getLogFileId() . "<br>";
print "DBINFO: " . $db->dbInfo() . "<br>";
// DB client encoding
print "DB client encoding: " . $db->dbGetEncoding() . "<br>";
print "DB search path: " . $db->dbGetSchema() . "<br>";
$to_db_version = '15.2';
print "VERSION DB: " . $db->dbVersion() . "<br>";
print "SERVER ENCODING: " . $db->dbVersionInfo('server_encoding') . "<br>";
if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
print "ALL OUTPUT [TEST]: <pre>" . print_r(pg_version($dbh), true) . "</pre><br>";
} else {
print "NO DB HANDLER<br>";
}
print "<b>TRUNCATE test_foo</b><br>";
$db->dbExec("TRUNCATE test_foo");
/* $q = <<<SQL
INSERT INTO test_foo (test, array_composite) VALUES ('C', '{"(a,1,1.5)","(b,2,2.5)"}')
SQL;
$db->dbExecParams($q);
pg_query($db->dbGetDbh(), $q);
$q = <<<SQL
INSERT INTO test_foo (test, array_composite) VALUES ($1, $2)
SQL;
// $db->dbExecParams($q, ['D', '{"(a b,1,1.5)","(c,3,4.5)"}']);
$db->dbExecParams($q, ['D', '{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}']);
*/
$uniqid = \CoreLibs\Create\Uids::uniqIdShort();
$binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: '');
$query_params = [
$uniqid,
true,
'STRING A',
2,
2.5,
1,
date('H:m:s'),
date('Y-m-d H:i:s'),
json_encode(['a' => 'string', 'b' => 1, 'c' => 1.5, 'f' => true, 'g' => ['a', 1, 1.5]]),
null,
'{"a", "b"}',
'{1,2}',
'{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}',
'("Text", 4, 6.3)',
$binary_data
];
$query_insert = <<<SQL
INSERT INTO test_foo (
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_int_1,
array_composite,
composite_item,
some_binary
) VALUES (
$1, $2, $3, $4, $5, $6,
$7, $8, $9, $10,
$11, $12,
$13,
$14,
$15
)
RETURNING
test_foo_id,
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_int_1,
array_composite,
composite_item,
some_binary
SQL;
$status = $db->dbExecParams($query_insert, $query_params);
echo "<b>*</b><br>";
echo "INSERT ALL COLUMN TYPES: "
. Support::printToString($query_params) . " |<br>"
. "QUERY: " . $db->dbGetQuery() . " |<br>"
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |<br>"
. "RETURNING EXT: <pre>" . print_r($db->dbGetReturningExt(), true) . "</pre> |<br>"
. "RETURNING RETURN: <pre>" . print_r($db->dbGetReturningArray(), true) . "<pre> |<br>"
. "ERROR: " . $db->dbGetLastError(true) . "<br>";
echo "<hr>";
$query_select = <<<SQL
SELECT
test_foo_id,
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
number_real, number_double, number_numeric_3, number_serial,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_char_2, array_int_1, array_int_2, array_composite,
composite_item, (composite_item).*,
some_binary
FROM
test_foo
WHERE
test = $1;
SQL;
$res = $db->dbReturnRowParams($query_select, [$uniqid]);
// auto switch:
// ^int
// bool
// with flags:
// json(b) => array
// bytes => string? or resource?
// numeric => float (can have precision cut)
$pos = 0;
$name = '';
if (is_array($res)) {
$cursor = $db->dbGetCursor();
var_dump($res);
print "Field Name/Types: <pre>" . print_r($db->dbGetFieldNameTypes(), true) . "</pre>";
print "Get type for: 'number_a':" . $db->dbGetFieldType('number_a') . "<br>";
print "Get type for: 0: " . $db->dbGetFieldType(0) . "<br>";
print "Get name for: 0: " . $db->dbGetFieldName(0) . "<br>";
}
$db->dbSetConvertFlag(Convert::on);
$db->dbSetConvertFlag(Convert::json);
$db->dbSetConvertFlag(Convert::numeric);
$db->dbSetConvertFlag(Convert::bytea);
$res = $db->dbReturnRowParams($query_select, [$uniqid]);
if (is_array($res)) {
var_dump($res);
}
print "</body></html>";
// __END__

View File

@@ -36,7 +36,7 @@ $log = new CoreLibs\Logging\Logging([
'log_per_date' => true,
]);
$PAGE_NAME = 'TEST CLASS: DEBUG LOGGING';
$PAGE_NAME = 'TEST CLASS: LOGGING';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
@@ -44,12 +44,12 @@ print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$log->logger2Debug();
echo "<hr>";
print "Level 250: " . Level::fromValue(250)->getName() . "<br>";
print "Flag: per_run (from int): " . Flag::fromValue(2)->getName() . "<br>";
print "Flag: per_run getName(): " . Flag::per_class->getName() . "<br>";
print "Flag: per_run ->name: " . Flag::per_class->name . "<br>";
print "Flag: per_run ->value: " . Flag::per_class->value . "<br>";
print "Flag: per_class (16) (from int): " . Flag::fromValue(16)->getName() . "<br>";
print "Flag: per_class getName(): " . Flag::per_class->getName() . "<br>";
print "Flag: per_class ->name: " . Flag::per_class->name . "<br>";
print "Flag: per_class ->value: " . Flag::per_class->value . "<br>";
$log->setLogUniqueId();
print "LogUniqId: " . $log->getLogUniqueId() . "<br>";
@@ -76,10 +76,19 @@ with
and > and <
EOM));
$log->info('Info message', ['info' => 'log']);
$log->notice('Notice message', ['notice' => 'log']);
$log->warning('Warning message', ['warning' => 'log']);
$log->error('Cannot process data', ['error' => 'log']);
$log->critical('Critical message', ['critical' => 'log']);
$log->alert('Alert message', ['Alert' => 'log']);
$log->emergency('Emergency message', ['Emergency' => 'log']);
print "Log File: " . $log->getLogFile() . "<br>";
$log->setLogFlag(Flag::per_run);
$log->debug('PER RUN', 'per run logging');
print "Log File: " . $log->getLogFile() . "<br>";
$log->unsetLogFlag(Flag::per_run);
// init empty
unset($LOG_FILE_ID);
$ll = new CoreLibs\Logging\Logging([

View File

@@ -69,6 +69,7 @@ print "<body>";
// key: file name, value; name
$test_files = [
'class_test.db.php' => 'Class Test: DB',
'class_test.db.types.php' => 'Class Test: DB COLUMN TYPES',
'class_test.db.single.php' => 'Class Test: DB SINGLE',
'class_test.db.dbReturn.php' => 'Class Test: DB dbReturn',
'class_test.convert.colors.php' => 'Class Test: CONVERT COLORS',

View File

@@ -22,6 +22,7 @@ $DB_CONFIG = [
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
// 'db_convert_type' => ['json'] // on, json, numeric, bytea
],
// same as above, but uses pg bouncer
'test_pgbouncer' => [

View File

@@ -575,7 +575,7 @@ function actionIndicator(loc, overlay = true) // eslint-disable-line no-unused-v
*/
function actionIndicatorShow(loc, overlay = true)
{
// console.log('Indicator: SHOW [%s]', loc);
// console.log('{Indicator}: SHOW [%s]', loc);
if (!$('#indicator').is(':visible')) {
if (!$('#indicator').hasClass('progress')) {
$('#indicator').addClass('progress');
@@ -597,7 +597,7 @@ function actionIndicatorShow(loc, overlay = true)
*/
function actionIndicatorHide(loc, overlay = true)
{
// console.log('Indicator: HIDE [%s]', loc);
// console.log('{Indicator}: HIDE [%s]', loc);
$('#indicator').hide();
if (overlay === true) {
overlayBoxHide();
@@ -677,7 +677,7 @@ function ClearCall() // eslint-disable-line no-unused-vars
*/
function showActionIndicator(loc) // eslint-disable-line no-unused-vars
{
// console.log('Indicator: SHOW [%s]', loc);
// console.log('{Indicator}: SHOW [%s]', loc);
// check if indicator element exists
if ($('#indicator').length == 0) {
var el = document.createElement('div');
@@ -715,7 +715,7 @@ function showActionIndicator(loc) // eslint-disable-line no-unused-vars
*/
function hideActionIndicator(loc) // eslint-disable-line no-unused-vars
{
// console.log('Indicator: HIDE [%s]', loc);
// console.log('{Indicator}: HIDE [%s]', loc);
// check if indicator is visible
if ($('#indicator').is(':visible')) {
// hide indicator

View File

@@ -41,7 +41,8 @@ class EditBase
/**
* construct form generator
*
* @param array<mixed> $db_config db config array, mandatory
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class, null auto set
* @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set
* @param \CoreLibs\ACL\Login $login login class for ACL settings

View File

@@ -54,7 +54,8 @@ class ArrayIO extends \CoreLibs\DB\IO
* constructor for the array io class, set the
* primary key name automatically (from array)
*
* @param array<mixed> $db_config db connection config
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db connection config
* @param array<mixed> $table_array table array config
* @param string $table_name table name string
* @param \CoreLibs\Logging\Logging $log Logging class
@@ -587,7 +588,7 @@ class ArrayIO extends \CoreLibs\DB\IO
// 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) {
foreach (array_keys($this->table_array) as $column) {
// check FK ...
if (
isset($this->table_array[$column]['fk']) &&

View File

@@ -259,6 +259,8 @@ namespace CoreLibs\DB;
use CoreLibs\Create\Hash;
use CoreLibs\Debug\Support;
use CoreLibs\Create\Uids;
use CoreLibs\Convert\Json;
use CoreLibs\DB\Options\Convert;
// 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
@@ -328,6 +330,17 @@ class IO
private string $db_type;
/** @var string ssl flag (for postgres only), disable, allow, prefer, require */
private string $db_ssl;
/** @var array<string> flag for converting types from settings */
private array $db_convert_type = [];
// convert type settings
// 0: OFF (CONVERT_OFF)
// >0: ON
// 1: convert intN/bool (CONVERT_ON)
// 2: convert json/jsonb to array (CONVERT_JSON)
// 4: convert numeric/floatN to float (CONVERT_NUMERIC)
// 8: convert bytea to string data (CONVERT_BYTEA)
/** @var int type settings as bit mask, 0 for off, anything >2 will aways set 1 too */
private int $convert_type = Convert::off->value;
// FOR BELOW: (This should be private and only readable through some method)
// cursor array for cached readings
/** @var array<string,mixed> extended cursoers string index with content */
@@ -343,6 +356,8 @@ class IO
private array $field_names = [];
/** @var array<string> field type names */
private array $field_types = [];
/** @var array<string,string> field name to type */
private array $field_name_types = [];
/** @var array<mixed> always return as array, even if only one */
private array $insert_id_arr = [];
/** @var string primary key name for insert recovery from insert_id_arr */
@@ -392,8 +407,11 @@ class IO
public \CoreLibs\Logging\Logging $log;
/**
* main DB concstructor with auto connection to DB and failure set on failed connection
* @param array<mixed> $db_config DB configuration array
* main DB concstructor with auto connection to DB
* and failure set on failed connection
*
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config DB configuration array
* @param \CoreLibs\Logging\Logging $log Logging class
*/
public function __construct(
@@ -402,17 +420,8 @@ class IO
) {
// attach logger
$this->log = $log;
// 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 the config options
$this->__setConfigOptions($db_config);
// set debug, either via global var, or from config, else set to false
$this->dbSetDebug(
// set if logging level is Debug
@@ -497,6 +506,84 @@ class IO
// PRIVATE METHODS
// *************************************************************
/**
* Setup DB config and options
*
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config
* @return bool
*/
private function __setConfigOptions(array $db_config): bool
{
// 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'] ?? '';
// port
$this->db_port = !empty($db_config['db_port']) ?
(int)$db_config['db_port'] : 5432;
if ($this->db_port < 0 || $this->db_port > 65535) {
$this->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'] : '';
// db type
$this->db_type = $db_config['db_type'] ?? '';
if (!in_array($this->db_type, ['pgsql'])) {
$this->db_type = 'pgsql';
}
// ssl setting
$this->db_ssl = !empty($db_config['db_ssl']) ?
$db_config['db_ssl'] : 'allow';
if (!in_array($this->db_ssl, ['allow', 'disable', 'require', 'prefer'])) {
$this->db_ssl = 'allow';
}
// trigger convert type
// ['on', 'json', 'numeric', 'bytea'] allowed
// if on is not set but other valid than on is assumed
foreach ($db_config['db_convert_type'] ?? [] as $db_convert_type) {
if (!in_array($db_convert_type, ['on', 'json', 'numeric', 'bytea'])) {
continue;
}
$this->db_convert_type[] = $db_convert_type;
$this->__setConvertType($db_convert_type);
}
// return status true: ok, false: options error
return true;
}
/**
* Set the convert bit flags
*
* @param string $db_convert_type One of 'on', 'json', 'numeric', 'bytea'
* @return void
*/
private function __setConvertType(string $db_convert_type): void
{
switch ($db_convert_type) {
case 'on':
$this->convert_type |= Convert::on->value;
break;
case 'json':
$this->convert_type |= Convert::on->value;
$this->convert_type |= Convert::json->value;
break;
case 'numeric':
$this->convert_type |= Convert::on->value;
$this->convert_type |= Convert::numeric->value;
break;
case 'bytea':
$this->convert_type |= Convert::on->value;
$this->convert_type |= Convert::bytea->value;
break;
}
}
/**
* based on $this->db_type
* here we need to load the db pgsql include one
@@ -525,8 +612,10 @@ class IO
}
/**
* internal connection function. Used to connect to the DB if there is no connection done yet.
* 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
@@ -571,6 +660,7 @@ class IO
/**
* close db connection
* only used by the deconstructor
*
* @return void has no return
*/
private function __closeDB(): void
@@ -585,6 +675,7 @@ class IO
* checks if query is a SELECT, SHOW or WITH, if not error, 0 return
* NOTE:
* Query needs to start with SELECT, SHOW or WITH
*
* @param string $query query to check
* @return bool true if matching, false if not
*/
@@ -602,6 +693,7 @@ class IO
* 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
@@ -620,6 +712,7 @@ class IO
/**
* 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
*/
@@ -635,6 +728,7 @@ class IO
* 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
*/
@@ -698,15 +792,46 @@ class IO
. \CoreLibs\Debug\Support::prAr($error_data)
. ']';
}
// we need to trace back where the first non class call was done
// add this stack trace to the context
$call_stack = [];
foreach (array_reverse(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)) as $call_trace) {
if (
!empty($call_trace['file']) &&
str_ends_with($call_trace['file'], '/DB/IO.php')
) {
break;
}
$call_stack[] =
($call_trace['file'] ?? 'n/f') . ':'
. ($call_trace['line'] ?? '-') . ':'
. (!empty($call_trace['class']) ? $call_trace['class'] . '->' : '')
. $call_trace['function'];
}
$context = [
'call_trace' => array_reverse($call_stack)
];
switch ($id) {
case 'DB_ERROR':
$this->log->error($debug_id . ' :' . $prefix . $error_string);
$this->log->error(
$debug_id . ' :' . $prefix . $error_string,
$context
);
break;
case 'DB_WARNING':
$this->log->warning($debug_id . ' :' . $prefix . $error_string);
$this->log->warning(
$debug_id . ' :' . $prefix . $error_string,
$context
);
break;
default:
$this->log->debug($debug_id, $error_string, $prefix);
// used named arguments so we can easy change the order of debug
$this->log->debug(
group_id: $debug_id,
message: $error_string,
prefix: $prefix,
context: $context
);
break;
}
}
@@ -740,6 +865,7 @@ class IO
* 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
@@ -751,6 +877,7 @@ class IO
/**
* Check if there is a cursor and write this cursors error info
*
* @param \PgSql\Result|false $cursor current cursor for pg_result_error,
* pg_last_error too, but pg_result_error
* is more accurate (PgSql\Result)
@@ -811,6 +938,7 @@ class IO
* 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
@@ -841,6 +969,7 @@ class IO
/**
* write an error
*
* @param integer $error_id Any Error ID, used in debug message string
* @param \PgSql\Result|false $cursor Optional cursor, passed on to preprocessor
* @param string $msg optional message added to debug
@@ -868,6 +997,7 @@ class IO
/**
* write a warning
*
* @param integer $warning_id Integer warning id added to debug
* @param \PgSql\Result|false $cursor Optional cursor, passed on to preprocessor
* @param string $msg optional message added to debug
@@ -895,6 +1025,7 @@ class IO
/**
* if there is the 'to_encoding' var set,
* and the field is in the wrong encoding converts it to the target
*
* @param array<mixed>|false $row Array from fetch_row
* @return array<mixed>|false Convert fetch_row array, or false
*/
@@ -903,11 +1034,8 @@ class IO
if (is_bool($row)) {
return false;
}
// only do if array, else pass through row (can be false)
if (
!is_array($row) ||
empty($this->to_encoding)
) {
// do not do anything if no to encoding is set
if (empty($this->to_encoding)) {
return $row;
}
// go through each row and convert the encoding if needed
@@ -929,8 +1057,70 @@ class IO
return $row;
}
/**
* Convert column content to the type in the name/pos field
* Note that on default it will only convert types that 100% map to PHP
* - intN
* - bool
* everything else will stay string.
* Fruther flags in the conert_type allow to convert:
* - json/jsonb to array
* - bytea to string
* Dangerous convert:
* - numeric/float to float (precision can be lost)
*
* @param array<mixed>|false $row
* @return array<mixed>|false
*/
private function __dbConvertType(array|false $row): array|false
{
if (is_bool($row)) {
return false;
}
// if convert type is not turned on
if (!$this->convert_type) {
return $row;
}
foreach ($row as $key => $value) {
// always bool/int
if (
$this->dbGetFieldType($key) != 'interval' &&
str_starts_with($this->dbGetFieldType($key) ?: '', 'int')
) {
$row[$key] = (int)$value;
}
if ($this->dbGetFieldType($key) == 'bool') {
$row[$key] = $this->dbBoolean($value);
}
if (
$this->convert_type & Convert::json->value &&
str_starts_with($this->dbGetFieldType($key) ?: '', 'json')
) {
$row[$key] = Json::jsonConvertToArray($value);
}
if (
$this->convert_type & Convert::numeric->value &&
(
str_starts_with($this->dbGetFieldType($key) ?: '', 'numeric') ||
str_starts_with($this->dbGetFieldType($key) ?: '', 'float')
// $this->dbGetFieldType($key) == 'real'
)
) {
$row[$key] = (float)$value;
}
if (
$this->convert_type & Convert::bytea->value &&
$this->dbGetFieldType($key) == 'bytea'
) {
$row[$key] = $this->dbUnescapeBytea($value);
}
}
return $row;
}
/**
* for debug purpose replaces $1, $2, etc with actual data
*
* @param string $query Query to replace values in
* @param array<mixed> $data The data array
* @return string string of query with data inside
@@ -964,7 +1154,9 @@ class IO
}
/**
* extracts schema and table from the query, if no schema returns just empty string
* 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
*/
@@ -1003,6 +1195,7 @@ class IO
/**
* 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 bool True for connection OK, else false
*/
@@ -1021,6 +1214,7 @@ class IO
/**
* dbReturn
* Read data from previous written data cache
*
* @param string $query_hash The hash for the current query
* @param bool $assoc_only Only return assoc value (key named)
* @return array<mixed> Current position query data from cache
@@ -1099,6 +1293,7 @@ class IO
* - checks for insert if returning is set/pk name
* - sets internal hash for query
* - checks multiple call count
*
* @param string $query Query string
* @param array<mixed> $params Query params, needed for hash creation
* @param string $pk_name primary key
@@ -1183,7 +1378,7 @@ class IO
) {
$this->returning_id = true;
}
// $this->debug('DB IO', 'Q: '.$this->query.', RETURN: '.$this->returning_id);
// $this->debug('DB IO', 'Q: ' . $this->query . ', RETURN: ' . $this->returning_id);
// for DEBUG, only on first time ;)
$this->__dbDebug(
'db',
@@ -1229,6 +1424,7 @@ class IO
/**
* 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
@@ -1254,11 +1450,18 @@ class IO
$this->field_names = [];
for ($i = 0; $i < $this->num_fields; $i++) {
$this->field_names[] = $this->db_functions->__dbFieldName($this->cursor, $i) ?: '';
// if (!empty($this->field_names[$i]))
// $this->field_name_types[$this->field_names[$i]] = null;
}
$this->field_types = [];
for ($i = 0; $i < $this->num_fields; $i++) {
$this->field_types[] = $this->db_functions->__dbFieldType($this->cursor, $i) ?: '';
}
// combined array
$this->field_name_types = array_combine(
$this->field_names,
$this->field_types
);
} elseif ($this->__checkQueryForInsert($this->query)) {
// if not select do here
// count affected rows
@@ -1292,6 +1495,7 @@ class IO
* 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 bool $returning_id
* @param string $query
* @param string|null $pk_name
@@ -1384,6 +1588,7 @@ class IO
* 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
@@ -1405,6 +1610,7 @@ class IO
* returns the db init error
* if failed to connect it is set to false
* else true
*
* @return bool Connection status
*/
public function dbGetConnectionStatus(): bool
@@ -1414,6 +1620,7 @@ class IO
/**
* get certain settings like username, db name
*
* @param string $name what setting to query
* @return int|string|bool setting value, if not allowed name return false
*/
@@ -1458,6 +1665,7 @@ class IO
/**
* 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
@@ -1497,6 +1705,7 @@ class IO
/**
* Server version as integer value
*
* @return integer Version as integer
*/
public function dbVersionNumeric(): int
@@ -1506,6 +1715,7 @@ class IO
/**
* return current database version (server side) as string
*
* @return string database version as string
*/
public function dbVersion(): string
@@ -1515,6 +1725,7 @@ class IO
/**
* extended version info, can access all additional information data
*
* @param string $parameter Array parameter name, if not valid returns
* empty string
* @param bool $strip Strip extended server info string, default true
@@ -1528,6 +1739,7 @@ class IO
/**
* All possible parameter names for dbVersionInfo
*
* @return array<mixed> List of all parameter names
*/
public function dbVersionInfoParameters(): array
@@ -1537,6 +1749,7 @@ class IO
/**
* returns bool 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
*/
@@ -1605,6 +1818,7 @@ class IO
/**
* 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
@@ -1632,6 +1846,7 @@ class IO
/**
* neutral function to escape a string for DB writing
*
* @param string|int|float|bool $string string to escape
* @return string escaped string
*/
@@ -1643,6 +1858,7 @@ class IO
/**
* 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
*/
@@ -1653,6 +1869,7 @@ class IO
/**
* string escape for column and table names
*
* @param string $string string to escape
* @return string escaped string
*/
@@ -1663,6 +1880,7 @@ class IO
/**
* escape data for writing to bytea type column field
*
* @param string $data data to escape to bytea
* @return string escaped bytea string
*/
@@ -1673,6 +1891,7 @@ class IO
/**
* unescape bytea data back to normal binrary data
*
* @param string $bytea bytea data stream
* @return string binary data string
*/
@@ -1683,6 +1902,7 @@ class IO
/**
* clear up any data for valid DB insert
*
* @param int|float|string|bool|null $value to escape data
* @param string $kbn escape trigger type
* @return string escaped value
@@ -1749,10 +1969,11 @@ class IO
* if the input is a single char 't' or 'f
* it will return the bool 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 bool $rev do reverse (bool to string)
* @return bool|string correct php bool true/false
* or postgresql 't'/'f'
* @return bool|string [default=false]: corretc postgresql -> php,
* true: convert php to postgresql
*/
public function dbBoolean(string|bool|int $string, bool $rev = false): bool|string
{
@@ -1784,6 +2005,7 @@ class IO
/**
* 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)
@@ -1835,6 +2057,7 @@ class IO
/**
* 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
@@ -1851,6 +2074,7 @@ class IO
/**
* 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>|false array of table data, false on error (table not found)
@@ -1954,6 +2178,10 @@ class IO
'data' => [],
// field names as array
'field_names' => [],
// field types as array (pos in field names is pos here)
'field_types' => [],
// name to type assoc array (from field names and field types)
'field_name_types' => [],
// number of fields (field names)
'num_fields' => 0,
// number of rows that will be maximum returned
@@ -2117,6 +2345,12 @@ class IO
);
}
$this->field_types = $this->cursor_ext[$query_hash]['field_types'];
// combined name => type
$this->cursor_ext[$query_hash]['field_name_types'] = array_combine(
$this->field_names,
$this->field_types
);
$this->field_name_types = $this->cursor_ext[$query_hash]['field_name_types'];
// reset first call var
$first_call = false;
// reset the internal pos counter
@@ -2139,9 +2373,11 @@ class IO
!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->__dbConvertType(
$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: '
@@ -2238,6 +2474,7 @@ class IO
* 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
* Wrapper for dbExecParams without params
*
* @param string $query the query, if not given,
* the query class var will be used
* if this was not set, method will quit with false
@@ -2307,10 +2544,9 @@ class IO
}
}
// 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 \PgSql\Result|false $cursor the cursor from db_exec or
* pg_query/pg_exec/mysql_query
* if not set will use internal cursor,
@@ -2333,9 +2569,11 @@ class IO
return false;
}
return $this->__dbConvertEncoding(
$this->db_functions->__dbFetchArray(
$cursor,
$this->db_functions->__dbResultType($assoc_only)
$this->__dbConvertType(
$this->db_functions->__dbFetchArray(
$cursor,
$this->db_functions->__dbResultType($assoc_only)
)
)
);
}
@@ -2343,6 +2581,7 @@ class IO
/**
* returns the FIRST row of the given query
* wrapper for dbReturnRowParms
*
* @param string $query the query to be executed
* @param bool $assoc_only if true, only return assoc entry (default false)
* @return array<mixed>|false row array or false on error
@@ -2388,6 +2627,7 @@ class IO
/**
* creates an array of hashes of the query (all data)
* Wrapper for dbReturnArrayParams
*
* @param string $query the query to be executed
* @param bool $assoc_only if true, only name ref are returned (default true)
* @return array<mixed>|false array of hashes (row -> fields), false on error
@@ -2432,6 +2672,20 @@ class IO
return $rows;
}
// ***************************
// CURSOR RETURN
// ***************************
/**
* Get current set cursor or false if not set or error
*
* @return \PgSql\Result|false
*/
public function dbGetCursor(): \PgSql\Result|false
{
return $this->cursor;
}
// ***************************
// CURSOR EXT CACHE RESET
// ***************************
@@ -2557,6 +2811,7 @@ class IO
/**
* gets how often a query was called already
*
* @param string $query query string
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
@@ -2581,6 +2836,7 @@ class IO
* 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
@@ -2693,6 +2949,7 @@ class IO
/**
* 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 \PgSql\Result|false false on error, or result on OK
@@ -2791,6 +3048,7 @@ class IO
* executes the query async so other methods can be run at the same time
* Wrapper for dbExecParamsAsync
* NEEDS : dbCheckAsync
*
* @param string $query query to run
* @param string $pk_name optional primary key name, only used with
* insert for returning call
@@ -2890,6 +3148,7 @@ class IO
/**
* checks a previous async query and returns data if finished
* NEEDS : dbExecAsync
*
* @return \PgSql\Result|bool cursor resource if the query is still running,
* false if an error occured or cursor of that query
*/
@@ -2930,6 +3189,7 @@ class IO
/**
* Returns the current async running query hash
*
* @return string Current async running query hash
*/
public function dbGetAsyncRunning(): string
@@ -2948,6 +3208,7 @@ class IO
/**
* 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
@@ -3163,6 +3424,54 @@ class IO
return $this->db_debug;
}
/**
* Undocumented function
*
* @param Convert $convert
* @return void
*/
public function dbSetConvertFlag(Convert $convert): void
{
$this->convert_type |= $convert->value;
}
/**
* Undocumented function
*
* @param Convert $convert
* @return void
*/
public function dbUnsetConvertFlag(Convert $convert): void
{
$this->convert_type &= ~$convert->value;
}
/**
* Reset to origincal config file set
*
* @return void
*/
public function dbResetConvertFlag(): void
{
foreach ($this->db_convert_type as $db_convert_type) {
$this->__setConvertType($db_convert_type);
}
}
/**
* Undocumented function
*
* @param Convert $convert
* @return bool
*/
public function dbGetConvertFlag(Convert $convert): bool
{
if ($this->convert_type & $convert->value) {
return true;
}
return false;
}
/**
* set max query calls, set to -1 to disable loop
* protection. this will generate a warning
@@ -3314,12 +3623,11 @@ class IO
* 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
* @return void
*/
public function dbSetToEncoding(string $encoding): string
public function dbSetToEncoding(string $encoding): void
{
$this->to_encoding = $encoding;
return $this->to_encoding;
}
/**
@@ -3555,6 +3863,45 @@ class IO
return $this->field_types;
}
/**
* Get the field name to type connection list
*
* @return array<string,string>
*/
public function dbGetFieldNameTypes(): array
{
return $this->field_name_types;
}
/**
* Get the field name for a position
*
* @param int $pos Position number in query
* @return false|string Field name or false for not found
*/
public function dbGetFieldName(int $pos): false|string
{
return $this->field_names[$pos] ?? false;
}
/**
* Return a field type for a field name or pos,
* will return false if field is not found in list
*
* @param string|int $name_pos Field name or pos to get the type for
* @return false|string Either the field type or
* false for not found in list
*/
public function dbGetFieldType(int|string $name_pos): false|string
{
if (is_numeric($name_pos)) {
$field_type = $this->field_types[$name_pos] ?? false;
} else {
$field_type = $this->field_name_types[$name_pos] ?? false;
}
return $field_type;
}
/**
* Returns the value for given key in statement
* Will write error if statemen id does not exist

View File

@@ -0,0 +1,63 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/6/9
* DESCRIPTION:
* DB Options for convert type
*
* off: no conversion (all string)
* on: int/bool only
* json: json/jsonb to array
* numeric: any numeric or float to float
* bytes: decode bytea to string
*/
declare(strict_types=1);
namespace CoreLibs\DB\Options;
enum Convert: int
{
/** do not convert */
case off = 0;
/** convert only int/bool */
case on = 1;
/** also convert json to php array */
case json = 2;
/** also convert any float/real/numeric to php float */
case numeric = 4;
/** also decode bytea string to php string */
case bytea = 8;
/**
* get internal name from string value
*
* @param non-empty-string $name
* @return self
*/
public static function fromName(string $name): self
{
return match ($name) {
'Off', 'off', 'OFF', 'convert_off', 'CONVERT_OFF' => self::off,
'On', 'on', 'ON', 'convert_on', 'CONVERT_ON' => self::on,
'Json', 'json', 'JSON', 'convert_json', 'CONVERT_JSON' => self::json,
'Numeric', 'numeric', 'NUMERIC', 'convert_numeric', 'CONVERT_NUMERIC' => self::numeric,
'Bytea', 'bytea', 'BYTEA', 'convert_bytea', 'CONVERT_BYTEA' => self::bytea,
default => self::off,
};
}
/**
* Get internal name from int value
*
* @param int $value
* @return self
*/
public static function fromValue(int $value): self
{
return self::from($value);
}
}
// __END__

View File

@@ -407,13 +407,6 @@ class Logging
}
$this->setLogFlag($log_flag_key);
}
// init per run uid
if ($this->getLogFlag(Flag::per_run)) {
$this->setLogUniqueId();
} elseif ($this->getLogFlag(Flag::per_date)) {
// init file date
$this->log_file_date = date('Y-m-d');
}
}
/**
@@ -535,7 +528,7 @@ class Logging
* Prepare the log message with all needed info blocks:
* [timestamp] [host name] [file path + file] [running uid] {class} <debug level/group id> - message
*
* @param string $level_str Log level we will write to
* @param Level $level Log level we will write to
* @param string|Stringable $message The message to write
* @param mixed[] $context Any additional info we want to attach in any format
* @param string $group_id A group id, only used in DEBUG level,
@@ -543,11 +536,15 @@ class Logging
* @return string
*/
private function prepareLog(
string $level_str,
Level $level,
string|\Stringable $message,
array $context = [],
string $group_id = '',
): string {
// only prepare if to write log level is in set log level
if (!$this->checkLogLevel($level)) {
return '';
}
// file + line: call not this but one before (the one that calls this)
$file_line = Support::getCallerFileLine(2) ??
System::getPageName(System::FULL_PATH);
@@ -562,7 +559,7 @@ class Logging
$timestamp = Support::printTime();
// if group id is empty replace it with current level
$group_str = $level_str;
$group_str = $level->getName();
if (!empty($group_id)) {
$group_str .= ':' . $group_id;
}
@@ -570,7 +567,7 @@ class Logging
$context_str = '';
if ($context != []) {
// TODO this here has to be changed to something better
$context_str = ' ' . print_r($context, true);
$context_str = ' :' . print_r($context, true);
}
// build log string
return '[' . $timestamp . '] '
@@ -776,6 +773,13 @@ class Logging
public function setLogFlag(Flag $flag): void
{
$this->log_flags |= $flag->value;
// init per run uid
if ($this->getLogFlag(Flag::per_run)) {
$this->setLogUniqueId();
} elseif ($this->getLogFlag(Flag::per_date)) {
// init file date
$this->setLogDate();
}
}
/**
@@ -906,6 +910,42 @@ class Logging
// MAIN CALLS
// *********************************************************************
/**
* Commong log interface
*
* extended with group_id, prefix that are ONLY used for debug level
*
* @param Level $level
* @param string|\Stringable $message
* @param mixed[] $context
* @param string $group_id
* @param string $prefix
* @return bool
*/
public function log(
Level $level,
string|\Stringable $message,
array $context = [],
string $group_id = '',
string $prefix = '',
): bool {
// if we are not debug, ignore group_id and prefix
if ($level != Level::Debug) {
$group_id = '';
$prefix = '';
}
return $this->writeErrorMsg(
$level,
$this->prepareLog(
$level,
$prefix . $message,
$context,
$group_id
),
$group_id
);
}
/**
* DEBUG: 100
*
@@ -913,23 +953,23 @@ class Logging
*
* @param string $group_id id for error message, groups messages together
* @param string|Stringable $message the actual error message
* @param mixed[] $context
* @param string $prefix Attach some block before $string.
* Will not be stripped even
* when strip is true
* if strip is false, recommended to add that to $string
* @param mixed[] $context
* @return bool True if logged, false if not logged
*/
public function debug(
string $group_id,
string|\Stringable $message,
string $prefix = '',
array $context = []
array $context = [],
string $prefix = ''
): bool {
return $this->writeErrorMsg(
$this->log_level,
Level::Debug,
$this->prepareLog(
Level::Debug->getName(),
Level::Debug,
$prefix . $message,
$context,
$group_id
@@ -950,7 +990,7 @@ class Logging
return $this->writeErrorMsg(
Level::Info,
$this->prepareLog(
Level::Info->getName(),
Level::Info,
$message,
$context,
)
@@ -969,7 +1009,7 @@ class Logging
return $this->writeErrorMsg(
Level::Notice,
$this->prepareLog(
Level::Notice->getName(),
Level::Notice,
$message,
$context,
)
@@ -988,7 +1028,7 @@ class Logging
return $this->writeErrorMsg(
Level::Warning,
$this->prepareLog(
Level::Warning->getName(),
Level::Warning,
$message,
$context,
)
@@ -1007,7 +1047,7 @@ class Logging
return $this->writeErrorMsg(
Level::Error,
$this->prepareLog(
Level::Error->getName(),
Level::Error,
$message,
$context,
)
@@ -1026,7 +1066,7 @@ class Logging
return $this->writeErrorMsg(
Level::Critical,
$this->prepareLog(
Level::Critical->getName(),
Level::Critical,
$message,
$context,
)
@@ -1045,7 +1085,7 @@ class Logging
return $this->writeErrorMsg(
Level::Alert,
$this->prepareLog(
Level::Alert->getName(),
Level::Alert,
$message,
$context,
)
@@ -1064,7 +1104,7 @@ class Logging
return $this->writeErrorMsg(
Level::Emergency,
$this->prepareLog(
Level::Emergency->getName(),
Level::Emergency,
$message,
$context,
)

View File

@@ -308,7 +308,8 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
/**
* construct form generator
*
* @param array<mixed> $db_config db config array, mandatory
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,mixed> $login_acl Login ACL array,

View File

@@ -36,6 +36,7 @@ class Image
): string|false {
// get image type flags
$image_types = [
0 => 'UNKOWN-IMAGE',
1 => 'gif',
2 => 'jpg',
3 => 'png'
@@ -69,7 +70,7 @@ class Image
}
// does this picture exist and is it a picture
if (file_exists($filename) && is_file($filename)) {
[$width, $height, $type] = getimagesize($filename) ?: [];
[$width, $height, $type] = getimagesize($filename) ?: [0, 0, 0];
$convert_prefix = '';
$create_file = false;
$delete_filename = '';
@@ -98,7 +99,7 @@ class Image
if (!is_file($filename)) {
$filename .= '-0';
}
[$width, $height, $type] = getimagesize($filename) ?: [];
[$width, $height, $type] = getimagesize($filename) ?: [0, 0, 0];
}
// if no size given, set size to original
if (!$size_x || $size_x < 1) {
@@ -117,7 +118,7 @@ class Image
$status = exec($convert_string, $output, $return);
// get the size of the converted data, if converted
if (is_file($thumbnail)) {
[$width, $height, $type] = getimagesize($thumbnail) ?: [];
[$width, $height, $type] = getimagesize($thumbnail) ?: [0, 0, 0];
}
}
if ($height > $size_y) {
@@ -217,7 +218,7 @@ class Image
return $thumbnail;
}
// $this->debug('IMAGE PREPARE', "FILENAME OK, THUMB WIDTH/HEIGHT OK");
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [];
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null];
$thumbnail_write_path = null;
$thumbnail_web_path = null;
// path set first
@@ -447,7 +448,7 @@ class Image
if (!function_exists('exif_read_data') || !is_writeable($filename)) {
return;
}
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [];
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null];
// add @ to avoid "file not supported error"
$exif = @exif_read_data($filename);
$orientation = null;

View File

@@ -39,6 +39,7 @@ return array(
'CoreLibs\\Create\\Uids' => $baseDir . '/lib/CoreLibs/Create/Uids.php',
'CoreLibs\\DB\\Extended\\ArrayIO' => $baseDir . '/lib/CoreLibs/DB/Extended/ArrayIO.php',
'CoreLibs\\DB\\IO' => $baseDir . '/lib/CoreLibs/DB/IO.php',
'CoreLibs\\DB\\Options\\Convert' => $baseDir . '/lib/CoreLibs/DB/Options/Convert.php',
'CoreLibs\\DB\\SQL\\Interface\\SqlFunctions' => $baseDir . '/lib/CoreLibs/DB/SQL/Interface/SqlFunctions.php',
'CoreLibs\\DB\\SQL\\PgSQL' => $baseDir . '/lib/CoreLibs/DB/SQL/PgSQL.php',
'CoreLibs\\Debug\\FileWriter' => $baseDir . '/lib/CoreLibs/Debug/FileWriter.php',

View File

@@ -90,6 +90,7 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9
'CoreLibs\\Create\\Uids' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/Uids.php',
'CoreLibs\\DB\\Extended\\ArrayIO' => __DIR__ . '/../..' . '/lib/CoreLibs/DB/Extended/ArrayIO.php',
'CoreLibs\\DB\\IO' => __DIR__ . '/../..' . '/lib/CoreLibs/DB/IO.php',
'CoreLibs\\DB\\Options\\Convert' => __DIR__ . '/../..' . '/lib/CoreLibs/DB/Options/Convert.php',
'CoreLibs\\DB\\SQL\\Interface\\SqlFunctions' => __DIR__ . '/../..' . '/lib/CoreLibs/DB/SQL/Interface/SqlFunctions.php',
'CoreLibs\\DB\\SQL\\PgSQL' => __DIR__ . '/../..' . '/lib/CoreLibs/DB/SQL/PgSQL.php',
'CoreLibs\\Debug\\FileWriter' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/FileWriter.php',