Compare commits

...

29 Commits

Author SHA1 Message Date
Clemens Schwaighofer
9a28c86991 json test file update 2026-01-22 11:51:32 +09:00
Clemens Schwaighofer
38b2ffe82a jsonConvertToArray with JSON_INVALID_UTF8_IGNORE does not work
json_decode JSON_INVALID_UTF8_IGNORE is not honoring any of those flags at the moment
2026-01-22 11:44:11 +09:00
Clemens Schwaighofer
6e547abccb Fix text in check runners 2026-01-22 11:31:01 +09:00
Clemens Schwaighofer
676af5e1a4 Add json validation and update tests
Also add removal of THROW flag for json decode to not throw an exception if if wanted

Add jquery 4.0.0 libs
2026-01-22 11:08:18 +09:00
Clemens Schwaighofer
118aacee28 Bug in RamdomKey::validateRandomKeyData when passing large character sets
The check for stringlength was done on the set key range in the class var and not the variable passed on in the method.
So if we cold call randomKey with a key character list and not the default list it failed with invalid max length
2026-01-14 15:07:38 +09:00
Clemens Schwaighofer
564e23ecd7 Update and fix Strings stringToTime()
Allow parsing of more flexible interval strings including
long names (day, hour, minute, second, millisecond),
negative values, no spaces between components, and
throwing exceptions on invalid input if requested.

The following types are now allowed
- d|day|days
- h|hour|hours
- m|min|mins|minute|minutes
- s|sec|secs|second|seconds
- ms|msec|msecs|msecond|mseconds|millis|millisec|millisecs|millisecond|milliseconds

Also fix the milisecond parsing that was done completly wrong
the milliseoncds where just added after a "." as decimals without converting them at all.
Now the value is divided by 1000 and added to the existing number, and as before only if ms exist

The negative check is now included in the main parse regex, so a second regex check is no longer necessary

Spaces between values, before or anywhere are now more flexible.

Exceptions are thrown if the regex cannot parse anything, or it returns only one master entry and no matches
2026-01-14 13:16:53 +09:00
Clemens Schwaighofer
2889012592 Add no dash character list allowed in Strings parseCharacterRanges function
Same rules with returning unique elements array
2026-01-14 10:49:14 +09:00
Clemens Schwaighofer
cd65604073 Add parseCharacterRanges function to Strings.php and tests
Parses character ranges like A-Z into individual characters and returns as an array
2026-01-14 10:36:09 +09:00
Clemens Schwaighofer
a3cf5f45f9 Fix function name in Create/RandomKey 2026-01-07 17:21:26 +09:00
Clemens Schwaighofer
6f3dacdec0 Remove double color settings entry in Phan config 2026-01-07 13:32:31 +09:00
Clemens Schwaighofer
2ab1ee90ef Add color output in phan 2026-01-07 13:17:54 +09:00
Clemens Schwaighofer
b8c0aff975 Update phive phars with correct version and update scripts to use both
new "-c" switch for all checking scripts to swtich to the composer version from the phive installed version

NOTE: phpstan plugins only work in the composer version.

Default is the phive version
2026-01-06 18:15:50 +09:00
Clemens Schwaighofer
c5fed66237 PHP 8.5 fixes and updates
All tested with PHP 8.4 and PHP 8.3 too

Major changes:
- cube root Math (cbrt) now throws InvalidArgumentException if NAN is returned instead of returning NAN
- Byte convert from string to int will throw errors if value is too large (\LengthException)
- new flag for returning string type but for this bcmath must be installed (\RuntimeException if no bcmath)
- Updated curl class and remove close handler as not needed and deprecated as of PHP 8.5
- Curl phpunit tests: convert string to JSON convert flow for return content check (to avoid per PHP version check)
- image close handler for ImageMagick removed as not needed and deprecated as of PHP 8.5
- updated all check calls too use phive tools if possible (except phpunit) and all scripts can have dynamic php version set
2026-01-06 15:55:47 +09:00
Clemens Schwaighofer
157169d3ba Fix gittignore for package lock json 2025-11-27 17:59:53 +09:00
Clemens Schwaighofer
60fe0e0def Remove package lock for npm, add it to gitignore 2025-11-27 17:58:55 +09:00
Clemens Schwaighofer
e473e7899d Fix logging line and call method information
The logging line number and file was for the previous call position, not for
where the actual log entry was called

Also fix for ErrorMessage class calls with shifting the start position up depending on which method is called.

Output shows file and line where the message/log call was done and the function/class method where the log call was done
2025-11-27 17:54:28 +09:00
Clemens Schwaighofer
8af71b70a3 general SQL update to use uuid for uid, update edit.jq.js for some testing 2025-11-06 11:51:29 +09:00
Clemens Schwaighofer
7d10b4c5af Change UUIdv4 validation to properly check for version 4 UUIDs 2025-11-04 11:51:08 +09:00
Clemens Schwaighofer
c81b602657 phpunit: redirect error_log message to temp file so they are not printed to console
This is for any logging part that logs to emergency where emergency logging is
set to log to the error_log too
2025-10-09 15:05:00 +09:00
Clemens Schwaighofer
59cc5f2060 phpstan checks and fixes 2025-10-09 11:58:47 +09:00
Clemens Schwaighofer
e072aaf4d6 Add ISO Type datetime format to support, and make this default loggint time format 2025-10-09 11:14:06 +09:00
Clemens Schwaighofer
259f9cebf3 Had to roll back previous MessageLevel changes
Keep everything lower case as is
any upper case check have to go through fromName, this will convert to lowercase anyway
2025-09-10 13:41:42 +09:00
Clemens Schwaighofer
bd4f674f0f Add a noset level and add uppercase support to fromName()
Noset is for unset status, which is different from unknown.
Also allow upper case so we can use "OK" and "ERROR" in addition to "ok" and "error".
2025-09-10 13:28:39 +09:00
Clemens Schwaighofer
87293bf633 Add a security info md file 2025-08-26 14:14:36 +09:00
Clemens Schwaighofer
be46d6e101 Readme update for url fix for the JS utils files 2025-08-18 09:36:10 +09:00
Clemens Schwaighofer
433bc3d539 All array variable names are no longer $array to not confuse 2025-07-23 15:22:25 +09:00
Clemens Schwaighofer
4ed645bac3 Update email check with better domain name check (ASCII), logging class debug output update 2025-07-15 13:13:11 +09:00
Clemens Schwaighofer
908376c1a5 Array sort method doc update 2025-06-26 11:43:28 +09:00
Clemens Schwaighofer
c329e7a2da Add JSON_UNESCAPED_UNICODE as default flag for json convert to array calls 2025-06-26 11:39:38 +09:00
53 changed files with 11224 additions and 1903 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ www/composer.lock
www/vendor
**/.env
**/.target
package-lock.json

View File

@@ -26,8 +26,8 @@
use Phan\Config;
return [
// "target_php_version" => "8.2",
"minimum_target_php_version" => "8.2",
// "target_php_version" => "8.3",
"minimum_target_php_version" => "8.3",
// turn color on (-C)
"color_issue_messages_if_supported" => true,
// If true, missing properties will be created when

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^10.3.5" installed="10.5.46" location="./tools/phpunit" copy="false"/>
<phar name="phpcbf" version="^3.7.2" installed="3.13.0" location="./tools/phpcbf" copy="false"/>
<phar name="phpcs" version="^3.10.3" installed="3.13.0" location="./tools/phpcs" copy="false"/>
<phar name="phpstan" version="^2.0" installed="2.1.17" location="./tools/phpstan" copy="false"/>
<phar name="phan" version="^5.4.3" installed="5.4.3" location="./tools/phan" copy="false"/>
<phar name="psalm" version="^5.15.0" installed="5.24.0" location="./tools/psalm" copy="false"/>
<phar name="phpunit" version="~9.6" installed="9.6.31" location="./tools/phpunit" copy="false"/>
<phar name="phpcbf" version="4" installed="4.0.1" location="./tools/phpcbf" copy="false"/>
<phar name="phpcs" version="4" installed="4.0.1" location="./tools/phpcs" copy="false"/>
<phar name="phpstan" version="^2.0" installed="2.1.33" location="./tools/phpstan" copy="false"/>
<phar name="phan" version="^5.4.3" installed="5.5.2" location="./tools/phan" copy="false"/>
<phar name="psalm" version="^5.26.1" installed="5.26.1" location="./tools/psalm" copy="false"/>
<phar name="phpdox" version="^0.12.0" installed="0.12.0" location="./tools/phpdox" copy="false"/>
<phar name="phpdocumentor" version="^3.4.2" installed="3.4.3" location="./tools/phpDocumentor" copy="false"/>
<phar name="php-cs-fixer" version="^3.34.1" installed="3.57.2" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpdocumentor" version="^3.4.2" installed="3.9.1" location="./tools/phpDocumentor" copy="false"/>
<phar name="php-cs-fixer" version="^3.34.1" installed="3.92.4" location="./tools/php-cs-fixer" copy="false"/>
</phive>

View File

@@ -1,6 +1,100 @@
base=$(pwd)"/";
#!/bin/env bash
function error() {
if [ -t 1 ]; then echo "[MAK] ERROR: $*" >&2; fi; exit 0;
}
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h | --help] [-p | --php VERSION] [-c | --composer]
Runs phan static analyzer.
If -p is not set, the default intalled PHP is used.
Available options:
-h, --help Print this help and exit
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
-c, --composer Use composer version and not the default phives bundle
EOF
exit
}
BASE_PATH=$(pwd)"/";
PHP_BIN_PATH=$(which php);
if [ -z "${PHP_BIN_PATH}" ]; then
echo "Cannot find php binary";
exit;
fi;
DEFAULT_PHP_VERSION=$(${PHP_BIN_PATH} -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;");
if [ -z "${DEFAULT_PHP_VERSION}" ]; then
echo "Cannot set default PHP version";
exit;
fi;
php_version="";
no_php_version=0;
use_composer=0;
while [ -n "${1-}" ]; do
case "${1}" in
-p | --php)
php_version="${2-}";
shift
;;
-c | --composer)
use_composer=1;
shift
;;
-h | --help)
usage
;;
# invalid option
-?*)
error "[!] Unknown option: '$1'."
;;
esac
shift;
done;
if [ -z "${php_version}" ]; then
php_version="${DEFAULT_PHP_VERSION}";
no_php_version=1;
fi;
php_bin="${PHP_BIN_PATH}${php_version}";
echo "Use PHP Version: ${php_version}";
if [ "${use_composer}" -eq 1 ]; then
echo "Use composer installed";
else
echo "Use phive installed";
fi;
if [ ! -f "${php_bin}" ]; then
echo "Set php ${php_bin} does not exist";
exit;
fi;
# must be run in ${base}
cd $base || exit;
#PHAN_DISABLE_XDEBUG_WARN=1;${base}tools/phan --progress-bar -C --analyze-twice
PHAN_DISABLE_XDEBUG_WARN=1;${base}vendor/bin/phan --progress-bar -C --analyze-twice
cd "$BASE_PATH" || exit;
export PHAN_DISABLE_XDEBUG_WARN=1;
PHAN_CALL=(
"${php_bin}"
);
if [ "${use_composer}" -eq 1 ]; then
PHAN_CALL+=("${BASE_PATH}vendor/bin/phan");
else
PHAN_CALL+=("${BASE_PATH}tools/phan");
fi;
PHAN_CALL+=(
"--progress-bar"
"-C"
"--analyze-twice"
)
"${PHAN_CALL[@]}";
if [ "${no_php_version}" -eq 0 ]; then
echo "*** CALLED WITH PHP ${php_bin} ***";
${php_bin} --version;
else
echo "Default PHP used: $(php --version)";
fi;
cd ~ || exit;
# __END__

View File

@@ -1,5 +1,92 @@
base=$(pwd)"/";
# must be run in ${base}
cd $base || exit;
${base}tools/phpstan;
#!/bin/env bash
function error() {
if [ -t 1 ]; then echo "[MAK] ERROR: $*" >&2; fi; exit 0;
}
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-h | --help] [-p | --php VERSION] [-c | --composer]
Runs phan static analyzer.
If -p is not set, the default intalled PHP is used.
Available options:
-h, --help Print this help and exit
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
-c, --composer Use composer version and not the default phives bundle
EOF
exit
}
BASE_PATH=$(pwd)"/";
PHP_BIN_PATH=$(which php);
if [ -z "${PHP_BIN_PATH}" ]; then
echo "Cannot find php binary";
exit;
fi;
DEFAULT_PHP_VERSION=$(${PHP_BIN_PATH} -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;");
if [ -z "${DEFAULT_PHP_VERSION}" ]; then
echo "Cannot set default PHP version";
exit;
fi;
php_version="";
no_php_version=0;
use_composer=0;
while [ -n "${1-}" ]; do
case "${1}" in
-p | --php)
php_version="${2-}";
shift
;;
-c | --composer)
use_composer=1;
shift
;;
-h | --help)
usage
;;
# invalid option
-?*)
error "[!] Unknown option: '$1'."
;;
esac
shift;
done;
if [ -z "${php_version}" ]; then
php_version="${DEFAULT_PHP_VERSION}";
no_php_version=1;
fi;
php_bin="${PHP_BIN_PATH}${php_version}";
echo "Use PHP Version: ${php_version}";
if [ "${use_composer}" -eq 1 ]; then
echo "Use composer installed";
else
echo "Use phive installed";
fi;
if [ ! -f "${php_bin}" ]; then
echo "Set php ${php_bin} does not exist";
exit;
fi;
BASE_PATH=$(pwd)"/";
cd "$BASE_PATH" || exit;
PHPSTAN_CALL=(
"${php_bin}"
);
if [ "${use_composer}" -eq 1 ]; then
PHPSTAN_CALL+=("${BASE_PATH}vendor/bin/phpstan");
else
PHPSTAN_CALL+=("${BASE_PATH}tools/phpstan");
fi;
"${PHPSTAN_CALL[@]}";
if [ "${no_php_version}" -eq 0 ]; then
echo "*** CALLED WITH PHP ${php_bin} ***";
${php_bin} --version;
else
echo "Default PHP used: $(php --version)";
fi;
cd ~ || exit;

View File

@@ -6,7 +6,7 @@ function error() {
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-t] [-v] [-p VERSION]
Usage: $(basename "${BASH_SOURCE[0]}") [-h | --help] [-p | --php VERSION] [-c | --composer] [-t | --testdox] [-v | --verbose]
Runs all the PHP unit tests.
@@ -15,8 +15,9 @@ If -p is not set, the default intalled PHP is used.
Available options:
-h, --help Print this help and exit
-t, --testdox Enable testdox output for phpunit
-t, --testdox Enable testdox output for PHPunit
-v, --verbose Enable verbose output for PHPunit
-c, --composer Use composer version and not the default phives bundle
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
EOF
exit
@@ -45,6 +46,7 @@ opt_testdox="";
opt_verbose="";
php_version="";
no_php_version=0;
use_composer=0;
while [ -n "${1-}" ]; do
case "${1}" in
-t | --testdox)
@@ -53,6 +55,10 @@ while [ -n "${1-}" ]; do
-v | --verbose)
opt_verbose="--verbose";
;;
-c | --composer)
use_composer=1;
shift
;;
-p | --php)
php_version="${2-}";
shift
@@ -74,21 +80,38 @@ if [ -z "${php_version}" ]; then
fi;
php_bin="${PHP_BIN_PATH}${php_version}";
echo "Use PHP Version: ${php_version}";
if [ "${use_composer}" -eq 1 ]; then
echo "Use composer installed";
else
echo "Use phive installed";
fi;
if [ ! -f "${php_bin}" ]; then
echo "Set php ${php_bin} does not exist";
exit;
fi;
php_bin="${php_bin} ";
# Note 4dev/tests/bootstrap.php has to be set as bootstrap file in phpunit.xml
phpunit_call="${php_bin}${BASE_PATH}vendor/bin/phpunit ${opt_testdox} ${opt_verbose} -c ${PHPUNIT_CONFIG} ${BASE_PATH}4dev/tests/";
${phpunit_call};
PHPUNIT_CALL=(
"${php_bin}"
);
if [ "${use_composer}" -eq 1 ]; then
PHPUNIT_CALL+=("${BASE_PATH}vendor/bin/phpunit");
else
PHPUNIT_CALL+=("${BASE_PATH}tools/phpunit");
fi;
PHPUNIT_CALL+=(
"${opt_testdox}"
"${opt_verbose}"
"-c" "${PHPUNIT_CONFIG}"
"${BASE_PATH}4dev/tests/"
);
"${PHPUNIT_CALL[@]}" || exit;
echo -e "\nPHPUnit Config: ${PHPUNIT_CONFIG}";
if [ "${no_php_version}" -eq 0 ]; then
echo "CALLED WITH PHP: ${php_bin}$(${php_bin} --version)";
echo "*** CALLED WITH PHP ${php_bin} ***";
${php_bin} --version;
else
echo "Default PHP used: $(php --version)";
fi;

View File

@@ -9,5 +9,6 @@
CREATE TABLE generic (
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
date_updated TIMESTAMP WITHOUT TIME ZONE,
uuid UUID DEFAULT gen_random_uuid(),
uid VARCHAR
);

View File

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

View File

@@ -490,11 +490,11 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
],
'micro interval with microtime' => [
'18999d 0h 38m 10s 1235ms',
1641515890.1235,
1641515891.235,
],
'micro interval with microtime' => [
'18999d 0h 38m 10s 1234567890ms',
1641515890.1234567,
1642750457.89,
],
'negative interval no microtime' => [
'-18999d 0h 38m 10s',
@@ -503,23 +503,246 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
// short for mini tests
'microtime only' => [
'0s 1235ms',
0.1235,
1.235,
],
'seconds only' => [
'30s 1235ms',
30.1235,
31.235,
],
'minutes only' => [
'1m 30s 1235ms',
90.1235,
91.235,
],
'hours only' => [
'1h 1m 30s 1235ms',
3690.1235,
3691.235,
],
'days only' => [
'1d 1h 1m 30s 1235ms',
90090.1235,
90091.235,
],
'days only with long name' => [
'1day 1hour 1min 30second 1235millisecond',
90091.235,
],
// Test day variations
'day singular' => [
'5day',
432000,
],
'days plural' => [
'3days',
259200,
],
'days with space' => [
'2days 5h',
190800,
],
'day without space' => [
'1day1h',
90000,
],
// Test hour variations
'hour singular' => [
'2hour',
7200,
],
'hours plural' => [
'4hours',
14400,
],
'hours with space' => [
'3hours 30m',
12600,
],
'hour without space' => [
'1hour30m',
5400,
],
// Test minute variations
'min short' => [
'45min',
2700,
],
'minute singular' => [
'1minute',
60,
],
'minutes plural' => [
'10minutes',
600,
],
'minutes with space' => [
'5minutes 20s',
320,
],
'min without space' => [
'2min30s',
150,
],
// Test second variations
'sec short' => [
'30sec',
30,
],
'second singular' => [
'1second',
1,
],
'seconds plural' => [
'45seconds',
45,
],
'seconds with space' => [
'15seconds 500ms',
15.5,
],
'sec without space' => [
'10sec250ms',
10.25,
],
// Test millisecond variations
'ms short' => [
'500ms',
0.5,
],
'millis short' => [
'250millis',
0.25,
],
'millisec medium singular' => [
'250millisec',
0.25,
],
'millisecs medium plural' => [
'250millisecs',
0.25,
],
'misec medium singular' => [
'250millisec',
0.25,
],
'msecs medium plural' => [
'250millisecs',
0.25,
],
'millisecond long singular' => [
'1millisecond',
0.001,
],
'milliseconds long plural' => [
'999milliseconds',
0.999,
],
// Test negative values
'negative days' => [
'-5d',
-432000,
],
'negative hours' => [
'-3h',
-10800,
],
'negative minutes' => [
'-45m',
-2700,
],
'negative seconds' => [
'-30s',
-30,
],
'negative milliseconds' => [
'-500ms',
-0.5,
],
'negative complex' => [
'-2days 3hours 15minutes 30seconds 250milliseconds',
-184530.25,
],
// Test combined formats
'all components short' => [
'1d 2h 3m 4s 5ms',
93784.005,
],
'all components long' => [
'2days 3hours 4minutes 5seconds 678milliseconds',
183845.678,
],
'mixed short and long' => [
'1day 2h 3minutes 4sec 100ms',
93784.1,
],
'no spaces between components' => [
'1d2h3m4s5ms',
93784.005,
],
'only days and milliseconds' => [
'5d 123ms',
432000.123,
],
'only hours and seconds' => [
'2h 45s',
7245,
],
'only minutes and milliseconds' => [
'30m 500ms',
1800.5,
],
// Test zero values
'zero seconds' => [
'0s',
0,
],
'zero with milliseconds' => [
'0s 123ms',
0.123,
],
// Test large values
'large days' => [
'365days',
31536000,
],
'large hours' => [
'48hours',
172800,
],
'large minutes' => [
'1440minutes',
86400,
],
'large seconds' => [
'86400seconds',
86400,
],
// Test edge cases with spaces
'extra spaces' => [
'1d 2h 3m 4s 5ms',
93784.005,
],
'mixed spaces and no spaces' => [
'1d 2h3m 4s5ms',
93784.005,
],
// Test single component each
'only days short' => [
'7d',
604800,
],
'only hours short' => [
'12h',
43200,
],
'only minutes short' => [
'90m',
5400,
],
'only seconds short' => [
'120s',
120,
],
'only milliseconds short' => [
'1500ms',
1.5,
],
'already set' => [
1641515890,
@@ -529,10 +752,18 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'xyz',
'xyz',
],
'empty data' => [
' ',
' ',
],
'out of bound data' => [
'99999999999999999999d',
8.64E+24
],
'spaces inbetween' => [
' - 9 d 2h 58minutes 35 seconds 123 ms ',
-788315.123,
]
];
}
@@ -555,6 +786,36 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
);
}
/**
* Undocumented function
*
* @covers ::stringToTime
* @testdox stringToTime invalid input will throw exception if requested
*
* @return void
*/
public function testStringToTimeException(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessageMatches("/^Invalid time string format, cannot parse: /");
\CoreLibs\Combined\DateTime::stringToTime('1x 2y 3z', true);
}
/**
* Undocumented function
*
* @covers ::stringToTime
* @testdox stringToTime empty input will throw exception if requested
*
* @return void
*/
public function testStringToTimeExceptionEmpty(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessageMatches("/^Invalid time string format, no interval value found: /");
\CoreLibs\Combined\DateTime::stringToTime(' ', true);
}
/**
* Undocumented function
*

View File

@@ -123,47 +123,6 @@ final class CoreLibsConvertByteTest extends TestCase
];
}
/**
* Undocumented function
*
* @return array
*/
public function byteStringProvider(): array
{
return [
'negative number' => [
0 => '-117.42 MB',
1 => -123123794,
2 => -117420000,
],
'megabyte' => [
0 => '242.98 MB',
1 => 254782996,
2 => 242980000
],
'megabyte si' => [
0 => '254.78 MiB',
1 => 267156193,
2 => 254780000
],
'petabyte' => [
0 => '1 EiB',
1 => 1152921504606846976,
2 => 1000000000000000000,
],
'max int' => [
0 => '8 EB',
1 => -9223372036854775807 - 1,
2 => 8000000000000000000,
],
'exabyte, overflow' => [
0 => '867.36EB',
1 => 3873816255479021568,
2 => 363028535651074048,
]
];
}
/**
* Undocumented function
*
@@ -180,7 +139,7 @@ final class CoreLibsConvertByteTest extends TestCase
* @return void
*/
public function testHumanReadableByteFormat(
$input,
string|int|float $input,
string $expected,
string $expected_si,
string $expected_no_space,
@@ -217,6 +176,73 @@ final class CoreLibsConvertByteTest extends TestCase
);
}
/**
* Undocumented function
*
* @return array
*/
public function byteStringProvider(): array
{
return [
'negative number' => [
0 => '-117.42 MB',
1 => -123123794,
2 => -117420000,
3 => "-123123793",
4 => "-117420000",
5 => null,
],
'megabyte' => [
0 => '242.98 MB',
1 => 254782996,
2 => 242980000,
3 => "254782996",
4 => "242980000",
5 => null,
],
'megabyte si' => [
0 => '254.78 MiB',
1 => 267156193,
2 => 254780000,
3 => "267156193",
4 => "254780000",
5 => null,
],
'petabyte' => [
0 => '1 EiB',
1 => 1152921504606846976,
2 => 1000000000000000000,
3 => "1152921504606846976",
4 => "1000000000000000000",
5 => null,
],
'max int' => [
0 => '8 EB',
1 => 0,
2 => 0,
3 => "9223372036854775808",
4 => "8000000000000000000",
5 => \LengthException::class,
],
'exabyte, overflow' => [
0 => '867.36EB',
1 => 0,
2 => 0,
3 => "999997996235794808832",
4 => "867360000000000000000",
5 => \LengthException::class,
],
'huge exabyte, overflow' => [
0 => '1000EB',
1 => 0,
2 => 0,
3 => "1152921504606846976000",
4 => "1000000000000000000000",
5 => \LengthException::class,
],
];
}
/**
* Undocumented function
*
@@ -227,10 +253,22 @@ final class CoreLibsConvertByteTest extends TestCase
* @param string|int|float $input
* @param string|int|float $expected
* @param string|int|float $expected_si
* @param string|int|float $expected_string
* @param string|int|float $expected_string_si
* @param ?string $exception
* @return void
*/
public function testStringByteFormat($input, $expected, $expected_si): void
{
public function testStringByteFormat(
string|int|float $input,
string|int|float $expected,
string|int|float $expected_si,
string|int|float $expected_string,
string|int|float $expected_string_si,
?string $exception
): void {
if ($exception !== null) {
$this->expectException($exception);
}
$this->assertEquals(
$expected,
\CoreLibs\Convert\Byte::stringByteFormat($input)
@@ -239,6 +277,17 @@ final class CoreLibsConvertByteTest extends TestCase
$expected_si,
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::BYTE_FORMAT_SI)
);
$this->assertEquals(
$expected_string,
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::RETURN_AS_STRING)
);
$this->assertEquals(
$expected_string_si,
\CoreLibs\Convert\Byte::stringByteFormat(
$input,
\CoreLibs\Convert\Byte::BYTE_FORMAT_SI | \CoreLibs\Convert\Byte::RETURN_AS_STRING
)
);
}
/**

View File

@@ -164,6 +164,51 @@ final class CoreLibsConvertJsonTest extends TestCase
);
}
/**
* test with flags
*
* @covers ::jsonConvertToArray
* @testdox jsonConvertToArray flag test, if flag is used
*
* @return void
*/
public function testJsonConvertToArrayWithFlags(): void
{
$input = '{"valid":"json","invalid":"\xB1\x31"}';
/* $expected_without_flag = [
'valid' => 'json'
];
$expected_with_flag = [
'valid' => 'json',
'invalid' => "\xB1\x31"
]; */
// no idea why in both it throws an erro
$expected_without_flag = [];
$expected_with_flag = [];
$this->assertEquals(
$expected_without_flag,
\CoreLibs\Convert\Json::jsonConvertToArray($input)
);
$this->assertEquals(
$expected_with_flag,
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_INVALID_UTF8_IGNORE)
);
}
public function testJsonConvertToArrayRemoveThrowFlag(): void
{
$input = '{"valid":"json","invalid":"\xB1\x31"}';
// show NOT throw an exception
try {
$this->assertEquals(
[],
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_THROW_ON_ERROR)
);
} catch (\Exception $e) {
$this->fail('Exception was thrown despite flag removal');
}
}
/**
* test json error states
*
@@ -189,6 +234,49 @@ final class CoreLibsConvertJsonTest extends TestCase
);
}
/**
* test json error states
*
* @covers ::jsonValidate
* @dataProvider jsonErrorProvider
* @testdox jsonValidate $input will be $expected_i/$expected_s [$_dataName]
*
* @param string|null $input
* @param int $expected_i
* @param string $expected_s
* @return void
*/
public function testJsonValidateGetLastError(?string $input, int $expected_i, string $expected_s): void
{
\CoreLibs\Convert\Json::jsonValidate($input);
$this->assertEquals(
$expected_i,
\CoreLibs\Convert\Json::jsonGetLastError()
);
$this->assertEquals(
$expected_s,
\CoreLibs\Convert\Json::jsonGetLastError(true)
);
}
/**
* test json validation
*
* @covers ::jsonValidate
* @testdox jsonValidate test valid and invalid json
*
* @return void
*/
public function testJsonValidate(): void
{
$this->assertTrue(
\CoreLibs\Convert\Json::jsonValidate('{"valid": "json"}')
);
$this->assertFalse(
\CoreLibs\Convert\Json::jsonValidate('not valid json')
);
}
/**
* Undocumented function
*

View File

@@ -122,9 +122,9 @@ final class CoreLibsConvertMathTest extends TestCase
public function providerCbrt(): array
{
return [
'cube root of 2' => [2, 1.25992, 5],
'cube root of 3' => [3, 1.44225, 5],
'cube root of -1' => [-1, 'NAN', 0],
'cube root of 2' => [2, 1.25992, 5, null],
'cube root of 3' => [3, 1.44225, 5, null],
'cube root of -1' => [-1, 'NAN', 0, \InvalidArgumentException::class],
];
}
@@ -138,10 +138,14 @@ final class CoreLibsConvertMathTest extends TestCase
* @param float|int $number
* @param float $expected
* @param int $round_to
* @param ?string $exception
* @return void
*/
public function testCbrt(float|int $number, float|string $expected, int $round_to): void
public function testCbrt(float|int $number, float|string $expected, int $round_to, ?string $exception): void
{
if ($exception !== null) {
$this->expectException($exception);
}
$this->assertEquals(
$expected,
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)

View File

@@ -698,6 +698,89 @@ final class CoreLibsConvertStringsTest extends TestCase
'Cannot match last preg error string'
);
}
/**
* Undocumented function
*
* @return array
*/
public function parseCharacterRangesProvider(): array
{
return [
'simple a-z' => [
['a-z'],
implode('', range('a', 'z')),
null,
],
'simple A-Z' => [
['A-Z'],
implode('', range('A', 'Z')),
null,
],
'simple 0-9' => [
['0-9'],
implode('', range('0', '9')),
null,
],
'mixed ranges' => [
['a-c', 'X-Z', '3-5'],
'abcXYZ345',
null,
],
'reverse ranges' => [
['z-a'],
'abcdefghijklmnopqrstuvwxyz',
null,
],
'overlapping ranges' => [
['a-f', 'd-j'],
'abcdefghij',
null,
],
'mixed valid and overlap ranges' => [
['a-f', 'z-a', '0-3'],
'abcdefghijklmnopqrstuvwxyz0123',
null,
],
'range without dashes' => [
['abcddfff'],
'abcdf',
null,
],
'invalid ranges' => [
['a-あ', 'A-あ', '0-あ'],
'',
\InvalidArgumentException::class,
],
];
}
/**
* Undocumented function
*
* @covers ::parseCharacterRanges
* @dataProvider parseCharacterRangesProvider
* @testdox parseCharacterRanges $input to $expected [$_dataName]
*
* @param array $input
* @param string $expected
* @param string|null $expected_exception
* @return void
*/
public function testParseCharacterRanges(
array $input,
string $expected,
?string $expected_exception
): void {
if ($expected_exception !== null) {
$this->expectException($expected_exception);
}
$this->assertEquals(
$expected,
implode('', \CoreLibs\Convert\Strings::parseCharacterRanges(implode('', $input)))
);
}
}
// __END__

View File

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

View File

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

View File

@@ -59,8 +59,6 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
continue;
}
$this->url_basic = $url;
// split out the last / part for url set test
curl_close($handle);
// print "Open: $url\n";
break;
}
@@ -969,76 +967,77 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
"query" => ["foo-get" => "bar"]
]);
$this->assertEquals("200", $response["code"], "multi call: get response code not matching");
if (PHP_VERSION_ID >= 80400) {
$this->assertEquals(
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_FIRST_CALL":"get",'
. '"HTTP_ACCEPT":"*\/*"},"REQUEST_TYPE":"GET","PARAMS":{"foo-get":"bar"},"BODY":null}',
$response['content'],
'multi call: get content not matching'
);
} else {
$this->assertEquals(
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
. '"HTTP_HOST":"soba.egplusww.jp"},'
. '"REQUEST_TYPE":"GET",'
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
$response['content'],
'multi call: get content not matching'
);
}
$request_expected = json_decode(
<<<JSON
{
"HEADERS":{
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",
"HTTP_HOST":"soba.egplusww.jp"
},
"REQUEST_TYPE":"GET",
"PARAMS":{"foo-get":"bar"},"BODY":null
}
JSON,
true
);
$this->assertEquals(
$request_expected,
json_decode($response['content'], true),
'multi call: get content not matching'
);
// post
$response = $curl->post($this->url_basic, [
"headers" => ["second-call" => "post"],
"body" => ["foo-post" => "baz"]
]);
$this->assertEquals("200", $response["code"], "multi call: post response code not matching");
if (PHP_VERSION_ID >= 80400) {
$this->assertEquals(
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*"},'
. '"REQUEST_TYPE":"POST","PARAMS":[],"BODY":{"foo-post":"baz"}}',
$response['content'],
'multi call: post content not matching'
);
} else {
$this->assertEquals(
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
. '"HTTP_HOST":"soba.egplusww.jp"},'
. '"REQUEST_TYPE":"POST",'
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
$response['content'],
'multi call: post content not matching'
);
}
$request_expected = json_decode(
<<<JSON
{
"HEADERS":{
"HTTP_HOST":"soba.egplusww.jp",
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
"HTTP_SECOND_CALL":"post",
"HTTP_ACCEPT":"*\/*"
},
"REQUEST_TYPE":"POST",
"PARAMS":[],
"BODY":{"foo-post":"baz"}
}
JSON,
true
);
$this->assertEquals(
$request_expected,
json_decode($response['content'], true),
'multi call: post content not matching'
);
// delete
$response = $curl->delete($this->url_basic, [
"headers" => ["third-call" => "delete"],
]);
$this->assertEquals("200", $response["code"], "multi call: delete response code not matching");
if (PHP_VERSION_ID >= 80400) {
$this->assertEquals(
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"},'
. '"REQUEST_TYPE":"DELETE","PARAMS":[],"BODY":[]}',
$response['content'],
'multi call: delete content not matching'
);
} else {
$this->assertEquals(
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
. '"HTTP_HOST":"soba.egplusww.jp"},'
. '"REQUEST_TYPE":"DELETE",'
. '"PARAMS":[],"BODY":[]}',
$response['content'],
'multi call: delete content not matching'
);
}
$request_expected = json_decode(
<<<JSON
{
"HEADERS":{
"HTTP_HOST":"soba.egplusww.jp",
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"
},
"REQUEST_TYPE":"DELETE",
"PARAMS":[],
"BODY":[]
}
JSON,
true
);
$this->assertEquals(
$request_expected,
json_decode($response['content'], true),
'multi call: delete content not matching'
);
}
// MARK: auth header set via config

View File

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

11
SECURITY.md Normal file
View File

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

1567
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -74,9 +74,21 @@ foreach ($bytes as $byte) {
print '<div style="width: 35%; text-align: right; padding-right: 2px;">';
print "(" . number_format($byte) . "/" . $byte . ") bytes :";
$_bytes = Byte::humanReadableByteFormat($byte);
print '</div><div style="width: 10%;">' . $_bytes;
print '</div><div style="width: 10%;">';
print Byte::stringByteFormat($_bytes);
print '</div>';
print '<div style="width: 10%;">' . $_bytes . '</div>';
print '<div style="width: 40%;">';
try {
print Byte::stringByteFormat($_bytes);
} catch (\LengthException $e) {
print "LengthException 1: " . $e->getMessage();
try {
print "<br>S: " . Byte::stringByteFormat($_bytes, Byte::RETURN_AS_STRING);
} catch (\LengthException $e) {
print "LengthException 2: " . $e->getMessage();
} catch (\RuntimeException $e) {
print "RuntimeException 1: " . $e->getMessage();
}
}
print "</div>";
//
print "</div>";
@@ -87,13 +99,85 @@ foreach ($bytes as $byte) {
print "bytes [si]:";
$_bytes = Byte::humanReadableByteFormat($byte, Byte::BYTE_FORMAT_SI);
print '</div><div style="width: 10%;">' . $_bytes;
print '</div><div style="width: 10%;">';
print Byte::stringByteFormat($_bytes);
print '</div><div style="width: 40%;">';
try {
print Byte::stringByteFormat($_bytes);
} catch (\LengthException $e) {
print "LengthException A: " . $e->getMessage();
try {
print "<br>Ssi: " . Byte::stringByteFormat($_bytes, Byte::RETURN_AS_STRING | Byte::BYTE_FORMAT_SI);
} catch (\LengthException $e) {
print "LengthException B: " . $e->getMessage();
} catch (\RuntimeException $e) {
print "RuntimeException A: " . $e->getMessage();
}
}
print "</div>";
//
print "</div>";
}
$string_bytes = [
'-117.42 MB',
'242.98 MB',
'254.78 MiB',
'1 EiB',
'8 EB',
'867.36EB',
'1000EB',
'10000EB',
];
print "<b>BYTE STRING TO BYTES TESTS</b><br>";
foreach ($string_bytes as $string) {
print '<div style="display: flex; border-bottom: 1px dashed gray;">';
//
print '<div style="width: 35%; text-align: right; padding-right: 2px;">';
print "string byte ($string) to bytes :";
try {
$_bytes = Byte::stringByteFormat($string);
} catch (\LengthException $e) {
print "<br>LengthException A: " . $e->getMessage();
$_bytes = 0;
}
try {
$_bytes_string = Byte::stringByteFormat($string, Byte::RETURN_AS_STRING);
} catch (\LengthException $e) {
print "<br>LengthException B: " . $e->getMessage();
$_bytes_string = '';
} catch (\RuntimeException $e) {
print "<br>RuntimeException: " . $e->getMessage();
$_bytes_string = '';
}
try {
$_bytes_si = Byte::stringByteFormat($string, Byte::BYTE_FORMAT_SI);
} catch (\LengthException $e) {
print "<br>LengthException A: " . $e->getMessage();
$_bytes_si = 0;
}
try {
$_bytes_string_si = Byte::stringByteFormat($string, Byte::RETURN_AS_STRING | Byte::BYTE_FORMAT_SI);
} catch (\LengthException $e) {
print "<br>LengthException B: " . $e->getMessage();
$_bytes_string_si = '';
} catch (\RuntimeException $e) {
print "<br>RuntimeException: " . $e->getMessage();
$_bytes_string_si = '';
}
print '</div>';
print '<div style="width: 20%;">'
. "F:" . number_format((int)$_bytes)
. '<br>B: ' . $_bytes
. '<br>S: ' . $_bytes_string
. "<br>Fsi:" . number_format((int)$_bytes_si)
. '<br>Bsi: ' . $_bytes_si
. '<br>Ssi: ' . $_bytes_string_si;
print '</div>';
print '<div style="width: 10%;">';
print "B: " . Byte::humanReadableByteFormat($_bytes) . "<br>";
print "Bsi: " . Byte::humanReadableByteFormat($_bytes_si, Byte::BYTE_FORMAT_SI);
print "</div>";
print "</div>";
}
print "</body></html>";
// __END__

View File

@@ -225,6 +225,36 @@ foreach ($intervals as $interval) {
print "STRINGTOTIME: $reverse_interval: " . DateTime::stringToTime($reverse_interval) . "<br>";
}
print "<hr>";
$interval_strings = [
'10d 5h 30m 15s 123456ms',
'18999d 0h 38m 10s 1235ms',
'18999 d 0 h 38 m 10s 1235ms',
'-2h 15m 5s',
'45s 500ms',
'0s',
'0ms',
'1s 5ms',
'1s 50ms',
'1s 500ms',
'1s 5000ms',
'10day 5hour 30min 15sec 123456millis',
'10day 5hour 30min 15sec 123456millisec',
'10day 5hour 30min 15sec 123456msec',
'-2days 3hours 15minutes 30seconds 250milliseconds',
'',
' ',
'invalid',
];
foreach ($interval_strings as $interval_string) {
print "STRINGTOTIME: $interval_string: " . DateTime::stringToTime($interval_string) . "<br>";
try {
// test exception
DateTime::stringToTime($interval_string, throw_exception:true);
} catch (\InvalidArgumentException $e) {
print "ERROR: " . $e->getMessage() . "<br><pre>" . $e . "</pre><br>";
}
}
print "<hr>";
$check_dates = [
'2021-05-01',
'2021-05-40'

View File

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

View File

@@ -40,6 +40,8 @@ print "Log ERROR: " . $log->prAr($em->getFlagLogError()) . "<br>";
print "FN: " . ml::fromName('Affe')->name . "<br>";
print "NU: " . ml::fromValue(100)->name . "<br>";
print "NU: " . ml::fromValue(1000)->name . "<br>";
print "OK.: " . ml::ok->name . "<br>";
print "OK^: " . ml::fromName('OK')->name . "<br>";
$em->setErrorMsg('123', 'error', 'msg this is bad, auto logged if debug');
$em->setErrorMsg('123', 'error', 'msg this is bad, auto logged if debug', 'target-id', 'other-style');
@@ -56,6 +58,14 @@ $em->setErrorMsg('100-2', 'error', 'Input wring', jump_target:['target' => 'foo-
$em->setMessage('error', 'I have no id set', jump_target:['target' => 'bar-123', 'info' => 'Jump Bar']);
$em->setMessage('error', 'Jump empty', jump_target:['target' => 'bar-empty']);
function inLine(\CoreLibs\Logging\ErrorMessage $em): void
{
$em->log->error('Direct log before from ', context:['function' => __FUNCTION__]);
$em->setMessage('error', 'Inline call', context:['test' => 'inLine Function']);
$em->log->error('Direct log from ', context:['function' => __FUNCTION__]);
}
inLine($em);
print "ErrorsLast: <pre>" . $log->prAr($em->getLastErrorMsg()) . "</pre>";
print "ErrorsIds: <pre>" . $log->prAr($em->getErrorIds()) . "</pre>";
print "Errors: <pre>" . $log->prAr($em->getErrorMsg()) . "</pre>";

View File

@@ -46,6 +46,7 @@ $json = '["f: {b"""ar}]';
$output = Json::jsonConvertToArray($json);
print "S::E-JSON: $json: " . DgS::printAr($output) . "<br>";
print "S::E-JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
print "S::E Validate: " . Json::jsonValidate($json) . ": " . Json::jsonGetLastError(true) . "<br>";
// direct
$json = '{"direct": "static function call"}';
@@ -58,6 +59,24 @@ $output = $json_class::jsonConvertToArray($json);
print "J/S::E-JSON: $json: " . DgS::printAr($output) . "<br>";
print "J/S::E-JSON ERROR: " . $json_class::jsonGetLastError() . ": " . $json_class::jsonGetLastError(true) . "<br>";
echo "<hr>";
$json = '{"valid":"json","invalid":"\xB1\x31"}';
$json = '{"valid":"json","invalid":"abc\x80def"}';
$output_no_flag = Json::jsonConvertToArray($json);
print "No Flag JSON: $json: " . DgS::printAr($output_no_flag) . "<br>";
print "No Flag JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
$output_flag = Json::jsonConvertToArray($json, flags:JSON_INVALID_UTF8_IGNORE);
print "No Flag JSON: $json: " . DgS::printAr($output_flag) . "<br>";
print "No Flag JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
$output_raw = json_decode($json, true, flags:JSON_INVALID_UTF8_IGNORE);
print "No Flag JSON RAW (F-1): $json: " . DgS::printAr($output_raw) . "<br>";
$output_raw = json_decode($json, true, flags:JSON_INVALID_UTF8_SUBSTITUTE);
print "No Flag JSON RAW (F-2): $json: " . DgS::printAr($output_raw) . "<br>";
$output_raw = json_decode($json, true);
print "No Flag JSON RAW: $json: " . DgS::printAr($output_raw) . "<br>";
echo "<hr>";
// $json = '{"foo": "bar"}';
// $output = Jason::jsonConvertToArray($json);
// print "S::JSON: $json: " . DgS::printAr($output) . "<br>";

View File

@@ -121,6 +121,12 @@ Class TestP
public function test(): void
{
$this->log->info('TestL::test call');
$this->subCall();
}
public function subCall(): void
{
$this->log->info('TestL::sub_call call');
}
}

View File

@@ -34,6 +34,20 @@ print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$output = RandomKey::randomKeyGen(
12,
[
0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K',
11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e',
31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o',
41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', 49 => 'x', 50 => 'y',
51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8',
61 => '9'
]
);
print "CUSTOM OUTPUT: " . $output . "<br>";
$key_length = 10;
$key_length_b = 5;
$key_lenght_long = 64;
@@ -54,6 +68,7 @@ print "===<Br>";
$_array = new CoreLibs\Create\RandomKey(['A', 'F', 'B'], ['1', '5', '9']);
print "C->RANDOMKEYGEN(pre set): " . $_array->randomKeyGen() . "<br>";
print "</body></html>";
// __END__

View File

@@ -122,6 +122,16 @@ print "Combined strings A: "
. Strings::buildCharStringFromLists(['A', 'B', 'C'], ['0', '1', '2']) . "<br>";
print "Combined strings B: "
. Strings::buildCharStringFromLists([['F'], ['G'], 'H'], [['5', ['6']], ['0'], '1', '2']) . "<br>";
print "Combined strings C: "
. Strings::buildCharStringFromLists([
0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K',
11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e',
31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o',
41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', 49 => 'x', 50 => 'y',
51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8',
61 => '9'
]) . "<br>";
$input_string = "AaBbCc";
print "Unique: " . Strings::removeDuplicates($input_string) . "<br>";
@@ -140,6 +150,28 @@ $preg_error = Strings::isValidRegex($regex_string);
print "[B] LAST PREGE ERROR: " . preg_last_error() . " -> "
. Strings::getLastRegexErrorString() . " -> " . preg_last_error_msg() . "<br>";
$base_strings = [
'abcddfff',
'A-Z',
'a-z',
'A-Za-z',
'A-Df-g',
'A-D0-9',
'D-A7-0',
'A-FB-G',
'0-9',
'あ-お',
'ア-オ',
];
foreach ($base_strings as $string) {
try {
$parsed = Strings::parseCharacterRanges($string);
print "Parsed ranges for '$string': " . DgS::printAr($parsed) . "<br>";
} catch (\InvalidArgumentException $e) {
print "Error parsing ranges for '$string': " . $e->getMessage() . "<br>";
}
}
print "</body></html>";
// __END__

View File

@@ -56,6 +56,8 @@ print "UNIQU ID LONG : " . Uids::uniqIdLong() . "<br>";
$uuidv4 = Uids::uuidv4();
if (!Uids::validateUuuidv4($uuidv4)) {
print "Invalid UUIDv4: " . $uuidv4 . "<br>";
} else {
print "Valid UUIDv4: " . $uuidv4 . "<br>";
}
if (!Uids::validateUuuidv4("foobar")) {
print "Invalid UUIDv4: hard coded<Br>";

View File

@@ -134,8 +134,8 @@ function setCenter(id, left, top)
{
// get size of id
var dimensions = {
height: $('#' + id).height(),
width: $('#' + id).width()
height: $('#' + id).height() ?? 0,
width: $('#' + id).width() ?? 0
};
var type = $('#' + id).css('position');
var viewport = getWindowSize();
@@ -146,14 +146,14 @@ function setCenter(id, left, top)
// console.log('Left: %s, Top: %s (%s)', parseInt((viewport.width / 2) - (dimensions.width / 2) + offset.left), parseInt((viewport.height / 2) - (dimensions.height / 2) + offset.top), parseInt((viewport.height / 2) - (dimensions.height / 2)));
if (left) {
$('#' + id).css({
left: parseInt((viewport.width / 2) - (dimensions.width / 2) + offset.left) + 'px'
left: ((viewport.width / 2) - (dimensions.width / 2) + offset.left) + 'px'
});
}
if (top) {
// if we have fixed, we do not add the offset, else it moves out of the screen
var top_pos = type == 'fixed' ?
parseInt((viewport.height / 2) - (dimensions.height / 2)) :
parseInt((viewport.height / 2) - (dimensions.height / 2) + offset.top);
(viewport.height / 2) - (dimensions.height / 2) :
(viewport.height / 2) - (dimensions.height / 2) + offset.top;
$('#' + id).css({
top: top_pos + 'px'
});

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -46,10 +46,15 @@ function runFunctionArgsArray(name, args) {
fn.apply(window, args);
}
function isObject(val) {
if (val === null) {
return false;
}
return typeof val === "function" || typeof val === "object";
return val !== null && typeof val === "object" && !Array.isArray(val);
}
function isArray(val) {
return val !== null && Array.isArray(val);
}
function isIterable(val) {
if (val == null) return false;
if (typeof val[Symbol.iterator] === "function" && typeof val !== "string") return true;
return typeof val === "object" && val.constructor === Object;
}
function getObjectCount(object) {
if (!isObject(object)) {
@@ -158,7 +163,7 @@ var HtmlElementCreator = class {
if (base.id == id) {
base.sub.push(deepCopyFunction(attach));
} else {
if (isObject(base.sub) && base.sub.length > 0) {
if (isArray(base.sub) && base.sub.length > 0) {
for (var i = 0; i < base.sub.length; i++) {
this.ael(base.sub[i], attach, id);
}
@@ -297,7 +302,7 @@ var HtmlElementCreator = class {
line += ' name="' + (tree.name ? tree.name : tree.id) + '"';
}
}
if (isObject(tree.css) && tree.css.length > 0) {
if (isArray(tree.css) && tree.css.length > 0) {
line += ' class="';
for (i = 0; i < tree.css.length; i++) {
line += tree.css[i] + " ";
@@ -314,7 +319,7 @@ var HtmlElementCreator = class {
}
line += ">";
content.push(line);
if (isObject(tree.sub) && tree.sub.length > 0) {
if (isArray(tree.sub) && tree.sub.length > 0) {
if (tree.content) {
content.push(tree.content);
}
@@ -1331,6 +1336,17 @@ var LoginNavMenu = class {
}
};
// src/utils/BrowserDetect.mjs
function isWebkit() {
return "GestureEvent" in window;
}
function isMobileWebKit() {
return "ongesturechange" in window;
}
function isDesktopWebKit() {
return typeof window !== "undefined" && "safari" in window && "pushNotification" in window.safari;
}
// src/utils.mjs
var aiob = new ActionIndicatorOverlayBox();
var hec = new HtmlElementCreator();
@@ -1433,6 +1449,12 @@ function executeFunctionByName2(functionName, context) {
function isObject2(val) {
return isObject(val);
}
function isArray2(val) {
return isArray(val);
}
function isIterable2(val) {
return isIterable(val);
}
function getObjectCount2(object) {
return getObjectCount(object);
}
@@ -1613,3 +1635,12 @@ function createActionBox(target_id = "actionBox", title = "", contents = {}, hea
function adjustActionBoxHeight(target_id = "actionBox", override = 0, content_override = 0) {
ab.adjustActionBoxHeight(target_id, override, content_override);
}
function isWebkit2() {
return isWebkit();
}
function isMobileWebKit2() {
return isMobileWebKit();
}
function isDesktopWebKit2() {
return isDesktopWebKit();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -10,12 +10,16 @@ class Email
/** @var array<int,string> */
private static array $email_regex_check = [
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
// . "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
// fixed pattern matching for domain
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // MASTER
1 => "@(.*)@(.*)", // double @
2 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@", // wrong part before @
3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
5 => "\.([a-zA-Z]{2,6}){1}$", // wrong top level part
// 3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
3 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // wrong part after @
// 4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
4 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.", // wrong domain name part
5 => "\.[a-zA-Z]{2,6}$", // wrong top level part
6 => "@(.*)\.{2,}", // double .. in domain name part
7 => "@.*\.$" // ends with a dot, top level, domain missing
];

View File

@@ -114,7 +114,7 @@ class Encoding
(($char != $r_char && (!self::$mb_error_char ||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
ord($char) != 194
ord($char[0]) != 194
) {
$failed[] = $char;
}

View File

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

View File

@@ -395,39 +395,68 @@ class DateTime
* does a reverse of the timeStringFormat and converts the string from
* xd xh xm xs xms to a timestamp.microtime format
*
* @param string|int|float $timestring formatted interval
* @return string|int|float converted float interval, or string as is
* @param string|int|float $timestring formatted interval
* @param bool $throw_exception [default=false] if set to true will throw exception
* instead of returning input value as is
* @return string|int|float converted float interval, or string as is
*/
public static function stringToTime(string|int|float $timestring): string|int|float
{
public static function stringToTime(
string|int|float $timestring,
bool $throw_exception = false
): string|int|float {
$timestamp = 0;
if (!preg_match("/(d|h|m|s|ms)/", (string)$timestring)) {
return $timestring;
}
$timestring = (string)$timestring;
// pos for preg match read + multiply factor
$timegroups = [2 => 86400, 4 => 3600, 6 => 60, 8 => 1];
$matches = [];
// if start with -, strip and set negative
$negative = false;
if (preg_match("/^-/", $timestring)) {
$negative = true;
$timestring = substr($timestring, 1);
}
// preg match: 0: full string
// 2, 4, 6, 8 are the to need values
preg_match("/^((\d+)d ?)?((\d+)h ?)?((\d+)m ?)?((\d+)s ?)?((\d+)ms)?$/", $timestring, $matches);
if (
!preg_match(
"/^\s*(-)?\s*"
. "((\d+)\s*d(?:ay(?:s)?)?)?\s*"
. "((\d+)\s*h(?:our(?:s)?)?)?\s*"
. "((\d+)\s*m(?:in(?:ute)?(?:s)?)?)?\s*"
. "((\d+)\s*s(?:ec(?:ond)?(?:s)?)?)?\s*"
. "((\d+)\s*m(?:illi)?s(?:ec(?:ond)?(?:s)?)?)?\s*"
. "$/",
(string)$timestring,
$matches
)
) {
if ($throw_exception) {
throw new \InvalidArgumentException(
'Invalid time string format, cannot parse: "' . (string)$timestring . '"',
1
);
}
return $timestring;
}
if (count($matches) < 2) {
if ($throw_exception) {
throw new \InvalidArgumentException(
'Invalid time string format, no interval value found: "' . (string)$timestring . '"',
2
);
}
return $timestring;
}
// pos for preg match read + multiply factor
$timegroups = [3 => 86400, 5 => 3600, 7 => 60, 9 => 1];
// if start with -, strip and set negative
$negative = false;
if (!empty($matches[1])) {
$negative = true;
}
// multiply the returned matches and sum them up. the last one (ms) is added with .
foreach ($timegroups as $i => $time_multiply) {
if (isset($matches[$i]) && is_numeric($matches[$i])) {
$timestamp += (float)$matches[$i] * $time_multiply;
}
}
if (isset($matches[10]) && is_numeric($matches[10])) {
$timestamp .= '.' . $matches[10];
if (isset($matches[11]) && is_numeric($matches[11])) {
// for milliseconds, we need to divide by 1000 and add them
$timestamp += (float)($matches[11] / 1000);
}
if ($negative) {
// cast to flaot so we can do a negative multiplication
// cast to float so we can do a negative multiplication
$timestamp = (float)$timestamp * -1;
}
return $timestamp;

View File

@@ -14,6 +14,7 @@ class Byte
public const BYTE_FORMAT_NOSPACE = 1;
public const BYTE_FORMAT_ADJUST = 2;
public const BYTE_FORMAT_SI = 4;
public const RETURN_AS_STRING = 8;
/**
* This function replaces the old byteStringFormat
@@ -77,7 +78,7 @@ class Byte
// labels in order of size [Y, Z]
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
// exp position calculation
$exp = floor(log($abs_bytes, $unit));
$exp = (int)floor(log($abs_bytes, $unit));
// avoid printing out anything larger than max labels
if ($exp >= count($labels)) {
$exp = count($labels) - 1;
@@ -119,7 +120,9 @@ 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 \InvalidArgumentException 1: no valid flag set
* @throws \InvalidArgumentException no valid flag set
* @throws \LengthException number too large to convert to int
* @throws \RuntimeException BCMath extension not loaded if flag is set to string
*/
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
{
@@ -129,7 +132,12 @@ class Byte
} else {
$si = false;
}
if ($flags != 0 && $flags != 4) {
if ($flags & self::RETURN_AS_STRING) {
$return_as_string = true;
} else {
$return_as_string = false;
}
if ($flags != 0 && $flags != 4 && $flags != 8 && $flags != 12) {
throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
}
// matches in regex
@@ -142,6 +150,10 @@ class Byte
strtolower((string)$number),
$matches
);
$number_negative = false;
if (!empty($matches[1])) {
$number_negative = true;
}
if (isset($matches[2]) && isset($matches[3])) {
// remove all non valid characters from the number
$number = preg_replace('/[^0-9\.]/', '', $matches[2]);
@@ -152,12 +164,48 @@ class Byte
if ($unit) {
$number = $number * pow($si ? 1000 : 1024, stripos($valid_units_, $unit[0]) ?: 0);
}
// if the number is too large, we cannot convert to int directly
if ($number <= PHP_INT_MIN || $number >= PHP_INT_MAX) {
// if we do not want to convert to string
if (!$return_as_string) {
throw new \LengthException(
'Number too large be converted to int: ' . (string)$number
);
}
// for string, check if bcmath is loaded, if not this will not work
if (!extension_loaded('bcmath')) {
throw new \RuntimeException(
'Number too large be converted to int and BCMath extension not loaded: ' . (string)$number
);
}
}
// string return
if ($return_as_string) {
// return as string to avoid overflow
// $number = (string)round($number);
$number = bcmul(number_format(
$number,
12,
'.',
''
), "1");
if ($number_negative) {
$number = '-' . $number;
}
return $number;
}
// convert to INT to avoid +E output
$number = (int)round($number);
// if negative input, keep nnegative
if (!empty($matches[1])) {
if ($number_negative) {
$number *= -1;
}
// check if number is negative but should be, this is Lenght overflow
if (!$number_negative && $number < 0) {
throw new \LengthException(
'Number too large be converted to int: ' . (string)$number
);
}
}
// if not matching return as is
return $number;

View File

@@ -27,10 +27,14 @@ class Json
* set original value as array
* @return array<mixed> returns an array from the json values
*/
public static function jsonConvertToArray(?string $json, bool $override = false): array
public static function jsonConvertToArray(?string $json, bool $override = false, int $flags = 0): array
{
if ($json !== null) {
$_json = json_decode($json, true);
// if flags has JSON_THROW_ON_ERROR remove it
if ($flags & JSON_THROW_ON_ERROR) {
$flags = $flags & ~JSON_THROW_ON_ERROR;
}
$_json = json_decode($json, true, flags:$flags);
if (self::$json_last_error = json_last_error()) {
if ($override == true) {
// init return as array with original as element
@@ -55,16 +59,31 @@ class Json
* Deos not throw errors
*
* @param array<mixed> $data
* @param int $flags json_encode flags as is
* @param int $flags [JSON_UNESCAPED_UNICODE] json_encode flags as is
* @return string JSON string or '{}' if false
*/
public static function jsonConvertArrayTo(array $data, int $flags = 0): string
public static function jsonConvertArrayTo(array $data, int $flags = JSON_UNESCAPED_UNICODE): string
{
$json_string = json_encode($data, $flags) ?: '{}';
self::$json_last_error = json_last_error();
return (string)$json_string;
}
/**
* Validate if a json string could be decoded.
* Weill set the internval last error state and info can be read with jsonGetLastError
*
* @param string $json
* @param int $flags only JSON_INVALID_UTF8_IGNORE is currently allowed
* @return bool
*/
public static function jsonValidate(string $json, int $flags = 0): bool
{
$json_valid = json_validate($json, flags:$flags);
self::$json_last_error = json_last_error();
return $json_valid;
}
/**
* returns human readable string for json errors thrown in jsonConvertToArray
* Source: https://www.php.net/manual/en/function.json-last-error.php

View File

@@ -62,10 +62,15 @@ class Math
*
* @param float $number Number to cubic root
* @return float Calculated value
* @throws \InvalidArgumentException if $number is negative
*/
public static function cbrt(float|int $number): float
{
return pow((float)$number, 1.0 / 3);
$value = pow((float)$number, 1.0 / 3);
if (is_nan($value)) {
throw new \InvalidArgumentException('cube root from this number is not supported: ' . $number);
}
return $value;
}
/**
@@ -199,15 +204,17 @@ class Math
callback: fn ($col) => is_array($row) ?
array_reduce(
array: $row,
callback: fn ($a, $v, $i = null) => $a + $v * (
// TODO check that v is not an array
callback: fn ($a, $v, $i = null) => $a + $v * ( /** @phpstan-ignore-line Possible array + int */
// if last entry missing for full copy add a 0 to it
$col[$i ?? array_search($v, $row, true)] ?? 0 /** @phpstan-ignore-line */
$col[$i ?? array_search($v, $row, true)] ?? 0
),
initial: 0,
) :
array_reduce(
array: $col,
callback: fn ($a, $v) => $a + $v * $row,
// TODO check that v is not an array
callback: fn ($a, $v) => $a + $v * $row, /** @phpstan-ignore-line Possible array + int */
initial: 0,
),
array: $bCols,

View File

@@ -268,6 +268,55 @@ class Strings
));
}
/**
* Split up character ranges in format A-Z, a-z, 0-9
*
* @param string $input
* @return string[]
*/
public static function parseCharacterRanges(string $input): array
{
// if not alphanumeric, throw value error
if (!preg_match("/^[A-Za-z0-9\-\s]+$/u", $input)) {
throw new \InvalidArgumentException(
"The input string contains invalid characters, "
. "only alphanumeric, dash (-), space and 'or' are allowed: "
. $input
);
}
// Remove all spaces
$input = str_replace(' ', '', $input);
$result = [];
// if there is no - inside, return unique characters as array
if (strpos($input, '-') === false) {
return array_unique(mb_str_split($input));
}
// Find all patterns like "A-Z" (character-dash-character)
preg_match_all('/(.)-(.)/u', $input, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$start = $match[1];
$end = $match[2];
// Get ASCII/Unicode values
$startOrd = ord($start[0]);
$endOrd = ord($end[0]);
// make sure start is before end
if ($startOrd > $endOrd) {
[$startOrd, $endOrd] = [$endOrd, $startOrd];
}
// Generate range of characters
for ($i = $startOrd; $i <= $endOrd; $i++) {
$char = chr($i);
if (!in_array($char, $result)) {
$result[] = $char;
}
}
}
// make the result unique
$result = array_unique($result);
return $result;
}
/**
* Check if a regex is valid. Does not return the detail regex parser error
*

View File

@@ -27,7 +27,7 @@ class RandomKey
/** @var int character count in they key character range */
private static int $key_character_range_length = 0;
/** @var int default key lenghth */
/** @deprecated Will be removed */
/** @deprecated Will be removed, as setting has moved to randomKeyGen */
private static int $key_length = 4;
/**
@@ -49,7 +49,7 @@ class RandomKey
private static function validateRandomKeyData(array ...$key_range): string
{
$key_character_range = Strings::buildCharStringFromLists(...$key_range);
if (strlen(self::$key_character_range) <= 1) {
if (strlen($key_character_range) <= 1) {
return '';
}
return $key_character_range;
@@ -107,7 +107,7 @@ class RandomKey
* @param int $key_length key length
* @return bool true for valid, false for invalid length
*/
private static function validateRandomKeyLenght(int $key_length): bool
private static function validateRandomKeyLength(int $key_length): bool
{
if (
$key_length > 0 &&
@@ -125,12 +125,12 @@ class RandomKey
*
* @param int $key_length key length
* @return bool true/false for set status
* @deprecated This function does no longer set the key length, the randomKeyGen parameter has to b used
* @deprecated This function does no longer set the key length, the randomKeyGen parameter has to be used
*/
public static function setRandomKeyLength(int $key_length): bool
{
// only if valid int key with valid length
if (self::validateRandomKeyLenght($key_length) === true) {
if (self::validateRandomKeyLength($key_length) === true) {
self::$key_length = $key_length;
return true;
} else {
@@ -176,7 +176,7 @@ class RandomKey
$key_character_range_length = self::getRandomKeyDataLength();
}
// if not valid key length, fallback to default
if (!self::validateRandomKeyLenght($key_length)) {
if (!self::validateRandomKeyLength($key_length)) {
$key_length = self::KEY_LENGTH_DEFAULT;
}
// create random string

View File

@@ -81,7 +81,7 @@ class Uids
*/
public static function validateUuuidv4(string $uuidv4): bool
{
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/", $uuidv4)) {
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i", $uuidv4)) {
return false;
}
return true;

View File

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

View File

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

View File

@@ -131,6 +131,7 @@ class ErrorMessage
// set a jump target
$this->setJumpTarget($jump_target['target'] ?? null, $jump_target['info'] ?? null, $level);
// write to log for abort/crash
$this->log->setErrorMessageCallSetErrorMsg();
switch ($level) {
case 'notice':
$this->log->notice($message ?? $str, array_merge([
@@ -210,6 +211,7 @@ class ErrorMessage
?bool $log_error = null,
?bool $log_warning = null,
): void {
$this->log->setErrorMessageCallSetMessage();
$this->setErrorMsg(
$error_id ?? '',
$level,
@@ -289,7 +291,7 @@ class ErrorMessage
*/
public function getLastErrorMsg(): array
{
return $this->error_str[array_key_last($this->error_str)] ?? [
return $this->error_str[array_key_last($this->error_str) ?? -1] ?? [
'level' => '',
'str' => '',
'id' => '',

View File

@@ -112,7 +112,7 @@ enum Level: int
}
/**
* Returns true if the passed $level is higher or equal to $this
* Returns true if the passed $level is included in set level
*
* @param Level $level
* @return bool

View File

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

View File

@@ -29,11 +29,19 @@ use Stringable;
class Logging
{
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
public const MIN_LOG_MAX_FILESIZE = 10 * 1024;
public const int MIN_LOG_MAX_FILESIZE = 10 * 1024;
/** @var string log file extension, not changeable */
private const LOG_FILE_NAME_EXT = "log";
private const string LOG_FILE_NAME_EXT = "log";
/** @var string log file block separator, not changeable */
private const LOG_FILE_BLOCK_SEPARATOR = '.';
private const string LOG_FILE_BLOCK_SEPARATOR = '.';
/** @var int the base stack trace level for the line number */
private const int DEFAULT_STACK_TRACE_LEVEL_LINE = 1;
/** @var array<string,int> */
private const array STACK_OVERRIDE_CHECK = [
'setErrorMsg' => 2,
'setMessage' => 3,
];
// MARK: OPTION array
// NOTE: the second party array{} hs some errors
@@ -96,6 +104,11 @@ class Logging
'type' => 'bool', 'mandatory' => false,
'default' => false, 'deprecated' => true, 'use' => 'log_per_date'
],
// if turned off uses old time format without time zone
'log_time_format_iso' => [
'type' => 'bool', 'mandatory' => false,
'default' => true, 'deprecated' => false
]
];
// options
@@ -133,6 +146,12 @@ class Logging
/** @var string Y-m-d file in file name */
private string $log_file_date = '';
// speical flags for ErrorMessage calls
/** @var bool Flag to set if called from ErrorMessage::setErrorMsg */
private bool $error_message_call_set_error_msg = false;
/** @var bool Flag to set if called from ErrorMessage::setMessage */
private bool $error_message_call_set_message = false;
/**
* 1: create a new log file per run (time stamp + unique ID)
* 2: add Y-m-d and do automatic daily rotation
@@ -622,31 +641,65 @@ class Logging
$file_line = '';
$caller_class_method = '-';
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "[" . $level->getName() . "] [$message] prepareLog:<br>" . Support::printAr($traces);
// file + line: call not this but one before (the one that calls this)
// start from this level, if unset fall down until we are at null
$start_trace_level = 2;
for ($trace_level = $start_trace_level; $trace_level >= 0; $trace_level--) {
if (isset($traces[$trace_level])) {
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
. ':' . ($traces[$trace_level]['line'] ?? '-');
// as namespace\class->method
$caller_class_method =
// get the last call before we are in the Logging class
($traces[$trace_level]['class'] ?? '')
// connector, if unkown use ==
. ($traces[$trace_level]['type'] ?? '')
// method/function: prepareLog->(debug|info|...)->[THIS]
. $traces[$trace_level]['function'];
break;
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
// set stack trace level +1 if called from ErrorMessage::setMessage
if ($this->error_message_call_set_message) {
$stack_trace_start_level_line = 3;
} elseif ($this->error_message_call_set_error_msg) {
$stack_trace_start_level_line = 2;
}
// if we have line > default, then check if valid, else reset to default
if ($stack_trace_start_level_line > self::DEFAULT_STACK_TRACE_LEVEL_LINE) {
// check if function at level is one of the override checks
$fn_check = $traces[$stack_trace_start_level_line]['function'] ?? '';
if (
!isset(self::STACK_OVERRIDE_CHECK[$fn_check]) ||
self::STACK_OVERRIDE_CHECK[$fn_check] != $stack_trace_start_level_line
) {
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
}
}
$this->error_message_call_set_message = false;
$this->error_message_call_set_error_msg = false;
// set stack trace level +1 if called from ErrorMessage::setMessage
// print "[" . $level->getName() . "] [$message] [" . $stack_trace_start_level_line . "] "
// . "prepareLog:<br>" . Support::printAr($traces);
// file + line: call not this but one before (the one that calls this)
// start from this level, if unset fall down until we are at null
// NOTE this has to be pushed to 3 for setMessage wrap calls
for ($trace_level = $stack_trace_start_level_line; $trace_level >= 0; $trace_level--) {
if (!isset($traces[$trace_level])) {
continue;
}
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
. ':' . ($traces[$trace_level]['line'] ?? '-');
// call function is one stack level above
$trace_level++;
// skip setting if we are in the top level already
if (!isset($traces[$trace_level])) {
break;
}
// as namespace\class->method
$caller_class_method =
// get the last call before we are in the Logging class
($traces[$trace_level]['class'] ?? '')
// connector, if unkown use ==
. ($traces[$trace_level]['type'] ?? '')
// method/function: prepareLog->(debug|info|...)->[THIS]
. $traces[$trace_level]['function'];
break;
}
// if not line is set
if (empty($file_line)) {
$file_line = System::getPageName(System::FULL_PATH);
}
// print "CLASS: " . $class . "<br>";
// get timestamp
$timestamp = Support::printTime();
if (!empty($this->options['log_time_format_iso'])) {
$timestamp = Support::printIsoTime();
} else {
$timestamp = Support::printTime();
}
// if group id is empty replace it with current level
$group_str = $level->getName();
@@ -1008,6 +1061,30 @@ class Logging
return $this->log_max_filesize;
}
// *********************************************************************
// MARK: ErrorMessage class overrides
// *********************************************************************
/**
* call if called from Error Message setMessage wrapper
*
* @return void
*/
public function setErrorMessageCallSetMessage(): void
{
$this->error_message_call_set_message = true;
}
/**
* call if called from Error Message setMessage wrapper
*
* @return void
*/
public function setErrorMessageCallSetErrorMsg(): void
{
$this->error_message_call_set_error_msg = true;
}
// *********************************************************************
// MARK: OPTIONS CALLS
// *********************************************************************
@@ -1475,14 +1552,15 @@ class Logging
Level::Error, Level::Critical, Level::Alert, Level::Emergency
] as $l
) {
print "Check: " . $this->log_level->getName() . " | " . $l->getName() . "<br>";
if ($this->log_level->isHigherThan($l)) {
print "L: " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
print "L(gt): " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
}
if ($this->log_level->includes($l)) {
print "L: " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
print "L(le): " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
}
if ($this->log_level->isLowerThan($l)) {
print "L: " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
print "L(lt): " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
}
echo "<br>";
}

View File

@@ -365,9 +365,6 @@ class Image
imagepng($thumb, $thumbnail_write_path . $thumbnail);
break;
}
// free up resources (in case we are called in a loop)
imagedestroy($source);
imagedestroy($thumb);
} else {
throw new \RuntimeException(
'Invalid source image file. Only JPEG/PNG are allowed: ' . $filename,
@@ -543,8 +540,6 @@ class Image
imagepng($img, $filename);
break;
}
// clean up image if we have an image
imagedestroy($img);
}
}

View File

@@ -614,8 +614,6 @@ class Curl implements Interface\RequestsInterface
// print "CURLINFO_HEADER_OUT: <pre>" . curl_getinfo($handle, CURLINFO_HEADER_OUT) . "</pre>";
// get response code and bail on not authorized
$http_response = $this->handleCurlResponse($handle, $http_result, $options['http_errors']);
// close handler
$this->handleCurlClose($handle);
// return response and result
return [
'code' => (string)$http_response,
@@ -838,17 +836,6 @@ class Curl implements Interface\RequestsInterface
);
}
/**
* close the current curl handle
*
* @param \CurlHandle $handle
* @return void
*/
private function handleCurlClose(\CurlHandle $handle): void
{
curl_close($handle);
}
// *********************************************************************
// MARK: PUBLIC METHODS
// *********************************************************************