Compare commits

...

36 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
44 changed files with 3180 additions and 1075 deletions

View File

@@ -360,10 +360,7 @@ return [
'directory_list' => [
'src',
'vendor/egrajp/smarty-extended/src',
'vendor/phan/phan/src/Phan',
'vendor/phpunit/phpunit/src',
'vendor/psr/log/src',
'vendor/vimeo/psalm/src/Psalm',
'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
`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"
},
"require-dev": {
"phpstan/phpstan": "^1.10",
"phan/phan": "v5.x-dev",
"phpunit/phpunit": "^9",
"egrajp/smarty-extended": "^4.3",
"vimeo/psalm": "^5.0@dev",
"gullevek/dotenv": "dev-master"
},
"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"
colors="true"
verbose="true"
bootstrap="test/phpunit/bootstrap.php"
>
</phpunit>

View File

@@ -1 +1 @@
9.7.4
9.12.0

View File

@@ -44,7 +44,7 @@ class EditBase
* construct form generator
*
* 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\Language\L10n $l10n l10n language class, null auto set
* @param \CoreLibs\ACL\Login $login login class for ACL settings
@@ -154,7 +154,7 @@ class EditBase
$q = "UPDATE " . $table_name
. " SET order_number = " . $row_data_order[$i]
. " WHERE " . $table_name . "_id = " . $row_data_id[$i];
$q = $this->form->dbExec($q);
$q = $this->form->dba->dbExec($q);
}
} // for all article ids ...
} // if write
@@ -173,7 +173,7 @@ class EditBase
$options_name = [];
$options_selected = [];
// DB read data for menu
while (is_array($res = $this->form->dbReturn($q))) {
while (is_array($res = $this->form->dba->dbReturn($q))) {
$row_data[] = [
"id" => $res[$table_name . "_id"],
"name" => $res["name"],
@@ -431,9 +431,9 @@ class EditBase
$elements[] = $this->form->formCreateElement('template');
break;
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";
$this->form->dbExec($q);
$this->form->dba->dbExec($q);
// gets all files in the current dir and dirs given ending with .php
$folders = ['../admin/', '../frontend/'];
$files = ['*.php'];
@@ -461,16 +461,16 @@ class EditBase
if ($t_q) {
$t_q .= ', ';
}
$t_q .= "('" . $this->form->dbEscapeString($pathinfo['dirname']) . "', '"
. $this->form->dbEscapeString($pathinfo['basename']) . "')";
$t_q .= "('" . $this->form->dba->dbEscapeString($pathinfo['dirname']) . "', '"
. $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');
} else {
// show file menu
// just show name of file ...
$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
$elements[] = $this->form->formCreateElement('hostname');
$elements[] = $this->form->formCreateElement('name');

View File

@@ -51,6 +51,23 @@ class File
// return lines in file
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__

View File

@@ -236,6 +236,54 @@ class ArrayHandler
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
* just glues things together

View File

@@ -108,7 +108,12 @@ class DateTime
if (preg_match("/(h|m|s|ms)/", (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
$negative = false;
if ((int)$timestamp < 0) {
@@ -120,6 +125,10 @@ class DateTime
$time_string = '';
// if timestamp is zero, return zero string
if ($timestamp == 0) {
// if no seconds and we have no microseconds either, show no micro seconds
if ($ms == 0) {
$ms = null;
}
$time_string = '0s';
} else {
for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) {
@@ -133,11 +142,8 @@ class DateTime
}
// only add ms if we have an ms value
if ($ms !== null) {
// if we have ms and it has leading zeros, remove them, but only if it is nut just 0
$ms = preg_replace("/^0+(\d+)$/", '${1}', $ms);
if (!is_string($ms) || empty($ms)) {
$ms = '0';
}
// prefix the milliseoncds with 0. and round it max 3 digits and then convert to int
$ms = round((float)('0.' . $ms), 3) * 1000;
// add ms if there
if ($show_micro) {
$time_string .= ' ' . $ms . 'ms';
@@ -151,6 +157,240 @@ class DateTime
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
* xd xh xm xs xms to a timestamp.microtime format
@@ -435,9 +675,9 @@ class DateTime
foreach ($period as $dt) {
$curr = $dt->format('D');
if ($curr == 'Sat' || $curr == 'Sun') {
$days[2] ++;
$days[2]++;
} else {
$days[1] ++;
$days[1]++;
}
}
if ($return_named === true) {

View File

@@ -37,7 +37,7 @@ class Byte
* BYTE_FORMAT_ADJUST: sprintf adjusted two 2 decimals
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @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
{
@@ -63,7 +63,7 @@ class Byte
$si = false;
}
if ($flags > 7) {
throw new \Exception("Invalid flags parameter: $flags", 1);
throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
}
// si or normal
@@ -119,7 +119,7 @@ class Byte
* @param int $flags bitwise flag with use space turned on
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @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
{
@@ -130,7 +130,7 @@ class Byte
$si = false;
}
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 = [];

View File

@@ -118,6 +118,22 @@ class Strings
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__

View File

@@ -39,13 +39,13 @@ class ArrayIO extends \CoreLibs\DB\IO
{
// main calss variables
/** @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 */
public string $table_name; // the table_name
private string $table_name; // the table_name
/** @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 */
public int|string|null $pk_id; // the PK id
private int|string|null $pk_id; // the PK id
// security values
/** @var int base acl for current page */
private int $base_acl_level = 0;
@@ -55,7 +55,7 @@ class ArrayIO extends \CoreLibs\DB\IO
* primary key name automatically (from array)
*
* 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 string $table_name table name string
* @param \CoreLibs\Logging\Logging $log Logging class
@@ -74,24 +74,21 @@ class ArrayIO extends \CoreLibs\DB\IO
// instance db_io class
parent::__construct($db_config, $log);
// 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['1022'] = 'Could not run Array Query';
$this->table_array = $table_array;
$this->table_name = $table_name;
// error abort if no table array or no table name
if (empty($table_array) || empty($table_name)) {
$this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing');
throw new \RuntimeException('MAJOR ERROR: Core settings missing', 1999);
}
$this->setTableArray($table_array);
$this->setTableName($table_name);
// set primary key for given table_array
foreach ($this->table_array as $key => $value) {
if (!empty($value['pk'])) {
$this->pk_name = $key;
if (empty($value['pk'])) {
continue;
}
$this->setPkName($key);
break;
}
$this->dbArrayIOSetAcl($base_acl_level, $acl_admin);
}
@@ -104,6 +101,144 @@ class ArrayIO extends \CoreLibs\DB\IO
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
* This is needed for table array ACL checks
@@ -198,8 +333,8 @@ class ArrayIO extends \CoreLibs\DB\IO
public function dbCheckPkSet(): bool
{
// if pk_id is set, overrule ...
if ($this->pk_id) {
$this->table_array[$this->pk_name]['value'] = $this->pk_id;
if (!empty($this->getPkId())) {
$this->table_array[$this->pk_name]['value'] = $this->getPkId();
}
// if not set ... produce error
if (!$this->table_array[$this->pk_name]['value']) {
@@ -287,7 +422,7 @@ class ArrayIO extends \CoreLibs\DB\IO
$q .= ' AND ' . $q_where;
}
// if 0, error
$this->pk_id = null;
$this->setPkId(null);
if (!$this->dbExec($q)) {
$this->__dbError(1022);
}
@@ -374,7 +509,7 @@ class ArrayIO extends \CoreLibs\DB\IO
}
}
// possible dbFetchArray errors ...
$this->pk_id = $this->table_array[$this->pk_name]['value'];
$this->setPkId($this->table_array[$this->pk_name]['value']);
} else {
$this->__dbError(1022);
}
@@ -397,10 +532,6 @@ class ArrayIO extends \CoreLibs\DB\IO
if (count($table_array)) {
$this->table_array = $table_array;
}
// PK ID check
// if ($this->pk_id && !$this->table_array[$this->pk_name]["value"]) {
// $this->table_array[$this->pk_name]["value"]=$this->pk_id;
// }
// checken ob PKs gesetzt, wenn alle -> update, wenn keiner -> insert, wenn ein paar -> ERROR!
if (!$this->table_array[$this->pk_name]['value']) {
$insert = 1;
@@ -624,16 +755,11 @@ class ArrayIO extends \CoreLibs\DB\IO
$q .= ' AND ' . $q_where;
}
// 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 {
$q = 'INSERT INTO ' . $this->table_name . ' ';
$q .= '(' . $q_vars . ') ';
$q .= 'VALUES (' . $q_data . ')';
// write primary key too
// if ($q_data)
// $q .= ", ";
// $q .= $this->pk_name." = ".$this->table_array[$this->pk_name]['value']." ";
// $this->pk_id = $this->table_array[$this->pk_name]['value'];
}
// return success or not
if (!$this->dbExec($q)) {
@@ -646,7 +772,7 @@ class ArrayIO extends \CoreLibs\DB\IO
$insert_id = 0;
}
$this->table_array[$this->pk_name]['value'] = $insert_id;
$this->pk_id = $insert_id;
$this->setPkId($insert_id);
}
// return the table if needed
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
* Defaults to skip level 0 wich is this methid
*
* @param integer $start_level From what level on, as defaul starts with 1
* to exclude self
* @param integer $start_level [=1] From what level on, starts with 1 to exclude self
* @return array<mixed> All method names in list where max is last called
*/
public static function getCallerMethodList(int $start_level = 1): array
@@ -304,15 +303,46 @@ class Support
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$methods = [];
foreach ($traces as $level => $data) {
if ($level >= $start_level) {
if (!empty($data['function'])) {
array_unshift($methods, $data['function']);
}
if ($level < $start_level) {
continue;
}
if (!empty($data['function'])) {
array_unshift($methods, $data['function']);
}
}
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
* Is mostly used in debug log statements to get the class where the debug

View File

@@ -17,6 +17,8 @@ class ErrorMessage
{
/** @var array<int,array{id:string,level:string,str:string,target:string,target_style:string,highlight:string[]}> */
private array $error_str = [];
/** @var array<string,array{info:string,level:string}> */
private array $jump_targets = [];
/** @var \CoreLibs\Logging\Logging $log */
public \CoreLibs\Logging\Logging $log;
@@ -27,13 +29,19 @@ class ErrorMessage
* init ErrorMessage
*
* @param \CoreLibs\Logging\Logging $log
* @param bool $log_error [=false]
* @param null|bool $log_error [=null], defaults to false if log is not level debug
*/
public function __construct(
\CoreLibs\Logging\Logging $log,
bool $log_error = false
?bool $log_error = null
) {
$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;
}
@@ -42,6 +50,7 @@ class ErrorMessage
* error_id: internal Error ID (should be unique)
* level: error level, can only be ok, info, warn, error, abort, crash
* 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
* error: input error or error in executing request
* abort: an internal error happened as mandatory information that normally is
@@ -54,6 +63,8 @@ class ErrorMessage
* 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
@@ -63,6 +74,9 @@ class ErrorMessage
* @param string $target_style Alternate color style for the error message
* @param array<string> $highlight Any additional error data as error OR
* highlight points for field highlights
* @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,
@@ -75,6 +89,7 @@ class ErrorMessage
string $target = '',
string $target_style = '',
array $highlight = [],
array $jump_target = [],
?string $message = null,
array $context = [],
?bool $log_error = null,
@@ -96,8 +111,16 @@ class ErrorMessage
'target_style' => $target_style,
'highlight' => $highlight,
];
// set a jump target
$this->setJumpTarget($jump_target['target'] ?? null, $jump_target['info'] ?? null, $level);
// write to log for abort/crash
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([
@@ -139,6 +162,9 @@ class ErrorMessage
* @param string $target_style Alternate color style for the error message
* @param array<string> $highlight Any additional error data as error OR
* highlight points for field highlights
* @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,
@@ -151,6 +177,7 @@ class ErrorMessage
string $target = '',
string $target_style = '',
array $highlight = [],
array $jump_target = [],
?string $message = null,
array $context = [],
?bool $log_error = null,
@@ -162,12 +189,45 @@ class ErrorMessage
$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,
];
}
// *********************************************************************
// GETTERS
// *********************************************************************
@@ -210,6 +270,26 @@ class ErrorMessage
];
}
/**
* 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
// *********************************************************************

View File

@@ -15,6 +15,7 @@ enum MessageLevel: int
{
case ok = 100;
case info = 200;
case notice = 250;
case warn = 300;
case error = 400;
case abort = 500;
@@ -30,6 +31,7 @@ enum MessageLevel: int
return match (strtolower($name)) {
'ok' => self::ok,
'info' => self::info,
'notice' => self::notice,
'warn', 'warning' => self::warn,
'error' => self::error,
'abort' => self::abort,

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -78,7 +78,7 @@ class Image
if (!empty($dummy) && file_exists($filename) && is_file($filename)) {
$return_data = $filename;
} 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 {
$return_data = $dummy;
@@ -204,11 +204,11 @@ class Image
E_USER_DEPRECATED
);
// 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;
if (!is_dir($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;
}
}
@@ -470,12 +470,20 @@ class Image
*
* @param string $filename path + filename to rotate. This file must be writeable
* @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
{
// function exists & file is writeable, else do nothing
if (!function_exists('exif_read_data') || !is_writeable($filename)) {
return;
if (!function_exists('exif_read_data')) {
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];
// add @ to avoid "file not supported error"

View File

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

View File

@@ -33,7 +33,7 @@ if [ ! -z "${2}" ] && [ -z "${php_bin}" ]; then
esac;
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};

View File

@@ -28,10 +28,10 @@ final class CoreLibsCheckFileTest extends TestCase
public function filesList(): array
{
return [
['filename.txt', 'txt', 5],
['filename.csv', 'csv', 15],
['filename.tsv', 'tsv', 0],
['file_does_not_exits', '', -1],
['filename.txt', 'txt', 5, 'text/plain'],
['filename.csv', 'csv', 15, 'text/csv'],
['filename.tsv', 'tsv', 0, 'text/plain'],
['file_does_not_exits', '', -1, ''],
];
}
@@ -63,6 +63,15 @@ final class CoreLibsCheckFileTest extends TestCase
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
*
@@ -115,6 +124,51 @@ final class CoreLibsCheckFileTest extends TestCase
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__

View File

@@ -1098,16 +1098,109 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
* @testdox arrayFlatForKey array $input will be $expected [$_dataName]
*
* @param array $input
* @param string $search
* @param array $expected
* @return void
*/
public function testArrayFlatForKey(array $input, $search, array $expected): void
public function testArrayFlatForKey(array $input, string $search, array $expected): void
{
$this->assertEquals(
$expected,
\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__

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
*
@@ -74,6 +102,11 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
public function intervalProvider(): array
{
return [
'on hour' => [
3600,
false,
'1h 0m 0s'
],
'interval no microtime' => [
1641515890,
false,
@@ -82,7 +115,7 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'interval with microtime' => [
1641515890,
true,
'18999d 0h 38m 10s',
'18999d 0h 38m 10s 0ms',
],
'micro interval no microtime' => [
1641515890.123456,
@@ -92,7 +125,7 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'micro interval with microtime' => [
1641515890.123456,
true,
'18999d 0h 38m 10s 1235ms',
'18999d 0h 38m 10s 124ms',
],
'negative interval no microtime' => [
-1641515890,
@@ -103,27 +136,27 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'microtime only' => [
0.123456,
true,
'0s 1235ms',
'0s 123ms',
],
'seconds only' => [
30.123456,
true,
'30s 1235ms',
'30s 123ms',
],
'minutes only' => [
90.123456,
true,
'1m 30s 1235ms',
'1m 30s 123ms',
],
'hours only' => [
3690.123456,
true,
'1h 1m 30s 1235ms',
'1h 1m 30s 123ms',
],
'days only' => [
90090.123456,
true,
'1d 1h 1m 30s 1235ms',
'1d 1h 1m 30s 123ms',
],
'already set' => [
'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
*
@@ -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
*
@@ -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
*
@@ -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
*
@@ -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
*
@@ -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
*
@@ -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
*
@@ -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
*

View File

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

View File

@@ -256,6 +256,80 @@ final class CoreLibsConvertStringsTest extends TestCase
$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__

View File

@@ -232,7 +232,7 @@ final class CoreLibsDBIOTest extends TestCase
$this->assertEquals(
$error,
$last_error,
'Assert query warning'
'Assert query error'
);
return [$last_warning, $last_error];
}
@@ -251,8 +251,6 @@ final class CoreLibsDBIOTest extends TestCase
*/
public function testDbVersion(): void
{
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -276,8 +274,6 @@ final class CoreLibsDBIOTest extends TestCase
*/
public function testDbVersionNumeric(): void
{
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -306,8 +302,6 @@ final class CoreLibsDBIOTest extends TestCase
*/
public function testDbVersionInfoParameters(): void
{
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -365,8 +359,6 @@ final class CoreLibsDBIOTest extends TestCase
*/
public function testDbVersionInfo(string $parameter, string $expected): void
{
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -1592,8 +1584,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error,
bool $run_many_times = false
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -1832,8 +1822,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error,
string $insert_data
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -2002,8 +1990,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error,
string $insert_data
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -3069,8 +3055,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error,
string $insert_data
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -3465,7 +3449,7 @@ final class CoreLibsDBIOTest extends TestCase
$read_query,
null,
null,
//
// warning: 20
true, '20', '',
//
'result', '', '',
@@ -3482,6 +3466,31 @@ final class CoreLibsDBIOTest extends TestCase
'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)
'wrong parmeter count' => [
'wrong_param_count',
@@ -3554,8 +3563,6 @@ final class CoreLibsDBIOTest extends TestCase
string $insert_data,
array $prepare_cursor,
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -3575,6 +3582,9 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbPrepare($stm_name, $query) :
$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 (is_string($expected_prepare) && $expected_prepare == '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
// check test result
$execute_result = $query_data === null ?
$db->dbExecute($stm_name) :
$db->dbExecute($stm_name, $query_data);
if ($expected_execute == 'result') {
// if PHP or newer, must be Object PgSql\Result
$this->assertIsObject(
$execute_result
);
// also check that this is correct instance type
$this->assertInstanceOf(
'PgSql\Result',
$execute_result
);
// if this is an select use dbFetchArray to get data and test
} else {
$this->assertEquals(
$expected_execute,
$execute_result
);
}
// error/warning check
$this->subAssertErrorTest($db, $warning_execute, $error_execute);
// now check test result if expected return is result
if (
$expected_execute == 'result' &&
!empty($expected_data_query)
) {
// $expected_data_query
// $expected_data
$rows = $db->dbReturnArray($expected_data_query);
$this->assertEquals(
$expected_data,
$rows
);
}
if (
$expected_execute == 'result' &&
$execute_result !== false &&
empty($expected_data_query) &&
count($expected_data)
) {
// compare previously read data to compare data
$compare_data = [];
// read in the query data
while (is_array($row = $db->dbFetchArray($execute_result, true))) {
$compare_data[] = $row;
if (!$error_prepare) {
$execute_result = $query_data === null ?
$db->dbExecute($stm_name) :
$db->dbExecute($stm_name, $query_data);
if ($expected_execute == 'result') {
// if PHP or newer, must be Object PgSql\Result
$this->assertIsObject(
$execute_result
);
// also check that this is correct instance type
$this->assertInstanceOf(
'PgSql\Result',
$execute_result
);
// if this is an select use dbFetchArray to get data and test
} else {
$this->assertEquals(
$expected_execute,
$execute_result
);
}
// error/warning check
$this->subAssertErrorTest($db, $warning_execute, $error_execute);
// now check test result if expected return is result
if (
$expected_execute == 'result' &&
!empty($expected_data_query)
) {
// $expected_data_query
// $expected_data
$rows = $db->dbReturnArray($expected_data_query);
$this->assertEquals(
$expected_data,
$rows
);
}
if (
$expected_execute == 'result' &&
$execute_result !== false &&
empty($expected_data_query) &&
count($expected_data)
) {
// compare previously read data to compare data
$compare_data = [];
// read in the query data
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
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
$this->assertEquals(
$prepare_cursor[$key],
$db->dbGetPrepareCursorValue($stm_name, $key),
'Prepared cursor: ' . $key . ': failed assertion'
);
// check dbGetPrepareCursorValue
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
$this->assertEquals(
$prepare_cursor[$key],
$db->dbGetPrepareCursorValue($stm_name, $key),
'Prepared cursor: ' . $key . ': failed assertion'
);
}
}
// reset all data
@@ -3844,8 +3856,6 @@ final class CoreLibsDBIOTest extends TestCase
string $expected_get_var,
string $expected_get_db
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log
@@ -3907,9 +3917,13 @@ final class CoreLibsDBIOTest extends TestCase
'id' => '51',
'error' => 'Max query call needs to be set to at least 1',
// 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' => '',
'msg' => '',
'message' => '',
'context' => [
'max_calls' => 0
]
]
],
'trigger warning' => [
@@ -3942,8 +3956,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error_id,
array $expected_history
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -3969,7 +3981,7 @@ final class CoreLibsDBIOTest extends TestCase
foreach ($expected_history as $key => $value) {
// check if starts with / because this is regex (timestamp)
// if (substr($expected_2, 0, 1) == '/) {
if (strpos($value, '/') === 0) {
if (!is_array($value) && strpos($value, '/') === 0) {
// this is regex
$this->assertMatchesRegularExpression(
$value,
@@ -4057,8 +4069,6 @@ final class CoreLibsDBIOTest extends TestCase
bool $expected_set_flag,
string $expected_get_encoding
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log
@@ -4140,8 +4150,6 @@ final class CoreLibsDBIOTest extends TestCase
?string $encoding_php,
string $text
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log
@@ -4271,8 +4279,6 @@ final class CoreLibsDBIOTest extends TestCase
string $table,
string $primary_key
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -4329,7 +4335,7 @@ final class CoreLibsDBIOTest extends TestCase
// NOTE if there are different INSERTS before the primary keys
// will not match anymore. Must be updated by hand
// 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
// 1: params
// 1: pk name for db exec
@@ -4529,8 +4535,6 @@ final class CoreLibsDBIOTest extends TestCase
array|string|int|null $expected_ret_ext,
array $expected_ret_arr
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -4874,8 +4878,6 @@ final class CoreLibsDBIOTest extends TestCase
array $expected_col_names,
array $expected_col_types
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -5029,6 +5031,147 @@ final class CoreLibsDBIOTest extends TestCase
$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
// - complex write sets
// dbWriteData, dbWriteDataExt
@@ -5157,8 +5300,6 @@ final class CoreLibsDBIOTest extends TestCase
string $warning_final,
string $error_final
): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log

View File

@@ -460,8 +460,8 @@ final class CoreLibsDebugSupportTest extends TestCase
* Undocumented function
*
* @cover ::getCallerFileLine
* @testWith ["vendor/phpunit/phpunit/src/Framework/TestCase.php:"]
* @testdox getCallerFileLine check based on regex /[\w\-\/]/vendor/phpunit/phpunit/src/Framework/TestCase.php:\d+ [$_dataName]
* @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 .../Framework/TestCase.php:\d+ [$_dataName]
*
* @param string $expected
* @return void
@@ -469,7 +469,14 @@ final class CoreLibsDebugSupportTest extends TestCase
public function testGetCallerFileLine(): void
{
// 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(
$regex,
Support::getCallerFileLine()
@@ -506,7 +513,7 @@ final class CoreLibsDebugSupportTest extends TestCase
public function testGetCallerMethodList(array $expected): void
{
$compare = Support::getCallerMethodList();
// 10: legact
// 10: legacy
// 11: direct
// 12: full call
switch (count($compare)) {
@@ -514,27 +521,30 @@ final class CoreLibsDebugSupportTest extends TestCase
// add nothing
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
$compare,
'assert expected 10'
);
break;
case 11:
// add one "run" before "runBare"
// array_splice(
// $expected,
// 7,
// 0,
// ['run']
// );
array_splice(
$expected,
0,
0,
['include']
);
if ($compare[0] == 'include') {
// add include at first
array_splice(
$expected,
0,
0,
['include']
);
} else {
array_splice(
$expected,
6,
0,
['run']
);
}
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
$compare,
'assert expected 11'
);
break;
@@ -554,13 +564,38 @@ final class CoreLibsDebugSupportTest extends TestCase
);
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
$compare,
'assert expected 12'
);
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)
*

View File

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

View File

@@ -44,6 +44,11 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'str' => 'INFO',
'expected' => 'info',
],
'notice' => [
'level' => 'notice',
'str' => 'NOTICE',
'expected' => 'notice',
],
'warn' => [
'level' => 'warn',
'str' => 'WARN',
@@ -91,9 +96,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
public function testErrorMessageLevelOk(string $level, string $str, string $expected): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages',
'log_file_id' => 'testErrorMessagesLevelOk',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug,
'log_level' => Level::Error,
]);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setMessage(
@@ -123,9 +128,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
public function testErrorMessageOk(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages',
'log_file_id' => 'testErrorMessagesOk',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug
'log_level' => Level::Error
]);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setErrorMsg(
@@ -238,6 +243,22 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
'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' => [
'id' => '300',
'level' => 'crash',
@@ -293,7 +314,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
* Undocumented function
*
* @dataProvider providerErrorMessageLog
* @testdox Test Log writing [$_dataName]
* @testdox Test Log writing with log level Error [$_dataName]
*
* @param string $id
* @param string $level
@@ -303,7 +324,7 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
* @param string $expected
* @return void
*/
public function testErrorMessageLog(
public function testErrorMessageLogErrorLevel(
string $id,
string $level,
string $str,
@@ -312,7 +333,63 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
string $expected
): void {
$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_level' => Level::Debug,
'log_per_run' => true
@@ -331,8 +408,8 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
$log->getLogFolder() . $log->getLogFile()
) ?: '';
}
// if n
if ($level == 'error' && ($log_error === null || $log_error === false)) {
// if error, and log is debug level, only explicit false are not logged
if ($level == 'error' && $log_error === false) {
$this->assertStringNotContainsString(
$expected,
$file_content
@@ -344,6 +421,84 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
);
}
}
/**
* 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()
);
}
}
// __END__

View File

@@ -20,7 +20,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
. "\[[\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\\\\]+((::|->)\w+)?}\s{1}"; // class

View File

@@ -16,17 +16,89 @@ final class CoreLibsOutputImageTest extends TestCase
/**
* Undocumented function
*
* @testdox Output\Image Class tests
* @covers ::createThumbnail
* @testdox createThumbnail checks
*
* @return void
*/
public function testOutputImage()
public function testCreateThumbnail(): void
{
// $this->assertTrue(true, 'Output Image Tests not implemented');
$this->markTestIncomplete(
'Output\Image Tests have not yet been implemented'
// CONVERT does not exist
$this->expectException(\RuntimeException::class);
\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