Compare commits

...

42 Commits

Author SHA1 Message Date
Clemens Schwaighofer
94edb7f556 Add unique page name check for edit pages, bug fixes in Form Template Generate and DB Array IO classes 2024-03-21 12:45:39 +09:00
Clemens Schwaighofer
3f5b3f02ad Update Readme to reflect that current version is v9 2024-03-08 14:41:07 +09:00
Clemens Schwaighofer
a270922b4b Published: v9.12.0 2024-03-07 15:04:18 +09:00
Clemens Schwaighofer
8c607ae610 tools update, bug fix Strings to return path if null, Fix form list loader, fix paths 2024-03-07 15:01:19 +09:00
Clemens Schwaighofer
d3d4cf512f add php code sniffer xml 2023-12-05 17:38:01 +09:00
Clemens Schwaighofer
efae352c35 Published: v9.11.1 2023-11-29 10:51:19 +09:00
Clemens Schwaighofer
a1614bace2 Bug Fix in DB IO with parameter detection with <> 2023-11-29 10:50:25 +09:00
Clemens Schwaighofer
62ce863f2c Release: v9.11.0 2023-11-02 14:08:23 +09:00
Clemens Schwaighofer
b6ef3b29f6 ArrayHandler add next/prev key search 2023-11-02 14:06:31 +09:00
Clemens Schwaighofer
1f178628a2 Release: v9.10.1 2023-10-31 10:24:18 +09:00
Clemens Schwaighofer
67b725cb65 DB\IO fixes for logging 2023-10-31 10:21:52 +09:00
Clemens Schwaighofer
648d2c3eb5 Release: v9.10.0 2023-10-23 17:14:47 +09:00
Clemens Schwaighofer
428c10b547 new Support::getCallStack, new DateTime::intervalStringFormat, exception updates Convert\Byte, Output\Image 2023-10-23 17:13:23 +09:00
Clemens Schwaighofer
dd705be420 Release: v9.9.1 2023-10-16 16:15:09 +09:00
Clemens Schwaighofer
a1ba1257ac DB\IO dbWarning context updates 2023-10-16 16:14:10 +09:00
Clemens Schwaighofer
9569145054 Release: v9.9.0 2023-10-16 15:07:36 +09:00
Clemens Schwaighofer
13602bdd73 DB\IO update with better error reporting and auto update for placeholders in queries 2023-10-16 15:02:30 +09:00
Clemens Schwaighofer
1ef91305d4 Release: v9.8.3 2023-10-12 17:13:32 +09:00
Clemens Schwaighofer
4ab382990e Output\Image Exceptions update 2023-10-12 17:12:15 +09:00
Clemens Schwaighofer
c61a68b131 Release: v9.8.2 2023-10-06 16:53:33 +09:00
Clemens Schwaighofer
c7254c45b7 Update DB\IO with fixed regex for query params detection 2023-10-06 16:52:42 +09:00
Clemens Schwaighofer
4edacc0d13 phpunit tools symlink fix, phpunit boostrap update 2023-10-04 15:01:23 +09:00
Clemens Schwaighofer
128d6533fc Remove composer install phan/phpstan/etc and switch to phive install 2023-10-04 14:49:49 +09:00
Clemens Schwaighofer
c740fb1af1 Release: v9.8.1 2023-10-02 17:34:37 +09:00
Clemens Schwaighofer
64e60d97e7 Logging\ErrorMsg fix for jump target list return array 2023-10-02 17:33:49 +09:00
Clemens Schwaighofer
f414224c54 Release: v9.8.0 2023-10-02 14:06:32 +09:00
Clemens Schwaighofer
71c9fd401d Logging\ErrorMsg add jump target list 2023-10-02 14:04:59 +09:00
Clemens Schwaighofer
0a885f215c Release: v9.7.9 2023-10-02 12:31:21 +09:00
Clemens Schwaighofer
dad6b797e0 ErrorMsg auto log if log level is debug, Form\Generate split out DBArrayIO class 2023-10-02 12:29:50 +09:00
Clemens Schwaighofer
2d7c3c2bba Release: v9.7.8 2023-09-27 11:44:09 +09:00
Clemens Schwaighofer
fb8216ae86 ErrorMessage add notice level 2023-09-27 11:43:12 +09:00
Clemens Schwaighofer
df5070ffbb Release: v9.7.7 2023-09-27 11:26:07 +09:00
Clemens Schwaighofer
872409ef54 All null primary keys in dbWriteDataExt in DB\IO 2023-09-27 11:25:18 +09:00
Clemens Schwaighofer
c4d5cad9e8 Release: v9.7.6 2023-09-27 09:44:52 +09:00
Clemens Schwaighofer
90550746ab Bug fix in DB\ArrayIO for pk_id access 2023-09-27 09:43:32 +09:00
Clemens Schwaighofer
724031b944 Release: v9.7.5 2023-09-26 18:47:58 +09:00
Clemens Schwaighofer
027c35f9f0 Bug fix for Edit Order not loading 2023-09-26 18:47:02 +09:00
Clemens Schwaighofer
6ad844b519 Release: v9.7.4 2023-09-15 18:33:54 +09:00
Clemens Schwaighofer
e10987ce8b ErrorMsg update with target_style 2023-09-15 18:32:55 +09:00
Clemens Schwaighofer
b3617954eb Release: v9.7.3 2023-09-15 18:24:33 +09:00
Clemens Schwaighofer
67fd7b172a ErrorMessage: flag to also log 'error' level messages to log 2023-09-11 13:37:58 +09:00
Clemens Schwaighofer
fe729453ac Released: v9.7.2 2023-09-11 13:37:25 +09:00
45 changed files with 3423 additions and 1095 deletions

View File

@@ -360,10 +360,7 @@ return [
'directory_list' => [ 'directory_list' => [
'src', 'src',
'vendor/egrajp/smarty-extended/src', 'vendor/egrajp/smarty-extended/src',
'vendor/phan/phan/src/Phan',
'vendor/phpunit/phpunit/src',
'vendor/psr/log/src', 'vendor/psr/log/src',
'vendor/vimeo/psalm/src/Psalm',
'vendor/gullevek/dotenv', 'vendor/gullevek/dotenv',
], ],

9
.phive/phars.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^9.6" installed="9.6.17" location="./tools/phpunit" copy="false"/>
<phar name="phpcs" version="^3.7.2" installed="3.9.0" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.7.2" installed="3.9.0" location="./tools/phpcbf" copy="false"/>
<phar name="psalm" version="^5.15.0" installed="5.23.1" location="./tools/psalm" copy="false"/>
<phar name="phpstan" version="^1.10.37" installed="1.10.63" location="./tools/phpstan" copy="false"/>
<phar name="phan" version="^5.4.2" installed="5.4.3" location="./tools/phan" copy="false"/>
</phive>

View File

@@ -22,4 +22,22 @@ Alternative setup composer local zip file repot:
## Install package ## Install package
`composer require egrajp/corelibs-composer-all:^8.0` `composer require egrajp/corelibs-composer-all:^9.0`
## Tests
All tests must be run from the base folder
### phan
`phan --progress-bar -C --analyze-twic`
### phpstan
`phpstan`
### phpunit
PHP unit is installed via "phiev"
`tools/phpunit test/phpunit`

View File

@@ -20,11 +20,7 @@
"psr/log": "^3.0@dev" "psr/log": "^3.0@dev"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^1.10",
"phan/phan": "v5.x-dev",
"phpunit/phpunit": "^9",
"egrajp/smarty-extended": "^4.3", "egrajp/smarty-extended": "^4.3",
"vimeo/psalm": "^5.0@dev",
"gullevek/dotenv": "dev-master" "gullevek/dotenv": "dev-master"
}, },
"repositories": { "repositories": {

18
phpcs.xml Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<ruleset name="MyStandard">
<description>PSR12 override rules (strict, standard). Switch spaces indent to tab.</description>
<arg name="tab-width" value="4"/>
<rule ref="PSR1"/>
<rule ref="PSR12">
<!-- turn off white space check for tab -->
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
</rule>
<!-- no space indent, must be tab, 4 is tab iwdth -->
<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="indent" value="4"/>
<property name="tabIndent" value="true"/>
</properties>
</rule>
</ruleset>

View File

@@ -2,5 +2,6 @@
cacheResultFile="/tmp/phpunit-corelibs-composer.result.cache" cacheResultFile="/tmp/phpunit-corelibs-composer.result.cache"
colors="true" colors="true"
verbose="true" verbose="true"
bootstrap="test/phpunit/bootstrap.php"
> >
</phpunit> </phpunit>

View File

@@ -1 +1 @@
9.7.1 9.12.0

View File

@@ -35,6 +35,8 @@ class EditBase
private \CoreLibs\Output\Form\Generate $form; private \CoreLibs\Output\Form\Generate $form;
/** @var \CoreLibs\Logging\Logging */ /** @var \CoreLibs\Logging\Logging */
public \CoreLibs\Logging\Logging $log; public \CoreLibs\Logging\Logging $log;
/** @var \CoreLibs\Language\L10n */
public \CoreLibs\Language\L10n $l;
/** @var \CoreLibs\ACL\Login */ /** @var \CoreLibs\ACL\Login */
public \CoreLibs\ACL\Login $login; public \CoreLibs\ACL\Login $login;
@@ -42,7 +44,7 @@ class EditBase
* construct form generator * construct form generator
* *
* phpcs:ignore * 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 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_convert_placeholder?:bool,db_convert_placeholder_target?:string,db_debug_replace_placeholder?:bool} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class, null auto set * @param \CoreLibs\Logging\Logging $log Logging class, null auto set
* @param \CoreLibs\Language\L10n $l10n l10n language 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 * @param \CoreLibs\ACL\Login $login login class for ACL settings
@@ -57,6 +59,7 @@ class EditBase
) { ) {
$this->log = $log; $this->log = $log;
$this->login = $login; $this->login = $login;
$this->l = $l10n;
// smarty template engine (extended Translation version) // smarty template engine (extended Translation version)
$this->smarty = new \CoreLibs\Template\SmartyExtend( $this->smarty = new \CoreLibs\Template\SmartyExtend(
$l10n, $l10n,
@@ -77,7 +80,7 @@ class EditBase
echo "I am sorry, but this page cannot be viewed by a mobile phone"; echo "I am sorry, but this page cannot be viewed by a mobile phone";
exit; exit;
} }
// $this->form->log->debug('POST', $this->form->log->prAr($_POST)); // $this->log->debug('POST', $this->log->prAr($_POST));
} }
/** /**
@@ -151,7 +154,7 @@ class EditBase
$q = "UPDATE " . $table_name $q = "UPDATE " . $table_name
. " SET order_number = " . $row_data_order[$i] . " SET order_number = " . $row_data_order[$i]
. " WHERE " . $table_name . "_id = " . $row_data_id[$i]; . " WHERE " . $table_name . "_id = " . $row_data_id[$i];
$q = $this->form->dbExec($q); $q = $this->form->dba->dbExec($q);
} }
} // for all article ids ... } // for all article ids ...
} // if write } // if write
@@ -170,7 +173,7 @@ class EditBase
$options_name = []; $options_name = [];
$options_selected = []; $options_selected = [];
// DB read data for menu // DB read data for menu
while (is_array($res = $this->form->dbReturn($q))) { while (is_array($res = $this->form->dba->dbReturn($q))) {
$row_data[] = [ $row_data[] = [
"id" => $res[$table_name . "_id"], "id" => $res[$table_name . "_id"],
"name" => $res["name"], "name" => $res["name"],
@@ -179,7 +182,7 @@ class EditBase
} // while read data ... } // while read data ...
// html title // html title
$this->HEADER['HTML_TITLE'] = $this->form->l->__('Edit Order'); $this->HEADER['HTML_TITLE'] = $this->l->__('Edit Order');
$messages = []; $messages = [];
$error = $_POST['error'] ?? 0; $error = $_POST['error'] ?? 0;
@@ -428,9 +431,9 @@ class EditBase
$elements[] = $this->form->formCreateElement('template'); $elements[] = $this->form->formCreateElement('template');
break; break;
case 'edit_pages': case 'edit_pages':
if (!isset($this->form->table_array['edit_page_id']['value'])) { if (!isset($this->form->dba->getTableArray()['edit_page_id']['value'])) {
$q = "DELETE FROM temp_files"; $q = "DELETE FROM temp_files";
$this->form->dbExec($q); $this->form->dba->dbExec($q);
// gets all files in the current dir and dirs given ending with .php // gets all files in the current dir and dirs given ending with .php
$folders = ['../admin/', '../frontend/']; $folders = ['../admin/', '../frontend/'];
$files = ['*.php']; $files = ['*.php'];
@@ -458,16 +461,16 @@ class EditBase
if ($t_q) { if ($t_q) {
$t_q .= ', '; $t_q .= ', ';
} }
$t_q .= "('" . $this->form->dbEscapeString($pathinfo['dirname']) . "', '" $t_q .= "('" . $this->form->dba->dbEscapeString($pathinfo['dirname']) . "', '"
. $this->form->dbEscapeString($pathinfo['basename']) . "')"; . $this->form->dba->dbEscapeString($pathinfo['basename']) . "')";
} }
$this->form->dbExec($q . $t_q, 'NULL'); $this->form->dba->dbExec($q . $t_q, 'NULL');
$elements[] = $this->form->formCreateElement('filename'); $elements[] = $this->form->formCreateElement('filename');
} else { } else {
// show file menu // show file menu
// just show name of file ... // just show name of file ...
$this->DATA['filename_exist'] = 1; $this->DATA['filename_exist'] = 1;
$this->DATA['filename'] = $this->form->table_array['filename']['value']; $this->DATA['filename'] = $this->form->dba->getTableArray()['filename']['value'];
} // File Name View IF } // File Name View IF
$elements[] = $this->form->formCreateElement('hostname'); $elements[] = $this->form->formCreateElement('hostname');
$elements[] = $this->form->formCreateElement('name'); $elements[] = $this->form->formCreateElement('name');
@@ -632,7 +635,7 @@ class EditBase
'editAdmin_' . $this->smarty->lang 'editAdmin_' . $this->smarty->lang
); );
$this->form->log->debug('DEBUGEND', '==================================== [Form END]'); $this->log->debug('DEBUGEND', '==================================== [Form END]');
} }
} }

View File

@@ -51,6 +51,23 @@ class File
// return lines in file // return lines in file
return $lines; return $lines;
} }
/**
* get the mime type of a file via finfo
* if file not found, throws exception
* else returns '' for any other finfo read problem
*
* @param string $read_file File to read, relative or absolute path
* @return string
*/
public static function getMimeType(string $read_file): string
{
$finfo = new \finfo(FILEINFO_MIME_TYPE);
if (!is_file($read_file)) {
throw new \UnexpectedValueException('[getMimeType] File not found: ' . $read_file);
}
return $finfo->file($read_file) ?: '';
}
} }
// __END__ // __END__

View File

@@ -236,6 +236,54 @@ class ArrayHandler
return $hit_list; return $hit_list;
} }
/**
* main wrapper function for next/prev key
*
* @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 $array, int|string $key, bool $next = true): int|string|null
{
$keys = array_keys($array);
if (($position = array_search($key, $keys, true)) === false) {
return null;
}
$next_position = $next ? $position + 1 : $position - 1;
if (!isset($keys[$next_position])) {
return null;
}
return $keys[$next_position];
}
/**
* Get previous array key from an array
* null on not found
*
* @param array<mixed> $array
* @param int|string $key
* @return int|string|null Next key, or null for not found
*/
public static function arrayGetPrevKey(array $array, int|string $key): int|string|null
{
return self::arrayGetKey($array, $key, false);
}
/**
* Get next array key from an array
* null on not found
*
* @param array<mixed> $array
* @param int|string $key
* @return int|string|null Next key, or null for not found
*/
public static function arrayGetNextKey(array $array, int|string $key): int|string|null
{
return self::arrayGetKey($array, $key, true);
}
/** /**
* correctly recursive merges as an array as array_merge_recursive * correctly recursive merges as an array as array_merge_recursive
* just glues things together * just glues things together

View File

@@ -108,7 +108,12 @@ class DateTime
if (preg_match("/(h|m|s|ms)/", (string)$timestamp)) { if (preg_match("/(h|m|s|ms)/", (string)$timestamp)) {
return (string)$timestamp; return (string)$timestamp;
} }
list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 4)), 2, null); // split to 6 (nano seconds)
list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 6)), 2, null);
// if micro seconds is on and we have none, set to 0
if ($show_micro && $ms === null) {
$ms = 0;
}
// if negative remember // if negative remember
$negative = false; $negative = false;
if ((int)$timestamp < 0) { if ((int)$timestamp < 0) {
@@ -120,6 +125,10 @@ class DateTime
$time_string = ''; $time_string = '';
// if timestamp is zero, return zero string // if timestamp is zero, return zero string
if ($timestamp == 0) { if ($timestamp == 0) {
// if no seconds and we have no microseconds either, show no micro seconds
if ($ms == 0) {
$ms = null;
}
$time_string = '0s'; $time_string = '0s';
} else { } else {
for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) { for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) {
@@ -133,11 +142,8 @@ class DateTime
} }
// only add ms if we have an ms value // only add ms if we have an ms value
if ($ms !== null) { if ($ms !== null) {
// if we have ms and it has leading zeros, remove them, but only if it is nut just 0 // prefix the milliseoncds with 0. and round it max 3 digits and then convert to int
$ms = preg_replace("/^0+(\d+)$/", '${1}', $ms); $ms = round((float)('0.' . $ms), 3) * 1000;
if (!is_string($ms) || empty($ms)) {
$ms = '0';
}
// add ms if there // add ms if there
if ($show_micro) { if ($show_micro) {
$time_string .= ' ' . $ms . 'ms'; $time_string .= ' ' . $ms . 'ms';
@@ -151,6 +157,240 @@ class DateTime
return (string)$time_string; return (string)$time_string;
} }
/**
* update timeStringFormat with year and month support
*
* The following flags have to be set to be timeStringFormat compatible.
* Not that on seconds overflow this method will throw an exception, timeStringFormat returned -1s
* show_only_days: true,
* skip_zero: false,
* skip_last_zero: false,
* truncate_nanoseconds: true,
* truncate_zero_seconds_if_microseconds: false
*
* @param int|float $seconds Seconds to convert, maxium 6 decimals,
* else \UnexpectedValueException will be thrown
* if days too large or years too large \LengthException is thrown
* @param string $truncate_after [=''] Truncate after which time name, will not round, hard end
* values are parts names or interval short names (y, d, f, ...)
* if illegal value \UnexpectedValueException is thrown
* @param bool $natural_seperator [=false] use ',' and 'and', if off use space
* @param bool $name_space_seperator [=false] add a space between the number and the time name
* @param bool $show_microseconds [=true] show microseconds
* @param bool $short_time_name [=true] use the short time names (eg s instead of seconds)
* @param bool $skip_last_zero [=true] skip all trailing zero values, eg 5m 0s => 5m
* @param bool $skip_zero [=true] do not show zero values anywhere, eg 1h 0m 20s => 1h 20s
* @param bool $show_only_days [=false] do not show years or months, show only days
* if truncate after is set to year or month
* throws \UnexpectedValueException
* @param bool $auto_fix_microseconds [=false] if the micro seconds decimals are more than 6, round them
* on defaul throw \UnexpectedValueException
* @param bool $truncate_nanoseconds [=false] if microseconds decimals >3 then normal we show 123.4ms
* cut the .4 is set to true
* @param bool $truncate_zero_seconds_if_microseconds [=true] if we have 0.123 seconds then if true no seconds
* will be shown
* @return string
* @throws \UnexpectedValueException if seconds has more than 6 decimals
* if truncate has an illegal value
* if truncate is set to year or month and show_only_days is turned on
* @throws \LengthException if seconds is too large and show_days_only is selected and days is negetive
* or if years is negativ
*/
public static function intervalStringFormat(
int|float $seconds,
string $truncate_after = '',
bool $natural_seperator = false,
bool $name_space_seperator = false,
bool $show_microseconds = true,
bool $short_time_name = true,
bool $skip_last_zero = true,
bool $skip_zero = true,
bool $show_only_days = false,
bool $auto_fix_microseconds = false,
bool $truncate_nanoseconds = false,
bool $truncate_zero_seconds_if_microseconds = true,
): string {
// auto fix long seconds, else \UnexpectedValueException will be thrown on error
// check if we have float and -> round to 6
if ($auto_fix_microseconds === true && is_float($seconds)) {
$seconds = round($seconds, 6);
}
// flag negative + set abs
$negative = $seconds < 0 ? '-' : '';
$seconds = abs($seconds);
// create base time
$date_now = new \DateTime("@0");
try {
$date_seconds = new \DateTime("@$seconds");
} catch (\Exception $e) {
throw new \UnexpectedValueException(
'Seconds value is invalid, too large or more than six decimals: ' . $seconds,
1,
$e
);
}
$interval = date_diff($date_now, $date_seconds);
// if show_only_days and negative but input postive alert that this has to be done in y/m/d ...
if ($interval->y < 0) {
throw new \LengthException('Input seconds value is too large for years output: ' . $seconds, 2);
} elseif ($interval->days < 0 && $show_only_days === true) {
throw new \LengthException('Input seconds value is too large for days output: ' . $seconds, 3);
}
$parts = [
'years' => 'y',
'months' => 'm',
'days' => 'd',
'hours' => 'h',
'minutes' => 'i',
'seconds' => 's',
'microseconds' => 'f',
];
$short_name = [
'years' => 'y', 'months' => 'm', 'days' => 'd',
'hours' => 'h', 'minutes' => 'm', 'seconds' => 's',
'microseconds' => 'ms'
];
// $skip = false;
if (!empty($truncate_after)) {
// if truncate after not in key or value in parts
if (!in_array($truncate_after, array_keys($parts)) && !in_array($truncate_after, array_values($parts))) {
throw new \UnexpectedValueException(
'truncate_after has an invalid value: ' . $truncate_after,
4
);
}
// if truncate after is y or m and we have show_only_days, throw exception
if ($show_only_days === true && in_array($truncate_after, ['y', 'years', 'm', 'months'])) {
throw new \UnexpectedValueException(
'If show_only_days is turned on, the truncate_after cannot be years or months: '
. $truncate_after,
5
);
}
// $skip = true;
}
$formatted = [];
$zero_formatted = [];
$value_set = false;
$add_zero_seconds = false;
foreach ($parts as $time_name => $part) {
if (
// skip for micro seconds
($show_microseconds === false && $part == 'f') ||
// skip for if days only and we have year or month
($show_only_days === true && in_array($part, ['y', 'm']))
) {
continue;
}
$add_value = 0;
if ($show_only_days === true && $part == 'd') {
$value = $interval->days;
} else {
$value = $interval->$part;
}
// print "-> V: $value | $part, $time_name"
// . " | Set: " . ($value_set ? 'Y' : 'N') . ", SkipZ: " . ($skip_zero ? 'Y' : 'N')
// . " | SkipLZ: " . ($skip_last_zero ? 'Y' : 'N')
// . " | " . ($value != 0 ? 'Not zero' : 'ZERO') . "<br>";
if ($value != 0) {
if ($part == 'f') {
if ($truncate_nanoseconds === true) {
$value = round($value, 3);
}
$value *= 1000;
// anything above that is nano seconds?
}
if ($value) {
$value_set = true;
}
$add_value = 1;
} elseif (
$value == 0 &&
$value_set === true && (
$skip_last_zero === false ||
$skip_zero === false
)
) {
$add_value = 2;
}
// echo "ADD VALUE: $add_value<br>";
if ($add_value) {
// build format
$format = "$value";
if ($name_space_seperator) {
$format .= " ";
}
if ($short_time_name) {
$format .= $short_name[$time_name];
} elseif ($value == 1) {
$format .= substr($time_name, 0, -1);
} else {
$format .= $time_name;
}
if ($add_value == 1) {
if (count($zero_formatted) && $skip_zero === false) {
$formatted = array_merge($formatted, $zero_formatted);
}
$zero_formatted = [];
$formatted[] = $format;
} elseif ($add_value == 2) {
$zero_formatted[] = $format;
}
}
// if seconds is zero
if (
$part == 's' && $value == 0 &&
$show_microseconds === true &&
$truncate_zero_seconds_if_microseconds === false
) {
$add_zero_seconds = true;
}
// stop after a truncate is matching
if ($part == $truncate_after || $truncate_after == $time_name) {
break;
}
}
// add all zero entries if we have skip last off
if (count($zero_formatted) && $skip_last_zero === false) {
$formatted = array_merge($formatted, $zero_formatted);
}
// print "=> F: " . print_r($formatted, true)
// . " | Z: " . print_r($zero_list, true)
// . " | ZL: " . print_r($zero_last_list, true)
// . "<br>";
if (count($formatted) == 0) {
// if we have truncate on, then we assume nothing was found
if (!empty($truncate_after)) {
if (in_array($truncate_after, array_values($parts))) {
$truncate_after = array_flip($parts)[$truncate_after];
}
$time_name = $truncate_after;
} else {
$time_name = 'seconds';
}
return '0' . ($name_space_seperator ? ' ' : '')
. ($short_time_name ? $short_name[$time_name] : $time_name);
} elseif (count($formatted) == 1) {
return $negative .
($add_zero_seconds ?
'0'
. ($name_space_seperator ? ' ' : '')
. ($short_time_name ? $short_name['seconds'] : 'seconds')
. ' '
: ''
)
. $formatted[0];
} elseif ($natural_seperator === false) {
return $negative . implode(' ', $formatted);
} else {
$str = implode(', ', array_slice($formatted, 0, -1));
if (!empty($formatted[count($formatted) - 1])) {
$str .= ' and ' . (string)array_pop($formatted);
}
return $negative . $str;
}
}
/** /**
* does a reverse of the timeStringFormat and converts the string from * does a reverse of the timeStringFormat and converts the string from
* xd xh xm xs xms to a timestamp.microtime format * xd xh xm xs xms to a timestamp.microtime format
@@ -435,9 +675,9 @@ class DateTime
foreach ($period as $dt) { foreach ($period as $dt) {
$curr = $dt->format('D'); $curr = $dt->format('D');
if ($curr == 'Sat' || $curr == 'Sun') { if ($curr == 'Sat' || $curr == 'Sun') {
$days[2] ++; $days[2]++;
} else { } else {
$days[1] ++; $days[1]++;
} }
} }
if ($return_named === true) { if ($return_named === true) {

View File

@@ -37,7 +37,7 @@ class Byte
* BYTE_FORMAT_ADJUST: sprintf adjusted two 2 decimals * BYTE_FORMAT_ADJUST: sprintf adjusted two 2 decimals
* BYTE_FORMAT_SI: use 1000 instead of 1024 * BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string converted byte number (float) with suffix * @return string converted byte number (float) with suffix
* @throws \Exception 1: no valid flag set * @throws \InvalidArgumentException 1: no valid flag set
*/ */
public static function humanReadableByteFormat(string|int|float $bytes, int $flags = 0): string public static function humanReadableByteFormat(string|int|float $bytes, int $flags = 0): string
{ {
@@ -63,7 +63,7 @@ class Byte
$si = false; $si = false;
} }
if ($flags > 7) { if ($flags > 7) {
throw new \Exception("Invalid flags parameter: $flags", 1); throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
} }
// si or normal // si or normal
@@ -119,7 +119,7 @@ class Byte
* @param int $flags bitwise flag with use space turned on * @param int $flags bitwise flag with use space turned on
* BYTE_FORMAT_SI: use 1000 instead of 1024 * BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string|int|float converted value or original value * @return string|int|float converted value or original value
* @throws \Exception 1: no valid flag set * @throws \InvalidArgumentException 1: no valid flag set
*/ */
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
{ {
@@ -130,7 +130,7 @@ class Byte
$si = false; $si = false;
} }
if ($flags != 0 && $flags != 4) { if ($flags != 0 && $flags != 4) {
throw new \Exception("Invalid flags parameter: $flags", 1); throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
} }
// matches in regex // matches in regex
$matches = []; $matches = [];

View File

@@ -118,6 +118,22 @@ class Strings
return $value; return $value;
} }
} }
/**
* Strip any duplicated slahes from a path
* eg: //foo///bar/foo.inc -> /foo/bar/foo.inc
*
* @param string $path Path to strip slashes from
* @return string Clean path, on error returns original path
*/
public static function stripMultiplePathSlashes(string $path): string
{
return preg_replace(
'#/+#',
'/',
$path
) ?? $path;
}
} }
// __END__ // __END__

View File

@@ -39,13 +39,13 @@ class ArrayIO extends \CoreLibs\DB\IO
{ {
// main calss variables // main calss variables
/** @var array<mixed> */ /** @var array<mixed> */
public array $table_array; // the array from the table to work on private array $table_array; // the array from the table to work on
/** @var string */ /** @var string */
public string $table_name; // the table_name private string $table_name; // the table_name
/** @var string */ /** @var string */
public string $pk_name = ''; // the primary key from this table private string $pk_name = ''; // the primary key from this table
/** @var int|string|null */ /** @var int|string|null */
public int|string|null $pk_id; // the PK id private int|string|null $pk_id; // the PK id
// security values // security values
/** @var int base acl for current page */ /** @var int base acl for current page */
private int $base_acl_level = 0; private int $base_acl_level = 0;
@@ -55,7 +55,7 @@ class ArrayIO extends \CoreLibs\DB\IO
* primary key name automatically (from array) * primary key name automatically (from array)
* *
* phpcs:ignore * 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{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_convert_placeholder?:bool,db_convert_placeholder_target?:string,db_debug_replace_placeholder?:bool} $db_config db connection config
* @param array<mixed> $table_array table array config * @param array<mixed> $table_array table array config
* @param string $table_name table name string * @param string $table_name table name string
* @param \CoreLibs\Logging\Logging $log Logging class * @param \CoreLibs\Logging\Logging $log Logging class
@@ -74,24 +74,21 @@ class ArrayIO extends \CoreLibs\DB\IO
// instance db_io class // instance db_io class
parent::__construct($db_config, $log); parent::__construct($db_config, $log);
// more error vars for this class // more error vars for this class
$this->error_string['1999'] = 'No table array or table name set'; $this->error_string['1998'] = 'No table name set';
$this->error_string['1999'] = 'No table array set';
$this->error_string['1021'] = 'No Primary Key given'; $this->error_string['1021'] = 'No Primary Key given';
$this->error_string['1022'] = 'Could not run Array Query'; $this->error_string['1022'] = 'Could not run Array Query';
$this->table_array = $table_array; $this->setTableArray($table_array);
$this->table_name = $table_name; $this->setTableName($table_name);
// error abort if no table array or no table name
if (empty($table_array) || empty($table_name)) {
$this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing');
throw new \RuntimeException('MAJOR ERROR: Core settings missing', 1999);
}
// set primary key for given table_array // set primary key for given table_array
foreach ($this->table_array as $key => $value) { foreach ($this->table_array as $key => $value) {
if (!empty($value['pk'])) { if (empty($value['pk'])) {
$this->pk_name = $key; continue;
} }
$this->setPkName($key);
break;
} }
$this->dbArrayIOSetAcl($base_acl_level, $acl_admin); $this->dbArrayIOSetAcl($base_acl_level, $acl_admin);
} }
@@ -104,6 +101,144 @@ class ArrayIO extends \CoreLibs\DB\IO
parent::__destruct(); parent::__destruct();
} }
/**
* Set the overall table array
*
* @param array<mixed> $table_array
* @return void
* @throws \RuntimeException 1999 for empty table array
*/
public function setTableArray(array $table_array): void
{
$this->table_array = $table_array;
if (empty($this->table_array)) {
$this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing: table_arrry');
throw new \RuntimeException('MAJOR ERROR: Core settings missing: table_array', 1999);
}
}
/**
* return full table array, or [] if empty
* of reset is set to true, will reset array first
*
* @param bool $reset [=false] run a reset before returning
* @return array<mixed>
*/
public function getTableArray(bool $reset = false): array
{
if (!$reset) {
return $this->table_array ?? [];
}
$table_array = $this->table_array ?? [];
reset($table_array);
return $table_array;
}
/**
* get a table array entry under the key with element pos
*
* @param string $key
* @param string $pos
* @return mixed
*/
public function getTableArrayEntry(string $key, string $pos): mixed
{
return $this->table_array[$key][$pos] ?? null;
}
/**
* set a new value at key with pos
*
* @param mixed $value
* @param string $key
* @param string $pos
* @return void
*/
public function setTableArrayEntry(mixed $value, string $key, string $pos): void
{
$this->table_array[$key][$pos] = $value;
}
/**
* unset entry at key with pos
*
* @param string $key
* @param string $pos
* @return void
*/
public function unsetTableArrayEntry(string $key, string $pos): void
{
unset($this->table_array[$key][$pos]);
}
/**
* Set table name
*
* @param string $table_name
* @return void
* @throws \RuntimeException 1998 for empty table name
*/
public function setTableName(string $table_name): void
{
$this->table_name = $table_name;
if (empty($this->table_name)) {
$this->__dbError(1998, false, 'MAJOR ERROR: Core settings missing: table_name');
throw new \RuntimeException('MAJOR ERROR: Core settings missing: table_name', 1998);
}
}
/**
* Return table name or empty string if not net
*
* @return string
*/
public function getTableName(): string
{
return $this->table_name ?? '';
}
/**
* Set primary key name
*
* @param string $pk_name
* @return void
*/
public function setPkName(string $pk_name): void
{
$this->pk_name = $pk_name;
}
/**
* get primary key name
*
* @return string
*/
public function getPkName(): string
{
return $this->pk_name;
}
/**
* set primary key id, can be null for not yet set
*
* @param int|string|null $pk_id
* @return void
*/
public function setPkId(int|string|null $pk_id): void
{
$this->pk_id = $pk_id;
}
/**
* return primary key id, or null if not set
*
* @return int|string|null
*/
public function getPkId(): int|string|null
{
return $this->pk_id ?? null;
}
/** /**
* set the base acl level and admin acl flag * set the base acl level and admin acl flag
* This is needed for table array ACL checks * This is needed for table array ACL checks
@@ -198,8 +333,8 @@ class ArrayIO extends \CoreLibs\DB\IO
public function dbCheckPkSet(): bool public function dbCheckPkSet(): bool
{ {
// if pk_id is set, overrule ... // if pk_id is set, overrule ...
if ($this->pk_id) { if (!empty($this->getPkId())) {
$this->table_array[$this->pk_name]['value'] = $this->pk_id; $this->table_array[$this->pk_name]['value'] = $this->getPkId();
} }
// if not set ... produce error // if not set ... produce error
if (!$this->table_array[$this->pk_name]['value']) { if (!$this->table_array[$this->pk_name]['value']) {
@@ -287,7 +422,7 @@ class ArrayIO extends \CoreLibs\DB\IO
$q .= ' AND ' . $q_where; $q .= ' AND ' . $q_where;
} }
// if 0, error // if 0, error
$this->pk_id = null; $this->setPkId(null);
if (!$this->dbExec($q)) { if (!$this->dbExec($q)) {
$this->__dbError(1022); $this->__dbError(1022);
} }
@@ -374,7 +509,7 @@ class ArrayIO extends \CoreLibs\DB\IO
} }
} }
// possible dbFetchArray errors ... // possible dbFetchArray errors ...
$this->pk_id = $this->table_array[$this->pk_name]['value']; $this->setPkId($this->table_array[$this->pk_name]['value']);
} else { } else {
$this->__dbError(1022); $this->__dbError(1022);
} }
@@ -397,10 +532,6 @@ class ArrayIO extends \CoreLibs\DB\IO
if (count($table_array)) { if (count($table_array)) {
$this->table_array = $table_array; $this->table_array = $table_array;
} }
// PK ID check
// if ($this->pk_id && !$this->table_array[$this->pk_name]["value"]) {
// $this->table_array[$this->pk_name]["value"]=$this->pk_id;
// }
// checken ob PKs gesetzt, wenn alle -> update, wenn keiner -> insert, wenn ein paar -> ERROR! // checken ob PKs gesetzt, wenn alle -> update, wenn keiner -> insert, wenn ein paar -> ERROR!
if (!$this->table_array[$this->pk_name]['value']) { if (!$this->table_array[$this->pk_name]['value']) {
$insert = 1; $insert = 1;
@@ -624,16 +755,11 @@ class ArrayIO extends \CoreLibs\DB\IO
$q .= ' AND ' . $q_where; $q .= ' AND ' . $q_where;
} }
// set pk_id ... if it has changed or so // set pk_id ... if it has changed or so
$this->pk_id = $this->table_array[$this->pk_name]['value']; $this->setPkId($this->table_array[$this->pk_name]['value']);
} else { } else {
$q = 'INSERT INTO ' . $this->table_name . ' '; $q = 'INSERT INTO ' . $this->table_name . ' ';
$q .= '(' . $q_vars . ') '; $q .= '(' . $q_vars . ') ';
$q .= 'VALUES (' . $q_data . ')'; $q .= 'VALUES (' . $q_data . ')';
// write primary key too
// if ($q_data)
// $q .= ", ";
// $q .= $this->pk_name." = ".$this->table_array[$this->pk_name]['value']." ";
// $this->pk_id = $this->table_array[$this->pk_name]['value'];
} }
// return success or not // return success or not
if (!$this->dbExec($q)) { if (!$this->dbExec($q)) {
@@ -646,7 +772,7 @@ class ArrayIO extends \CoreLibs\DB\IO
$insert_id = 0; $insert_id = 0;
} }
$this->table_array[$this->pk_name]['value'] = $insert_id; $this->table_array[$this->pk_name]['value'] = $insert_id;
$this->pk_id = $insert_id; $this->setPkId($insert_id);
} }
// return the table if needed // return the table if needed
return $this->table_array; return $this->table_array;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,220 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/10/10
* DESCRIPTION:
* Convert placeholders in query from PDO style ? or :named to \PG style $number
* pr the other way around
*/
declare(strict_types=1);
namespace CoreLibs\DB\Support;
class ConvertPlaceholder
{
/**
* Convert PDO type query with placeholders to \PG style and vica versa
* For PDO to: ? and :named
* For \PG to: $number
*
* If the query has a mix of ?, :named or $numbrer the \OutOfRangeException exception
* will be thrown
*
* If the convert_to is either pg or pdo, nothing will be changed
*
* found has -1 if an error occoured in the preg_match_all call
*
* @param string $query Query with placeholders to convert
* @param array<mixed> $params The parameters that are used for the query, and will be updated
* @param string $convert_to Either pdo or pg, will be converted to lower case for check
* @return array{original:array{query:string,params:array<mixed>},type:''|'named'|'numbered'|'question_mark',found:int,matches:array<string>,params_lookup:array<mixed>,query:string,params:array<mixed>}
* @throws \OutOfRangeException 200
*/
public static function convertPlaceholderInQuery(
string $query,
array $params,
string $convert_to = 'pg'
): array {
$convert_to = strtolower($convert_to);
$matches = [];
$pattern = '/'
// prefix string part, must match towards
. '(?:\'.*?\')?\s*(?:\?\?|[(=,])\s*'
// match for replace part
. '(?:'
// digit -> ignore
. '\d+|'
// other string -> ignore
. '(?:\'.*?\')|'
// :name named part (PDO)
. '(:\w+)|'
// ? question mark part (PDO)
. '(?:(?:\?\?)?\s*(\?{1}))|'
// $n numbered part (\PG php)
. '(\$[1-9]{1}(?:[0-9]{1,})?)'
// end match
. ')'
// single line -> add line break to matches in "."
. '/s';
// matches:
// 1: :named
// 2: ? question mark
// 3: $n numbered
$found = preg_match_all($pattern, $query, $matches, PREG_UNMATCHED_AS_NULL);
// if false or null set to -1
// || $found === null
if ($found === false) {
$found = -1;
}
/** @var array<string> 1: named */
$named_matches = array_filter($matches[1]);
/** @var array<string> 2: open ? */
$qmark_matches = array_filter($matches[2]);
/** @var array<string> 3: $n matches */
$numbered_matches = array_filter($matches[3]);
// count matches
$count_named = count($named_matches);
$count_qmark = count($qmark_matches);
$count_numbered = count($numbered_matches);
// throw if mixed
if (
($count_named && $count_qmark) ||
($count_named && $count_numbered) ||
($count_qmark && $count_numbered)
) {
throw new \OutOfRangeException('Cannot have named, question mark and numbered in the same query', 200);
}
// rebuild
$matches_return = [];
$type = '';
$query_new = '';
$params_new = [];
$params_lookup = [];
if ($count_named && $convert_to == 'pg') {
$type = 'named';
$matches_return = $named_matches;
// only check for :named
$pattern_replace = '/((?:\'.*?\')?\s*(?:\?\?|[(=,])\s*)(\d+|(?:\'.*?\')|(:\w+))/s';
// 0: full
// 1: pre part
// 2: keep part UNLESS '3' is set
// 3: replace part :named
$pos = 0;
$query_new = preg_replace_callback(
$pattern_replace,
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params) {
// only count up if $match[3] is not yet in lookup table
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
$pos++;
$params_lookup[$matches[3]] = '$' . $pos;
$params_new[] = $params[$matches[3]] ??
throw new \RuntimeException(
'Cannot lookup ' . $matches[3] . ' in params list',
210
);
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $matches[1] . (
empty($matches[3]) ?
$matches[2] :
$params_lookup[$matches[3]] ??
throw new \RuntimeException(
'Cannot lookup ' . $matches[3] . ' in params lookup list',
211
)
);
},
$query
);
} elseif ($count_qmark && $convert_to == 'pg') {
$type = 'question_mark';
$matches_return = $qmark_matches;
// order and data stays the same
$params_new = $params;
// only check for ?
$pattern_replace = '/((?:\'.*?\')?\s*(?:\?\?|[(=,])\s*)(\d+|(?:\'.*?\')|(?:(?:\?\?)?\s*(\?{1})))/s';
// 0: full
// 1: pre part
// 2: keep part UNLESS '3' is set
// 3: replace part ?
$pos = 0;
$query_new = preg_replace_callback(
$pattern_replace,
function ($matches) use (&$pos, &$params_lookup) {
// only count pos up for actual replacements we will do
if (!empty($matches[3])) {
$pos++;
$params_lookup[] = '$' . $pos;
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $matches[1] . (
empty($matches[3]) ?
$matches[2] :
'$' . $pos
);
},
$query
);
// for each ?:DTN: -> replace with $1 ... $n, any remaining :DTN: remove
} elseif ($count_numbered && $convert_to == 'pdo') {
// convert numbered to named
$type = 'numbered';
$matches_return = $numbered_matches;
// only check for $n
$pattern_replace = '/((?:\'.*?\')?\s*(?:\?\?|[(=,])\s*)(\d+|(?:\'.*?\')|(\$[1-9]{1}(?:[0-9]{1,})?))/s';
// 0: full
// 1: pre part
// 2: keep part UNLESS '3' is set
// 3: replace part $numbered
$pos = 0;
$query_new = preg_replace_callback(
$pattern_replace,
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params) {
// only count up if $match[3] is not yet in lookup table
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
$pos++;
$params_lookup[$matches[3]] = ':' . $pos . '_named';
$params_new[] = $params[($pos - 1)] ??
throw new \RuntimeException(
'Cannot lookup ' . ($pos - 1) . ' in params list',
220
);
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $matches[1] . (
empty($matches[3]) ?
$matches[2] :
$params_lookup[$matches[3]] ??
throw new \RuntimeException(
'Cannot lookup ' . $matches[3] . ' in params lookup list',
221
)
);
},
$query
);
}
// return, old query is always set
return [
// original
'original' => [
'query' => $query,
'params' => $params,
],
// type found, empty if nothing was done
'type' => $type,
// int: found, not found; -1: problem (set from false)
'found' => (int)$found,
'matches' => $matches_return,
// old to new lookup check
'params_lookup' => $params_lookup,
// new
'query' => $query_new ?? '',
'params' => $params_new,
];
}
}
// __END__

View File

@@ -295,8 +295,7 @@ class Support
* Will start with start_level to skip unwanted from stack * Will start with start_level to skip unwanted from stack
* Defaults to skip level 0 wich is this methid * Defaults to skip level 0 wich is this methid
* *
* @param integer $start_level From what level on, as defaul starts with 1 * @param integer $start_level [=1] From what level on, starts with 1 to exclude self
* to exclude self
* @return array<mixed> All method names in list where max is last called * @return array<mixed> All method names in list where max is last called
*/ */
public static function getCallerMethodList(int $start_level = 1): array public static function getCallerMethodList(int $start_level = 1): array
@@ -304,15 +303,46 @@ class Support
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$methods = []; $methods = [];
foreach ($traces as $level => $data) { foreach ($traces as $level => $data) {
if ($level >= $start_level) { if ($level < $start_level) {
if (!empty($data['function'])) { continue;
array_unshift($methods, $data['function']); }
} if (!empty($data['function'])) {
array_unshift($methods, $data['function']);
} }
} }
return $methods; return $methods;
} }
/**
* Get the full call stack from a certain starting level
* The return string is
* file:line:class->method
*
* Note that '::' is used for static calls
*
* @param int $start_level [=1] starts with 1 to exclude itself
* @return array<string> string with file, line, class and method
*/
public static function getCallStack(int $start_level = 1): array
{
$call_stack = [];
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach ($backtrace as $level => $call_trace) {
if ($level < $start_level) {
continue;
}
$call_stack[] =
($call_trace['file'] ?? 'n/f') . ':'
. ($call_trace['line'] ?? '-') . ':'
. (!empty($call_trace['class']) ?
$call_trace['class'] . ($call_trace['type'] ?? '') :
''
)
. $call_trace['function'];
}
return $call_stack;
}
/** /**
* Get the current class where this function is called * Get the current class where this function is called
* Is mostly used in debug log statements to get the class where the debug * Is mostly used in debug log statements to get the class where the debug

View File

@@ -15,15 +15,34 @@ use CoreLibs\Logging\Logger\MessageLevel;
class ErrorMessage class ErrorMessage
{ {
/** @var array<int,array{id:string,level:string,str:string,target:string,highlight:string[]}> */ /** @var array<int,array{id:string,level:string,str:string,target:string,target_style:string,highlight:string[]}> */
private array $error_str = []; private array $error_str = [];
/** @var array<string,array{info:string,level:string}> */
private array $jump_targets = [];
/** @var \CoreLibs\Logging\Logging $log */ /** @var \CoreLibs\Logging\Logging $log */
public \CoreLibs\Logging\Logging $log; public \CoreLibs\Logging\Logging $log;
/** @var bool $log_error global flag to log error level message */
private bool $log_error = false;
/**
* init ErrorMessage
*
* @param \CoreLibs\Logging\Logging $log
* @param null|bool $log_error [=null], defaults to false if log is not level debug
*/
public function __construct( public function __construct(
\CoreLibs\Logging\Logging $log \CoreLibs\Logging\Logging $log,
?bool $log_error = null
) { ) {
$this->log = $log; $this->log = $log;
// if log default logging is debug then log_error is default set to true
if ($this->log->loggingLevelIsDebug() && $log_error === null) {
$log_error = true;
} else {
$log_error = $log_error ?? false;
}
$this->log_error = $log_error;
} }
/** /**
@@ -31,6 +50,7 @@ class ErrorMessage
* error_id: internal Error ID (should be unique) * error_id: internal Error ID (should be unique)
* level: error level, can only be ok, info, warn, error, abort, crash * level: error level, can only be ok, info, warn, error, abort, crash
* ok and info are positive response: success * ok and info are positive response: success
* notice: a debug message for information only
* warn: success, but there might be some things that are not 100% ok * warn: success, but there might be some things that are not 100% ok
* error: input error or error in executing request * error: input error or error in executing request
* abort: an internal error happened as mandatory information that normally is * abort: an internal error happened as mandatory information that normally is
@@ -41,25 +61,42 @@ class ErrorMessage
* not set: unkown, will be logged as "emergency" * not set: unkown, will be logged as "emergency"
* target/highlight: id target name for frontend where to attach this message * target/highlight: id target name for frontend where to attach this message
* highlight is a list of other target points to highlight * highlight is a list of other target points to highlight
* for highlight targets css names are $level without a prefix and should be
* nested in the target element "input .error { ... }"
* jump_target: a target id for to jump and message, is stored in separate jump array
* where the target is unique, first one set is used for info message
* target_style: if not set uses 'error-' $level as css style. applies to targets or main only
* *
* @param string $error_id Any internal error ID for this error * @param string $error_id Any internal error ID for this error
* @param string $level Error level in ok/info/warn/error * @param string $level Error level in ok/info/warn/error
* @param string $str Error message (out) * @param string $str Error message (out)
* @param string $target alternate attachment point for this error message * @param string $target alternate attachment point for this error message
* @param array<string> $highlight Any additional error data as error OR * @param string $target_style Alternate color style for the error message
* highlight points for field highlights * @param array<string> $highlight Any additional error data as error OR
* @param string|null $message If abort/crash, non localized $str * highlight points for field highlights
* @param array<mixed> $context Additionl info for abort/crash messages * @param array{}|array{target:string,info?:string} $jump_target with "target" for where to jump and
* "info" for string to show in jump list
* target must be set, if info not set, default message used
* @param string|null $message If abort/crash, non localized $str
* @param array<mixed> $context Additionl info for abort/crash messages
* @param bool|null $log_error [=null] log level 'error' to error, if null use global,
* else set for this call only
*/ */
public function setErrorMsg( public function setErrorMsg(
string $error_id, string $error_id,
string $level, string $level,
string $str, string $str,
string $target = '', string $target = '',
string $target_style = '',
array $highlight = [], array $highlight = [],
array $jump_target = [],
?string $message = null, ?string $message = null,
array $context = [], array $context = [],
?bool $log_error = null,
): void { ): void {
if ($log_error === null) {
$log_error = $this->log_error;
}
$original_level = $level; $original_level = $level;
$level = MessageLevel::fromName($level)->name; $level = MessageLevel::fromName($level)->name;
// if not string set, write message string if set, else level/error id // if not string set, write message string if set, else level/error id
@@ -71,10 +108,27 @@ class ErrorMessage
'level' => $level, 'level' => $level,
'str' => $str, 'str' => $str,
'target' => $target, 'target' => $target,
'target_style' => $target_style,
'highlight' => $highlight, 'highlight' => $highlight,
]; ];
// set a jump target
$this->setJumpTarget($jump_target['target'] ?? null, $jump_target['info'] ?? null, $level);
// write to log for abort/crash // write to log for abort/crash
switch ($level) { switch ($level) {
case 'notice':
$this->log->notice($message ?? $str, array_merge([
'id' => $error_id,
'level' => $original_level,
], $context));
break;
case 'error':
if ($log_error) {
$this->log->error($message ?? $str, array_merge([
'id' => $error_id,
'level' => $original_level,
], $context));
}
break;
case 'abort': case 'abort':
$this->log->critical($message ?? $str, array_merge([ $this->log->critical($message ?? $str, array_merge([
'id' => $error_id, 'id' => $error_id,
@@ -101,25 +155,77 @@ class ErrorMessage
* Note, the parameter order is different and does not need an error id * Note, the parameter order is different and does not need an error id
* This is for backend alerts * This is for backend alerts
* *
* @param string $level error level (ok/warn/info/error) * @param string $level error level (ok/warn/info/error)
* @param string $str error string * @param string $str error string
* @param string|null $error_id optional error id for precise error lookup * @param string|null $error_id optional error id for precise error lookup
* @param string $target Alternate id name for output target on frontend * @param string $target Alternate id name for output target on frontend
* @param array<string> $highlight Any additional error data as error OR * @param string $target_style Alternate color style for the error message
* highlight points for field highlights * @param array<string> $highlight Any additional error data as error OR
* @param string|null $message If abort/crash, non localized $str * highlight points for field highlights
* @param array<mixed> $context Additionl info for abort/crash messages * @param array{}|array{target:string,info?:string} $jump_target with "target" for where to jump and
* "info" for string to show in jump list
* target must be set, if info not set, default message used
* @param string|null $message If abort/crash, non localized $str
* @param array<mixed> $context Additionl info for abort/crash messages
* @param bool|null $log_error [=null] log level 'error' to error, if null use global,
* else set for this call only
*/ */
public function setMessage( public function setMessage(
string $level, string $level,
string $str, string $str,
?string $error_id = null, ?string $error_id = null,
string $target = '', string $target = '',
string $target_style = '',
array $highlight = [], array $highlight = [],
array $jump_target = [],
?string $message = null, ?string $message = null,
array $context = [], array $context = [],
?bool $log_error = null,
): void { ): void {
$this->setErrorMsg($error_id ?? '', $level, $str, $target, $highlight, $message, $context); $this->setErrorMsg(
$error_id ?? '',
$level,
$str,
$target,
$target_style,
$highlight,
$jump_target,
$message,
$context,
$log_error
);
}
/**
* Set a jump target. This can be used to jump directly a frontend html block
* with the target id set
*
* @param string|null $target
* @param string|null $info
* @param string $level [='error']
* @return void
*/
public function setJumpTarget(
?string $target,
?string $info,
string $level = 'error',
): void {
if (
empty($target) ||
array_key_exists($target, $this->jump_targets)
// !empty($this->jump_targets[$target])
// also check if this is an alphanumeric string? css id compatible?
) {
return;
}
if (empty($info)) {
$info = 'Jump to: ' . $target;
}
$level = MessageLevel::fromName($level)->name;
$this->jump_targets[$target] = [
'info' => $info,
'level' => $level,
];
} }
// ********************************************************************* // *********************************************************************
@@ -150,7 +256,7 @@ class ErrorMessage
* Gets the LAST entry in the array list. * Gets the LAST entry in the array list.
* If nothing found returns empty array set * If nothing found returns empty array set
* *
* @return array{id:string,level:string,str:string,target:string,highlight:string[]} Error block * @return array{id:string,level:string,str:string,target:string,target:string,highlight:string[]} Error block
*/ */
public function getLastErrorMsg(): array public function getLastErrorMsg(): array
{ {
@@ -159,9 +265,55 @@ class ErrorMessage
'str' => '', 'str' => '',
'id' => '', 'id' => '',
'target' => '', 'target' => '',
'target_string' => '',
'highlight' => [], 'highlight' => [],
]; ];
} }
/**
* Return the jump target list
*
* @return array{}|array<int,array{target:string,info:string,level:string}> List of jump targets with info text,
* or empty array if not set
*/
public function getJumpTarget(): array
{
$_jump_target = [];
foreach ($this->jump_targets as $target => $jump) {
$_jump_target[] = array_merge(
$jump,
[
'target' => $target,
]
);
}
return $_jump_target;
}
// *********************************************************************
// FLAG SETTERS
// *********************************************************************
/**
* Set the log error flag
*
* @param bool $flag True to log level error too, False for do not (Default)
* @return void
*/
public function setFlagLogError(bool $flag): void
{
$this->log_error = $flag;
}
/**
* Get the current log error flag
*
* @return bool
*/
public function getFlagLogError(): bool
{
return $this->log_error;
}
} }
// __END__ // __END__

View File

@@ -15,6 +15,7 @@ enum MessageLevel: int
{ {
case ok = 100; case ok = 100;
case info = 200; case info = 200;
case notice = 250;
case warn = 300; case warn = 300;
case error = 400; case error = 400;
case abort = 500; case abort = 500;
@@ -30,6 +31,7 @@ enum MessageLevel: int
return match (strtolower($name)) { return match (strtolower($name)) {
'ok' => self::ok, 'ok' => self::ok,
'info' => self::info, 'info' => self::info,
'notice' => self::notice,
'warn', 'warning' => self::warn, 'warn', 'warning' => self::warn,
'error' => self::error, 'error' => self::error,
'abort' => self::abort, 'abort' => self::abort,
@@ -46,6 +48,39 @@ enum MessageLevel: int
{ {
return self::tryFrom($value) ?? self::unknown; return self::tryFrom($value) ?? self::unknown;
} }
/**
* Returns true if the passed $level is higher or equal to $this
*
* @param MessageLevel $level
* @return bool
*/
public function includes(MessageLevel $level): bool
{
return $this->value <= $level->value;
}
/**
* If level is higher than set one
*
* @param MessageLevel $level
* @return bool
*/
public function isHigherThan(MessageLevel $level): bool
{
return $this->value > $level->value;
}
/**
* if level is lower than set one
*
* @param MessageLevel $level
* @return bool
*/
public function isLowerThan(MessageLevel $level): bool
{
return $this->value < $level->value;
}
} }
// __END__ // __END__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditOrder implements Interface\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private \CoreLibs\Output\Form\Generate $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* NOTE: this is a dummy array to just init the Form\Generate class and is not used for anything else
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'-'
],
'table_name' => '-',
'load_query' => '',
'show_fields' => [],
];
}
}
// __END__

View File

@@ -53,6 +53,7 @@ class EditPages implements Interface\TableArraysInterface
'value' => $_POST['name'] ?? '', 'value' => $_POST['name'] ?? '',
'output_name' => 'Page name', 'output_name' => 'Page name',
'mandatory' => 1, 'mandatory' => 1,
'error_check' => 'unique',
'type' => 'text' 'type' => 'text'
], ],
'order_number' => [ 'order_number' => [

View File

@@ -78,7 +78,7 @@ class Image
if (!empty($dummy) && file_exists($filename) && is_file($filename)) { if (!empty($dummy) && file_exists($filename) && is_file($filename)) {
$return_data = $filename; $return_data = $filename;
} else { } else {
throw new \Exception('Could not set dummy return file: ' . $dummy . ' in ' . $filename); throw new \RuntimeException('Could not set dummy return file: ' . $dummy . ' in ' . $filename);
} }
} else { } else {
$return_data = $dummy; $return_data = $dummy;
@@ -204,11 +204,11 @@ class Image
E_USER_DEPRECATED E_USER_DEPRECATED
); );
// NOTE: we need to depracte this // NOTE: we need to depracte this
$cache_folder = BASE . LAYOUT . CONTENT_PATH . CACHE . IMAGES; $cache_folder = BASE . CONTENT_PATH . LAYOUT . CACHE . IMAGES;
$web_folder = LAYOUT . CACHE . IMAGES; $web_folder = LAYOUT . CACHE . IMAGES;
if (!is_dir($cache_folder)) { if (!is_dir($cache_folder)) {
if (false === mkdir($cache_folder)) { if (false === mkdir($cache_folder)) {
$cache_folder = BASE . LAYOUT . CONTENT_PATH . CACHE; $cache_folder = BASE . CONTENT_PATH . LAYOUT . CACHE;
$web_folder = LAYOUT . CACHE; $web_folder = LAYOUT . CACHE;
} }
} }
@@ -470,12 +470,20 @@ class Image
* *
* @param string $filename path + filename to rotate. This file must be writeable * @param string $filename path + filename to rotate. This file must be writeable
* @return void * @return void
* @throws \RuntimeException if exit_read_data is not found
* @throws \UnexpectedValueException if file name not writeable or file name not found
*/ */
public static function correctImageOrientation(string $filename): void public static function correctImageOrientation(string $filename): void
{ {
// function exists & file is writeable, else do nothing // function exists & file is writeable, else do nothing
if (!function_exists('exif_read_data') || !is_writeable($filename)) { if (!function_exists('exif_read_data')) {
return; throw new \RuntimeException('Function \'exit_read_data\' does not exist');
}
if (!file_exists($filename) || !is_file($filename)) {
throw new \UnexpectedValueException('Missing image file: ' . $filename);
}
if (!is_writeable($filename)) {
throw new \UnexpectedValueException('File name is not writeable: ' . $filename);
} }
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null]; [$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null];
// add @ to avoid "file not supported error" // add @ to avoid "file not supported error"

View File

@@ -156,7 +156,7 @@ class ProgressBar
{ {
// avoid divison through 0 // avoid divison through 0
if ($this->max - $this->min == 0) { if ($this->max - $this->min == 0) {
$this->max ++; $this->max++;
} }
$percent = round(($step - $this->min) / ($this->max - $this->min) * 100); $percent = round(($step - $this->min) / ($this->max - $this->min) * 100);
if ($percent > 100) { if ($percent > 100) {
@@ -186,7 +186,7 @@ class ProgressBar
} }
// avoid divison through 0 // avoid divison through 0
if ($this->max - $this->min == 0) { if ($this->max - $this->min == 0) {
$this->max ++; $this->max++;
} }
$pixel = round(($step - $this->min) * ($bar - ($this->pedding * 2)) / ($this->max - $this->min)); $pixel = round(($step - $this->min) * ($bar - ($this->pedding * 2)) / ($this->max - $this->min));
if ($step <= $this->min) { if ($step <= $this->min) {

View File

@@ -1,2 +1,2 @@
base="/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All/"; base="/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All/";
vendor/bin/phan --progress-bar -C --analyze-twice tools/phan --progress-bar -C --analyze-twice

View File

@@ -1,2 +1,2 @@
base="/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All/"; base="/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All/";
vendor/bin/phpstan tools/phpstan

View File

@@ -33,7 +33,7 @@ if [ ! -z "${2}" ] && [ -z "${php_bin}" ]; then
esac; esac;
fi; fi;
phpunit_call="${php_bin}${base}vendor/bin/phpunit ${opt_testdox} -c ${base}phpunit.xml ${base}test/phpunit/"; phpunit_call="${php_bin}${base}tools/phpunit ${opt_testdox} -c ${base}phpunit.xml ${base}test/phpunit/";
${phpunit_call}; ${phpunit_call};

View File

@@ -28,10 +28,10 @@ final class CoreLibsCheckFileTest extends TestCase
public function filesList(): array public function filesList(): array
{ {
return [ return [
['filename.txt', 'txt', 5], ['filename.txt', 'txt', 5, 'text/plain'],
['filename.csv', 'csv', 15], ['filename.csv', 'csv', 15, 'text/csv'],
['filename.tsv', 'tsv', 0], ['filename.tsv', 'tsv', 0, 'text/plain'],
['file_does_not_exits', '', -1], ['file_does_not_exits', '', -1, ''],
]; ];
} }
@@ -63,6 +63,15 @@ final class CoreLibsCheckFileTest extends TestCase
return $list; return $list;
} }
public function mimeTypeProvider(): array
{
$list = [];
foreach ($this->filesList() as $row) {
$list[$row[0] . ' must be mime type ' . $row[3]] = [$row[0], $row[3]];
}
return $list;
}
/** /**
* Tests if file extension matches * Tests if file extension matches
* *
@@ -115,6 +124,51 @@ final class CoreLibsCheckFileTest extends TestCase
unlink($this->base_folder . $input); unlink($this->base_folder . $input);
} }
} }
/**
* Undocumented function
*
* @covers ::getMimeType
* @dataProvider mimeTypeProvider
* @testdox getMimeType $input must be mime type $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testGetMimeType(string $input, string $expected): void
{
if (!empty($expected)) {
$file = $this->base_folder . $input;
$fp = fopen($file, 'w');
switch ($expected) {
case 'text/csv':
for ($i = 1; $i <= 10; $i++) {
fwrite($fp, '"This is row","' . $expected . '",' . $i . PHP_EOL);
}
break;
case 'text/tsv':
for ($i = 1; $i <= 10; $i++) {
fwrite($fp, "\"This is row\"\t\"" . $expected . "\"\t\"" . $i . PHP_EOL);
}
break;
case 'text/plain':
fwrite($fp, 'This is mime type: ' . $expected . PHP_EOL);
break;
}
fclose($fp);
} else {
$this->expectException(\UnexpectedValueException::class);
}
$this->assertEquals(
$expected,
\CoreLibs\Check\File::getMimeType($this->base_folder . $input)
);
// unlink file
if (is_file($this->base_folder . $input)) {
unlink($this->base_folder . $input);
}
}
} }
// __END__ // __END__

View File

@@ -1098,16 +1098,109 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
* @testdox arrayFlatForKey array $input will be $expected [$_dataName] * @testdox arrayFlatForKey array $input will be $expected [$_dataName]
* *
* @param array $input * @param array $input
* @param string $search
* @param array $expected * @param array $expected
* @return void * @return void
*/ */
public function testArrayFlatForKey(array $input, $search, array $expected): void public function testArrayFlatForKey(array $input, string $search, array $expected): void
{ {
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Combined\ArrayHandler::arrayFlatForKey($input, $search) \CoreLibs\Combined\ArrayHandler::arrayFlatForKey($input, $search)
); );
} }
/**
* Undocumented function
*
* @return array
*/
public function providerArrayGetNextPrevKey(): array
{
return [
'find, ok' => [
'input' => [
'a' => 'First',
'b' => 'Second',
'c' => 'Third',
],
'b',
'a',
'c'
],
'find, first' => [
'input' => [
'a' => 'First',
'b' => 'Second',
'c' => 'Third',
],
'a',
null,
'b'
],
'find, last' => [
'input' => [
'a' => 'First',
'b' => 'Second',
'c' => 'Third',
],
'c',
'b',
null
],
'find, not found' => [
'input' => [
'a' => 'First',
'b' => 'Second',
'c' => 'Third',
],
'z',
null,
null
],
'int, index' => [
'input' => [
'a',
'b',
'c'
],
1,
0,
2
]
];
}
/**
* Undocumented function
*
* @covers ::arrayGetPrevKey, ::arrayGetNextKey
* @dataProvider providerArrayGetNextPrevKey
* @testdox arrayGetNextPrevKey get next/prev key for $search wtih $expected_prev/$expected_next [$_dataName]
*
* @param array $input
* @param int|string $search
* @param int|string|null $expected_prev
* @param int|string|null $expected_next
* @return void
*/
public function testArrayGetNextPrevKey(
array $input,
int|string $search,
int|string|null $expected_prev,
int|string|null $expected_next
): void {
$this->assertEquals(
$expected_prev,
\CoreLibs\Combined\ArrayHandler::arrayGetPrevKey($input, $search),
'Find prev key in array'
);
$this->assertEquals(
$expected_next,
\CoreLibs\Combined\ArrayHandler::arrayGetNextKey($input, $search),
'Find next key in array'
);
}
} }
// __END__ // __END__

View File

@@ -66,6 +66,34 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* date string convert test
*
* @covers ::dateStringFormat
* @dataProvider timestampProvider
* @testdox dateStringFormat $input (microtime $flag) will be $expected [$_dataName]
*
* @param int|float $input
* @param bool $flag
* @param string $expected
* @return void
*/
public function testDateStringFormat(
$input,
bool $flag_show_micro,
bool $flag_micro_as_float,
string $expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::dateStringFormat(
$input,
$flag_show_micro,
$flag_micro_as_float
)
);
}
/** /**
* interval for both directions * interval for both directions
* *
@@ -74,6 +102,11 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
public function intervalProvider(): array public function intervalProvider(): array
{ {
return [ return [
'on hour' => [
3600,
false,
'1h 0m 0s'
],
'interval no microtime' => [ 'interval no microtime' => [
1641515890, 1641515890,
false, false,
@@ -82,7 +115,7 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'interval with microtime' => [ 'interval with microtime' => [
1641515890, 1641515890,
true, true,
'18999d 0h 38m 10s', '18999d 0h 38m 10s 0ms',
], ],
'micro interval no microtime' => [ 'micro interval no microtime' => [
1641515890.123456, 1641515890.123456,
@@ -92,7 +125,7 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'micro interval with microtime' => [ 'micro interval with microtime' => [
1641515890.123456, 1641515890.123456,
true, true,
'18999d 0h 38m 10s 1235ms', '18999d 0h 38m 10s 124ms',
], ],
'negative interval no microtime' => [ 'negative interval no microtime' => [
-1641515890, -1641515890,
@@ -103,27 +136,27 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'microtime only' => [ 'microtime only' => [
0.123456, 0.123456,
true, true,
'0s 1235ms', '0s 123ms',
], ],
'seconds only' => [ 'seconds only' => [
30.123456, 30.123456,
true, true,
'30s 1235ms', '30s 123ms',
], ],
'minutes only' => [ 'minutes only' => [
90.123456, 90.123456,
true, true,
'1m 30s 1235ms', '1m 30s 123ms',
], ],
'hours only' => [ 'hours only' => [
3690.123456, 3690.123456,
true, true,
'1h 1m 30s 1235ms', '1h 1m 30s 123ms',
], ],
'days only' => [ 'days only' => [
90090.123456, 90090.123456,
true, true,
'1d 1h 1m 30s 1235ms', '1d 1h 1m 30s 123ms',
], ],
'already set' => [ 'already set' => [
'1d 1h 1m 30s 1235ms', '1d 1h 1m 30s 1235ms',
@@ -143,6 +176,306 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* time seconds convert test
*
* @covers ::timeStringFormat
* @dataProvider intervalProvider
* @testdox timeStringFormat $input (microtime $flag) will be $expected [$_dataName]
*
* @param string|int|float $input
* @param bool $flag
* @param string $expected
* @return void
*/
public function testTimeStringFormat(string|int|float $input, bool $flag, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::timeStringFormat($input, $flag)
);
}
/**
* interval seconds convert
*
* @covers ::intervalStringFormat
* @dataProvider intervalProvider
* @testdox intervalStringFormat $input (microtime $show_micro) will be $expected [$_dataName]
*
* @param string|int|float $input
* @param bool $show_micro
* @param string $expected
* @return void
*/
public function testIntervalStringFormat(string|int|float $input, bool $show_micro, string $expected): void
{
// we skip string input, that is not allowed
if (is_string($input)) {
$this->assertTrue(true, 'Skip strings');
return;
}
// invalid values throw exception in default
if ($input == 999999999999999) {
$this->expectException(\LengthException::class);
}
// below is equal to timeStringFormat
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::intervalStringFormat(
$input,
show_microseconds: $show_micro,
show_only_days: true,
skip_zero: false,
skip_last_zero: false,
truncate_nanoseconds: true,
truncate_zero_seconds_if_microseconds: false
)
);
}
/**
* Undocumented function
*
* @return array
*/
public function intervalExtendedProvider(): array
{
return [
// A
'(60) default value' => [
[
'seconds' => 60,
],
'expected' => '1m',
'exception' => null
],
'(60) default value, skip_last_zero:false' => [
[
'seconds' => 60,
'skip_last_zero' => false,
],
'expected' => '1m 0s 0ms',
'exception' => null
],
// B
'(120.1) default value' => [
[
'seconds' => 120.1,
],
'expected' => '2m 100ms',
'exception' => null
],
'(120.1) default value, skip_zero:false' => [
[
'seconds' => 120.1,
'skip_zero' => false,
],
'expected' => '2m 0s 100ms',
'exception' => null
],
'(120.1) default value, skip_last_zero:false' => [
[
'seconds' => 120.1,
'skip_last_zero' => false,
],
'expected' => '2m 100ms',
'exception' => null
],
// C
'(3601) default value' => [
[
'seconds' => 3601,
],
'expected' => '1h 1s',
'exception' => null
],
'(3601) default value, skip_zero:false' => [
[
'seconds' => 3601,
'skip_zero' => false,
],
'expected' => '1h 0m 1s',
'exception' => null
],
'(3601) default value, skip_last_zero:false' => [
[
'seconds' => 3601,
'skip_last_zero' => false,
],
'expected' => '1h 1s 0ms',
'exception' => null
],
// TODO create unit tests for ALL edge cases
// CREATE abort tests, simple, all others are handled in exception tests
'exception: \UnexpectedValueException:1' => [
[
'seconds' => 99999999999999999999999
],
'expected' => null,
'exception' => [
'class' => \UnexpectedValueException::class,
'code' => 1,
],
]
];
}
/**
* test all options for interval conversion
*
* @covers ::intervalStringFormat
* @dataProvider intervalExtendedProvider
* @testdox intervalStringFormat $input will be $expected / $exception [$_dataName]
*
* @param array<string,null|int|float|bool> $parameter_list
* @param string $expected
* @param array<string,mixed> $exception
* @return void
*/
public function testExtendedIntervalStringFormat(
array $parameter_list,
?string $expected,
?array $exception
): void {
if ($expected === null && $exception === null) {
$this->assertFalse(true, 'Cannot have expected and exception null in test data');
}
$parameters = [];
foreach (
[
'seconds' => null,
'truncate_after' => '',
'natural_seperator' => false,
'name_space_seperator' => false,
'show_microseconds' => true,
'short_time_name' => true,
'skip_last_zero' => true,
'skip_zero' => true,
'show_only_days' => false,
'auto_fix_microseconds' => false,
'truncate_nanoseconds' => false,
'truncate_zero_seconds_if_microseconds' => true,
] as $param => $default
) {
if (empty($parameter_list[$param]) && $default === null) {
$this->assertFalse(true, 'Parameter ' . $param . ' is mandatory ');
} elseif (!isset($parameter_list[$param]) || $parameter_list[$param] === null) {
$parameters[] = $default;
} else {
$parameters[] = $parameter_list[$param];
}
}
if ($expected !== null) {
$this->assertEquals(
$expected,
call_user_func_array('CoreLibs\Combined\DateTime::intervalStringFormat', $parameters)
);
} else {
if (empty($exception['class']) || empty($exception['code'])) {
$this->assertFalse(true, 'Exception tests need Exception name and Code');
}
$this->expectException($exception['class']);
$this->expectExceptionCode($exception['code']);
// if we have a message, must be regex
if (!empty($exception['message'])) {
$this->expectExceptionMessageMatches($exception['message']);
}
call_user_func_array('CoreLibs\Combined\DateTime::intervalStringFormat', $parameters);
}
}
/**
* Undocumented function
*
* @return array<mixed>
*/
public function exceptionsIntervalProvider(): array
{
return [
'UnexpectedValueException: 1 A' => [
'seconds' => 99999999999999999999999,
'params' => [],
'exception' => \UnexpectedValueException::class,
'exception_message' => "/^Seconds value is invalid, too large or more than six decimals: /",
'excpetion_code' => 1,
],
'UnexpectedValueException: 1 B' => [
'seconds' => 123.1234567,
'params' => [],
'exception' => \UnexpectedValueException::class,
'exception_message' => "/^Seconds value is invalid, too large or more than six decimals: /",
'excpetion_code' => 1,
],
// exception 2 is very likely covered by exception 1
'LengthException: 3' => [
'seconds' => 999999999999999999,
'params' => [
'show_only_days',
],
'exception' => \LengthException::class,
'exception_message' => "/^Input seconds value is too large for days output: /",
'excpetion_code' => 3,
],
'UnexpectedValueException: 4' => [
'seconds' => 1234567,
'params' => [
'truncate_after'
],
'exception' => \UnexpectedValueException::class,
'exception_message' => "/^truncate_after has an invalid value: /",
'excpetion_code' => 4,
],
'UnexpectedValueException: 5' => [
'seconds' => 1234567,
'params' => [
'show_only_days:truncate_after'
],
'exception' => \UnexpectedValueException::class,
'exception_message' =>
"/^If show_only_days is turned on, the truncate_after cannot be years or months: /",
'excpetion_code' => 5,
]
];
}
/**
* Test all exceptions
*
* @covers ::intervalStringFormat
* @dataProvider exceptionsIntervalProvider
* @testdox intervalStringFormat: test Exceptions
*
* @param int|float $seconds
* @param array<string> $params
* @param string $exception
* @param string $exception_message
* @param int $excpetion_code
* @return void
*/
public function testExceptionsIntervalStringFormat(
int|float $seconds,
array $params,
string $exception,
string $exception_message,
int $excpetion_code,
): void {
$this->expectException($exception);
$this->expectExceptionMessageMatches($exception_message);
$this->expectExceptionCode($excpetion_code);
if (empty($params)) {
\CoreLibs\Combined\DateTime::intervalStringFormat($seconds);
} else {
if (in_array('show_only_days', $params)) {
\CoreLibs\Combined\DateTime::intervalStringFormat($seconds, show_only_days:true);
} elseif (in_array('truncate_after', $params)) {
\CoreLibs\Combined\DateTime::intervalStringFormat($seconds, truncate_after: 'v');
} elseif (in_array('show_only_days:truncate_after', $params)) {
\CoreLibs\Combined\DateTime::intervalStringFormat($seconds, show_only_days:true, truncate_after: 'y');
}
}
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -203,6 +536,25 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @covers ::stringToTime
* @dataProvider reverseIntervalProvider
* @testdox stringToTime $input will be $expected [$_dataName]
*
* @param string|int|float $input
* @param string|int|float $expected
* @return void
*/
public function testStringToTime($input, $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::stringToTime($input)
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -238,6 +590,25 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @covers ::checkDate
* @dataProvider dateProvider
* @testdox checkDate $input will be $expected [$_dataName]
*
* @param string $input
* @param bool $expected
* @return void
*/
public function testCheckDate(string $input, bool $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::checkDate($input)
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -297,6 +668,25 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @covers ::checkDateTime
* @dataProvider dateTimeProvider
* @testdox checkDateTime $input will be $expected [$_dataName]
*
* @param string $input
* @param bool $expected
* @return void
*/
public function testCheckDateTime(string $input, bool $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::checkDateTime($input)
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -371,6 +761,37 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @covers ::compareDate
* @dataProvider dateCompareProvider
* @testdox compareDate $input_a compared to $input_b will be $expected [$_dataName]
*
* @param string $input_a
* @param string $input_b
* @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void
*/
public function testCompareDate(
string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::compareDate($input_a, $input_b)
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -466,6 +887,37 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @covers ::compareDateTime
* @dataProvider dateTimeCompareProvider
* @testdox compareDateTime $input_a compared to $input_b will be $expected [$_dataName]
*
* @param string $input_a
* @param string $input_b
* @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void
*/
public function testCompareDateTime(
string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::compareDateTime($input_a, $input_b)
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -520,214 +972,6 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @return array
*/
public function dateRangeHasWeekendProvider(): array
{
return [
'no weekend' => [
'2023-07-03',
'2023-07-04',
false
],
'start weekend sat' => [
'2023-07-01',
'2023-07-04',
true
],
'start weekend sun' => [
'2023-07-02',
'2023-07-04',
true
],
'end weekend sat' => [
'2023-07-03',
'2023-07-08',
true
],
'end weekend sun' => [
'2023-07-03',
'2023-07-09',
true
],
'long period > 6 days' => [
'2023-07-03',
'2023-07-27',
true
]
];
}
/**
* date string convert test
*
* @covers ::dateStringFormat
* @dataProvider timestampProvider
* @testdox dateStringFormat $input (microtime $flag) will be $expected [$_dataName]
*
* @param int|float $input
* @param bool $flag
* @param string $expected
* @return void
*/
public function testDateStringFormat(
$input,
bool $flag_show_micro,
bool $flag_micro_as_float,
string $expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::dateStringFormat(
$input,
$flag_show_micro,
$flag_micro_as_float
)
);
}
/**
* interval convert test
*
* @covers ::timeStringFormat
* @dataProvider intervalProvider
* @testdox timeStringFormat $input (microtime $flag) will be $expected [$_dataName]
*
* @param int|float $input
* @param bool $flag
* @param string $expected
* @return void
*/
public function testTimeStringFormat($input, bool $flag, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::timeStringFormat($input, $flag)
);
}
/**
* Undocumented function
*
* @covers ::stringToTime
* @dataProvider reverseIntervalProvider
* @testdox stringToTime $input will be $expected [$_dataName]
*
* @param string|int|float $input
* @param string|int|float $expected
* @return void
*/
public function testStringToTime($input, $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::stringToTime($input)
);
}
/**
* Undocumented function
*
* @covers ::checkDate
* @dataProvider dateProvider
* @testdox checkDate $input will be $expected [$_dataName]
*
* @param string $input
* @param bool $expected
* @return void
*/
public function testCheckDate(string $input, bool $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::checkDate($input)
);
}
/**
* Undocumented function
*
* @covers ::checkDateTime
* @dataProvider dateTimeProvider
* @testdox checkDateTime $input will be $expected [$_dataName]
*
* @param string $input
* @param bool $expected
* @return void
*/
public function testCheckDateTime(string $input, bool $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::checkDateTime($input)
);
}
/**
* Undocumented function
*
* @covers ::compareDate
* @dataProvider dateCompareProvider
* @testdox compareDate $input_a compared to $input_b will be $expected [$_dataName]
*
* @param string $input_a
* @param string $input_b
* @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void
*/
public function testCompareDate(
string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::compareDate($input_a, $input_b)
);
}
/**
* Undocumented function
*
* @covers ::compareDateTime
* @dataProvider dateTimeCompareProvider
* @testdox compareDateTime $input_a compared to $input_b will be $expected [$_dataName]
*
* @param string $input_a
* @param string $input_b
* @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void
*/
public function testCompareDateTime(
string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::compareDateTime($input_a, $input_b)
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -906,6 +1150,47 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
); );
} }
/**
* Undocumented function
*
* @return array
*/
public function dateRangeHasWeekendProvider(): array
{
return [
'no weekend' => [
'2023-07-03',
'2023-07-04',
false
],
'start weekend sat' => [
'2023-07-01',
'2023-07-04',
true
],
'start weekend sun' => [
'2023-07-02',
'2023-07-04',
true
],
'end weekend sat' => [
'2023-07-03',
'2023-07-08',
true
],
'end weekend sun' => [
'2023-07-03',
'2023-07-09',
true
],
'long period > 6 days' => [
'2023-07-03',
'2023-07-27',
true
]
];
}
/** /**
* Undocumented function * Undocumented function
* *

View File

@@ -253,7 +253,8 @@ final class CoreLibsConvertByteTest extends TestCase
*/ */
public function testHumanReadableByteFormatException(int $flag): void public function testHumanReadableByteFormatException(int $flag): void
{ {
$this->expectException(\Exception::class); $this->expectException(\InvalidArgumentException::class);
$this->expectExceptionCode(1);
\CoreLibs\Convert\Byte::humanReadableByteFormat(12, $flag); \CoreLibs\Convert\Byte::humanReadableByteFormat(12, $flag);
} }
@@ -272,7 +273,8 @@ final class CoreLibsConvertByteTest extends TestCase
*/ */
public function testStringByteFormatException(int $flag): void public function testStringByteFormatException(int $flag): void
{ {
$this->expectException(\Exception::class); $this->expectException(\InvalidArgumentException::class);
$this->expectExceptionCode(1);
\CoreLibs\Convert\Byte::stringByteFormat(12, $flag); \CoreLibs\Convert\Byte::stringByteFormat(12, $flag);
} }
} }

View File

@@ -256,6 +256,80 @@ final class CoreLibsConvertStringsTest extends TestCase
$output $output
); );
} }
/**
* provider for testStripMultiplePathSlashes
*
* @return array<mixed>
*/
public function stripMultiplePathSlashesProvider(): array
{
return [
'no slahses' => [
'input' => 'string_abc',
'expected' => 'string_abc',
],
'one slash' => [
'input' => 'some/foo',
'expected' => 'some/foo',
],
'two slashes' => [
'input' => 'some//foo',
'expected' => 'some/foo',
],
'three slashes' => [
'input' => 'some///foo',
'expected' => 'some/foo',
],
'slashes in front' => [
'input' => '/foo',
'expected' => '/foo',
],
'two slashes in front' => [
'input' => '//foo',
'expected' => '/foo',
],
'thee slashes in front' => [
'input' => '///foo',
'expected' => '/foo',
],
'slashes in back' => [
'input' => 'foo/',
'expected' => 'foo/',
],
'two slashes in back' => [
'input' => 'foo//',
'expected' => 'foo/',
],
'thee slashes in back' => [
'input' => 'foo///',
'expected' => 'foo/',
],
'multiple slashes' => [
'input' => '/foo//bar///string/end_times',
'expected' => '/foo/bar/string/end_times',
]
];
}
/**
* test multiple slashes clean up
*
* @covers ::stripMultiplePathSlashes
* @dataProvider stripMultiplePathSlashesProvider
* @testdox stripMultiplePathSlashes $input will be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testStripMultiplePathSlashes(string $input, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Strings::stripMultiplePathSlashes($input)
);
}
} }
// __END__ // __END__

View File

@@ -232,7 +232,7 @@ final class CoreLibsDBIOTest extends TestCase
$this->assertEquals( $this->assertEquals(
$error, $error,
$last_error, $last_error,
'Assert query warning' 'Assert query error'
); );
return [$last_warning, $last_error]; return [$last_warning, $last_error];
} }
@@ -251,8 +251,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersion(): void public function testDbVersion(): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -276,8 +274,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersionNumeric(): void public function testDbVersionNumeric(): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -306,8 +302,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersionInfoParameters(): void public function testDbVersionInfoParameters(): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -365,8 +359,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersionInfo(string $parameter, string $expected): void public function testDbVersionInfo(string $parameter, string $expected): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -1592,8 +1584,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
bool $run_many_times = false bool $run_many_times = false
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -1832,8 +1822,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
string $insert_data string $insert_data
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -2002,8 +1990,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
string $insert_data string $insert_data
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3069,8 +3055,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
string $insert_data string $insert_data
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3465,7 +3449,7 @@ final class CoreLibsDBIOTest extends TestCase
$read_query, $read_query,
null, null,
null, null,
// // warning: 20
true, '20', '', true, '20', '',
// //
'result', '', '', 'result', '', '',
@@ -3482,6 +3466,31 @@ final class CoreLibsDBIOTest extends TestCase
'returning_id' => false, 'returning_id' => false,
], ],
], ],
// prepare with different statement name
'prepare query with same statement name, different query' => [
'double_error',
$read_query,
// primary key
null,
// arguments (none)
null,
// expected return false, warning: no, error: 26
false, '', '26',
// return expected, warning, error
'', '', '',
// dummy query for second prepare with wrong query
$read_query . ' WHERE uid = $3',
[],
//
$insert_query,
//
[
'pk_name' => '',
'count' => 0,
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
'returning_id' => false,
],
],
// insert wrong data count compared to needed (execute 23) // insert wrong data count compared to needed (execute 23)
'wrong parmeter count' => [ 'wrong parmeter count' => [
'wrong_param_count', 'wrong_param_count',
@@ -3554,8 +3563,6 @@ final class CoreLibsDBIOTest extends TestCase
string $insert_data, string $insert_data,
array $prepare_cursor, array $prepare_cursor,
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3575,6 +3582,9 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbPrepare($stm_name, $query) : $db->dbPrepare($stm_name, $query) :
$db->dbPrepare($stm_name, $query, $pk_name); $db->dbPrepare($stm_name, $query, $pk_name);
} }
if ($error_prepare == '26') {
$prepare_result = $db->dbPrepare($stm_name, $expected_data_query);
}
// if result type, or if forced bool // if result type, or if forced bool
if (is_string($expected_prepare) && $expected_prepare == 'result') { if (is_string($expected_prepare) && $expected_prepare == 'result') {
// if PHP or newer, must be Object PgSql\Result // if PHP or newer, must be Object PgSql\Result
@@ -3597,66 +3607,68 @@ final class CoreLibsDBIOTest extends TestCase
// for non fail prepare test exec // for non fail prepare test exec
// check test result // check test result
$execute_result = $query_data === null ? if (!$error_prepare) {
$db->dbExecute($stm_name) : $execute_result = $query_data === null ?
$db->dbExecute($stm_name, $query_data); $db->dbExecute($stm_name) :
if ($expected_execute == 'result') { $db->dbExecute($stm_name, $query_data);
// if PHP or newer, must be Object PgSql\Result if ($expected_execute == 'result') {
$this->assertIsObject( // if PHP or newer, must be Object PgSql\Result
$execute_result $this->assertIsObject(
); $execute_result
// also check that this is correct instance type );
$this->assertInstanceOf( // also check that this is correct instance type
'PgSql\Result', $this->assertInstanceOf(
$execute_result 'PgSql\Result',
); $execute_result
// if this is an select use dbFetchArray to get data and test );
} else { // if this is an select use dbFetchArray to get data and test
$this->assertEquals( } else {
$expected_execute, $this->assertEquals(
$execute_result $expected_execute,
); $execute_result
} );
// error/warning check }
$this->subAssertErrorTest($db, $warning_execute, $error_execute); // error/warning check
// now check test result if expected return is result $this->subAssertErrorTest($db, $warning_execute, $error_execute);
if ( // now check test result if expected return is result
$expected_execute == 'result' && if (
!empty($expected_data_query) $expected_execute == 'result' &&
) { !empty($expected_data_query)
// $expected_data_query ) {
// $expected_data // $expected_data_query
$rows = $db->dbReturnArray($expected_data_query); // $expected_data
$this->assertEquals( $rows = $db->dbReturnArray($expected_data_query);
$expected_data, $this->assertEquals(
$rows $expected_data,
); $rows
} );
if ( }
$expected_execute == 'result' && if (
$execute_result !== false && $expected_execute == 'result' &&
empty($expected_data_query) && $execute_result !== false &&
count($expected_data) empty($expected_data_query) &&
) { count($expected_data)
// compare previously read data to compare data ) {
$compare_data = []; // compare previously read data to compare data
// read in the query data $compare_data = [];
while (is_array($row = $db->dbFetchArray($execute_result, true))) { // read in the query data
$compare_data[] = $row; while (is_array($row = $db->dbFetchArray($execute_result, true))) {
$compare_data[] = $row;
}
$this->assertEquals(
$expected_data,
$compare_data
);
} }
$this->assertEquals(
$expected_data,
$compare_data
);
}
// check dbGetPrepareCursorValue // check dbGetPrepareCursorValue
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) { foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
$this->assertEquals( $this->assertEquals(
$prepare_cursor[$key], $prepare_cursor[$key],
$db->dbGetPrepareCursorValue($stm_name, $key), $db->dbGetPrepareCursorValue($stm_name, $key),
'Prepared cursor: ' . $key . ': failed assertion' 'Prepared cursor: ' . $key . ': failed assertion'
); );
}
} }
// reset all data // reset all data
@@ -3844,8 +3856,6 @@ final class CoreLibsDBIOTest extends TestCase
string $expected_get_var, string $expected_get_var,
string $expected_get_db string $expected_get_db
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
@@ -3907,9 +3917,13 @@ final class CoreLibsDBIOTest extends TestCase
'id' => '51', 'id' => '51',
'error' => 'Max query call needs to be set to at least 1', 'error' => 'Max query call needs to be set to at least 1',
// run:: can be +1 if called in set and not direct // run:: can be +1 if called in set and not direct
'source' => "/^include::main::run::run::run::run::run::run::(run::)?runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall$/", // 'main::run::run::run::run::run::run::run::runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall
'source' => "/^(include::)?main::(run::)+runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall$/",
'pg_error' => '', 'pg_error' => '',
'msg' => '', 'message' => '',
'context' => [
'max_calls' => 0
]
] ]
], ],
'trigger warning' => [ 'trigger warning' => [
@@ -3942,8 +3956,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error_id, string $error_id,
array $expected_history array $expected_history
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3969,7 +3981,7 @@ final class CoreLibsDBIOTest extends TestCase
foreach ($expected_history as $key => $value) { foreach ($expected_history as $key => $value) {
// check if starts with / because this is regex (timestamp) // check if starts with / because this is regex (timestamp)
// if (substr($expected_2, 0, 1) == '/) { // if (substr($expected_2, 0, 1) == '/) {
if (strpos($value, '/') === 0) { if (!is_array($value) && strpos($value, '/') === 0) {
// this is regex // this is regex
$this->assertMatchesRegularExpression( $this->assertMatchesRegularExpression(
$value, $value,
@@ -4057,8 +4069,6 @@ final class CoreLibsDBIOTest extends TestCase
bool $expected_set_flag, bool $expected_set_flag,
string $expected_get_encoding string $expected_get_encoding
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
@@ -4140,8 +4150,6 @@ final class CoreLibsDBIOTest extends TestCase
?string $encoding_php, ?string $encoding_php,
string $text string $text
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
@@ -4271,8 +4279,6 @@ final class CoreLibsDBIOTest extends TestCase
string $table, string $table,
string $primary_key string $primary_key
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -4329,7 +4335,7 @@ final class CoreLibsDBIOTest extends TestCase
// NOTE if there are different INSERTS before the primary keys // NOTE if there are different INSERTS before the primary keys
// will not match anymore. Must be updated by hand // will not match anymore. Must be updated by hand
// IMPORTANT: if this is stand alone the primary key will not match and fail // IMPORTANT: if this is stand alone the primary key will not match and fail
$table_with_primary_key_id = 68; $table_with_primary_key_id = 70;
// 0: query + returning // 0: query + returning
// 1: params // 1: params
// 1: pk name for db exec // 1: pk name for db exec
@@ -4529,8 +4535,6 @@ final class CoreLibsDBIOTest extends TestCase
array|string|int|null $expected_ret_ext, array|string|int|null $expected_ret_ext,
array $expected_ret_arr array $expected_ret_arr
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -4874,8 +4878,6 @@ final class CoreLibsDBIOTest extends TestCase
array $expected_col_names, array $expected_col_names,
array $expected_col_types array $expected_col_types
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -5029,6 +5031,147 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbClose(); $db->dbClose();
} }
// query placeholder convert
public function queryPlaceholderReplaceProvider(): array
{
// WHERE row_varchar = $1
return [
'select, no change' => [
'query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
SQL,
'params' => [],
'found' => 0,
'expected_query' => '',
'expected_params' => [],
],
'select, params ?' => [
'query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = ?
SQL,
'params' => ['string a'],
'found' => 1,
'expected_query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = $1
SQL,
'expected_params' => ['string a'],
],
'select, params :' => [
'query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = :row_varchar
SQL,
'params' => [':row_varchar' => 'string a'],
'found' => 1,
'expected_query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = $1
SQL,
'expected_params' => ['string a'],
]
];
}
/**
* test query string with placeholders convert
*
* @dataProvider queryPlaceholderReplaceProvider
* @testdox Query replacement test [$_dataName]
*
* @param string $query
* @param array $params
* @param string $expected_query
* @param array $expected_params
* @return void
*/
public function testQueryPlaceholderReplace(
string $query,
array $params,
int $expected_found,
string $expected_query,
array $expected_params
): void {
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$db->dbSetConvertPlaceholder(true);
//
if ($db->dbCheckQueryForSelect($query)) {
$res = $db->dbReturnRowParams($query, $params);
$converted = $db->dbGetPlaceholderConverted();
} else {
$db->dbExecParams($query, $params);
$converted = $db->dbGetPlaceholderConverted();
}
$this->assertEquals(
$expected_found,
$converted['found'],
'Found not equal'
);
$this->assertEquals(
$expected_query,
$converted['query'],
'Query not equal'
);
$this->assertEquals(
$expected_params,
$converted['params'],
'Params not equal'
);
}
/**
* test exception for placeholder convert
* -> internally converted to error
*
* @testdox Query Replace error tests
*
* @return void
*/
public function testQueryPlaceholderReplaceException(): void
{
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$db->dbSetConvertPlaceholder(true);
$db->dbExecParams(
<<<SQL
SELECT foo FROM bar
WHERE a = ? and b = :bname
SQL,
['a', 'b']
);
$this->assertEquals(
200,
$db->dbGetLastError()
);
// catch unset, for :names
$db->dbExecParams(
<<<SQL
SELECT foo FROM bar
WHERE a = :aname and b = :bname
SQL,
[':foo' => 'a', ':bname' => 'b']
);
$this->assertEquals(
210,
$db->dbGetLastError()
);
// TODO: other way around for to pdo
}
// TODO implement below checks // TODO implement below checks
// - complex write sets // - complex write sets
// dbWriteData, dbWriteDataExt // dbWriteData, dbWriteDataExt
@@ -5157,8 +5300,6 @@ final class CoreLibsDBIOTest extends TestCase
string $warning_final, string $warning_final,
string $error_final string $error_final
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log

View File

@@ -460,8 +460,8 @@ final class CoreLibsDebugSupportTest extends TestCase
* Undocumented function * Undocumented function
* *
* @cover ::getCallerFileLine * @cover ::getCallerFileLine
* @testWith ["vendor/phpunit/phpunit/src/Framework/TestCase.php:"] * @testWith ["vendor/phpunit/phpunit/src/Framework/TestCase.php:6434","phar:///home/clemens/.phive/phars/phpunit-9.6.13.phar/phpunit/Framework/TestCase.php:6434"]
* @testdox getCallerFileLine check based on regex /[\w\-\/]/vendor/phpunit/phpunit/src/Framework/TestCase.php:\d+ [$_dataName] * @testdox getCallerFileLine check based on regex .../Framework/TestCase.php:\d+ [$_dataName]
* *
* @param string $expected * @param string $expected
* @return void * @return void
@@ -469,7 +469,14 @@ final class CoreLibsDebugSupportTest extends TestCase
public function testGetCallerFileLine(): void public function testGetCallerFileLine(): void
{ {
// regex prefix with path "/../" and then fixed vendor + \d+ // regex prefix with path "/../" and then fixed vendor + \d+
$regex = "/^\/[\w\-\/]+\/vendor\/phpunit\/phpunit\/src\/Framework\/TestCase.php:\d+$/"; // or phar start if phiev installed
// phar:///home/clemens/.phive/phars/phpunit-9.6.13.phar/phpunit/Framework/TestCase.php
$regex = "/^("
. "\/.*\/vendor\/phpunit\/phpunit\/src"
. "|"
. "phar:\/\/\/.*\.phive\/phars\/phpunit-\d+\.\d+\.\d+\.phar\/phpunit"
. ")"
. "\/Framework\/TestCase.php:\d+$/";
$this->assertMatchesRegularExpression( $this->assertMatchesRegularExpression(
$regex, $regex,
Support::getCallerFileLine() Support::getCallerFileLine()
@@ -506,7 +513,7 @@ final class CoreLibsDebugSupportTest extends TestCase
public function testGetCallerMethodList(array $expected): void public function testGetCallerMethodList(array $expected): void
{ {
$compare = Support::getCallerMethodList(); $compare = Support::getCallerMethodList();
// 10: legact // 10: legacy
// 11: direct // 11: direct
// 12: full call // 12: full call
switch (count($compare)) { switch (count($compare)) {
@@ -514,27 +521,30 @@ final class CoreLibsDebugSupportTest extends TestCase
// add nothing // add nothing
$this->assertEquals( $this->assertEquals(
$expected, $expected,
Support::getCallerMethodList(), $compare,
'assert expected 10' 'assert expected 10'
); );
break; break;
case 11: case 11:
// add one "run" before "runBare" if ($compare[0] == 'include') {
// array_splice( // add include at first
// $expected, array_splice(
// 7, $expected,
// 0, 0,
// ['run'] 0,
// ); ['include']
array_splice( );
$expected, } else {
0, array_splice(
0, $expected,
['include'] 6,
); 0,
['run']
);
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
Support::getCallerMethodList(), $compare,
'assert expected 11' 'assert expected 11'
); );
break; break;
@@ -554,13 +564,38 @@ final class CoreLibsDebugSupportTest extends TestCase
); );
$this->assertEquals( $this->assertEquals(
$expected, $expected,
Support::getCallerMethodList(), $compare,
'assert expected 12' 'assert expected 12'
); );
break; break;
} }
} }
/**
* Undocumented function
*
* @cover ::getCallStack
* @testdox getCallStack check if it returns data [$_dataName]
*
* @return void
*/
public function testGetCallStack(): void
{
$call_stack = Support::getCallStack();
// print "Get CALL: " . print_r(Support::getCallStack(), true) . "\n";
if ($call_stack < 8) {
$this->assertFalse(true, 'getCallStack too low: 8');
} else {
$this->assertTrue(true, 'getCallSteck ok');
}
// just test top entry
$first = array_shift($call_stack);
$this->assertStringEndsWith(
':tests\CoreLibsDebugSupportTest->testGetCallStack',
$first,
);
}
/** /**
* test the lowest one (one above base) * test the lowest one (one above base)
* *

View File

@@ -99,7 +99,7 @@ final class CoreLibsGetSystemTest extends TestCase
1 => 'phpunit', 1 => 'phpunit',
2 => 'phpunit', 2 => 'phpunit',
// NOTE: this can change, so it is a regex check // NOTE: this can change, so it is a regex check
3 => "/^(\/?.*\/?)?vendor\/bin\/phpunit$/", 3 => "/^(\/?.*\/?)?(vendor\/bin|tools)\/phpunit$/",
], ],
'some path with extension' => [ 'some path with extension' => [
0 => '/some/path/to/file.txt', 0 => '/some/path/to/file.txt',

View File

@@ -44,6 +44,11 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'str' => 'INFO', 'str' => 'INFO',
'expected' => 'info', 'expected' => 'info',
], ],
'notice' => [
'level' => 'notice',
'str' => 'NOTICE',
'expected' => 'notice',
],
'warn' => [ 'warn' => [
'level' => 'warn', 'level' => 'warn',
'str' => 'WARN', 'str' => 'WARN',
@@ -91,9 +96,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
public function testErrorMessageLevelOk(string $level, string $str, string $expected): void public function testErrorMessageLevelOk(string $level, string $str, string $expected): void
{ {
$log = new \CoreLibs\Logging\Logging([ $log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages', 'log_file_id' => 'testErrorMessagesLevelOk',
'log_folder' => self::LOG_FOLDER, 'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug, 'log_level' => Level::Error,
]); ]);
$em = new \CoreLibs\Logging\ErrorMessage($log); $em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setMessage( $em->setMessage(
@@ -106,6 +111,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'str' => $str, 'str' => $str,
'id' => '', 'id' => '',
'target' => '', 'target' => '',
'target_style' => '',
'highlight' => [], 'highlight' => [],
], ],
$em->getLastErrorMsg() $em->getLastErrorMsg()
@@ -122,9 +128,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
public function testErrorMessageOk(): void public function testErrorMessageOk(): void
{ {
$log = new \CoreLibs\Logging\Logging([ $log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages', 'log_file_id' => 'testErrorMessagesOk',
'log_folder' => self::LOG_FOLDER, 'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug 'log_level' => Level::Error
]); ]);
$em = new \CoreLibs\Logging\ErrorMessage($log); $em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setErrorMsg( $em->setErrorMsg(
@@ -139,6 +145,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'info', 'level' => 'info',
'str' => 'INFO MESSAGE', 'str' => 'INFO MESSAGE',
'target' => '', 'target' => '',
'target_style' => '',
'highlight' => [], 'highlight' => [],
], ],
$em->getLastErrorMsg() $em->getLastErrorMsg()
@@ -154,6 +161,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'info', 'level' => 'info',
'str' => 'INFO MESSAGE', 'str' => 'INFO MESSAGE',
'target' => '', 'target' => '',
'target_style' => '',
'highlight' => [], 'highlight' => [],
] ]
], ],
@@ -171,6 +179,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'error', 'level' => 'error',
'str' => 'ERROR MESSAGE', 'str' => 'ERROR MESSAGE',
'target' => '', 'target' => '',
'target_style' => '',
'highlight' => [], 'highlight' => [],
], ],
$em->getLastErrorMsg() $em->getLastErrorMsg()
@@ -186,6 +195,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'info', 'level' => 'info',
'str' => 'INFO MESSAGE', 'str' => 'INFO MESSAGE',
'target' => '', 'target' => '',
'target_style' => '',
'highlight' => [], 'highlight' => [],
], ],
[ [
@@ -193,6 +203,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'error', 'level' => 'error',
'str' => 'ERROR MESSAGE', 'str' => 'ERROR MESSAGE',
'target' => '', 'target' => '',
'target_style' => '',
'highlight' => [], 'highlight' => [],
] ]
], ],
@@ -200,14 +211,60 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
); );
} }
/**
* Undocumented function
*
* @return array
*/
public function providerErrorMessageLog(): array public function providerErrorMessageLog(): array
{ {
return [ return [
'error, not logged' => [
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'message' => null,
'log_error' => null,
'expected' => '<ERROR> ERROR MESSAGE',
],
'error, logged' => [
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'message' => null,
'log_error' => true,
'expected' => '<ERROR> ERROR MESSAGE',
],
'error, logged, message' => [
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'message' => 'OTHER ERROR MESSAGE',
'log_error' => true,
'expected' => '<ERROR> OTHER ERROR MESSAGE',
],
'notice' => [
'id' => '100',
'level' => 'notice',
'str' => 'NOTICE MESSAGE',
'message' => null,
'log_error' => null,
'expected' => '<NOTICE> NOTICE MESSAGE',
],
'notice, message' => [
'id' => '100',
'level' => 'notice',
'str' => 'NOTICE MESSAGE',
'message' => 'OTHER NOTICE MESSAGE',
'log_error' => null,
'expected' => '<NOTICE> OTHER NOTICE MESSAGE',
],
'crash' => [ 'crash' => [
'id' => '300', 'id' => '300',
'level' => 'crash', 'level' => 'crash',
'str' => 'CRASH MESSAGE', 'str' => 'CRASH MESSAGE',
'message' => null, 'message' => null,
'log_error' => null,
'expected' => '<ALERT> CRASH MESSAGE', 'expected' => '<ALERT> CRASH MESSAGE',
], ],
'crash, message' => [ 'crash, message' => [
@@ -215,6 +272,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'crash', 'level' => 'crash',
'str' => 'CRASH MESSAGE', 'str' => 'CRASH MESSAGE',
'message' => 'OTHER CRASH MESSAGE', 'message' => 'OTHER CRASH MESSAGE',
'log_error' => null,
'expected' => '<ALERT> OTHER CRASH MESSAGE', 'expected' => '<ALERT> OTHER CRASH MESSAGE',
], ],
'abort' => [ 'abort' => [
@@ -222,6 +280,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'abort', 'level' => 'abort',
'str' => 'ABORT MESSAGE', 'str' => 'ABORT MESSAGE',
'message' => null, 'message' => null,
'log_error' => null,
'expected' => '<CRITICAL> ABORT MESSAGE', 'expected' => '<CRITICAL> ABORT MESSAGE',
], ],
'abort, message' => [ 'abort, message' => [
@@ -229,6 +288,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'abort', 'level' => 'abort',
'str' => 'ABORT MESSAGE', 'str' => 'ABORT MESSAGE',
'message' => 'OTHER ABORT MESSAGE', 'message' => 'OTHER ABORT MESSAGE',
'log_error' => null,
'expected' => '<CRITICAL> OTHER ABORT MESSAGE', 'expected' => '<CRITICAL> OTHER ABORT MESSAGE',
], ],
'unknown' => [ 'unknown' => [
@@ -236,6 +296,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'wrong level', 'level' => 'wrong level',
'str' => 'WRONG LEVEL MESSAGE', 'str' => 'WRONG LEVEL MESSAGE',
'message' => null, 'message' => null,
'log_error' => null,
'expected' => '<EMERGENCY> WRONG LEVEL MESSAGE', 'expected' => '<EMERGENCY> WRONG LEVEL MESSAGE',
], ],
'unknown, message' => [ 'unknown, message' => [
@@ -243,6 +304,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'level' => 'wrong level', 'level' => 'wrong level',
'str' => 'WRONG LEVEL MESSAGE', 'str' => 'WRONG LEVEL MESSAGE',
'message' => 'OTHER WRONG LEVEL MESSAGE', 'message' => 'OTHER WRONG LEVEL MESSAGE',
'log_error' => null,
'expected' => '<EMERGENCY> OTHER WRONG LEVEL MESSAGE', 'expected' => '<EMERGENCY> OTHER WRONG LEVEL MESSAGE',
], ],
]; ];
@@ -252,14 +314,82 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
* Undocumented function * Undocumented function
* *
* @dataProvider providerErrorMessageLog * @dataProvider providerErrorMessageLog
* @testdox Test Log writing [$_dataName] * @testdox Test Log writing with log level Error [$_dataName]
* *
* @param string $id
* @param string $level
* @param string $str
* @param string|null $message
* @param bool|null $log_error
* @param string $expected
* @return void * @return void
*/ */
public function testErrorMessageLog(string $id, string $level, string $str, ?string $message, string $expected) public function testErrorMessageLogErrorLevel(
{ string $id,
string $level,
string $str,
?string $message,
?bool $log_error,
string $expected
): void {
$log = new \CoreLibs\Logging\Logging([ $log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages', 'log_file_id' => 'testErrorMessagesLogError',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Notice,
'log_per_run' => true
]);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setErrorMsg(
$id,
$level,
$str,
message: $message,
log_error: $log_error
);
$file_content = '';
if (is_file($log->getLogFolder() . $log->getLogFile())) {
$file_content = file_get_contents(
$log->getLogFolder() . $log->getLogFile()
) ?: '';
}
// if error, if null or false, it will not be logged
if ($level == 'error' && ($log_error === null || $log_error === false)) {
$this->assertStringNotContainsString(
$expected,
$file_content
);
} else {
$this->assertStringContainsString(
$expected,
$file_content
);
}
}
/**
* Undocumented function
*
* @dataProvider providerErrorMessageLog
* @testdox Test Log writing with log Level Debug [$_dataName]
*
* @param string $id
* @param string $level
* @param string $str
* @param string|null $message
* @param bool|null $log_error
* @param string $expected
* @return void
*/
public function testErrorMessageLogErrorDebug(
string $id,
string $level,
string $str,
?string $message,
?bool $log_error,
string $expected
): void {
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessagesLogDebug',
'log_folder' => self::LOG_FOLDER, 'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug, 'log_level' => Level::Debug,
'log_per_run' => true 'log_per_run' => true
@@ -269,14 +399,104 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
$id, $id,
$level, $level,
$str, $str,
message: $message message: $message,
log_error: $log_error
); );
$file_content = file_get_contents( $file_content = '';
$log->getLogFolder() . $log->getLogFile() if (is_file($log->getLogFolder() . $log->getLogFile())) {
) ?: ''; $file_content = file_get_contents(
$this->assertStringContainsString( $log->getLogFolder() . $log->getLogFile()
$expected, ) ?: '';
$file_content }
// if error, and log is debug level, only explicit false are not logged
if ($level == 'error' && $log_error === false) {
$this->assertStringNotContainsString(
$expected,
$file_content
);
} else {
$this->assertStringContainsString(
$expected,
$file_content
);
}
}
/**
* Undocumented function
*
* @testdox Test jump target set and reporting
*
* @return void
*/
public function testJumpTarget(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessagesLogDebug',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug,
'log_per_run' => true
]);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setJumpTarget(
'target-f',
'Target text'
);
$this->assertEquals(
[
['target' => 'target-f', 'info' => 'Target text', 'level' => 'error']
],
$em->getJumpTarget()
);
// set same target, keep as before
$em->setJumpTarget(
'target-f',
'Other text'
);
$this->assertEquals(
[
['target' => 'target-f', 'info' => 'Target text', 'level' => 'error']
],
$em->getJumpTarget()
);
// add new now two messages
$em->setJumpTarget(
'target-s',
'More text'
);
$this->assertEquals(
[
['target' => 'target-f', 'info' => 'Target text', 'level' => 'error'],
['target' => 'target-s', 'info' => 'More text', 'level' => 'error'],
],
$em->getJumpTarget()
);
// add empty info
$em->setJumpTarget(
'target-e',
''
);
$this->assertEquals(
[
['target' => 'target-f', 'info' => 'Target text', 'level' => 'error'],
['target' => 'target-s', 'info' => 'More text', 'level' => 'error'],
['target' => 'target-e', 'info' => 'Jump to: target-e', 'level' => 'error'],
],
$em->getJumpTarget()
);
// add through message
$em->setErrorMsg('E-101', 'abort', 'Abort message', jump_target:[
'target' => 'abort-target',
'info' => 'Abort error'
]);
$this->assertEquals(
[
['target' => 'target-f', 'info' => 'Target text', 'level' => 'error'],
['target' => 'target-s', 'info' => 'More text', 'level' => 'error'],
['target' => 'target-e', 'info' => 'Jump to: target-e', 'level' => 'error'],
['target' => 'abort-target', 'info' => 'Abort error', 'level' => 'abort'],
],
$em->getJumpTarget()
); );
} }
} }

View File

@@ -20,7 +20,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR; private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port . "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
. "\[[\w\-\.\/]+:\d+\]\s{1}" // folder/file . "\[(phar:\/\/)?[\w\-\.\/]+:\d+\]\s{1}" // folder/file [note phar:// is for phpunit]
. "\[\w+\]\s{1}" // run id . "\[\w+\]\s{1}" // run id
. "{[\w\\\\]+((::|->)\w+)?}\s{1}"; // class . "{[\w\\\\]+((::|->)\w+)?}\s{1}"; // class

View File

@@ -16,17 +16,89 @@ final class CoreLibsOutputImageTest extends TestCase
/** /**
* Undocumented function * Undocumented function
* *
* @testdox Output\Image Class tests * @covers ::createThumbnail
* @testdox createThumbnail checks
* *
* @return void * @return void
*/ */
public function testOutputImage() public function testCreateThumbnail(): void
{ {
// $this->assertTrue(true, 'Output Image Tests not implemented'); // CONVERT does not exist
$this->markTestIncomplete( $this->expectException(\RuntimeException::class);
'Output\Image Tests have not yet been implemented' \CoreLibs\Output\Image::createThumbnail('do_not_exist.png', 200, 200);
// set convert
$paths = [
'/bin',
'/usr/bin',
'/usr/local/bin',
];
// find convert
foreach ($paths as $path) {
if (
file_exists($path . DIRECTORY_SEPARATOR . 'convert') &&
is_file($path . DIRECTORY_SEPARATOR . 'convert')
) {
// image magick convert location
define('CONVERT', $path . DIRECTORY_SEPARATOR . 'convert');
break;
}
}
unset($paths);
// cannot set dummy file
$this->expectException(\Exception::class);
\CoreLibs\Output\Image::createThumbnail('do_not_exist.png', 200, 200);
}
/**
* Undocumented function
*
* @covers ::createThumbnailSimple
* @testdox createThumbnailSimple checks
*
* @return void
*/
public function testCreateThumbnailSimple(): void
{
// file does not exist
$this->expectException(\UnexpectedValueException::class);
\CoreLibs\Output\Image::createThumbnailSimple(
'do_not_exist.png',
200,
200,
cache_folder: '/tmp/',
web_folder: '/tmp/'
); );
// $this->markTestSkipped('No implementation for Output\Image at the moment'); // cache folder is not dir
$this->expectException(\UnexpectedValueException::class);
\CoreLibs\Output\Image::createThumbnailSimple(
'do_not_exist.png',
200,
200,
cache_folder: '/foo/bar/',
web_folder: '/tmp/'
);
// target cache folder is not writeable
// RuntimeException: imagecreatetruecolor failed
// RuntimeException: imagecolorallocatealpha failed
}
/**
* Undocumented function
*
* @covers ::correctImageOrientation
* @testdox correctImageOrientation checks
*
* @return void
*/
public function testCorrectImageOrientation(): void
{
// test file does not exist
$this->expectException(\UnexpectedValueException::class);
\CoreLibs\Output\Image::correctImageOrientation('do_not_exist.png');
// test folder not writeable
// test exit_read_data not present (how)?
// test image rotate
} }
} }

View File

@@ -0,0 +1,15 @@
<?php
$set = 0;
foreach (['/../../www', '/../www', '/../..', '/..', '/../../src', '/../src'] as $src) {
if (is_file(dirname(__DIR__) . $src . '/vendor/autoload.php')) {
require dirname(__DIR__) . $src . '/vendor/autoload.php';
$set = 1;
break;
}
}
if (!$set) {
die("Cannot find /vendor/autoload.php in reference to: " . dirname(__DIR__));
}
// __END__

1
tools/phan Symbolic link
View File

@@ -0,0 +1 @@
/home/clemens/.phive/phars/phan-5.4.3.phar

1
tools/phpcbf Symbolic link
View File

@@ -0,0 +1 @@
/home/clemens/.phive/phars/phpcbf-3.9.0.phar

1
tools/phpcs Symbolic link
View File

@@ -0,0 +1 @@
/home/clemens/.phive/phars/phpcs-3.9.0.phar

1
tools/phpstan Symbolic link
View File

@@ -0,0 +1 @@
/home/clemens/.phive/phars/phpstan-1.10.63.phar

1
tools/phpunit Symbolic link
View File

@@ -0,0 +1 @@
/home/clemens/.phive/phars/phpunit-9.6.17.phar

1
tools/psalm Symbolic link
View File

@@ -0,0 +1 @@
/home/clemens/.phive/phars/psalm-5.23.1.phar