Compare commits

..

5 Commits

Author SHA1 Message Date
Clemens Schwaighofer
59948c0f3d Name fix for MARK in SQLite DB IO 2025-07-22 18:16:43 +09:00
Clemens Schwaighofer
0022ea6b9a Comment update for table meta data read 2025-07-17 16:58:18 +09:00
Clemens Schwaighofer
1b387baea9 Actually ?1 is a valid SQLite set to assign the first parameter to both like $1 2025-07-17 16:55:59 +09:00
Clemens Schwaighofer
8574175e78 Placeholder fix for SQLite table meta data query 2025-07-17 16:32:39 +09:00
Clemens Schwaighofer
ec8c4d6e60 SQL lite from JIRA csv data import 2025-07-17 11:09:32 +09:00
17 changed files with 608 additions and 164 deletions

View File

@@ -24,12 +24,12 @@ final class CoreLibsCheckEmailTest extends TestCase
'get email regex invalid -1, will be 0' => [
-1,
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
],
'get email regex invalid 10, will be 0' => [
10,
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
],
'get email regex valid 1, will be 1' => [
1,
@@ -157,7 +157,7 @@ final class CoreLibsCheckEmailTest extends TestCase
'error' => 0,
'message' => 'Invalid email address',
'regex' => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
]
],
'error 1 will return double @ error' => [
@@ -181,7 +181,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 3,
'message' => 'Invalid domain part after @ sign',
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$"
]
],
'error 4 will be invalid domain' => [
@@ -189,7 +189,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 4,
'message' => 'Invalid domain name part',
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\."
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\."
]
],
'error 5 will be invalid domain top level only' => [
@@ -197,7 +197,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 5,
'message' => 'Wrong domain top level part',
'regex' => "\.[a-zA-Z]{2,6}$"
'regex' => "\.([a-zA-Z]{2,6}){1}$"
]
],
'error 6 will be domain double dot' => [

View File

@@ -105,15 +105,11 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Error,
]);
$errorLogTmpfile = tmpfile();
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setMessage(
$level,
$str
);
// for exceptions if log level is set to catch them
$error_log_content = stream_get_contents($errorLogTmpfile);
$this->assertEquals(
[
'level' => $expected,
@@ -381,8 +377,6 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
?bool $log_warning,
string $expected
): void {
$errorLogTmpfile = tmpfile();
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessagesLogError',
'log_folder' => self::LOG_FOLDER,
@@ -398,9 +392,6 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
log_error: $log_error,
log_warning: $log_warning
);
ini_set('error_log', $errorLogLocationBackup);
// for exceptions if log level is set to catch them
$error_log_content = stream_get_contents($errorLogTmpfile);
$file_content = '';
if (is_file($log->getLogFolder() . $log->getLogFile())) {
$file_content = file_get_contents(
@@ -456,8 +447,6 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'log_level' => Level::Debug,
'log_per_run' => true
]);
$errorLogTmpfile = tmpfile();
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setErrorMsg(
$id,
@@ -467,9 +456,6 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
log_error: $log_error,
log_warning: $log_warning
);
ini_set('error_log', $errorLogLocationBackup);
// for exceptions if log level is set to catch them
$error_log_content = stream_get_contents($errorLogTmpfile);
$file_content = '';
if (is_file($log->getLogFolder() . $log->getLogFile())) {
$file_content = file_get_contents(

View File

@@ -18,7 +18,7 @@ use CoreLibs\Logging\Logger\Flag;
final class CoreLibsLoggingLoggingTest extends TestCase
{
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
private const REGEX_BASE = "\[[\d\-\s\.:+T]+\]\s{1}" // date, just basic checks
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
. "\[(phar:\/\/)?[\w\-\.\/]+:\d+\]\s{1}" // folder/file [note phar:// is for phpunit]
. "\[\w+\]\s{1}" // run id

View File

@@ -119,8 +119,6 @@ On a version update the old phpunit folder in .libs has to be removed and the ne
The original edit.js javascript functions are now in utils.js or utils.min.js.
The development for those files is located in a different repository
The development for thos files is located in a different repository
General: <https://[service]/CodeBlocks/JavaScript.utils>
Org: <https://[serverice]/[org]/Code-Blocks.JavaScript.utils>
https://[service]/CodeBlocks/javascript-utils

View File

@@ -1,11 +0,0 @@
# Security Policy
This software follows the [Semver 2.0 scheme](https://semver.org/).
## Supported Versions
Only the latest version is supported
## Reporting a Vulnerability
Open a ticket to report a secuirty problem

View File

@@ -332,7 +332,7 @@ print "(kosrt, lower case, reverse): "
print "<hr>";
$nested = [
'B' => 'foo', 'a', '0', 9, /** @phpstan-ignore-line This is a test for wrong index */
'B' => 'foo', 'a', '0', 9,
'1' => ['z', 'b', 'a'],
'd' => ['zaip', 'bar', 'baz']
];

View File

@@ -68,14 +68,6 @@ function test2(): array
return DebugSupport::getCallerMethodList(1);
}
// date stueff
print "printTime(-1): " . DebugSupport::printTime() . "<br>";
print "printTime(2): " . DebugSupport::printTime(2) . "<br>";
print "printTime(3): " . DebugSupport::printTime(3) . "<br>";
print "printTime(5): " . DebugSupport::printTime(5) . "<br>";
print "printIsoTime(): " . DebugSupport::printIsoTime() . "<br>";
print "printIsoTime(false): " . DebugSupport::printIsoTime(false) . "<br>";
print "S::GETCALLERMETHOD: " . DebugSupport::getCallerMethod(0) . "<br>";
print "S::GETCALLERMETHOD: " . test() . "<br>";
print "S::GETCALLERMETHODLIST: <pre>" . print_r(test2(), true) . "</pre><br>";
@@ -154,7 +146,7 @@ print "LOG LEVEL: " . DebugSupport::printAr(\CoreLibs\Convert\SetVarType::setAr
$new_log->getLogLevel('debug', 'on')
)) . "<br>";
echo "<b>CLASS DEBUG CALL LEGACY</b><br>";
echo "<b>CLASS DEBUG CALL</b><br>";
// @codingStandardsIgnoreLine
class TestL

View File

@@ -40,8 +40,6 @@ print "Log ERROR: " . $log->prAr($em->getFlagLogError()) . "<br>";
print "FN: " . ml::fromName('Affe')->name . "<br>";
print "NU: " . ml::fromValue(100)->name . "<br>";
print "NU: " . ml::fromValue(1000)->name . "<br>";
print "OK.: " . ml::ok->name . "<br>";
print "OK^: " . ml::fromName('OK')->name . "<br>";
$em->setErrorMsg('123', 'error', 'msg this is bad, auto logged if debug');
$em->setErrorMsg('123', 'error', 'msg this is bad, auto logged if debug', 'target-id', 'other-style');

View File

@@ -18,7 +18,7 @@ class Email
// 3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
3 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // wrong part after @
// 4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
4 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.", // wrong domain name part
4 => "@@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.", // wrong domain name part
5 => "\.[a-zA-Z]{2,6}$", // wrong top level part
6 => "@(.*)\.{2,}", // double .. in domain name part
7 => "@.*\.$" // ends with a dot, top level, domain missing

View File

@@ -150,14 +150,14 @@ class ArrayHandler
* array search simple. looks for key, value combination, if found, returns true
* on default does not strict check, so string '4' will match int 4 and vica versa
*
* @param array<mixed> $in_array search in as array
* @param array<mixed> $array search in as array
* @param string|int $key key (key to search in)
* @param string|int|bool|array<string|int|bool> $value values list (what to find)
* @param bool $strict [false], if set to true, will strict check key/value
* @return bool true on found, false on not found
*/
public static function arraySearchSimple(
array $in_array,
array $array,
string|int $key,
string|int|bool|array $value,
bool $strict = false
@@ -166,7 +166,7 @@ class ArrayHandler
if (!is_array($value)) {
$value = [$value];
}
foreach ($in_array as $_key => $_value) {
foreach ($array as $_key => $_value) {
// if value is an array, we search
if (is_array($_value)) {
// call recursive, and return result if it is true, else continue
@@ -189,19 +189,19 @@ class ArrayHandler
* If prefix is turned on each found group will be prefixed with the
* search key
*
* @param array<mixed> $in_array array to search in
* @param array<mixed> $array array to search in
* @param array<mixed> $needles keys to find in array
* @param bool $flat [false] Turn on flat output
* @param bool $prefix [false] Prefix found with needle key
* @return array<mixed> Found values
*/
public static function arraySearchKey(
array $in_array,
array $array,
array $needles,
bool $flat = false,
bool $prefix = false
): array {
$iterator = new \RecursiveArrayIterator($in_array);
$iterator = new \RecursiveArrayIterator($array);
$recursive = new \RecursiveIteratorIterator(
$iterator,
\RecursiveIteratorIterator::SELF_FIRST
@@ -248,7 +248,7 @@ class ArrayHandler
* If not found return an array with the array block there the required key is missing,
* the path as string with seperator block set and the missing key entry
*
* @param array<mixed> $in_array
* @param array<mixed> $array
* @param string|int|float|bool $search_value
* @param string|array<string> $required_key
* @param ?string $search_key [null]
@@ -257,7 +257,7 @@ class ArrayHandler
* @return array<array{content?:array<mixed>,path?:string,missing_key?:array<string>}>
*/
public static function findArraysMissingKey(
array $in_array,
array $array,
string|int|float|bool $search_value,
string|array $required_key,
?string $search_key = null,
@@ -265,7 +265,7 @@ class ArrayHandler
string $current_path = ''
): array {
$results = [];
foreach ($in_array as $key => $value) {
foreach ($array as $key => $value) {
$path = $current_path ? $current_path . $path_separator . $key : $key;
if (is_array($value)) {
@@ -321,7 +321,7 @@ class ArrayHandler
* Find key => value entry and return set with key for all matching
* Can search recursively through nested arrays if recursive flag is set
*
* @param array<mixed> $in_array
* @param array<mixed> $array
* @param string $lookup
* @param int|string|float|bool $search
* @param bool $strict [false]
@@ -332,7 +332,7 @@ class ArrayHandler
* @return array<mixed>
*/
public static function selectArrayFromOption(
array $in_array,
array $array,
string $lookup,
int|string|float|bool $search,
bool $strict = false,
@@ -342,7 +342,7 @@ class ArrayHandler
string $flat_separator = self::DATA_SEPARATOR
): array {
// skip on empty
if ($in_array == []) {
if ($array == []) {
return [];
}
// init return result
@@ -352,7 +352,7 @@ class ArrayHandler
$search = strtolower($search);
}
foreach ($in_array as $key => $value) {
foreach ($array as $key => $value) {
// Handle current level search
if (isset($value[$lookup])) {
$compareValue = $value[$lookup];
@@ -399,14 +399,14 @@ class ArrayHandler
/**
* main wrapper function for next/prev key
*
* @param array<mixed> $in_array array to search in
* @param array<mixed> $array array to search in
* @param int|string $key key for next/prev
* @param bool $next [=true] if to search next or prev
* @return int|string|null Next/prev key or null for end/first
*/
private static function arrayGetKey(array $in_array, int|string $key, bool $next = true): int|string|null
private static function arrayGetKey(array $array, int|string $key, bool $next = true): int|string|null
{
$keys = array_keys($in_array);
$keys = array_keys($array);
if (($position = array_search($key, $keys, true)) === false) {
return null;
}
@@ -422,26 +422,26 @@ class ArrayHandler
* Get previous array key from an array
* null on not found
*
* @param array<mixed> $in_array
* @param array<mixed> $array
* @param int|string $key
* @return int|string|null Next key, or null for not found
*/
public static function arrayGetPrevKey(array $in_array, int|string $key): int|string|null
public static function arrayGetPrevKey(array $array, int|string $key): int|string|null
{
return self::arrayGetKey($in_array, $key, false);
return self::arrayGetKey($array, $key, false);
}
/**
* Get next array key from an array
* null on not found
*
* @param array<mixed> $in_array
* @param array<mixed> $array
* @param int|string $key
* @return int|string|null Next key, or null for not found
*/
public static function arrayGetNextKey(array $in_array, int|string $key): int|string|null
public static function arrayGetNextKey(array $array, int|string $key): int|string|null
{
return self::arrayGetKey($in_array, $key, true);
return self::arrayGetKey($array, $key, true);
}
/**
@@ -463,27 +463,27 @@ class ArrayHandler
}
// default key is not string
$key_is_string = false;
$in_arrays = func_get_args();
$arrays = func_get_args();
// if last is not array, then assume it is trigger for key is always string
if (!is_array(end($in_arrays))) {
if (array_pop($in_arrays)) {
if (!is_array(end($arrays))) {
if (array_pop($arrays)) {
$key_is_string = true;
}
}
// check that arrays count is at least two, else we don't have enough to do anything
if (count($in_arrays) < 2) {
if (count($arrays) < 2) {
throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments');
}
$merged = [];
while ($in_arrays) {
$in_array = array_shift($in_arrays);
if (!is_array($in_array)) {
while ($arrays) {
$array = array_shift($arrays);
if (!is_array($array)) {
throw new \TypeError(__FUNCTION__ . ' encountered a non array argument');
}
if (!$in_array) {
if (!$array) {
continue;
}
foreach ($in_array as $key => $value) {
foreach ($array as $key => $value) {
// if string or if key is assumed to be string do key match
// else add new entry
if (is_string($key) || $key_is_string === false) {
@@ -589,14 +589,14 @@ class ArrayHandler
* converts multi dimensional array to a flat array
* does NOT preserve keys
*
* @param array<mixed> $in_array multi dimensionial array
* @param array<mixed> $array multi dimensionial array
* @return array<mixed> flattened array
*/
public static function flattenArray(array $in_array): array
public static function flattenArray(array $array): array
{
$return = [];
array_walk_recursive(
$in_array,
$array,
function ($value) use (&$return) {
$return[] = $value;
}
@@ -607,13 +607,13 @@ class ArrayHandler
/**
* will loop through an array recursivly and write the array keys back
*
* @param array<mixed> $in_array multidemnsional array to flatten
* @param array<mixed> $array multidemnsional array to flatten
* @param array<mixed> $return recoursive pass on array of keys
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKey(array $in_array, array $return = []): array
public static function flattenArrayKey(array $array, array $return = []): array
{
foreach ($in_array as $key => $sub) {
foreach ($array as $key => $sub) {
$return[] = $key;
if (is_array($sub) && count($sub) > 0) {
$return = self::flattenArrayKey($sub, $return);
@@ -626,14 +626,14 @@ class ArrayHandler
* as above will flatten an array, but in this case only the outmost
* leave nodes, all other keyswill be skipped
*
* @param array<mixed> $in_array multidemnsional array to flatten
* @param array<mixed> $array multidemnsional array to flatten
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKeyLeavesOnly(array $in_array): array
public static function flattenArrayKeyLeavesOnly(array $array): array
{
$return = [];
array_walk_recursive(
$in_array,
$array,
function ($value, $key) use (&$return) {
$return[] = $key;
}
@@ -645,14 +645,14 @@ class ArrayHandler
* searches for key -> value in an array tree and writes the value one level up
* this will remove this leaf will all other values
*
* @param array<mixed> $in_array nested array
* @param array<mixed> $array nested array
* @param string|int $search key to find that has no sub leaf
* and will be pushed up
* @return array<mixed> modified, flattened array
*/
public static function arrayFlatForKey(array $in_array, string|int $search): array
public static function arrayFlatForKey(array $array, string|int $search): array
{
foreach ($in_array as $key => $value) {
foreach ($array as $key => $value) {
// if it is not an array do just nothing
if (!is_array($value)) {
continue;
@@ -660,14 +660,14 @@ class ArrayHandler
// probe it has search key
if (isset($value[$search])) {
// set as current
$in_array[$key] = $value[$search];
$array[$key] = $value[$search];
} else {
// call up next node down
// $in_array[$key] = call_user_func(__METHOD__, $value, $search);
$in_array[$key] = self::arrayFlatForKey($value, $search);
// $array[$key] = call_user_func(__METHOD__, $value, $search);
$array[$key] = self::arrayFlatForKey($value, $search);
}
}
return $in_array;
return $array;
}
/**
@@ -677,13 +677,13 @@ class ArrayHandler
*
* https://stackoverflow.com/a/369608
*
* @param array<mixed> $in_array Array where elements are located
* @param array<mixed> $array Array where elements are located
* @param array<mixed> $remove Elements to remove
* @return array<mixed> Array with $remove elements removed
*/
public static function arrayRemoveEntry(array $in_array, array $remove): array
public static function arrayRemoveEntry(array $array, array $remove): array
{
return array_diff($in_array, $remove);
return array_diff($array, $remove);
}
/**
@@ -693,20 +693,20 @@ class ArrayHandler
* key list is a list[string]
* if key list is empty, return array as is
*
* @param array<string,mixed> $in_array
* @param array<string,mixed> $array
* @param array<string> $key_list
* @return array<string,mixed>
*/
public static function arrayReturnMatchingKeyOnly(
array $in_array,
array $array,
array $key_list
): array {
// on empty return as is
if (empty($key_list)) {
return $in_array;
return $array;
}
return array_filter(
$in_array,
$array,
fn($key) => in_array($key, $key_list),
ARRAY_FILTER_USE_KEY
);
@@ -748,7 +748,7 @@ class ArrayHandler
* sort ascending or descending with or without lower case convert
* value only, will loose key connections unless preserve_keys is set to true
*
* @param array<mixed> $in_array Array to sort by values
* @param array<mixed> $array Array to sort by values
* @param bool $case_insensitive [false] Sort case insensitive
* @param bool $reverse [false] Reverse sort
* @param bool $maintain_keys [false] Maintain keys
@@ -756,7 +756,7 @@ class ArrayHandler
* @return array<mixed>
*/
public static function sortArray(
array $in_array,
array $array,
bool $case_insensitive = false,
bool $reverse = false,
bool $maintain_keys = false,
@@ -770,26 +770,26 @@ class ArrayHandler
};
$case_insensitive ? (
$maintain_keys ?
(uasort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
(usort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
(uasort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
(usort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
) :
(
$maintain_keys ?
($reverse ? arsort($in_array, $flag) : asort($in_array, $flag)) :
($reverse ? rsort($in_array, $flag) : sort($in_array, $flag))
($reverse ? arsort($array, $flag) : asort($array, $flag)) :
($reverse ? rsort($array, $flag) : sort($array, $flag))
);
return $in_array;
return $array;
}
/**
* sort by key ascending or descending and return
*
* @param array<mixed> $in_array Array to srt
* @param array<mixed> $array Array to srt
* @param bool $case_insensitive [false] Sort keys case insenstive
* @param bool $reverse [false] Reverse key sort
* @return array<mixed>
*/
public static function ksortArray(array $in_array, bool $case_insensitive = false, bool $reverse = false): array
public static function ksortArray(array $array, bool $case_insensitive = false, bool $reverse = false): array
{
$fk_sort_lower_case = function (string $a, string $b): int {
return strtolower($a) <=> strtolower($b);
@@ -804,12 +804,12 @@ class ArrayHandler
return $b <=> $a;
};
uksort(
$in_array,
$array,
$case_insensitive ?
($reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case) :
($reverse ? $fk_sort_reverse : $fk_sort)
);
return $in_array;
return $array;
}
}

View File

@@ -199,17 +199,15 @@ class Math
callback: fn ($col) => is_array($row) ?
array_reduce(
array: $row,
// TODO check that v is not an array
callback: fn ($a, $v, $i = null) => $a + $v * ( /** @phpstan-ignore-line Possible array + int */
callback: fn ($a, $v, $i = null) => $a + $v * (
// if last entry missing for full copy add a 0 to it
$col[$i ?? array_search($v, $row, true)] ?? 0
$col[$i ?? array_search($v, $row, true)] ?? 0 /** @phpstan-ignore-line */
),
initial: 0,
) :
array_reduce(
array: $col,
// TODO check that v is not an array
callback: fn ($a, $v) => $a + $v * $row, /** @phpstan-ignore-line Possible array + int */
callback: fn ($a, $v) => $a + $v * $row,
initial: 0,
),
array: $bCols,

View File

@@ -0,0 +1,90 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2025/7/17
* DESCRIPTION:
* SQLite IO basic interface
*/
declare(strict_types=1);
namespace CoreLibs\DB\SQL\Interface;
interface DatabaseInterface
{
/**
* Table meta data
* Note that if columns have multi
*
* @param string $table
* @return array<array<string,mixed>>|false
*/
public function dbShowTableMetaData(string $table): array|false;
/**
* for reading or simple execution, no return data
*
* @param string $query
* @return int|false
*/
public function dbExec(string $query): int|false;
/**
* Run a simple query and return its statement
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbQuery(string $query): \PDOStatement|false;
/**
* Execute one query with params
*
* @param string $query
* @param array<mixed> $params
* @return \PDOStatement|false
*/
public function dbExecParams(string $query, array $params): \PDOStatement|false;
/**
* Prepare query
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbPrepare(string $query): \PDOStatement|false;
/**
* execute a cursor
*
* @param \PDOStatement $cursor
* @param array<mixed> $params
* @return bool
*/
public function dbCursorExecute(\PDOStatement $cursor, array $params): bool;
/**
* return array with data, when finshed return false
* also returns false on error
*
* TODO: This is currently a one time run
* if the same query needs to be run again, the cursor_ext must be reest
* with dbCacheReset
*
* @param string $query
* @param array<mixed> $params
* @return array<mixed>|false
*/
public function dbReturnArray(string $query, array $params = []): array|false;
/**
* get current db handler
* this is for raw access
*
* @return \PDO
*/
public function getDbh(): \PDO;
}
// __END__

View File

@@ -0,0 +1,433 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/8/21
* DESCRIPTION:
* SQL Lite interface
* Note: This is a very simple library and in future should perhaps merge with the master
* CoreLibs SQL interface
*
* TODO: This should move to the CoreLibs\DB\IO class as a sub type for "sqlite" next to "pgsql"
*/
declare(strict_types=1);
namespace CoreLibs\DB;
use CoreLibs\Create\Hash;
class SqLite implements SQL\Interface\DatabaseInterface
{
/** @var \CoreLibs\Logging\Logging logging */
public \CoreLibs\Logging\Logging $log;
/** @var string database connection string */
private string $dsn;
/** @var \PDO database handler */
private \PDO $dbh;
/** @var PDOStatement|false one cursor, for internal handling */
// private \PDOStatement|false $cursor;
/** @var array<string,mixed> extended cursoers string index with content */
private array $cursor_ext = [];
/**
* init database system
*
* @param \CoreLibs\Logging\Logging $log
* @param string $dsn
*/
public function __construct(
\CoreLibs\Logging\Logging $log,
string $dsn
) {
$this->log = $log;
// open new connection
if ($this->__connectToDB($dsn) === false) {
throw new \ErrorException("Cannot load database: " . $dsn, 1);
}
}
// *********************************************************************
// MARK: PRIVATE METHODS
// *********************************************************************
/**
* Get a cursor dump with all info
*
* @param \PDOStatement $cursor
* @return string|false
*/
private function __dbGetCursorDump(\PDOStatement $cursor): string|false
{
// get the cursor info
ob_start();
$cursor->debugDumpParams();
$cursor_dump = ob_get_contents();
ob_end_clean();
return $cursor_dump;
}
/**
* fetch rows from a cursor (post execute)
*
* @param \PDOStatement $cursor
* @return array<mixed>|false
*/
private function __dbFetchArray(\PDOStatement $cursor): array|false
{
try {
// on empty array return false
// TODO make that more elegant?
return empty($row = $cursor->fetch(mode:\PDO::FETCH_NAMED)) ? false : $row;
} catch (\PDOException $e) {
$this->log->error(
"Cannot fetch from cursor",
[
"dsn" => $this->dsn,
"DumpParams" => $this->__dbGetCursorDump($cursor),
"PDOException" => $e
]
);
return false;
}
}
// MARK: open database
/**
* Open database
* reports errors for wrong DSN or failed connection
*
* @param string $dsn
* @return bool
*/
private function __connectToDB(string $dsn): bool
{
// check if dsn starts with ":"
if (!str_starts_with($dsn, "sqlite:")) {
$this->log->error(
"Invalid dsn string",
[
"dsn" => $dsn
]
);
return false;
}
// TODO: if not ":memory:" check if path to file is writeable by system
// avoid double open
if (!empty($this->dsn) && $dsn == $this->dsn) {
$this->log->info(
"Connection already establisehd with this dsn",
[
"dsn" => $dsn,
]
);
return true;
}
// TODO: check that folder is writeable
// set DSN and open connection
$this->dsn = $dsn;
try {
$this->dbh = new \PDO($this->dsn);
} catch (\PDOException $e) {
$this->log->error(
"Cannot open database",
[
"dsn" => $this->dsn,
"PDOException" => $e
]
);
return false;
}
return true;
}
// *********************************************************************
// MARK: PUBLIC METHODS
// *********************************************************************
// MARK: db meta data (table info)
/**
* Table meta data
* Note that if columns have multi entries multiple lines are returned
* ?1 is equal to $1 in this query
*
* @param string $table
* @return array<array<string,mixed>>|false
*/
public function dbShowTableMetaData(string $table): array|false
{
$table_info = [];
$query = <<<SQL
SELECT
ti.cid, ti.name, ti.type, ti.'notnull', ti.dflt_value, ti.pk,
il_ii.idx_name, il_ii.idx_unique, il_ii.idx_origin, il_ii.idx_partial
FROM
sqlite_schema AS m,
pragma_table_info(m.name) AS ti
LEFT JOIN (
SELECT
il.name AS idx_name, il.'unique' AS idx_unique, il.origin AS idx_origin, il.partial AS idx_partial,
ii.cid AS tbl_cid
FROM
sqlite_schema AS m,
pragma_index_list(m.name) AS il,
pragma_index_info(il.name) AS ii
WHERE m.name = ?1
) AS il_ii ON (ti.cid = il_ii.tbl_cid)
WHERE
m.name = ?1
SQL;
while (is_array($row = $this->dbReturnArray($query, [$table]))) {
$table_info[] = [
'cid' => $row['cid'],
'name' => $row['name'],
'type' => $row['type'],
'notnull' => $row['notnull'],
'dflt_value' => $row['dflt_value'],
'pk' => $row['pk'],
'idx_name' => $row['idx_name'],
'idx_unique' => $row['idx_unique'],
'idx_origin' => $row['idx_origin'],
'idx_partial' => $row['idx_partial'],
];
}
if (!$table_info) {
return false;
}
return $table_info;
}
// MARK: db exec
/**
* for reading or simple execution, no return data
*
* @param string $query
* @return int|false
*/
public function dbExec(string $query): int|false
{
try {
return $this->dbh->exec($query);
} catch (\PDOException $e) {
$this->log->error(
"Cannot execute query",
[
"dsn" => $this->dsn,
"query" => $query,
"PDOException" => $e
]
);
return false;
}
}
// MARK: db query
/**
* Run a simple query and return its statement
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbQuery(string $query): \PDOStatement|false
{
try {
return $this->dbh->query($query, \PDO::FETCH_NAMED);
} catch (\PDOException $e) {
$this->log->error(
"Cannot run query",
[
"dsn" => $this->dsn,
"query" => $query,
"PDOException" => $e
]
);
return false;
}
}
// MARK: db prepare & execute calls
/**
* Execute one query with params
*
* @param string $query
* @param array<mixed> $params
* @return \PDOStatement|false
*/
public function dbExecParams(string $query, array $params): \PDOStatement|false
{
// prepare query
if (($cursor = $this->dbPrepare($query)) === false) {
return false;
}
// execute the query, on failure return false
if ($this->dbCursorExecute($cursor, $params) === false) {
return false;
}
return $cursor;
}
/**
* Prepare query
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbPrepare(string $query): \PDOStatement|false
{
try {
// store query with cursor so we can reference?
return $this->dbh->prepare($query);
} catch (\PDOException $e) {
$this->log->error(
"Cannot open cursor",
[
"dsn" => $this->dsn,
"query" => $query,
"PDOException" => $e
]
);
return false;
}
}
/**
* execute a cursor
*
* @param \PDOStatement $cursor
* @param array<mixed> $params
* @return bool
*/
public function dbCursorExecute(\PDOStatement $cursor, array $params): bool
{
try {
return $cursor->execute($params);
} catch (\PDOException $e) {
// write error log
$this->log->error(
"Cannot execute prepared query",
[
"dsn" => $this->dsn,
"params" => $params,
"DumpParams" => $this->__dbGetCursorDump($cursor),
"PDOException" => $e
]
);
return false;
}
}
// MARK: db return array
/**
* Returns hash for query
* Hash is used in all internal storage systems for return data
*
* @param string $query The query to create the hash from
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return string Hash, as set by hash long
*/
public function dbGetQueryHash(string $query, array $params = []): string
{
return Hash::hashLong(
$query . (
$params !== [] ?
'#' . json_encode($params) : ''
)
);
}
/**
* resets all data stored to this query
* @param string $query The Query whose cache should be cleaned
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return bool False if query not found, true if success
*/
public function dbCacheReset(string $query, array $params = []): bool
{
$query_hash = $this->dbGetQueryHash($query, $params);
// clears cache for this query
if (empty($this->cursor_ext[$query_hash]['query'])) {
$this->log->error('Cannot reset cursor_ext with given query and params', [
"query" => $query,
"params" => $params,
]);
return false;
}
unset($this->cursor_ext[$query_hash]);
return true;
}
/**
* return array with data, when finshed return false
* also returns false on error
*
* TODO: This is currently a one time run
* if the same query needs to be run again, the cursor_ext must be reest
* with dbCacheReset
*
* @param string $query
* @param array<mixed> $params
* @return array<mixed>|false
*/
public function dbReturnArray(string $query, array $params = []): array|false
{
$query_hash = $this->dbGetQueryHash($query, $params);
if (!isset($this->cursor_ext[$query_hash])) {
$this->cursor_ext[$query_hash] = [
// cursor null: unset, if set \PDOStatement
'cursor' => null,
// the query used in this call
'query' => $query,
// parameter
'params' => $params,
// how many rows have been read from db
'read_rows' => 0,
// when fetch array or cache read returns false
// in loop read that means dbReturn retuns false without error
'finished' => false,
];
if (!empty($params)) {
if (($cursor = $this->dbExecParams($query, $params)) === false) {
return false;
}
} else {
if (($cursor = $this->dbQuery($query)) === false) {
return false;
}
}
$this->cursor_ext[$query_hash]['cursor'] = $cursor;
}
// flag finished if row is false
$row = $this->__dbFetchArray($this->cursor_ext[$query_hash]['cursor']);
if ($row === false) {
$this->cursor_ext[$query_hash]['finished'] = true;
} else {
$this->cursor_ext[$query_hash]['read_rows']++;
}
return $row;
}
// MARK: other interface
/**
* get current db handler
* this is for raw access
*
* @return \PDO
*/
public function getDbh(): \PDO
{
return $this->dbh;
}
}
// __END__

View File

@@ -263,11 +263,11 @@ class ConvertPlaceholder
}
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $params_lookup[$match]/* ??
return $params_lookup[$match] ??
throw new \RuntimeException(
'Cannot lookup ' . $match . ' in params lookup list',
211
)*/;
);
},
$converted_placeholders['original']['query']
);
@@ -327,11 +327,11 @@ class ConvertPlaceholder
}
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $params_lookup[$match]/* ??
return $params_lookup[$match] ??
throw new \RuntimeException(
'Cannot lookup ' . $match . ' in params lookup list',
231
)*/;
);
},
$converted_placeholders['original']['query']
);

View File

@@ -33,36 +33,6 @@ class Support
return $string;
}
/**
* print ISO type datetime with microseconds and timezone
* Y-m-dTH:i:s.uP
* if no micro time the ".u" part is omitted
*
* @param bool $set_micro_time
* @return string
*/
public static function printIsoTime(bool $set_micro_time = true): string
{
$datetime = new \DateTime();
// Format the DateTime object to ISO 8601 with microseconds
// 'Y-m-d\TH:i:s.uP' is the format string:
// Y: Full year (e.g., 2025)
// m: Month (01-12)
// d: Day of the month (01-31)
// T: Literal 'T' to separate date and time (escaped with a backslash)
// H: Hour (00-23)
// i: Minute (00-59)
// s: Second (00-59)
// u: Microseconds (e.g., 654321)
// P: Difference to Greenwich time (GMT) with colon (e.g., +09:00)
if ($set_micro_time) {
return $datetime->format('Y-m-d\TH:i:s.uP');
} else {
return $datetime->format('Y-m-d\TH:i:sP');
}
}
/**
* prints a html formatted (pre) data
*

View File

@@ -13,7 +13,6 @@ namespace CoreLibs\Logging\Logger;
enum MessageLevel: int
{
case noset = 0;
case ok = 100;
case success = 150; // special for file uploads
case info = 200;

View File

@@ -96,11 +96,6 @@ class Logging
'type' => 'bool', 'mandatory' => false,
'default' => false, 'deprecated' => true, 'use' => 'log_per_date'
],
// if turned off uses old time format without time zone
'log_time_format_iso' => [
'type' => 'bool', 'mandatory' => false,
'default' => true, 'deprecated' => false
]
];
// options
@@ -651,11 +646,7 @@ class Logging
}
// print "CLASS: " . $class . "<br>";
// get timestamp
if (!empty($this->options['log_time_format_iso'])) {
$timestamp = Support::printIsoTime();
} else {
$timestamp = Support::printTime();
}
$timestamp = Support::printTime();
// if group id is empty replace it with current level
$group_str = $level->getName();