Compare commits
56 Commits
v9.32.1
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a28c86991 | ||
|
|
38b2ffe82a | ||
|
|
6e547abccb | ||
|
|
676af5e1a4 | ||
|
|
118aacee28 | ||
|
|
564e23ecd7 | ||
|
|
2889012592 | ||
|
|
cd65604073 | ||
|
|
a3cf5f45f9 | ||
|
|
6f3dacdec0 | ||
|
|
2ab1ee90ef | ||
|
|
b8c0aff975 | ||
|
|
c5fed66237 | ||
|
|
157169d3ba | ||
|
|
60fe0e0def | ||
|
|
e473e7899d | ||
|
|
8af71b70a3 | ||
|
|
7d10b4c5af | ||
|
|
c81b602657 | ||
|
|
59cc5f2060 | ||
|
|
e072aaf4d6 | ||
|
|
259f9cebf3 | ||
|
|
bd4f674f0f | ||
|
|
87293bf633 | ||
|
|
be46d6e101 | ||
|
|
433bc3d539 | ||
|
|
4ed645bac3 | ||
|
|
908376c1a5 | ||
|
|
c329e7a2da | ||
|
|
ad7b59e26a | ||
|
|
c43bb0662d | ||
|
|
c4e83f94e9 | ||
|
|
a292abc2c5 | ||
|
|
6c5af91386 | ||
|
|
73fc74a43a | ||
|
|
b89238b922 | ||
|
|
9115fc9557 | ||
|
|
62d9cda3d0 | ||
|
|
dbc72472f9 | ||
|
|
3be3519e45 | ||
|
|
4707427ff4 | ||
|
|
73ac0b68b6 | ||
|
|
a501fa25de | ||
|
|
d4db235e5b | ||
|
|
c70cdf457f | ||
|
|
57aae073d7 | ||
|
|
4bebec2b47 | ||
|
|
991750aa5f | ||
| 426afdc1ff | |||
| ffff65a76d | |||
|
|
c22e68f19a | ||
|
|
074d5bed4c | ||
|
|
93cb7e0cab | ||
|
|
7fbce6529b | ||
|
|
6e086fe7b3 | ||
|
|
0ec19d5b75 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ www/composer.lock
|
|||||||
www/vendor
|
www/vendor
|
||||||
**/.env
|
**/.env
|
||||||
**/.target
|
**/.target
|
||||||
|
package-lock.json
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
use Phan\Config;
|
use Phan\Config;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// "target_php_version" => "8.2",
|
// "target_php_version" => "8.3",
|
||||||
"minimum_target_php_version" => "8.2",
|
"minimum_target_php_version" => "8.3",
|
||||||
// turn color on (-C)
|
// turn color on (-C)
|
||||||
"color_issue_messages_if_supported" => true,
|
"color_issue_messages_if_supported" => true,
|
||||||
// If true, missing properties will be created when
|
// If true, missing properties will be created when
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<phive xmlns="https://phar.io/phive">
|
<phive xmlns="https://phar.io/phive">
|
||||||
<phar name="phpunit" version="^10.3.5" installed="10.3.5" location="./tools/phpunit" copy="false"/>
|
<phar name="phpunit" version="~9.6" installed="9.6.31" location="./tools/phpunit" copy="false"/>
|
||||||
<phar name="phpcbf" version="^3.7.2" installed="3.10.3" location="./tools/phpcbf" copy="false"/>
|
<phar name="phpcbf" version="4" installed="4.0.1" location="./tools/phpcbf" copy="false"/>
|
||||||
<phar name="phpcs" version="^3.10.3" installed="3.10.3" location="./tools/phpcs" copy="false"/>
|
<phar name="phpcs" version="4" installed="4.0.1" location="./tools/phpcs" copy="false"/>
|
||||||
<phar name="phpstan" version="^2.0" installed="2.0.4" location="./tools/phpstan" 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.4.3" location="./tools/phan" copy="false"/>
|
<phar name="phan" version="^5.4.3" installed="5.5.2" location="./tools/phan" copy="false"/>
|
||||||
<phar name="psalm" version="^5.15.0" installed="5.24.0" location="./tools/psalm" 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="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="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.57.2" location="./tools/php-cs-fixer" copy="false"/>
|
<phar name="php-cs-fixer" version="^3.34.1" installed="3.92.4" location="./tools/php-cs-fixer" copy="false"/>
|
||||||
</phive>
|
</phive>
|
||||||
|
|||||||
@@ -1,5 +1,100 @@
|
|||||||
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
#!/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}
|
# must be run in ${base}
|
||||||
cd $base || exit;
|
cd "$BASE_PATH" || exit;
|
||||||
${base}tools/phan --progress-bar -C --analyze-twice;
|
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;
|
cd ~ || exit;
|
||||||
|
|
||||||
|
# __END__
|
||||||
|
|||||||
@@ -1,5 +1,92 @@
|
|||||||
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
#!/bin/env bash
|
||||||
# must be run in ${base}
|
|
||||||
cd $base || exit;
|
function error() {
|
||||||
${base}tools/phpstan;
|
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;
|
cd ~ || exit;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ function error() {
|
|||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
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.
|
Runs all the PHP unit tests.
|
||||||
|
|
||||||
@@ -15,15 +15,16 @@ If -p is not set, the default intalled PHP is used.
|
|||||||
Available options:
|
Available options:
|
||||||
|
|
||||||
-h, --help Print this help and exit
|
-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
|
-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
|
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
|
||||||
EOF
|
EOF
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
# set base variables
|
# set base variables
|
||||||
BASE_PATH="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
BASE_PATH=$(pwd)"/";
|
||||||
PHPUNIT_CONFIG="${BASE_PATH}phpunit.xml";
|
PHPUNIT_CONFIG="${BASE_PATH}phpunit.xml";
|
||||||
PHP_BIN_PATH=$(which php);
|
PHP_BIN_PATH=$(which php);
|
||||||
if [ -z "${PHP_BIN_PATH}" ]; then
|
if [ -z "${PHP_BIN_PATH}" ]; then
|
||||||
@@ -45,6 +46,7 @@ opt_testdox="";
|
|||||||
opt_verbose="";
|
opt_verbose="";
|
||||||
php_version="";
|
php_version="";
|
||||||
no_php_version=0;
|
no_php_version=0;
|
||||||
|
use_composer=0;
|
||||||
while [ -n "${1-}" ]; do
|
while [ -n "${1-}" ]; do
|
||||||
case "${1}" in
|
case "${1}" in
|
||||||
-t | --testdox)
|
-t | --testdox)
|
||||||
@@ -53,6 +55,10 @@ while [ -n "${1-}" ]; do
|
|||||||
-v | --verbose)
|
-v | --verbose)
|
||||||
opt_verbose="--verbose";
|
opt_verbose="--verbose";
|
||||||
;;
|
;;
|
||||||
|
-c | --composer)
|
||||||
|
use_composer=1;
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-p | --php)
|
-p | --php)
|
||||||
php_version="${2-}";
|
php_version="${2-}";
|
||||||
shift
|
shift
|
||||||
@@ -74,21 +80,38 @@ if [ -z "${php_version}" ]; then
|
|||||||
fi;
|
fi;
|
||||||
php_bin="${PHP_BIN_PATH}${php_version}";
|
php_bin="${PHP_BIN_PATH}${php_version}";
|
||||||
echo "Use PHP Version: ${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
|
if [ ! -f "${php_bin}" ]; then
|
||||||
echo "Set php ${php_bin} does not exist";
|
echo "Set php ${php_bin} does not exist";
|
||||||
exit;
|
exit;
|
||||||
fi;
|
fi;
|
||||||
php_bin="${php_bin} ";
|
|
||||||
|
|
||||||
# Note 4dev/tests/bootstrap.php has to be set as bootstrap file in phpunit.xml
|
# 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=(
|
||||||
|
"${php_bin}"
|
||||||
${phpunit_call};
|
);
|
||||||
|
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}";
|
echo -e "\nPHPUnit Config: ${PHPUNIT_CONFIG}";
|
||||||
if [ "${no_php_version}" -eq 0 ]; then
|
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
|
else
|
||||||
echo "Default PHP used: $(php --version)";
|
echo "Default PHP used: $(php --version)";
|
||||||
fi;
|
fi;
|
||||||
|
|||||||
@@ -9,5 +9,6 @@
|
|||||||
CREATE TABLE generic (
|
CREATE TABLE generic (
|
||||||
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
|
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
|
||||||
date_updated TIMESTAMP WITHOUT TIME ZONE,
|
date_updated TIMESTAMP WITHOUT TIME ZONE,
|
||||||
|
uuid UUID DEFAULT gen_random_uuid(),
|
||||||
uid VARCHAR
|
uid VARCHAR
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ final class CoreLibsCheckEmailTest extends TestCase
|
|||||||
'get email regex invalid -1, will be 0' => [
|
'get email regex invalid -1, will be 0' => [
|
||||||
-1,
|
-1,
|
||||||
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
"^[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' => [
|
'get email regex invalid 10, will be 0' => [
|
||||||
10,
|
10,
|
||||||
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
"^[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' => [
|
'get email regex valid 1, will be 1' => [
|
||||||
1,
|
1,
|
||||||
@@ -157,7 +157,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
|||||||
'error' => 0,
|
'error' => 0,
|
||||||
'message' => 'Invalid email address',
|
'message' => 'Invalid email address',
|
||||||
'regex' => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
'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' => [
|
'error 1 will return double @ error' => [
|
||||||
@@ -181,7 +181,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
|||||||
[
|
[
|
||||||
'error' => 3,
|
'error' => 3,
|
||||||
'message' => 'Invalid domain part after @ sign',
|
'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' => [
|
'error 4 will be invalid domain' => [
|
||||||
@@ -189,7 +189,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
|||||||
[
|
[
|
||||||
'error' => 4,
|
'error' => 4,
|
||||||
'message' => 'Invalid domain name part',
|
'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' => [
|
'error 5 will be invalid domain top level only' => [
|
||||||
@@ -197,7 +197,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
|||||||
[
|
[
|
||||||
'error' => 5,
|
'error' => 5,
|
||||||
'message' => 'Wrong domain top level part',
|
'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' => [
|
'error 6 will be domain double dot' => [
|
||||||
|
|||||||
@@ -0,0 +1,404 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// This code was created by Claude Sonnet 4
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use CoreLibs\Combined\ArrayHandler;
|
||||||
|
|
||||||
|
class CoreLibsCombinedArrayHandlerFindArraysMissingKeyTest extends TestCase
|
||||||
|
{
|
||||||
|
private const DATA_SEPARATOR = ':'; // Updated to match your class's separator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test finding missing single key when searching by value without specific key
|
||||||
|
*/
|
||||||
|
public function testFindMissingSingleKeyWithValueSearch()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'item1' => [
|
||||||
|
'name' => 'John',
|
||||||
|
'age' => 25
|
||||||
|
// missing 'email' key
|
||||||
|
],
|
||||||
|
'item2' => [
|
||||||
|
'name' => 'Jane',
|
||||||
|
'age' => 30,
|
||||||
|
'email' => 'jane@example.com'
|
||||||
|
],
|
||||||
|
'item3' => [
|
||||||
|
'name' => 'John', // same value as item1
|
||||||
|
'age' => 35,
|
||||||
|
'email' => 'john2@example.com'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'John', 'email');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals($array['item1'], $result[0]['content']);
|
||||||
|
$this->assertEquals('item1', $result[0]['path']);
|
||||||
|
$this->assertEquals(['email'], $result[0]['missing_key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test finding missing single key when searching by specific key-value pair
|
||||||
|
*/
|
||||||
|
public function testFindMissingSingleKeyWithKeyValueSearch()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'user1' => [
|
||||||
|
'id' => 1,
|
||||||
|
'name' => 'Alice'
|
||||||
|
// missing 'status' key
|
||||||
|
],
|
||||||
|
'user2' => [
|
||||||
|
'id' => 2,
|
||||||
|
'name' => 'Bob',
|
||||||
|
'status' => 'active'
|
||||||
|
],
|
||||||
|
'user3' => [
|
||||||
|
'id' => 1, // same id as user1
|
||||||
|
'name' => 'Charlie',
|
||||||
|
'status' => 'inactive'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 1, 'status', 'id');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals($array['user1'], $result[0]['content']);
|
||||||
|
$this->assertEquals('user1', $result[0]['path']);
|
||||||
|
$this->assertEquals(['status'], $result[0]['missing_key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test finding missing multiple keys
|
||||||
|
*/
|
||||||
|
public function testFindMissingMultipleKeys()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'record1' => [
|
||||||
|
'name' => 'Test',
|
||||||
|
'value' => 100
|
||||||
|
// missing both 'date' and 'status' keys
|
||||||
|
],
|
||||||
|
'record2' => [
|
||||||
|
'name' => 'Test',
|
||||||
|
'value' => 200,
|
||||||
|
'date' => '2023-01-01'
|
||||||
|
// missing 'status' key
|
||||||
|
],
|
||||||
|
'record3' => [
|
||||||
|
'name' => 'Test',
|
||||||
|
'value' => 300,
|
||||||
|
'date' => '2023-01-02',
|
||||||
|
'status' => 'complete'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'Test', ['date', 'status']);
|
||||||
|
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
|
||||||
|
// First result should be record1 missing both keys
|
||||||
|
$this->assertEquals($array['record1'], $result[0]['content']);
|
||||||
|
$this->assertEquals('record1', $result[0]['path']);
|
||||||
|
$this->assertContains('date', $result[0]['missing_key']);
|
||||||
|
$this->assertContains('status', $result[0]['missing_key']);
|
||||||
|
$this->assertCount(2, $result[0]['missing_key']);
|
||||||
|
|
||||||
|
// Second result should be record2 missing status key
|
||||||
|
$this->assertEquals($array['record2'], $result[1]['content']);
|
||||||
|
$this->assertEquals('record2', $result[1]['path']);
|
||||||
|
$this->assertEquals(['status'], $result[1]['missing_key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with nested arrays
|
||||||
|
*/
|
||||||
|
public function testFindMissingKeyInNestedArrays()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'section1' => [
|
||||||
|
'items' => [
|
||||||
|
'item1' => [
|
||||||
|
'name' => 'Product A',
|
||||||
|
'price' => 99.99
|
||||||
|
// missing 'category' key
|
||||||
|
],
|
||||||
|
'item2' => [
|
||||||
|
'name' => 'Product B',
|
||||||
|
'price' => 149.99,
|
||||||
|
'category' => 'electronics'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'section2' => [
|
||||||
|
'data' => [
|
||||||
|
'name' => 'Product A', // same name as nested item
|
||||||
|
'category' => 'books'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'Product A', 'category');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals($array['section1']['items']['item1'], $result[0]['content']);
|
||||||
|
$this->assertEquals('section1:items:item1', $result[0]['path']);
|
||||||
|
$this->assertEquals(['category'], $result[0]['missing_key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test when no arrays are missing the required key
|
||||||
|
*/
|
||||||
|
public function testNoMissingKeys()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'item1' => [
|
||||||
|
'name' => 'John',
|
||||||
|
'email' => 'john@example.com'
|
||||||
|
],
|
||||||
|
'item2' => [
|
||||||
|
'name' => 'Jane',
|
||||||
|
'email' => 'jane@example.com'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'John', 'email');
|
||||||
|
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test when search value is not found in any array
|
||||||
|
*/
|
||||||
|
public function testSearchValueNotFound()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'item1' => [
|
||||||
|
'name' => 'John',
|
||||||
|
'age' => 25
|
||||||
|
],
|
||||||
|
'item2' => [
|
||||||
|
'name' => 'Jane',
|
||||||
|
'age' => 30
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'Bob', 'email');
|
||||||
|
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with different data types for search value
|
||||||
|
*/
|
||||||
|
public function testDifferentSearchValueTypes()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'item1' => [
|
||||||
|
'active' => true,
|
||||||
|
'count' => 5
|
||||||
|
// missing 'label' key
|
||||||
|
],
|
||||||
|
'item2' => [
|
||||||
|
'active' => false,
|
||||||
|
'count' => 10,
|
||||||
|
'label' => 'test'
|
||||||
|
],
|
||||||
|
'item3' => [
|
||||||
|
'active' => true, // same boolean as item1
|
||||||
|
'count' => 15,
|
||||||
|
'label' => 'another'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test with boolean
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, true, 'label', 'active');
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals('item1', $result[0]['path']);
|
||||||
|
|
||||||
|
// Test with integer
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 5, 'label', 'count');
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals('item1', $result[0]['path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with empty array
|
||||||
|
*/
|
||||||
|
public function testEmptyArray()
|
||||||
|
{
|
||||||
|
$array = [];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'key');
|
||||||
|
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with array containing non-array values
|
||||||
|
*/
|
||||||
|
public function testMixedArrayTypes()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'string_value' => 'hello',
|
||||||
|
'numeric_value' => 123,
|
||||||
|
'array_value' => [
|
||||||
|
'name' => 'test',
|
||||||
|
// missing 'type' key
|
||||||
|
],
|
||||||
|
'another_array' => [
|
||||||
|
'name' => 'test',
|
||||||
|
'type' => 'example'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'type');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals($array['array_value'], $result[0]['content']);
|
||||||
|
$this->assertEquals('array_value', $result[0]['path']);
|
||||||
|
$this->assertEquals(['type'], $result[0]['missing_key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test path building with deeper nesting
|
||||||
|
*/
|
||||||
|
public function testDeepNestingPathBuilding()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'level1' => [
|
||||||
|
'level2' => [
|
||||||
|
'level3' => [
|
||||||
|
'items' => [
|
||||||
|
'target_item' => [
|
||||||
|
'name' => 'deep_test',
|
||||||
|
// missing 'required_field'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'deep_test', 'required_field');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals('level1:level2:level3:items:target_item', $result[0]['path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with custom path separator
|
||||||
|
*/
|
||||||
|
public function testCustomPathSeparator()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'level1' => [
|
||||||
|
'level2' => [
|
||||||
|
'item' => [
|
||||||
|
'name' => 'test',
|
||||||
|
// missing 'type' key
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'type', null, '/');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals('level1/level2/item', $result[0]['path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test default path separator behavior
|
||||||
|
*/
|
||||||
|
public function testDefaultPathSeparator()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'parent' => [
|
||||||
|
'child' => [
|
||||||
|
'name' => 'test',
|
||||||
|
// missing 'value' key
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Using default separator (should be ':')
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'value');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals('parent:child', $result[0]['path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test different path separators don't affect search logic
|
||||||
|
*/
|
||||||
|
public function testPathSeparatorDoesNotAffectSearchLogic()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'section' => [
|
||||||
|
'data' => [
|
||||||
|
'id' => 123,
|
||||||
|
'name' => 'item'
|
||||||
|
// missing 'status'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test with different separators - results should be identical except for path
|
||||||
|
$result1 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', ':');
|
||||||
|
$result2 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', '.');
|
||||||
|
$result3 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', '/');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result1);
|
||||||
|
$this->assertCount(1, $result2);
|
||||||
|
$this->assertCount(1, $result3);
|
||||||
|
|
||||||
|
// Content and missing_key should be the same
|
||||||
|
$this->assertEquals($result1[0]['content'], $result2[0]['content']);
|
||||||
|
$this->assertEquals($result1[0]['content'], $result3[0]['content']);
|
||||||
|
$this->assertEquals($result1[0]['missing_key'], $result2[0]['missing_key']);
|
||||||
|
$this->assertEquals($result1[0]['missing_key'], $result3[0]['missing_key']);
|
||||||
|
|
||||||
|
// Paths should be different based on separator
|
||||||
|
$this->assertEquals('section:data', $result1[0]['path']);
|
||||||
|
$this->assertEquals('section.data', $result2[0]['path']);
|
||||||
|
$this->assertEquals('section/data', $result3[0]['path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test type checking
|
||||||
|
*/
|
||||||
|
public function testStrictTypeChecking()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'item1' => [
|
||||||
|
'id' => '123', // string
|
||||||
|
'name' => 'test'
|
||||||
|
// missing 'status'
|
||||||
|
],
|
||||||
|
'item2' => [
|
||||||
|
'id' => 123, // integer
|
||||||
|
'name' => 'test2',
|
||||||
|
'status' => 'active'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Search for integer 123 - should only match item2
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id');
|
||||||
|
$this->assertEmpty($result); // item2 has the status key
|
||||||
|
|
||||||
|
// Search for string '123' - should only match item1
|
||||||
|
$result = ArrayHandler::findArraysMissingKey($array, '123', 'status', 'id');
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals('item1', $result[0]['path']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// __END__
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// This code was created by Claude Sonnet 4
|
||||||
|
// modification for value checks with assertEqualsCanonicalizing
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use CoreLibs\Combined\ArrayHandler;
|
||||||
|
|
||||||
|
class CoreLibsCombinedArrayHandlerKsortArrayTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test basic ascending sort (default behavior)
|
||||||
|
*/
|
||||||
|
public function testKsortArrayBasicAscending(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'zebra' => 'value1',
|
||||||
|
'apple' => 'value2',
|
||||||
|
'banana' => 'value3',
|
||||||
|
'cherry' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'apple' => 'value2',
|
||||||
|
'banana' => 'value3',
|
||||||
|
'cherry' => 'value4',
|
||||||
|
'zebra' => 'value1'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input);
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test descending sort with reverse=true
|
||||||
|
*/
|
||||||
|
public function testKsortArrayDescending(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'zebra' => 'value1',
|
||||||
|
'apple' => 'value2',
|
||||||
|
'banana' => 'value3',
|
||||||
|
'cherry' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'zebra' => 'value1',
|
||||||
|
'cherry' => 'value4',
|
||||||
|
'banana' => 'value3',
|
||||||
|
'apple' => 'value2'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input, false, true);
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case-insensitive ascending sort
|
||||||
|
*/
|
||||||
|
public function testKsortArrayCaseInsensitiveAscending(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'Zebra' => 'value1',
|
||||||
|
'apple' => 'value2',
|
||||||
|
'Banana' => 'value3',
|
||||||
|
'cherry' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'apple' => 'value2',
|
||||||
|
'Banana' => 'value3',
|
||||||
|
'cherry' => 'value4',
|
||||||
|
'Zebra' => 'value1'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input, true);
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case-insensitive descending sort
|
||||||
|
*/
|
||||||
|
public function testKsortArrayCaseInsensitiveDescending(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'Zebra' => 'value1',
|
||||||
|
'apple' => 'value2',
|
||||||
|
'Banana' => 'value3',
|
||||||
|
'cherry' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'Zebra' => 'value1',
|
||||||
|
'cherry' => 'value4',
|
||||||
|
'Banana' => 'value3',
|
||||||
|
'apple' => 'value2'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input, true, true);
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with mixed case keys to verify case sensitivity behavior
|
||||||
|
*/
|
||||||
|
public function testKsortArrayCaseSensitivityComparison(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'B' => 'value1',
|
||||||
|
'a' => 'value2',
|
||||||
|
'C' => 'value3',
|
||||||
|
'b' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Case-sensitive sort (uppercase comes before lowercase in ASCII)
|
||||||
|
$expectedCaseSensitive = [
|
||||||
|
'B' => 'value1',
|
||||||
|
'C' => 'value3',
|
||||||
|
'a' => 'value2',
|
||||||
|
'b' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Case-insensitive sort
|
||||||
|
$expectedCaseInsensitive = [
|
||||||
|
'a' => 'value2',
|
||||||
|
'B' => 'value1',
|
||||||
|
'b' => 'value4',
|
||||||
|
'C' => 'value3'
|
||||||
|
];
|
||||||
|
|
||||||
|
$resultCaseSensitive = ArrayHandler::ksortArray($input, false);
|
||||||
|
$resultCaseInsensitive = ArrayHandler::ksortArray($input, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expectedCaseSensitive, $resultCaseSensitive);
|
||||||
|
$this->assertEquals($expectedCaseInsensitive, $resultCaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with numeric string keys
|
||||||
|
*/
|
||||||
|
public function testKsortArrayNumericStringKeys(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'10' => 'value1',
|
||||||
|
'2' => 'value2',
|
||||||
|
'1' => 'value3',
|
||||||
|
'20' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
// String comparison, not numeric
|
||||||
|
$expected = [
|
||||||
|
'1' => 'value3',
|
||||||
|
'10' => 'value1',
|
||||||
|
'2' => 'value2',
|
||||||
|
'20' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input);
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with special characters in keys
|
||||||
|
*/
|
||||||
|
public function testKsortArraySpecialCharacters(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'key_with_underscore' => 'value1',
|
||||||
|
'key-with-dash' => 'value2',
|
||||||
|
'key.with.dot' => 'value3',
|
||||||
|
'key with space' => 'value4',
|
||||||
|
'keyWithCamelCase' => 'value5'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input);
|
||||||
|
|
||||||
|
// Verify it doesn't throw an error and maintains all keys
|
||||||
|
$this->assertCount(5, $result);
|
||||||
|
$this->assertArrayHasKey('key_with_underscore', $result);
|
||||||
|
$this->assertArrayHasKey('key-with-dash', $result);
|
||||||
|
$this->assertArrayHasKey('key.with.dot', $result);
|
||||||
|
$this->assertArrayHasKey('key with space', $result);
|
||||||
|
$this->assertArrayHasKey('keyWithCamelCase', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with empty array
|
||||||
|
*/
|
||||||
|
public function testKsortArrayEmpty(): void
|
||||||
|
{
|
||||||
|
$input = [];
|
||||||
|
$result = ArrayHandler::ksortArray($input);
|
||||||
|
$this->assertEquals([], $result);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with single element array
|
||||||
|
*/
|
||||||
|
public function testKsortArraySingleElement(): void
|
||||||
|
{
|
||||||
|
$input = ['onlykey' => 'onlyvalue'];
|
||||||
|
$result = ArrayHandler::ksortArray($input);
|
||||||
|
$this->assertEquals($input, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that original array is not modified (function returns new array)
|
||||||
|
*/
|
||||||
|
public function testKsortArrayDoesNotModifyOriginal(): void
|
||||||
|
{
|
||||||
|
$original = [
|
||||||
|
'zebra' => 'value1',
|
||||||
|
'apple' => 'value2',
|
||||||
|
'banana' => 'value3'
|
||||||
|
];
|
||||||
|
|
||||||
|
$originalCopy = $original; // Keep a copy for comparison
|
||||||
|
$result = ArrayHandler::ksortArray($original);
|
||||||
|
|
||||||
|
// Original array should remain unchanged
|
||||||
|
$this->assertEquals($originalCopy, $original);
|
||||||
|
$this->assertNotEquals(array_keys($original), array_keys($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with complex mixed data types as values
|
||||||
|
*/
|
||||||
|
public function testKsortArrayMixedValueTypes(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'string_key' => 'string_value',
|
||||||
|
'array_key' => ['nested', 'array'],
|
||||||
|
'int_key' => 42,
|
||||||
|
'bool_key' => true,
|
||||||
|
'null_key' => null
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input);
|
||||||
|
|
||||||
|
// Check that all keys are preserved and sorted
|
||||||
|
$expectedKeys = ['array_key', 'bool_key', 'int_key', 'null_key', 'string_key'];
|
||||||
|
$this->assertEquals($expectedKeys, array_keys($result));
|
||||||
|
|
||||||
|
// Check that values are preserved correctly
|
||||||
|
$this->assertEquals('string_value', $result['string_key']);
|
||||||
|
$this->assertEquals(['nested', 'array'], $result['array_key']);
|
||||||
|
$this->assertEquals(42, $result['int_key']);
|
||||||
|
$this->assertTrue($result['bool_key']);
|
||||||
|
$this->assertNull($result['null_key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test all parameter combinations
|
||||||
|
*/
|
||||||
|
public function testKsortArrayAllParameterCombinations(): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'Delta' => 'value1',
|
||||||
|
'alpha' => 'value2',
|
||||||
|
'Charlie' => 'value3',
|
||||||
|
'bravo' => 'value4'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test all 4 combinations
|
||||||
|
$result1 = ArrayHandler::ksortArray($input, false, false); // default
|
||||||
|
$result2 = ArrayHandler::ksortArray($input, false, true); // reverse only
|
||||||
|
$result3 = ArrayHandler::ksortArray($input, true, false); // lowercase only
|
||||||
|
$result4 = ArrayHandler::ksortArray($input, true, true); // both
|
||||||
|
|
||||||
|
// Each should produce different ordering
|
||||||
|
$this->assertNotEquals(array_keys($result1), array_keys($result2));
|
||||||
|
$this->assertNotEquals(array_keys($result1), array_keys($result3));
|
||||||
|
$this->assertNotEquals(array_keys($result1), array_keys($result4));
|
||||||
|
$this->assertNotEquals(array_keys($result2), array_keys($result3));
|
||||||
|
$this->assertNotEquals(array_keys($result2), array_keys($result4));
|
||||||
|
$this->assertNotEquals(array_keys($result3), array_keys($result4));
|
||||||
|
|
||||||
|
// But all should have same keys and values, just different order
|
||||||
|
$this->assertEqualsCanonicalizing(array_values($input), array_values($result1));
|
||||||
|
$this->assertEqualsCanonicalizing(array_values($input), array_values($result2));
|
||||||
|
$this->assertEqualsCanonicalizing(array_values($input), array_values($result3));
|
||||||
|
$this->assertEqualsCanonicalizing(array_values($input), array_values($result4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for comprehensive testing
|
||||||
|
*/
|
||||||
|
public function sortingParametersProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'default' => [false, false],
|
||||||
|
'reverse' => [false, true],
|
||||||
|
'lowercase' => [true, false],
|
||||||
|
'lowercase_reverse' => [true, true],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that function works with all parameter combinations using data provider
|
||||||
|
*
|
||||||
|
* @dataProvider sortingParametersProvider
|
||||||
|
*/
|
||||||
|
public function testKsortArrayWithDataProvider(bool $lowerCase, bool $reverse): void
|
||||||
|
{
|
||||||
|
$input = [
|
||||||
|
'Zebra' => 'animal1',
|
||||||
|
'apple' => 'fruit1',
|
||||||
|
'Banana' => 'fruit2',
|
||||||
|
'cat' => 'animal2'
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::ksortArray($input, $lowerCase, $reverse);
|
||||||
|
|
||||||
|
// Basic assertions that apply to all combinations
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertCount(4, $result);
|
||||||
|
$this->assertArrayHasKey('Zebra', $result);
|
||||||
|
$this->assertArrayHasKey('apple', $result);
|
||||||
|
$this->assertArrayHasKey('Banana', $result);
|
||||||
|
$this->assertArrayHasKey('cat', $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// __END__
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// created by Claude Sonnet 4
|
||||||
|
|
||||||
|
// testRecursiveSearchWithFlatResult had wrong retunr count
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use CoreLibs\Combined\ArrayHandler;
|
||||||
|
|
||||||
|
class CoreLibsCombinedArrayHandlerSelectArrayFromOptionTest extends TestCase
|
||||||
|
{
|
||||||
|
private array $testData;
|
||||||
|
private array $nestedTestData;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->testData = [
|
||||||
|
'item1' => [
|
||||||
|
'name' => 'John',
|
||||||
|
'age' => 25,
|
||||||
|
'status' => 'active',
|
||||||
|
'score' => 85.5
|
||||||
|
],
|
||||||
|
'item2' => [
|
||||||
|
'name' => 'jane',
|
||||||
|
'age' => 30,
|
||||||
|
'status' => 'inactive',
|
||||||
|
'score' => 92.0
|
||||||
|
],
|
||||||
|
'item3' => [
|
||||||
|
'name' => 'Bob',
|
||||||
|
'age' => 25,
|
||||||
|
'status' => 'active',
|
||||||
|
'score' => 78.3
|
||||||
|
],
|
||||||
|
'item4' => [
|
||||||
|
'name' => 'Alice',
|
||||||
|
'age' => 35,
|
||||||
|
'status' => 'pending',
|
||||||
|
'score' => 88.7
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->nestedTestData = [
|
||||||
|
'level1_a' => [
|
||||||
|
'name' => 'Level1A',
|
||||||
|
'type' => 'parent',
|
||||||
|
'children' => [
|
||||||
|
'child1' => [
|
||||||
|
'name' => 'Child1',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => true
|
||||||
|
],
|
||||||
|
'child2' => [
|
||||||
|
'name' => 'Child2',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => false
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'level1_b' => [
|
||||||
|
'name' => 'Level1B',
|
||||||
|
'type' => 'parent',
|
||||||
|
'children' => [
|
||||||
|
'child3' => [
|
||||||
|
'name' => 'Child3',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => true,
|
||||||
|
'nested' => [
|
||||||
|
'deep1' => [
|
||||||
|
'name' => 'Deep1',
|
||||||
|
'type' => 'deep',
|
||||||
|
'active' => true
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'item5' => [
|
||||||
|
'name' => 'Direct',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmptyArrayReturnsEmpty(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption([], 'name', 'John');
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBasicStringSearch(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'John');
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertArrayHasKey('item1', $result);
|
||||||
|
$this->assertEquals('John', $result['item1']['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBasicIntegerSearch(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($this->testData, 'age', 25);
|
||||||
|
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
$this->assertArrayHasKey('item1', $result);
|
||||||
|
$this->assertArrayHasKey('item3', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBasicFloatSearch(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($this->testData, 'score', 85.5);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertArrayHasKey('item1', $result);
|
||||||
|
$this->assertEquals(85.5, $result['item1']['score']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBasicBooleanSearch(): void
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'item1' => ['enabled' => true, 'name' => 'Test1'],
|
||||||
|
'item2' => ['enabled' => false, 'name' => 'Test2'],
|
||||||
|
'item3' => ['enabled' => true, 'name' => 'Test3']
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($data, 'enabled', true);
|
||||||
|
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
$this->assertArrayHasKey('item1', $result);
|
||||||
|
$this->assertArrayHasKey('item3', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStrictComparison(): void
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'item1' => ['value' => '25', 'name' => 'String25'],
|
||||||
|
'item2' => ['value' => 25, 'name' => 'Int25'],
|
||||||
|
'item3' => ['value' => 25.0, 'name' => 'Float25']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Non-strict should match all
|
||||||
|
$nonStrictResult = ArrayHandler::selectArrayFromOption($data, 'value', 25, false);
|
||||||
|
$this->assertCount(3, $nonStrictResult);
|
||||||
|
|
||||||
|
// Strict should only match exact type
|
||||||
|
$strictResult = ArrayHandler::selectArrayFromOption($data, 'value', 25, true);
|
||||||
|
$this->assertCount(1, $strictResult);
|
||||||
|
$this->assertArrayHasKey('item2', $strictResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCaseInsensitiveSearch(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'JANE', false, true);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertArrayHasKey('item2', $result);
|
||||||
|
$this->assertEquals('jane', $result['item2']['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCaseSensitiveSearch(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'JANE', false, false);
|
||||||
|
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveSearchWithFlatResult(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$this->nestedTestData,
|
||||||
|
'type',
|
||||||
|
'child',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
':*'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertCount(4, $result);
|
||||||
|
$this->assertArrayHasKey('level1_a:*children:*child1', $result);
|
||||||
|
$this->assertArrayHasKey('level1_a:*children:*child2', $result);
|
||||||
|
$this->assertArrayHasKey('level1_b:*children:*child3', $result);
|
||||||
|
$this->assertArrayHasKey('item5', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveSearchWithNestedResult(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$this->nestedTestData,
|
||||||
|
'type',
|
||||||
|
'child',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertCount(3, $result);
|
||||||
|
$this->assertArrayHasKey('level1_a', $result);
|
||||||
|
$this->assertArrayHasKey('level1_b', $result);
|
||||||
|
$this->assertArrayHasKey('item5', $result);
|
||||||
|
|
||||||
|
// Check nested structure is preserved
|
||||||
|
$this->assertArrayHasKey('children', $result['level1_a']);
|
||||||
|
$this->assertArrayHasKey('child1', $result['level1_a']['children']);
|
||||||
|
$this->assertArrayHasKey('child2', $result['level1_a']['children']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveSearchDeepNesting(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$this->nestedTestData,
|
||||||
|
'type',
|
||||||
|
'deep',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
':*'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertArrayHasKey('level1_b:*children:*child3:*nested:*deep1', $result);
|
||||||
|
$this->assertEquals('Deep1', $result['level1_b:*children:*child3:*nested:*deep1']['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCustomFlatSeparator(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$this->nestedTestData,
|
||||||
|
'type',
|
||||||
|
'child',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
'|'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('level1_a|children|child1', $result);
|
||||||
|
$this->assertArrayHasKey('level1_a|children|child2', $result);
|
||||||
|
$this->assertArrayHasKey('level1_b|children|child3', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNonRecursiveSearch(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$this->nestedTestData,
|
||||||
|
'type',
|
||||||
|
'child',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should only find direct matches, not nested ones
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertArrayHasKey('item5', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoMatchesFound(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'NonExistent');
|
||||||
|
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMissingLookupKey(): void
|
||||||
|
{
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($this->testData, 'nonexistent_key', 'value');
|
||||||
|
|
||||||
|
$this->assertEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCombinedStrictAndCaseInsensitive(): void
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'item1' => ['name' => 'Test', 'id' => '123'],
|
||||||
|
'item2' => ['name' => 'test', 'id' => 123],
|
||||||
|
'item3' => ['name' => 'TEST', 'id' => '123']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Case insensitive but strict type matching
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($data, 'id', '123', true, true);
|
||||||
|
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
$this->assertArrayHasKey('item1', $result);
|
||||||
|
$this->assertArrayHasKey('item3', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBooleanWithCaseInsensitive(): void
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'item1' => ['active' => true, 'name' => 'Test1'],
|
||||||
|
'item2' => ['active' => false, 'name' => 'Test2']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Case insensitive flag should not affect boolean comparison
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($data, 'active', true, false, true);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertArrayHasKey('item1', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testArrayWithNumericKeys(): void
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
0 => ['name' => 'First', 'type' => 'test'],
|
||||||
|
1 => ['name' => 'Second', 'type' => 'test'],
|
||||||
|
2 => ['name' => 'Third', 'type' => 'other']
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($data, 'type', 'test');
|
||||||
|
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
$this->assertArrayHasKey(0, $result);
|
||||||
|
$this->assertArrayHasKey(1, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveWithMixedKeyTypes(): void
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'string_key' => [
|
||||||
|
'name' => 'Parent',
|
||||||
|
'type' => 'parent',
|
||||||
|
0 => [
|
||||||
|
'name' => 'Child0',
|
||||||
|
'type' => 'child'
|
||||||
|
],
|
||||||
|
'child_key' => [
|
||||||
|
'name' => 'ChildKey',
|
||||||
|
'type' => 'child'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::selectArrayFromOption($data, 'type', 'child', false, false, true, true, ':*');
|
||||||
|
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
$this->assertArrayHasKey('string_key:*0', $result);
|
||||||
|
$this->assertArrayHasKey('string_key:*child_key', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllParametersCombined(): void
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'parent1' => [
|
||||||
|
'name' => 'Parent1',
|
||||||
|
'status' => 'ACTIVE',
|
||||||
|
'children' => [
|
||||||
|
'child1' => [
|
||||||
|
'name' => 'Child1',
|
||||||
|
'status' => 'active'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$data,
|
||||||
|
'status',
|
||||||
|
'active',
|
||||||
|
false, // not strict
|
||||||
|
true, // case insensitive
|
||||||
|
true, // recursive
|
||||||
|
true, // flat result
|
||||||
|
'|' // custom separator
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
$this->assertArrayHasKey('parent1', $result);
|
||||||
|
$this->assertArrayHasKey('parent1|children|child1', $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// __END__
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// This code was created by Claude Sonnet 4
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use CoreLibs\Combined\ArrayHandler;
|
||||||
|
|
||||||
|
class CoreLibsCombinedArrayHandlerSortArrayTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test basic ascending sort without maintaining keys
|
||||||
|
*/
|
||||||
|
public function testBasicAscendingSort()
|
||||||
|
{
|
||||||
|
$input = [3, 1, 4, 1, 5, 9];
|
||||||
|
$expected = [1, 1, 3, 4, 5, 9];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test basic descending sort without maintaining keys
|
||||||
|
*/
|
||||||
|
public function testBasicDescendingSort()
|
||||||
|
{
|
||||||
|
$input = [3, 1, 4, 1, 5, 9];
|
||||||
|
$expected = [9, 5, 4, 3, 1, 1];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, false, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
$this->assertEquals(array_keys($expected), array_keys($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test ascending sort with key maintenance
|
||||||
|
*/
|
||||||
|
public function testAscendingSortWithKeyMaintenance()
|
||||||
|
{
|
||||||
|
$input = ['c' => 3, 'a' => 1, 'd' => 4, 'b' => 1, 'e' => 5];
|
||||||
|
$expected = ['a' => 1, 'b' => 1, 'c' => 3, 'd' => 4, 'e' => 5];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, false, false, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test descending sort with key maintenance
|
||||||
|
*/
|
||||||
|
public function testDescendingSortWithKeyMaintenance()
|
||||||
|
{
|
||||||
|
$input = ['c' => 3, 'a' => 1, 'd' => 4, 'b' => 1, 'e' => 5];
|
||||||
|
$expected = ['e' => 5, 'd' => 4, 'c' => 3, 'a' => 1, 'b' => 1];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, false, true, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test string sorting with lowercase conversion
|
||||||
|
*/
|
||||||
|
public function testStringLowerCaseSort()
|
||||||
|
{
|
||||||
|
$input = ['Banana', 'apple', 'Cherry', 'date'];
|
||||||
|
$expected = ['apple', 'Banana', 'Cherry', 'date'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test string sorting with lowercase conversion in reverse
|
||||||
|
*/
|
||||||
|
public function testStringLowerCaseSortReverse()
|
||||||
|
{
|
||||||
|
$input = ['Banana', 'apple', 'Cherry', 'date'];
|
||||||
|
$expected = ['date', 'Cherry', 'Banana', 'apple'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, true, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test string sorting with lowercase conversion and key maintenance
|
||||||
|
*/
|
||||||
|
public function testStringLowerCaseSortWithKeys()
|
||||||
|
{
|
||||||
|
$input = ['b' => 'Banana', 'a' => 'apple', 'c' => 'Cherry', 'd' => 'date'];
|
||||||
|
$expected = ['a' => 'apple', 'b' => 'Banana', 'c' => 'Cherry', 'd' => 'date'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, true, false, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test string sorting with lowercase conversion, reverse, and key maintenance
|
||||||
|
*/
|
||||||
|
public function testStringLowerCaseSortReverseWithKeys()
|
||||||
|
{
|
||||||
|
$input = ['b' => 'Banana', 'a' => 'apple', 'c' => 'Cherry', 'd' => 'date'];
|
||||||
|
$expected = ['d' => 'date', 'c' => 'Cherry', 'b' => 'Banana', 'a' => 'apple'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, true, true, true);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test numeric string sorting with SORT_NUMERIC flag
|
||||||
|
*/
|
||||||
|
public function testNumericStringSorting()
|
||||||
|
{
|
||||||
|
$input = ['10', '2', '1', '20'];
|
||||||
|
$expected = ['1', '2', '10', '20'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, false, false, false, SORT_NUMERIC);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test natural string sorting with SORT_NATURAL flag
|
||||||
|
*/
|
||||||
|
public function testNaturalStringSorting()
|
||||||
|
{
|
||||||
|
$input = ['img1.png', 'img10.png', 'img2.png', 'img20.png'];
|
||||||
|
$expected = ['img1.png', 'img2.png', 'img10.png', 'img20.png'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, false, false, false, SORT_NATURAL);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with empty array
|
||||||
|
*/
|
||||||
|
public function testEmptyArray()
|
||||||
|
{
|
||||||
|
$input = [];
|
||||||
|
$expected = [];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with single element array
|
||||||
|
*/
|
||||||
|
public function testSingleElementArray()
|
||||||
|
{
|
||||||
|
$input = [42];
|
||||||
|
$expected = [42];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with array containing null values
|
||||||
|
*/
|
||||||
|
public function testArrayWithNullValues()
|
||||||
|
{
|
||||||
|
$input = [3, null, 1, null, 2];
|
||||||
|
$expected = [null, null, 1, 2, 3];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with mixed data types
|
||||||
|
*/
|
||||||
|
public function testMixedDataTypes()
|
||||||
|
{
|
||||||
|
$input = [3, '1', 4.5, '2', 1];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
// Should sort according to PHP's natural comparison rules
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertCount(5, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that original array is not modified (immutability)
|
||||||
|
*/
|
||||||
|
public function testOriginalArrayNotModified()
|
||||||
|
{
|
||||||
|
$original = [3, 1, 4, 1, 5, 9];
|
||||||
|
$input = $original;
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
$this->assertEquals($original, $input);
|
||||||
|
$this->assertNotEquals($input, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case sensitivity without lowercase flag
|
||||||
|
*/
|
||||||
|
public function testCaseSensitivityWithoutLowercase()
|
||||||
|
{
|
||||||
|
$input = ['Banana', 'apple', 'Cherry'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
// Capital letters should come before lowercase in ASCII sort
|
||||||
|
$this->assertEquals('Banana', $result[0]);
|
||||||
|
$this->assertEquals('Cherry', $result[1]);
|
||||||
|
$this->assertEquals('apple', $result[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test all parameters combination
|
||||||
|
*/
|
||||||
|
public function testAllParametersCombination()
|
||||||
|
{
|
||||||
|
$input = ['z' => 'Zebra', 'a' => 'apple', 'b' => 'Banana'];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, true, true, true, SORT_REGULAR);
|
||||||
|
|
||||||
|
// Should be sorted by lowercase, reversed, with keys maintained
|
||||||
|
$keys = array_keys($result);
|
||||||
|
$values = array_values($result);
|
||||||
|
|
||||||
|
$this->assertEquals(['z', 'b', 'a'], $keys);
|
||||||
|
$this->assertEquals(['Zebra', 'Banana', 'apple'], $values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test floating point numbers
|
||||||
|
*/
|
||||||
|
public function testFloatingPointNumbers()
|
||||||
|
{
|
||||||
|
$input = [3.14, 2.71, 1.41, 1.73];
|
||||||
|
$expected = [1.41, 1.73, 2.71, 3.14];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test with duplicate values and key maintenance
|
||||||
|
*/
|
||||||
|
public function testDuplicateValuesWithKeyMaintenance()
|
||||||
|
{
|
||||||
|
$input = ['first' => 1, 'second' => 2, 'third' => 1, 'fourth' => 2];
|
||||||
|
|
||||||
|
$result = ArrayHandler::sortArray($input, false, false, true);
|
||||||
|
|
||||||
|
$this->assertCount(4, $result);
|
||||||
|
$this->assertEquals([1, 1, 2, 2], array_values($result));
|
||||||
|
// Keys should be preserved
|
||||||
|
$this->assertArrayHasKey('first', $result);
|
||||||
|
$this->assertArrayHasKey('second', $result);
|
||||||
|
$this->assertArrayHasKey('third', $result);
|
||||||
|
$this->assertArrayHasKey('fourth', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for comprehensive parameter testing
|
||||||
|
*/
|
||||||
|
public function sortParameterProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'basic_ascending' => [
|
||||||
|
[3, 1, 4, 2],
|
||||||
|
false, false, false, SORT_REGULAR,
|
||||||
|
[1, 2, 3, 4]
|
||||||
|
],
|
||||||
|
'basic_descending' => [
|
||||||
|
[3, 1, 4, 2],
|
||||||
|
false, true, false, SORT_REGULAR,
|
||||||
|
[4, 3, 2, 1]
|
||||||
|
],
|
||||||
|
'lowercase_ascending' => [
|
||||||
|
['Banana', 'apple', 'Cherry'],
|
||||||
|
true, false, false, SORT_REGULAR,
|
||||||
|
['apple', 'Banana', 'Cherry']
|
||||||
|
],
|
||||||
|
'lowercase_descending' => [
|
||||||
|
['Banana', 'apple', 'Cherry'],
|
||||||
|
true, true, false, SORT_REGULAR,
|
||||||
|
['Cherry', 'Banana', 'apple']
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test various parameter combinations using data provider
|
||||||
|
*
|
||||||
|
* @dataProvider sortParameterProvider
|
||||||
|
*/
|
||||||
|
public function testSortParameterCombinations(
|
||||||
|
array $input,
|
||||||
|
bool $lowercase,
|
||||||
|
bool $reverse,
|
||||||
|
bool $maintainKeys,
|
||||||
|
int $params,
|
||||||
|
array $expected
|
||||||
|
) {
|
||||||
|
$result = ArrayHandler::sortArray($input, $lowercase, $reverse, $maintainKeys, $params);
|
||||||
|
|
||||||
|
if (!$maintainKeys) {
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
} else {
|
||||||
|
$this->assertEquals($expected, array_values($result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// __END__
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -490,11 +490,11 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
|||||||
],
|
],
|
||||||
'micro interval with microtime' => [
|
'micro interval with microtime' => [
|
||||||
'18999d 0h 38m 10s 1235ms',
|
'18999d 0h 38m 10s 1235ms',
|
||||||
1641515890.1235,
|
1641515891.235,
|
||||||
],
|
],
|
||||||
'micro interval with microtime' => [
|
'micro interval with microtime' => [
|
||||||
'18999d 0h 38m 10s 1234567890ms',
|
'18999d 0h 38m 10s 1234567890ms',
|
||||||
1641515890.1234567,
|
1642750457.89,
|
||||||
],
|
],
|
||||||
'negative interval no microtime' => [
|
'negative interval no microtime' => [
|
||||||
'-18999d 0h 38m 10s',
|
'-18999d 0h 38m 10s',
|
||||||
@@ -503,23 +503,246 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
|||||||
// short for mini tests
|
// short for mini tests
|
||||||
'microtime only' => [
|
'microtime only' => [
|
||||||
'0s 1235ms',
|
'0s 1235ms',
|
||||||
0.1235,
|
1.235,
|
||||||
],
|
],
|
||||||
'seconds only' => [
|
'seconds only' => [
|
||||||
'30s 1235ms',
|
'30s 1235ms',
|
||||||
30.1235,
|
31.235,
|
||||||
],
|
],
|
||||||
'minutes only' => [
|
'minutes only' => [
|
||||||
'1m 30s 1235ms',
|
'1m 30s 1235ms',
|
||||||
90.1235,
|
91.235,
|
||||||
],
|
],
|
||||||
'hours only' => [
|
'hours only' => [
|
||||||
'1h 1m 30s 1235ms',
|
'1h 1m 30s 1235ms',
|
||||||
3690.1235,
|
3691.235,
|
||||||
],
|
],
|
||||||
'days only' => [
|
'days only' => [
|
||||||
'1d 1h 1m 30s 1235ms',
|
'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' => [
|
'already set' => [
|
||||||
1641515890,
|
1641515890,
|
||||||
@@ -529,10 +752,18 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
|||||||
'xyz',
|
'xyz',
|
||||||
'xyz',
|
'xyz',
|
||||||
],
|
],
|
||||||
|
'empty data' => [
|
||||||
|
' ',
|
||||||
|
' ',
|
||||||
|
],
|
||||||
'out of bound data' => [
|
'out of bound data' => [
|
||||||
'99999999999999999999d',
|
'99999999999999999999d',
|
||||||
8.64E+24
|
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
|
* Undocumented function
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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
|
* Undocumented function
|
||||||
*
|
*
|
||||||
@@ -180,7 +139,7 @@ final class CoreLibsConvertByteTest extends TestCase
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testHumanReadableByteFormat(
|
public function testHumanReadableByteFormat(
|
||||||
$input,
|
string|int|float $input,
|
||||||
string $expected,
|
string $expected,
|
||||||
string $expected_si,
|
string $expected_si,
|
||||||
string $expected_no_space,
|
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
|
* Undocumented function
|
||||||
*
|
*
|
||||||
@@ -227,10 +253,22 @@ final class CoreLibsConvertByteTest extends TestCase
|
|||||||
* @param string|int|float $input
|
* @param string|int|float $input
|
||||||
* @param string|int|float $expected
|
* @param string|int|float $expected
|
||||||
* @param string|int|float $expected_si
|
* @param string|int|float $expected_si
|
||||||
|
* @param string|int|float $expected_string
|
||||||
|
* @param string|int|float $expected_string_si
|
||||||
|
* @param ?string $exception
|
||||||
* @return void
|
* @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(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
\CoreLibs\Convert\Byte::stringByteFormat($input)
|
\CoreLibs\Convert\Byte::stringByteFormat($input)
|
||||||
@@ -239,6 +277,17 @@ final class CoreLibsConvertByteTest extends TestCase
|
|||||||
$expected_si,
|
$expected_si,
|
||||||
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::BYTE_FORMAT_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
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
* 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
|
* Undocumented function
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -122,9 +122,9 @@ final class CoreLibsConvertMathTest extends TestCase
|
|||||||
public function providerCbrt(): array
|
public function providerCbrt(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'cube root of 2' => [2, 1.25992, 5],
|
'cube root of 2' => [2, 1.25992, 5, null],
|
||||||
'cube root of 3' => [3, 1.44225, 5],
|
'cube root of 3' => [3, 1.44225, 5, null],
|
||||||
'cube root of -1' => [-1, 'NAN', 0],
|
'cube root of -1' => [-1, 'NAN', 0, \InvalidArgumentException::class],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +138,14 @@ final class CoreLibsConvertMathTest extends TestCase
|
|||||||
* @param float|int $number
|
* @param float|int $number
|
||||||
* @param float $expected
|
* @param float $expected
|
||||||
* @param int $round_to
|
* @param int $round_to
|
||||||
|
* @param ?string $exception
|
||||||
* @return void
|
* @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(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)
|
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)
|
||||||
|
|||||||
283
4dev/tests/Convert/CoreLibsConvertStringsRegexValidateTest.php
Normal file
283
4dev/tests/Convert/CoreLibsConvertStringsRegexValidateTest.php
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// This code was created by Claude Sonnet 4
|
||||||
|
// FIX:
|
||||||
|
// '/test{/', // Unmatched brace -> this is valid
|
||||||
|
// '/test{1,}/', // Invalid quantifier -> this is valid
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use CoreLibs\Convert\Strings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test class for CoreLibs\Convert\Strings regex validation methods
|
||||||
|
*/
|
||||||
|
class CoreLibsConvertStringsRegexValidateTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test isValidRegex with valid regex patterns
|
||||||
|
*/
|
||||||
|
public function testIsValidRegexWithValidPatterns(): void
|
||||||
|
{
|
||||||
|
$validPatterns = [
|
||||||
|
'/^[a-zA-Z0-9]+$/',
|
||||||
|
'/test/',
|
||||||
|
'/\d+/',
|
||||||
|
'/^hello.*world$/',
|
||||||
|
'/[0-9]{3}-[0-9]{3}-[0-9]{4}/',
|
||||||
|
'#^https?://.*#i',
|
||||||
|
'~^[a-z]+~',
|
||||||
|
'|test|',
|
||||||
|
'/^$/m',
|
||||||
|
'/\w+/u',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($validPatterns as $pattern) {
|
||||||
|
$this->assertTrue(
|
||||||
|
Strings::isValidRegex($pattern),
|
||||||
|
"Pattern '{$pattern}' should be valid"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test isValidRegex with invalid regex patterns
|
||||||
|
*/
|
||||||
|
public function testIsValidRegexWithInvalidPatterns(): void
|
||||||
|
{
|
||||||
|
$invalidPatterns = [
|
||||||
|
'/[/', // Unmatched bracket
|
||||||
|
'/test[/', // Unmatched bracket
|
||||||
|
'/(?P<name>/', // Unmatched parenthesis
|
||||||
|
'/(?P<>test)/', // Invalid named group
|
||||||
|
'/test\\/', // Invalid escape at end
|
||||||
|
'/(test/', // Unmatched parenthesis
|
||||||
|
'/test)/', // Unmatched parenthesis
|
||||||
|
// '/test{/', // Unmatched brace -> this is valid
|
||||||
|
// '/test{1,}/', // Invalid quantifier -> this is valid
|
||||||
|
'/[z-a]/', // Invalid character range
|
||||||
|
'invalid', // No delimiters
|
||||||
|
'', // Empty string
|
||||||
|
'/(?P<123>test)/', // Invalid named group name
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($invalidPatterns as $pattern) {
|
||||||
|
$this->assertFalse(
|
||||||
|
Strings::isValidRegex($pattern),
|
||||||
|
"Pattern '{$pattern}' should be invalid"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getLastRegexErrorString returns correct error messages
|
||||||
|
*/
|
||||||
|
public function testGetLastRegexErrorStringReturnsCorrectMessages(): void
|
||||||
|
{
|
||||||
|
// Test with a valid regex first to ensure clean state
|
||||||
|
Strings::isValidRegex('/valid/');
|
||||||
|
$this->assertEquals('No error', Strings::getLastRegexErrorString());
|
||||||
|
|
||||||
|
// Test with invalid regex to trigger an error
|
||||||
|
Strings::isValidRegex('/[/');
|
||||||
|
$errorMessage = Strings::getLastRegexErrorString();
|
||||||
|
|
||||||
|
// The error message should be one of the defined messages
|
||||||
|
$this->assertContains($errorMessage, array_values(Strings::PREG_ERROR_MESSAGES));
|
||||||
|
$this->assertNotEquals('Unknown error', $errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getLastRegexErrorString with unknown error
|
||||||
|
*/
|
||||||
|
public function testGetLastRegexErrorStringWithUnknownError(): void
|
||||||
|
{
|
||||||
|
// This is harder to test directly since we can't easily mock preg_last_error()
|
||||||
|
// but we can test the fallback behavior by reflection or assume it works
|
||||||
|
|
||||||
|
// At minimum, ensure it returns a string
|
||||||
|
$result = Strings::getLastRegexErrorString();
|
||||||
|
$this->assertIsString($result);
|
||||||
|
$this->assertNotEmpty($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test validateRegex with valid patterns
|
||||||
|
*/
|
||||||
|
public function testValidateRegexWithValidPatterns(): void
|
||||||
|
{
|
||||||
|
$validPatterns = [
|
||||||
|
'/^test$/',
|
||||||
|
'/\d+/',
|
||||||
|
'/[a-z]+/i',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($validPatterns as $pattern) {
|
||||||
|
$result = Strings::validateRegex($pattern);
|
||||||
|
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertArrayHasKey('valid', $result);
|
||||||
|
$this->assertArrayHasKey('preg_error', $result);
|
||||||
|
$this->assertArrayHasKey('error', $result);
|
||||||
|
|
||||||
|
$this->assertTrue($result['valid'], "Pattern '{$pattern}' should be valid");
|
||||||
|
$this->assertEquals(PREG_NO_ERROR, $result['preg_error']);
|
||||||
|
$this->assertNull($result['error']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test validateRegex with invalid patterns
|
||||||
|
*/
|
||||||
|
public function testValidateRegexWithInvalidPatterns(): void
|
||||||
|
{
|
||||||
|
$invalidPatterns = [
|
||||||
|
'/[/', // Unmatched bracket
|
||||||
|
'/(?P<name>/', // Unmatched parenthesis
|
||||||
|
'/test\\/', // Invalid escape at end
|
||||||
|
'/(test/', // Unmatched parenthesis
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($invalidPatterns as $pattern) {
|
||||||
|
$result = Strings::validateRegex($pattern);
|
||||||
|
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertArrayHasKey('valid', $result);
|
||||||
|
$this->assertArrayHasKey('preg_error', $result);
|
||||||
|
$this->assertArrayHasKey('error', $result);
|
||||||
|
$this->assertArrayHasKey('pcre_error', $result);
|
||||||
|
|
||||||
|
$this->assertFalse($result['valid'], "Pattern '{$pattern}' should be invalid");
|
||||||
|
$this->assertNotEquals(PREG_NO_ERROR, $result['preg_error']);
|
||||||
|
$this->assertIsString($result['error']);
|
||||||
|
$this->assertNotNull($result['error']);
|
||||||
|
$this->assertNotEmpty($result['error']);
|
||||||
|
|
||||||
|
// Verify error message is from our defined messages or 'Unknown error'
|
||||||
|
$this->assertTrue(
|
||||||
|
in_array($result['error'], array_values(Strings::PREG_ERROR_MESSAGES)) ||
|
||||||
|
$result['error'] === 'Unknown error'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test validateRegex array structure
|
||||||
|
*/
|
||||||
|
public function testValidateRegexArrayStructure(): void
|
||||||
|
{
|
||||||
|
$result = Strings::validateRegex('/test/');
|
||||||
|
|
||||||
|
// Test array structure for valid regex
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertCount(4, $result);
|
||||||
|
$this->assertArrayHasKey('valid', $result);
|
||||||
|
$this->assertArrayHasKey('preg_error', $result);
|
||||||
|
$this->assertArrayHasKey('error', $result);
|
||||||
|
|
||||||
|
$result = Strings::validateRegex('/[/');
|
||||||
|
|
||||||
|
// Test array structure for invalid regex
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertCount(4, $result);
|
||||||
|
$this->assertArrayHasKey('valid', $result);
|
||||||
|
$this->assertArrayHasKey('preg_error', $result);
|
||||||
|
$this->assertArrayHasKey('error', $result);
|
||||||
|
$this->assertArrayHasKey('pcre_error', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that methods handle edge cases properly
|
||||||
|
*/
|
||||||
|
public function testEdgeCases(): void
|
||||||
|
{
|
||||||
|
// Empty string
|
||||||
|
$this->assertFalse(Strings::isValidRegex(''));
|
||||||
|
|
||||||
|
$result = Strings::validateRegex('');
|
||||||
|
$this->assertFalse($result['valid']);
|
||||||
|
|
||||||
|
// Very long pattern
|
||||||
|
$longPattern = '/' . str_repeat('a', 1000) . '/';
|
||||||
|
$this->assertTrue(Strings::isValidRegex($longPattern));
|
||||||
|
|
||||||
|
// Unicode patterns
|
||||||
|
$this->assertTrue(Strings::isValidRegex('/\p{L}+/u'));
|
||||||
|
$this->assertTrue(Strings::isValidRegex('/[α-ω]+/u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test PREG_ERROR_MESSAGES constant accessibility
|
||||||
|
*/
|
||||||
|
public function testPregErrorMessagesConstant(): void
|
||||||
|
{
|
||||||
|
$this->assertIsArray(Strings::PREG_ERROR_MESSAGES);
|
||||||
|
$this->assertNotEmpty(Strings::PREG_ERROR_MESSAGES);
|
||||||
|
|
||||||
|
// Check that all expected PREG constants are defined
|
||||||
|
$expectedKeys = [
|
||||||
|
PREG_NO_ERROR,
|
||||||
|
PREG_INTERNAL_ERROR,
|
||||||
|
PREG_BACKTRACK_LIMIT_ERROR,
|
||||||
|
PREG_RECURSION_LIMIT_ERROR,
|
||||||
|
PREG_BAD_UTF8_ERROR,
|
||||||
|
PREG_BAD_UTF8_OFFSET_ERROR,
|
||||||
|
PREG_JIT_STACKLIMIT_ERROR,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($expectedKeys as $key) {
|
||||||
|
$this->assertArrayHasKey($key, Strings::PREG_ERROR_MESSAGES);
|
||||||
|
$this->assertIsString(Strings::PREG_ERROR_MESSAGES[$key]);
|
||||||
|
$this->assertNotEmpty(Strings::PREG_ERROR_MESSAGES[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test error state isolation between method calls
|
||||||
|
*/
|
||||||
|
public function testErrorStateIsolation(): void
|
||||||
|
{
|
||||||
|
// Start with invalid regex
|
||||||
|
Strings::isValidRegex('/[/');
|
||||||
|
$firstError = Strings::getLastRegexErrorString();
|
||||||
|
$this->assertNotEquals('No error', $firstError);
|
||||||
|
|
||||||
|
// Use valid regex
|
||||||
|
Strings::isValidRegex('/valid/');
|
||||||
|
$secondError = Strings::getLastRegexErrorString();
|
||||||
|
$this->assertEquals('No error', $secondError);
|
||||||
|
|
||||||
|
// Verify validateRegex clears previous errors
|
||||||
|
$result = Strings::validateRegex('/valid/');
|
||||||
|
$this->assertTrue($result['valid']);
|
||||||
|
$this->assertEquals(PREG_NO_ERROR, $result['preg_error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test various regex delimiters
|
||||||
|
*/
|
||||||
|
public function testDifferentDelimiters(): void
|
||||||
|
{
|
||||||
|
$patterns = [
|
||||||
|
'/test/', // forward slash
|
||||||
|
'#test#', // hash
|
||||||
|
'~test~', // tilde
|
||||||
|
'|test|', // pipe
|
||||||
|
'@test@', // at symbol
|
||||||
|
'!test!', // exclamation
|
||||||
|
'%test%', // percent
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($patterns as $pattern) {
|
||||||
|
$this->assertTrue(
|
||||||
|
Strings::isValidRegex($pattern),
|
||||||
|
"Pattern with delimiter '{$pattern}' should be valid"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// __END__
|
||||||
@@ -24,117 +24,83 @@ final class CoreLibsConvertStringsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// 0: input
|
// 0: input
|
||||||
// 1: format
|
// 1: format
|
||||||
// 2: split characters as string, null for default
|
|
||||||
// 3: expected
|
// 3: expected
|
||||||
return [
|
return [
|
||||||
'all empty string' => [
|
'all empty string' => [
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
null,
|
|
||||||
''
|
''
|
||||||
],
|
],
|
||||||
'empty input string' => [
|
'empty input string' => [
|
||||||
'',
|
'',
|
||||||
'2-2',
|
'2-2',
|
||||||
null,
|
|
||||||
''
|
''
|
||||||
],
|
],
|
||||||
'empty format string string' => [
|
'empty format string string' => [
|
||||||
'1234',
|
'1234',
|
||||||
'',
|
'',
|
||||||
null,
|
|
||||||
'1234'
|
'1234'
|
||||||
],
|
],
|
||||||
'string format match' => [
|
'string format match' => [
|
||||||
'1234',
|
'1234',
|
||||||
'2-2',
|
'2-2',
|
||||||
null,
|
|
||||||
'12-34'
|
'12-34'
|
||||||
],
|
],
|
||||||
'string format trailing match' => [
|
'string format trailing match' => [
|
||||||
'1234',
|
'1234',
|
||||||
'2-2-',
|
'2-2-',
|
||||||
null,
|
|
||||||
'12-34'
|
'12-34'
|
||||||
],
|
],
|
||||||
'string format leading match' => [
|
'string format leading match' => [
|
||||||
'1234',
|
'1234',
|
||||||
'-2-2',
|
'-2-2',
|
||||||
null,
|
|
||||||
'12-34'
|
'12-34'
|
||||||
],
|
],
|
||||||
'string format double inside match' => [
|
'string format double inside match' => [
|
||||||
'1234',
|
'1234',
|
||||||
'2--2',
|
'2--2',
|
||||||
null,
|
|
||||||
'12--34',
|
'12--34',
|
||||||
],
|
],
|
||||||
'string format short first' => [
|
'string format short first' => [
|
||||||
'1',
|
'1',
|
||||||
'2-2',
|
'2-2',
|
||||||
null,
|
|
||||||
'1'
|
'1'
|
||||||
],
|
],
|
||||||
'string format match first' => [
|
'string format match first' => [
|
||||||
'12',
|
'12',
|
||||||
'2-2',
|
'2-2',
|
||||||
null,
|
|
||||||
'12'
|
'12'
|
||||||
],
|
],
|
||||||
'string format short second' => [
|
'string format short second' => [
|
||||||
'123',
|
'123',
|
||||||
'2-2',
|
'2-2',
|
||||||
null,
|
|
||||||
'12-3'
|
'12-3'
|
||||||
],
|
],
|
||||||
'string format too long' => [
|
'string format too long' => [
|
||||||
'1234567',
|
'1234567',
|
||||||
'2-2',
|
'2-2',
|
||||||
null,
|
|
||||||
'12-34-567'
|
'12-34-567'
|
||||||
],
|
],
|
||||||
'string format invalid format string' => [
|
|
||||||
'1234',
|
|
||||||
'2_2',
|
|
||||||
null,
|
|
||||||
'1234'
|
|
||||||
],
|
|
||||||
'different split character' => [
|
'different split character' => [
|
||||||
'1234',
|
'1234',
|
||||||
'2_2',
|
'2_2',
|
||||||
'_',
|
|
||||||
'12_34'
|
'12_34'
|
||||||
],
|
],
|
||||||
'mixed split characters' => [
|
'mixed split characters' => [
|
||||||
'123456',
|
'123456',
|
||||||
'2-2_2',
|
'2-2_2',
|
||||||
'-_',
|
|
||||||
'12-34_56'
|
'12-34_56'
|
||||||
],
|
],
|
||||||
'length mixed' => [
|
'length mixed' => [
|
||||||
'ABCD12345568ABC13',
|
'ABCD12345568ABC13',
|
||||||
'2-4_5-2#4',
|
'2-4_5-2#4',
|
||||||
'-_#',
|
|
||||||
'AB-CD12_34556-8A#BC13'
|
'AB-CD12_34556-8A#BC13'
|
||||||
],
|
],
|
||||||
'split with split chars in string' => [
|
'split with split chars in string' => [
|
||||||
'12-34',
|
'12-34',
|
||||||
'2-2',
|
'2-2',
|
||||||
null,
|
|
||||||
'12--3-4'
|
'12--3-4'
|
||||||
],
|
],
|
||||||
'mutltibyte string' => [
|
|
||||||
'あいうえ',
|
|
||||||
'2-2',
|
|
||||||
null,
|
|
||||||
'あいうえ'
|
|
||||||
],
|
|
||||||
'mutltibyte split string' => [
|
|
||||||
'1234',
|
|
||||||
'2-2',
|
|
||||||
null,
|
|
||||||
'1234'
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,29 +109,137 @@ final class CoreLibsConvertStringsTest extends TestCase
|
|||||||
*
|
*
|
||||||
* @covers ::splitFormatString
|
* @covers ::splitFormatString
|
||||||
* @dataProvider splitFormatStringProvider
|
* @dataProvider splitFormatStringProvider
|
||||||
* @testdox splitFormatString $input with format $format and splitters $split_characters will be $expected [$_dataName]
|
* @testdox splitFormatString $input with format $format will be $expected [$_dataName]
|
||||||
*
|
*
|
||||||
* @param string $input
|
* @param string $input
|
||||||
* @param string $format
|
* @param string $format
|
||||||
* @param string|null $split_characters
|
|
||||||
* @param string $expected
|
* @param string $expected
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testSplitFormatString(
|
public function testSplitFormatString(
|
||||||
string $input,
|
string $input,
|
||||||
string $format,
|
string $format,
|
||||||
|
string $expected
|
||||||
|
): void {
|
||||||
|
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
||||||
|
$input,
|
||||||
|
$format,
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** check exceptions */
|
||||||
|
public function splitFormatStringExceptionProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'string format with no splitter match' => [
|
||||||
|
'1234',
|
||||||
|
'22',
|
||||||
|
'12-34'
|
||||||
|
],
|
||||||
|
'invalid format string' => [
|
||||||
|
'1234',
|
||||||
|
'2あ2',
|
||||||
|
],
|
||||||
|
'mutltibyte string' => [
|
||||||
|
'あいうえ',
|
||||||
|
'2-2',
|
||||||
|
],
|
||||||
|
'mutltibyte split string' => [
|
||||||
|
'1234',
|
||||||
|
'2-2',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::splitFormatStringFixed
|
||||||
|
* @dataProvider splitFormatStringExceptionProvider
|
||||||
|
* @testdox splitFormatString Exception catch checks for $input with $format[$_dataName]
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testSplitFormatStringExceptions(string $input, string $format): void
|
||||||
|
{
|
||||||
|
// catch exception
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
\CoreLibs\Convert\Strings::splitFormatString($input, $format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test for split Format string fixed length
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function splitFormatStringFixedProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'normal split, default split char' => [
|
||||||
|
'abcdefg',
|
||||||
|
4,
|
||||||
|
null,
|
||||||
|
'abcd-efg'
|
||||||
|
],
|
||||||
|
'noraml split, other single split char' => [
|
||||||
|
'abcdefg',
|
||||||
|
4,
|
||||||
|
"=",
|
||||||
|
'abcd=efg'
|
||||||
|
],
|
||||||
|
'noraml split, other multiple split char' => [
|
||||||
|
'abcdefg',
|
||||||
|
4,
|
||||||
|
"-=-",
|
||||||
|
'abcd-=-efg'
|
||||||
|
],
|
||||||
|
'non ascii characters' => [
|
||||||
|
'あいうえお',
|
||||||
|
2,
|
||||||
|
"-",
|
||||||
|
'あい-うえ-お'
|
||||||
|
],
|
||||||
|
'empty string' => [
|
||||||
|
'',
|
||||||
|
4,
|
||||||
|
"-",
|
||||||
|
''
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::splitFormatStringFixed
|
||||||
|
* @dataProvider splitFormatStringFixedProvider
|
||||||
|
* @testdox splitFormatStringFixed $input with length $split_length and split chars $split_characters will be $expected [$_dataName]
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
* @param int $split_length
|
||||||
|
* @param string|null $split_characters
|
||||||
|
* @param string $expected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testSplitFormatStringFixed(
|
||||||
|
string $input,
|
||||||
|
int $split_length,
|
||||||
?string $split_characters,
|
?string $split_characters,
|
||||||
string $expected
|
string $expected
|
||||||
): void {
|
): void {
|
||||||
if ($split_characters === null) {
|
if ($split_characters === null) {
|
||||||
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
$output = \CoreLibs\Convert\Strings::splitFormatStringFixed(
|
||||||
$input,
|
$input,
|
||||||
$format
|
$split_length
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
$output = \CoreLibs\Convert\Strings::splitFormatStringFixed(
|
||||||
$input,
|
$input,
|
||||||
$format,
|
$split_length,
|
||||||
$split_characters
|
$split_characters
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -175,6 +249,36 @@ final class CoreLibsConvertStringsTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function splitFormatStringFixedExceptionProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'split length too short' => [
|
||||||
|
'abcdefg',
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
'split length longer than string' => [
|
||||||
|
'abcdefg',
|
||||||
|
20,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::splitFormatStringFixed
|
||||||
|
* @dataProvider splitFormatStringFixedExceptionProvider
|
||||||
|
* @testdox splitFormatStringFixed Exception catch checks for $input with $length [$_dataName]
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testSplitFormatStringFixedExceptions(string $input, int $length): void
|
||||||
|
{
|
||||||
|
// catch exception
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
\CoreLibs\Convert\Strings::splitFormatStringFixed($input, $length);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* Undocumented function
|
||||||
*
|
*
|
||||||
@@ -378,6 +482,305 @@ final class CoreLibsConvertStringsTest extends TestCase
|
|||||||
\CoreLibs\Convert\Strings::stripUTF8BomBytes($file)
|
\CoreLibs\Convert\Strings::stripUTF8BomBytes($file)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function allCharsInSetProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'find' => [
|
||||||
|
'abc',
|
||||||
|
'abcdef',
|
||||||
|
true
|
||||||
|
],
|
||||||
|
'not found' => [
|
||||||
|
'abcz',
|
||||||
|
'abcdef',
|
||||||
|
false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::allCharsInSet
|
||||||
|
* @dataProvider allCharsInSetProvider
|
||||||
|
* @testdox allCharsInSet $input in $haystack with expected $expected [$_dataName]
|
||||||
|
*
|
||||||
|
* @param string $needle
|
||||||
|
* @param string $haystack
|
||||||
|
* @param bool $expected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testAllCharsInSet(string $needle, string $haystack, bool $expected): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
\CoreLibs\Convert\Strings::allCharsInSet($needle, $haystack)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildCharStringFromListsProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'test a' => [
|
||||||
|
'abc',
|
||||||
|
['a', 'b', 'c'],
|
||||||
|
],
|
||||||
|
'test b' => [
|
||||||
|
'abc123',
|
||||||
|
['a', 'b', 'c'],
|
||||||
|
['1', '2', '3'],
|
||||||
|
],
|
||||||
|
'test c: no params' => [
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
'test c: empty 1' => [
|
||||||
|
'',
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
'test nested' => [
|
||||||
|
'abc',
|
||||||
|
[['a'], ['b'], ['c']],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::buildCharStringFromLists
|
||||||
|
* @dataProvider buildCharStringFromListsProvider
|
||||||
|
* @testdox buildCharStringFromLists all $input convert to $expected [$_dataName]
|
||||||
|
*
|
||||||
|
* @param string $expected
|
||||||
|
* @param array ...$input
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testBuildCharStringFromLists(string $expected, array ...$input): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
\CoreLibs\Convert\Strings::buildCharStringFromLists(...$input)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function removeDuplicatesProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'test no change' => [
|
||||||
|
'ABCDEFG',
|
||||||
|
'ABCDEFG',
|
||||||
|
],
|
||||||
|
'test simple' => [
|
||||||
|
'aa',
|
||||||
|
'a'
|
||||||
|
],
|
||||||
|
'test keep lower and uppwer case' => [
|
||||||
|
'AaBbCc',
|
||||||
|
'AaBbCc'
|
||||||
|
],
|
||||||
|
'test unqiue' => [
|
||||||
|
'aabbcc',
|
||||||
|
'abc'
|
||||||
|
],
|
||||||
|
'test multibyte no change' => [
|
||||||
|
'あいうえお',
|
||||||
|
'あいうえお',
|
||||||
|
],
|
||||||
|
'test multibyte' => [
|
||||||
|
'ああいいううええおお',
|
||||||
|
'あいうえお',
|
||||||
|
],
|
||||||
|
'test multibyte special' => [
|
||||||
|
'あぁいぃうぅえぇおぉ',
|
||||||
|
'あぁいぃうぅえぇおぉ',
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::removeDuplicates
|
||||||
|
* @dataProvider removeDuplicatesProvider
|
||||||
|
* @testdox removeDuplicates make $input unqiue to $expected [$_dataName]
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
* @param string $expected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testRemoveDuplicates(string $input, string $expected): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
\CoreLibs\Convert\Strings::removeDuplicates($input)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function isValidRegexSimpleProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'valid regex' => [
|
||||||
|
'/^[A-z]$/',
|
||||||
|
true,
|
||||||
|
[
|
||||||
|
'valid' => true,
|
||||||
|
'preg_error' => 0,
|
||||||
|
'error' => null,
|
||||||
|
'pcre_error' => null
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'invalid regex A' => [
|
||||||
|
'/^[A-z]$',
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
'valid' => false,
|
||||||
|
'preg_error' => 1,
|
||||||
|
'error' => 'Internal PCRE error',
|
||||||
|
'pcre_error' => 'Internal error'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'invalid regex B' => [
|
||||||
|
'/^[A-z$',
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
'valid' => false,
|
||||||
|
'preg_error' => 1,
|
||||||
|
'error' => 'Internal PCRE error',
|
||||||
|
'pcre_error' => 'Internal error'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::isValidRegexSimple
|
||||||
|
* @dataProvider isValidRegexSimpleProvider
|
||||||
|
* @testdox isValidRegexSimple make $input unqiue to $expected [$_dataName]
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
* @param bool $expected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testIsValidRegexSimple(string $input, bool $expected, array $expected_extended): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
\CoreLibs\Convert\Strings::isValidRegex($input),
|
||||||
|
'Regex is not valid'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected_extended,
|
||||||
|
\CoreLibs\Convert\Strings::validateRegex($input),
|
||||||
|
'Validation of regex failed'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
// for true null is set, so we get here No Error
|
||||||
|
$expected_extended['error'] ?? \CoreLibs\Convert\Strings::PREG_ERROR_MESSAGES[0],
|
||||||
|
\CoreLibs\Convert\Strings::getLastRegexErrorString(),
|
||||||
|
'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__
|
// __END__
|
||||||
|
|||||||
@@ -13,32 +13,6 @@ use PHPUnit\Framework\TestCase;
|
|||||||
*/
|
*/
|
||||||
final class CoreLibsCreateRandomKeyTest extends TestCase
|
final class CoreLibsCreateRandomKeyTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Undocumented function
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function keyLenghtProvider(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'valid key length' => [
|
|
||||||
0 => 6,
|
|
||||||
1 => true,
|
|
||||||
2 => 6,
|
|
||||||
],
|
|
||||||
'negative key length' => [
|
|
||||||
0 => -1,
|
|
||||||
1 => false,
|
|
||||||
2 => 4,
|
|
||||||
],
|
|
||||||
'tpp big key length' => [
|
|
||||||
0 => 300,
|
|
||||||
1 => false,
|
|
||||||
2 => 4,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* Undocumented function
|
||||||
*
|
*
|
||||||
@@ -47,109 +21,67 @@ final class CoreLibsCreateRandomKeyTest extends TestCase
|
|||||||
public function randomKeyGenProvider(): array
|
public function randomKeyGenProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'default key length' => [
|
// just key length
|
||||||
|
'default key length, default char set' => [
|
||||||
0 => null,
|
0 => null,
|
||||||
1 => 4
|
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT
|
||||||
],
|
],
|
||||||
'set -1 key length default' => [
|
'set -1 key length, default char set' => [
|
||||||
0 => -1,
|
0 => -1,
|
||||||
1 => 4,
|
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||||
],
|
],
|
||||||
'set too large key length' => [
|
'set 0 key length, default char set' => [
|
||||||
|
0 => -1,
|
||||||
|
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||||
|
],
|
||||||
|
'set too large key length, default char set' => [
|
||||||
0 => 300,
|
0 => 300,
|
||||||
1 => 4,
|
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||||
],
|
],
|
||||||
'set override key lenght' => [
|
'set override key lenght, default char set' => [
|
||||||
0 => 6,
|
0 => 6,
|
||||||
1 => 6,
|
1 => 6,
|
||||||
],
|
],
|
||||||
|
// just character set
|
||||||
|
'default key length, different char set A' => [
|
||||||
|
0 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||||
|
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||||
|
2 => [
|
||||||
|
'A', 'B', 'C'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'different key length, different char set B' => [
|
||||||
|
0 => 16,
|
||||||
|
1 => 16,
|
||||||
|
2 => [
|
||||||
|
'A', 'B', 'C'
|
||||||
|
],
|
||||||
|
3 => [
|
||||||
|
'1', '2', '3'
|
||||||
|
]
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alternative more efficient version using strpos
|
||||||
/**
|
/**
|
||||||
* 1
|
* check if all characters are in set
|
||||||
*
|
*
|
||||||
* @return array
|
* @param string $input
|
||||||
|
* @param string $allowed_chars
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function keepKeyLengthProvider(): array
|
private function allCharsInSet(string $input, string $allowed_chars): bool
|
||||||
{
|
{
|
||||||
return [
|
$inputLength = strlen($input);
|
||||||
'set too large' => [
|
|
||||||
0 => 6,
|
|
||||||
1 => 300,
|
|
||||||
2 => 6,
|
|
||||||
],
|
|
||||||
'set too small' => [
|
|
||||||
0 => 8,
|
|
||||||
1 => -2,
|
|
||||||
2 => 8,
|
|
||||||
],
|
|
||||||
'change valid' => [
|
|
||||||
0 => 10,
|
|
||||||
1 => 6,
|
|
||||||
2 => 6,
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
for ($i = 0; $i < $inputLength; $i++) {
|
||||||
* run before each test and reset to default 4
|
if (strpos($allowed_chars, $input[$i]) === false) {
|
||||||
*
|
return false;
|
||||||
* @before
|
}
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function resetKeyLength(): void
|
|
||||||
{
|
|
||||||
\CoreLibs\Create\RandomKey::setRandomKeyLength(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* check that first length is 4
|
|
||||||
*
|
|
||||||
* @covers ::getRandomKeyLength
|
|
||||||
* @testWith [4]
|
|
||||||
* @testdox getRandomKeyLength on init will be $expected [$_dataName]
|
|
||||||
*
|
|
||||||
* @param integer $expected
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function testGetRandomKeyLengthInit(int $expected): void
|
|
||||||
{
|
|
||||||
$this->assertEquals(
|
|
||||||
$expected,
|
|
||||||
\CoreLibs\Create\RandomKey::getRandomKeyLength()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undocumented function
|
|
||||||
*
|
|
||||||
* @covers ::setRandomKeyLength
|
|
||||||
* @covers ::getRandomKeyLength
|
|
||||||
* @dataProvider keyLenghtProvider
|
|
||||||
* @testdox setRandomKeyLength $input will be $expected, compare to $compare [$_dataName]
|
|
||||||
*
|
|
||||||
* @param integer $input
|
|
||||||
* @param boolean $expected
|
|
||||||
* @param integer $compare
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function testSetRandomKeyLength(int $input, bool $expected, int $compare): void
|
|
||||||
{
|
|
||||||
// set
|
|
||||||
$this->assertEquals(
|
|
||||||
$expected,
|
|
||||||
\CoreLibs\Create\RandomKey::setRandomKeyLength($input)
|
|
||||||
);
|
|
||||||
// read test, if false, use compare check
|
|
||||||
if ($expected === false) {
|
|
||||||
$input = $compare;
|
|
||||||
}
|
}
|
||||||
$this->assertEquals(
|
|
||||||
$input,
|
return true;
|
||||||
\CoreLibs\Create\RandomKey::getRandomKeyLength()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,43 +95,41 @@ final class CoreLibsCreateRandomKeyTest extends TestCase
|
|||||||
* @param integer $expected
|
* @param integer $expected
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testRandomKeyGen(?int $input, int $expected): void
|
public function testRandomKeyGen(?int $input, int $expected, array ...$key_range): void
|
||||||
{
|
{
|
||||||
|
$__key_data = \CoreLibs\Create\RandomKey::KEY_CHARACTER_RANGE_DEFAULT;
|
||||||
|
if (count($key_range)) {
|
||||||
|
$__key_data = join('', array_unique(array_merge(...$key_range)));
|
||||||
|
}
|
||||||
if ($input === null) {
|
if ($input === null) {
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
strlen(\CoreLibs\Create\RandomKey::randomKeyGen())
|
strlen(\CoreLibs\Create\RandomKey::randomKeyGen())
|
||||||
);
|
);
|
||||||
} else {
|
} elseif ($input !== null && !count($key_range)) {
|
||||||
|
$random_key = \CoreLibs\Create\RandomKey::randomKeyGen($input);
|
||||||
|
$this->assertTrue(
|
||||||
|
$this->allCharsInSet($random_key, $__key_data),
|
||||||
|
'Characters not valid'
|
||||||
|
);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
strlen(\CoreLibs\Create\RandomKey::randomKeyGen($input))
|
strlen($random_key),
|
||||||
|
'String length not matching'
|
||||||
|
);
|
||||||
|
} elseif (count($key_range)) {
|
||||||
|
$random_key = \CoreLibs\Create\RandomKey::randomKeyGen($input, ...$key_range);
|
||||||
|
$this->assertTrue(
|
||||||
|
$this->allCharsInSet($random_key, $__key_data),
|
||||||
|
'Characters not valid'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
strlen($random_key),
|
||||||
|
'String length not matching'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that if set to n and then invalid, it keeps the previous one
|
|
||||||
* or if second change valid, second will be shown
|
|
||||||
*
|
|
||||||
* @covers ::setRandomKeyLength
|
|
||||||
* @dataProvider keepKeyLengthProvider
|
|
||||||
* @testdox keep setRandomKeyLength set with $input_valid and then $input_invalid will be $expected [$_dataName]
|
|
||||||
*
|
|
||||||
* @param integer $input_valid
|
|
||||||
* @param integer $input_invalid
|
|
||||||
* @param integer $expected
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function testKeepKeyLength(int $input_valid, int $input_invalid, int $expected): void
|
|
||||||
{
|
|
||||||
\CoreLibs\Create\RandomKey::setRandomKeyLength($input_valid);
|
|
||||||
\CoreLibs\Create\RandomKey::setRandomKeyLength($input_invalid);
|
|
||||||
$this->assertEquals(
|
|
||||||
$expected,
|
|
||||||
\CoreLibs\Create\RandomKey::getRandomKeyLength()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// __END__
|
// __END__
|
||||||
|
|||||||
@@ -105,11 +105,15 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
|||||||
'log_folder' => self::LOG_FOLDER,
|
'log_folder' => self::LOG_FOLDER,
|
||||||
'log_level' => Level::Error,
|
'log_level' => Level::Error,
|
||||||
]);
|
]);
|
||||||
|
$errorLogTmpfile = tmpfile();
|
||||||
|
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||||
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
||||||
$em->setMessage(
|
$em->setMessage(
|
||||||
$level,
|
$level,
|
||||||
$str
|
$str
|
||||||
);
|
);
|
||||||
|
// for exceptions if log level is set to catch them
|
||||||
|
$error_log_content = stream_get_contents($errorLogTmpfile);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
'level' => $expected,
|
'level' => $expected,
|
||||||
@@ -377,6 +381,8 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
|||||||
?bool $log_warning,
|
?bool $log_warning,
|
||||||
string $expected
|
string $expected
|
||||||
): void {
|
): void {
|
||||||
|
$errorLogTmpfile = tmpfile();
|
||||||
|
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||||
$log = new \CoreLibs\Logging\Logging([
|
$log = new \CoreLibs\Logging\Logging([
|
||||||
'log_file_id' => 'testErrorMessagesLogError',
|
'log_file_id' => 'testErrorMessagesLogError',
|
||||||
'log_folder' => self::LOG_FOLDER,
|
'log_folder' => self::LOG_FOLDER,
|
||||||
@@ -392,6 +398,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
|||||||
log_error: $log_error,
|
log_error: $log_error,
|
||||||
log_warning: $log_warning
|
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 = '';
|
$file_content = '';
|
||||||
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
||||||
$file_content = file_get_contents(
|
$file_content = file_get_contents(
|
||||||
@@ -447,6 +456,8 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
|||||||
'log_level' => Level::Debug,
|
'log_level' => Level::Debug,
|
||||||
'log_per_run' => true
|
'log_per_run' => true
|
||||||
]);
|
]);
|
||||||
|
$errorLogTmpfile = tmpfile();
|
||||||
|
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||||
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
||||||
$em->setErrorMsg(
|
$em->setErrorMsg(
|
||||||
$id,
|
$id,
|
||||||
@@ -456,6 +467,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
|||||||
log_error: $log_error,
|
log_error: $log_error,
|
||||||
log_warning: $log_warning
|
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 = '';
|
$file_content = '';
|
||||||
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
||||||
$file_content = file_get_contents(
|
$file_content = file_get_contents(
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use CoreLibs\Logging\Logger\Flag;
|
|||||||
final class CoreLibsLoggingLoggingTest extends TestCase
|
final class CoreLibsLoggingLoggingTest extends TestCase
|
||||||
{
|
{
|
||||||
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
|
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
|
||||||
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
|
private const REGEX_BASE = "\[[\d\-\s\.:+T]+\]\s{1}" // date, just basic checks
|
||||||
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
|
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
|
||||||
. "\[(phar:\/\/)?[\w\-\.\/]+:\d+\]\s{1}" // folder/file [note phar:// is for phpunit]
|
. "\[(phar:\/\/)?[\w\-\.\/]+:\d+\]\s{1}" // folder/file [note phar:// is for phpunit]
|
||||||
. "\[\w+\]\s{1}" // run id
|
. "\[\w+\]\s{1}" // run id
|
||||||
@@ -249,7 +249,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
|||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$log->loggingLevelIsDebug()
|
$log->loggingLevelIsDebug()
|
||||||
);
|
);
|
||||||
// not set, should be debug]
|
// not set, should be debug
|
||||||
$log = new \CoreLibs\Logging\Logging([
|
$log = new \CoreLibs\Logging\Logging([
|
||||||
'log_file_id' => 'testSetLoggingLevel',
|
'log_file_id' => 'testSetLoggingLevel',
|
||||||
'log_folder' => self::LOG_FOLDER,
|
'log_folder' => self::LOG_FOLDER,
|
||||||
@@ -297,6 +297,71 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
|||||||
$log->setLoggingLevel('NotGood');
|
$log->setLoggingLevel('NotGood');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::setErrorLogWriteLevel
|
||||||
|
* @covers ::getErrorLogWriteLevel
|
||||||
|
* @testdox setErrorLogWriteLevel set/get checks
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testSetErrorLogWriteLevel(): void
|
||||||
|
{
|
||||||
|
// valid that is not Debug
|
||||||
|
$log = new \CoreLibs\Logging\Logging([
|
||||||
|
'log_file_id' => 'testSetErrorLogWriteLevel',
|
||||||
|
'log_folder' => self::LOG_FOLDER,
|
||||||
|
'error_log_write_level' => Level::Error
|
||||||
|
]);
|
||||||
|
$this->assertEquals(
|
||||||
|
Level::Error,
|
||||||
|
$log->getErrorLogWriteLevel()
|
||||||
|
);
|
||||||
|
// not set on init
|
||||||
|
$log = new \CoreLibs\Logging\Logging([
|
||||||
|
'log_file_id' => 'testSetErrorLogWriteLevel',
|
||||||
|
'log_folder' => self::LOG_FOLDER,
|
||||||
|
]);
|
||||||
|
$this->assertEquals(
|
||||||
|
Level::Emergency,
|
||||||
|
$log->getErrorLogWriteLevel()
|
||||||
|
);
|
||||||
|
// invalid, should be Emergency, will throw excpetion too
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage(
|
||||||
|
'Option: "error_log_write_level" is not of instance \CoreLibs\Logging\Logger\Level'
|
||||||
|
);
|
||||||
|
$log = new \CoreLibs\Logging\Logging([
|
||||||
|
'log_file_id' => 'testSetLoggingLevel',
|
||||||
|
'log_folder' => self::LOG_FOLDER,
|
||||||
|
'error_log_write_level' => 'I'
|
||||||
|
]);
|
||||||
|
$this->assertEquals(
|
||||||
|
Level::Emergency,
|
||||||
|
$log->getErrorLogWriteLevel()
|
||||||
|
);
|
||||||
|
// set valid then change
|
||||||
|
$log = new \CoreLibs\Logging\Logging([
|
||||||
|
'log_file_id' => 'testSetErrorLogWriteLevel',
|
||||||
|
'log_folder' => self::LOG_FOLDER,
|
||||||
|
'error_log_write_level' => Level::Error
|
||||||
|
]);
|
||||||
|
$this->assertEquals(
|
||||||
|
Level::Error,
|
||||||
|
$log->getErrorLogWriteLevel()
|
||||||
|
);
|
||||||
|
$log->setErrorLogWriteLevel(Level::Notice);
|
||||||
|
$this->assertEquals(
|
||||||
|
Level::Notice,
|
||||||
|
$log->getErrorLogWriteLevel()
|
||||||
|
);
|
||||||
|
// illegal logging level
|
||||||
|
$this->expectException(\Psr\Log\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessageMatches("/^Level \"NotGood\" is not defined, use one of: /");
|
||||||
|
$log->setErrorLogWriteLevel('NotGood');
|
||||||
|
}
|
||||||
|
|
||||||
// setLogFileId
|
// setLogFileId
|
||||||
// getLogFileId
|
// getLogFileId
|
||||||
|
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$this->url_basic = $url;
|
$this->url_basic = $url;
|
||||||
// split out the last / part for url set test
|
|
||||||
curl_close($handle);
|
|
||||||
// print "Open: $url\n";
|
// print "Open: $url\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -969,76 +967,77 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
|||||||
"query" => ["foo-get" => "bar"]
|
"query" => ["foo-get" => "bar"]
|
||||||
]);
|
]);
|
||||||
$this->assertEquals("200", $response["code"], "multi call: get response code not matching");
|
$this->assertEquals("200", $response["code"], "multi call: get response code not matching");
|
||||||
if (PHP_VERSION_ID >= 80400) {
|
$request_expected = json_decode(
|
||||||
$this->assertEquals(
|
<<<JSON
|
||||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
{
|
||||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_FIRST_CALL":"get",'
|
"HEADERS":{
|
||||||
. '"HTTP_ACCEPT":"*\/*"},"REQUEST_TYPE":"GET","PARAMS":{"foo-get":"bar"},"BODY":null}',
|
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||||
$response['content'],
|
"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",
|
||||||
'multi call: get content not matching'
|
"HTTP_HOST":"soba.egplusww.jp"
|
||||||
);
|
},
|
||||||
} else {
|
"REQUEST_TYPE":"GET",
|
||||||
$this->assertEquals(
|
"PARAMS":{"foo-get":"bar"},"BODY":null
|
||||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
}
|
||||||
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
|
JSON,
|
||||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
true
|
||||||
. '"REQUEST_TYPE":"GET",'
|
);
|
||||||
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
|
$this->assertEquals(
|
||||||
$response['content'],
|
$request_expected,
|
||||||
'multi call: get content not matching'
|
json_decode($response['content'], true),
|
||||||
);
|
'multi call: get content not matching'
|
||||||
}
|
);
|
||||||
// post
|
// post
|
||||||
$response = $curl->post($this->url_basic, [
|
$response = $curl->post($this->url_basic, [
|
||||||
"headers" => ["second-call" => "post"],
|
"headers" => ["second-call" => "post"],
|
||||||
"body" => ["foo-post" => "baz"]
|
"body" => ["foo-post" => "baz"]
|
||||||
]);
|
]);
|
||||||
$this->assertEquals("200", $response["code"], "multi call: post response code not matching");
|
$this->assertEquals("200", $response["code"], "multi call: post response code not matching");
|
||||||
if (PHP_VERSION_ID >= 80400) {
|
$request_expected = json_decode(
|
||||||
$this->assertEquals(
|
<<<JSON
|
||||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
{
|
||||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
"HEADERS":{
|
||||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*"},'
|
"HTTP_HOST":"soba.egplusww.jp",
|
||||||
. '"REQUEST_TYPE":"POST","PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||||
$response['content'],
|
"HTTP_SECOND_CALL":"post",
|
||||||
'multi call: post content not matching'
|
"HTTP_ACCEPT":"*\/*"
|
||||||
);
|
},
|
||||||
} else {
|
"REQUEST_TYPE":"POST",
|
||||||
$this->assertEquals(
|
"PARAMS":[],
|
||||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
"BODY":{"foo-post":"baz"}
|
||||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
|
}
|
||||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
JSON,
|
||||||
. '"REQUEST_TYPE":"POST",'
|
true
|
||||||
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
);
|
||||||
$response['content'],
|
$this->assertEquals(
|
||||||
'multi call: post content not matching'
|
$request_expected,
|
||||||
);
|
json_decode($response['content'], true),
|
||||||
}
|
'multi call: post content not matching'
|
||||||
|
);
|
||||||
// delete
|
// delete
|
||||||
$response = $curl->delete($this->url_basic, [
|
$response = $curl->delete($this->url_basic, [
|
||||||
"headers" => ["third-call" => "delete"],
|
"headers" => ["third-call" => "delete"],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals("200", $response["code"], "multi call: delete response code not matching");
|
$this->assertEquals("200", $response["code"], "multi call: delete response code not matching");
|
||||||
if (PHP_VERSION_ID >= 80400) {
|
$request_expected = json_decode(
|
||||||
$this->assertEquals(
|
<<<JSON
|
||||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
{
|
||||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
"HEADERS":{
|
||||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"},'
|
"HTTP_HOST":"soba.egplusww.jp",
|
||||||
. '"REQUEST_TYPE":"DELETE","PARAMS":[],"BODY":[]}',
|
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||||
$response['content'],
|
"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"
|
||||||
'multi call: delete content not matching'
|
},
|
||||||
);
|
"REQUEST_TYPE":"DELETE",
|
||||||
} else {
|
"PARAMS":[],
|
||||||
$this->assertEquals(
|
"BODY":[]
|
||||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
}
|
||||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
|
JSON,
|
||||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
true
|
||||||
. '"REQUEST_TYPE":"DELETE",'
|
);
|
||||||
. '"PARAMS":[],"BODY":[]}',
|
$this->assertEquals(
|
||||||
$response['content'],
|
$request_expected,
|
||||||
'multi call: delete content not matching'
|
json_decode($response['content'], true),
|
||||||
);
|
'multi call: delete content not matching'
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: auth header set via config
|
// MARK: auth header set via config
|
||||||
|
|||||||
@@ -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 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
11
SECURITY.md
Normal 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
1567
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -51,6 +51,9 @@ $test_array = [
|
|||||||
'element_c' => [
|
'element_c' => [
|
||||||
'type' => 'email'
|
'type' => 'email'
|
||||||
],
|
],
|
||||||
|
'element_d' => [
|
||||||
|
'type' => 'butter'
|
||||||
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -60,6 +63,8 @@ echo "ARRAYSEARCHRECURSIVE(email, [array], type): "
|
|||||||
. DgS::printAr(ArrayHandler::arraySearchRecursive('email', $test_array, 'type')) . "<br>";
|
. DgS::printAr(ArrayHandler::arraySearchRecursive('email', $test_array, 'type')) . "<br>";
|
||||||
echo "ARRAYSEARCHRECURSIVE(email, [array]['input'], type): "
|
echo "ARRAYSEARCHRECURSIVE(email, [array]['input'], type): "
|
||||||
. DgS::printAr(ArrayHandler::arraySearchRecursive('email', $test_array['input'], 'type')) . "<br>";
|
. DgS::printAr(ArrayHandler::arraySearchRecursive('email', $test_array['input'], 'type')) . "<br>";
|
||||||
|
echo "ARRAYSEARCHRECURSIVE(email, [array]['input'], wrong): "
|
||||||
|
. DgS::printAr(ArrayHandler::arraySearchRecursive('email', $test_array['input'], 'wrong')) . "<br>";
|
||||||
// all return
|
// all return
|
||||||
echo "ARRAYSEARCHRECURSIVEALL(email, [array], type): "
|
echo "ARRAYSEARCHRECURSIVEALL(email, [array], type): "
|
||||||
. Dgs::printAr((array)ArrayHandler::arraySearchRecursiveAll('email', $test_array, 'type')) . "<br>";
|
. Dgs::printAr((array)ArrayHandler::arraySearchRecursiveAll('email', $test_array, 'type')) . "<br>";
|
||||||
@@ -68,7 +73,15 @@ echo "ARRAYSEARCHRECURSIVEALL(email, [array], type): "
|
|||||||
|
|
||||||
// simple search
|
// simple search
|
||||||
echo "ARRAYSEARCHSIMPLE([array], type, email): "
|
echo "ARRAYSEARCHSIMPLE([array], type, email): "
|
||||||
. (string)ArrayHandler::arraySearchSimple($test_array, 'type', 'email') . "<br>";
|
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', 'email')) . "<br>";
|
||||||
|
echo "ARRAYSEARCHSIMPLE([array], type, not): "
|
||||||
|
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', 'not')) . "<br>";
|
||||||
|
echo "ARRAYSEARCHSIMPLE([array], type, [email,butter]): "
|
||||||
|
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', ['email', 'butter'])) . "<br>";
|
||||||
|
echo "ARRAYSEARCHSIMPLE([array], type, [email,not]): "
|
||||||
|
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', ['email', 'not'])) . "<br>";
|
||||||
|
echo "ARRAYSEARCHSIMPLE([array], type, [never,not]): "
|
||||||
|
. Dgs::prBl(ArrayHandler::arraySearchSimple($test_array, 'type', ['never', 'not'])) . "<br>";
|
||||||
|
|
||||||
$array_1 = [
|
$array_1 = [
|
||||||
'foo' => 'bar'
|
'foo' => 'bar'
|
||||||
@@ -168,6 +181,31 @@ $data = [
|
|||||||
$search = ['image', 'result_image', 'nothing', 'EMPTY'];
|
$search = ['image', 'result_image', 'nothing', 'EMPTY'];
|
||||||
$result = ArrayHandler::arraySearchKey($data, $search);
|
$result = ArrayHandler::arraySearchKey($data, $search);
|
||||||
print "ARRAYSEARCHKEY: Search: " . DgS::printAr($search) . ", Found: " . DgS::printAr($result) . "<br>";
|
print "ARRAYSEARCHKEY: Search: " . DgS::printAr($search) . ", Found: " . DgS::printAr($result) . "<br>";
|
||||||
|
$result = ArrayHandler::arraySearchKey($data, $search, true);
|
||||||
|
print "ARRAYSEARCHKEY: FLAT: Search: " . DgS::printAr($search) . ", Found: " . DgS::printAr($result) . "<br>";
|
||||||
|
$result = ArrayHandler::arraySearchKey($data, $search, true, true);
|
||||||
|
print "ARRAYSEARCHKEY: FLAT:PREFIX: Search: " . DgS::printAr($search) . ", Found: " . DgS::printAr($result) . "<br>";
|
||||||
|
$result = ArrayHandler::arraySearchKey($data, ["EMPTY"], true);
|
||||||
|
print "ARRAYSEARCHKEY: FLAT:PREFIX: Search: " . DgS::printAr(["EMPTY"]) . ", Found: " . DgS::printAr($result) . "<br>";
|
||||||
|
|
||||||
|
// $data = [
|
||||||
|
// [
|
||||||
|
// [name] => qrc_apcd,
|
||||||
|
// [value] => 5834367225,
|
||||||
|
// ],
|
||||||
|
// [
|
||||||
|
// [name] => qrc_other,
|
||||||
|
// [value] => test,
|
||||||
|
// ],
|
||||||
|
// [
|
||||||
|
// [name] => qrc_car_type,
|
||||||
|
// [value] => T33P17,
|
||||||
|
// ],
|
||||||
|
// [
|
||||||
|
// [name] => qrc_deaer_store,
|
||||||
|
// [value] => 9990:001,
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
|
||||||
// $test = [
|
// $test = [
|
||||||
// 'A' => [
|
// 'A' => [
|
||||||
@@ -263,6 +301,233 @@ $out = array_intersect_key(
|
|||||||
);
|
);
|
||||||
print "array intersect key: " . DgS::printAr($keys) . ": " . DgS::printAr($out) . "<br>";
|
print "array intersect key: " . DgS::printAr($keys) . ": " . DgS::printAr($out) . "<br>";
|
||||||
|
|
||||||
|
print "array + suffix: " . DgS::printAr(ArrayHandler::arrayModifyKey($array, key_mod_suffix:'_attached')) . "<br>";
|
||||||
|
|
||||||
|
print "<hr>";
|
||||||
|
$unsorted = [9, 5, 'A', 4, 'B', 6, 'c', 'C', 'a'];
|
||||||
|
$unsorted_keys = [
|
||||||
|
'A' => 9, 'B' => 5, 'C' => 'A', 'D' => 4, 'E' => 'B', 'F' => 6, 'G' => 'c',
|
||||||
|
'H1' => 'D', 'B1' => 'd', 'H' => 'C', 'I' => 'a'
|
||||||
|
];
|
||||||
|
print "Unsorted: " . DgS::printAr($unsorted) . "<br>";
|
||||||
|
print "(sort): " . DgS::printAr(ArrayHandler::sortArray($unsorted)) . "<br>";
|
||||||
|
print "(sort, lower): " . DgS::printAr(ArrayHandler::sortArray($unsorted, case_insensitive:true)) . "<br>";
|
||||||
|
print "(sort, reverse): " . DgS::printAr(ArrayHandler::sortArray($unsorted, reverse:true)) . "<br>";
|
||||||
|
print "(sort, lower, reverse): "
|
||||||
|
. DgS::printAr(ArrayHandler::sortArray($unsorted, case_insensitive:true, reverse:true)) . "<br>";
|
||||||
|
print "(sort, keys): " . DgS::printAr(ArrayHandler::sortArray($unsorted_keys, maintain_keys:true)) . "<br>";
|
||||||
|
print "(sort, keys, lower): "
|
||||||
|
. DgS::printAr(ArrayHandler::sortArray($unsorted_keys, maintain_keys:true, case_insensitive:true)) . "<br>";
|
||||||
|
|
||||||
|
print "<hr>";
|
||||||
|
$unsorted = [9 => 'A', 5 => 'B', 'A' => 'C', 4 => 'D', 'B' => 'E', 6 => 'F', 'c' => 'G', 'C' => 'H', 'a' => 'I'];
|
||||||
|
print "Unsorted Keys: " . DgS::printAr($unsorted) . "<br>";
|
||||||
|
print "(sort): " . DgS::printAr(ArrayHandler::sortArray($unsorted)) . "<br>";
|
||||||
|
print "(sort, keys): " . DgS::printAr(ArrayHandler::sortArray($unsorted, maintain_keys:true)) . "<br>";
|
||||||
|
print "(kosrt): " . DgS::printAr(ArrayHandler::ksortArray($unsorted)) . "<br>";
|
||||||
|
print "(kosrt, reverse): " . DgS::printAr(ArrayHandler::ksortArray($unsorted, reverse:true)) . "<br>";
|
||||||
|
print "(kosrt, lower case, reverse): "
|
||||||
|
. DgS::printAr(ArrayHandler::ksortArray($unsorted, case_insensitive:true, reverse:true)) . "<br>";
|
||||||
|
|
||||||
|
|
||||||
|
print "<hr>";
|
||||||
|
$nested = [
|
||||||
|
'B' => 'foo', 'a', '0', 9, /** @phpstan-ignore-line This is a test for wrong index */
|
||||||
|
'1' => ['z', 'b', 'a'],
|
||||||
|
'd' => ['zaip', 'bar', 'baz']
|
||||||
|
];
|
||||||
|
print "Nested: " . DgS::printAr($nested) . "<br>";
|
||||||
|
print "(sort): " . DgS::printAr(ArrayHandler::sortArray($nested)) . "<br>";
|
||||||
|
print "(ksort): " . DgS::printAr(ArrayHandler::ksortArray($nested)) . "<br>";
|
||||||
|
|
||||||
|
print "<hr>";
|
||||||
|
|
||||||
|
$search_array = [
|
||||||
|
'table_lookup' => [
|
||||||
|
'match' => [
|
||||||
|
['param' => 'access_d_cd', 'data' => 'a_cd', 'time_validation' => 'on_load',],
|
||||||
|
['param' => 'other_block', 'data' => 'b_cd'],
|
||||||
|
['pflaume' => 'other_block', 'data' => 'c_cd'],
|
||||||
|
['param' => 'third_block', 'data' => 'd_cd', 'time_validation' => 'cool'],
|
||||||
|
['special' => 'other_block', 'data' => 'e_cd', 'time_validation' => 'other'],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
print "Search: " . DgS::printAr($search_array) . "<br>";
|
||||||
|
print "Result (all): " . Dgs::printAr(ArrayHandler::findArraysMissingKey(
|
||||||
|
$search_array,
|
||||||
|
'other_block',
|
||||||
|
'time_validation'
|
||||||
|
)) . "<br>";
|
||||||
|
print "Result (key): " . Dgs::printAr(ArrayHandler::findArraysMissingKey(
|
||||||
|
$search_array,
|
||||||
|
'other_block',
|
||||||
|
'time_validation',
|
||||||
|
'pflaume'
|
||||||
|
)) . "<br>";
|
||||||
|
print "Result (key): " . Dgs::printAr(ArrayHandler::findArraysMissingKey(
|
||||||
|
$search_array,
|
||||||
|
'other_block',
|
||||||
|
['data', 'time_validation'],
|
||||||
|
'pflaume'
|
||||||
|
)) . "<br>";
|
||||||
|
|
||||||
|
print "<hr>";
|
||||||
|
|
||||||
|
$search_array = [
|
||||||
|
'a' => [
|
||||||
|
'lookup' => 1,
|
||||||
|
'value' => 'Foo',
|
||||||
|
'other' => 'Bar',
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
'lookup' => 1,
|
||||||
|
'value' => 'AAA',
|
||||||
|
'other' => 'Other',
|
||||||
|
],
|
||||||
|
'c' => [
|
||||||
|
'lookup' => 0,
|
||||||
|
'value' => 'CCC',
|
||||||
|
'other' => 'OTHER',
|
||||||
|
],
|
||||||
|
'd' => [
|
||||||
|
'd-1' => [
|
||||||
|
'lookup' => 1,
|
||||||
|
'value' => 'D SUB 1',
|
||||||
|
'other' => 'Other B',
|
||||||
|
],
|
||||||
|
'd-2' => [
|
||||||
|
'lookup' => 0,
|
||||||
|
'value' => 'D SUB 2',
|
||||||
|
'other' => 'Other C',
|
||||||
|
],
|
||||||
|
'more' => [
|
||||||
|
'lookup' => 1,
|
||||||
|
'd-more-1' => [
|
||||||
|
'lookup' => 1,
|
||||||
|
'value' => 'D MORE SUB 1',
|
||||||
|
'other' => 'Other C',
|
||||||
|
],
|
||||||
|
'd-more-2' => [
|
||||||
|
'lookup' => 0,
|
||||||
|
'value' => 'D MORE SUB 0',
|
||||||
|
'other' => 'Other C',
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
print "Search: " . DgS::printAr($search_array) . "<br>";
|
||||||
|
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
|
||||||
|
$search_array,
|
||||||
|
'lookup',
|
||||||
|
1,
|
||||||
|
)) . "<br>";
|
||||||
|
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
|
||||||
|
$search_array,
|
||||||
|
'lookup',
|
||||||
|
1,
|
||||||
|
recursive:true
|
||||||
|
)) . "<br>";
|
||||||
|
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
|
||||||
|
$search_array,
|
||||||
|
'lookup',
|
||||||
|
1,
|
||||||
|
recursive:true,
|
||||||
|
flat_separator:'-=-'
|
||||||
|
)) . "<br>";
|
||||||
|
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
|
||||||
|
$search_array,
|
||||||
|
'lookup',
|
||||||
|
1,
|
||||||
|
recursive:true,
|
||||||
|
flat_result:false
|
||||||
|
)) . "<br>";
|
||||||
|
print "Result: " . DgS::printAr(ArrayHandler::selectArrayFromOption(
|
||||||
|
$search_array,
|
||||||
|
'other',
|
||||||
|
'Other',
|
||||||
|
case_insensitive:false,
|
||||||
|
)) . "<br>";
|
||||||
|
|
||||||
|
$nestedTestData = [
|
||||||
|
'level1_a' => [
|
||||||
|
'name' => 'Level1A',
|
||||||
|
'type' => 'parent',
|
||||||
|
'children' => [
|
||||||
|
'child1' => [
|
||||||
|
'name' => 'Child1',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => true
|
||||||
|
],
|
||||||
|
'child2' => [
|
||||||
|
'name' => 'Child2',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => false
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'level1_b' => [
|
||||||
|
'name' => 'Level1B',
|
||||||
|
'type' => 'parent',
|
||||||
|
'children' => [
|
||||||
|
'child3' => [
|
||||||
|
'name' => 'Child3',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => true,
|
||||||
|
'nested' => [
|
||||||
|
'deep1' => [
|
||||||
|
'name' => 'Deep1',
|
||||||
|
'type' => 'deep',
|
||||||
|
'active' => true
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'item5' => [
|
||||||
|
'name' => 'Direct',
|
||||||
|
'type' => 'child',
|
||||||
|
'active' => false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$nestedTestData,
|
||||||
|
'type',
|
||||||
|
'child',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
':*'
|
||||||
|
);
|
||||||
|
print "*1*Result: " . DgS::printAr($result) . "<br>";
|
||||||
|
$data = [
|
||||||
|
'parent1' => [
|
||||||
|
'name' => 'Parent1',
|
||||||
|
'status' => 'ACTIVE',
|
||||||
|
'children' => [
|
||||||
|
'child1' => [
|
||||||
|
'name' => 'Child1',
|
||||||
|
'status' => 'active'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ArrayHandler::selectArrayFromOption(
|
||||||
|
$data,
|
||||||
|
'status',
|
||||||
|
'active',
|
||||||
|
false, // not strict
|
||||||
|
true, // case insensitive
|
||||||
|
true, // recursive
|
||||||
|
true, // flat result
|
||||||
|
'|' // custom separator
|
||||||
|
);
|
||||||
|
print "*2*Result: " . DgS::printAr($result) . "<br>";
|
||||||
|
|
||||||
print "</body></html>";
|
print "</body></html>";
|
||||||
|
|
||||||
// __END__
|
// __END__
|
||||||
|
|||||||
@@ -74,9 +74,21 @@ foreach ($bytes as $byte) {
|
|||||||
print '<div style="width: 35%; text-align: right; padding-right: 2px;">';
|
print '<div style="width: 35%; text-align: right; padding-right: 2px;">';
|
||||||
print "(" . number_format($byte) . "/" . $byte . ") bytes :";
|
print "(" . number_format($byte) . "/" . $byte . ") bytes :";
|
||||||
$_bytes = Byte::humanReadableByteFormat($byte);
|
$_bytes = Byte::humanReadableByteFormat($byte);
|
||||||
print '</div><div style="width: 10%;">' . $_bytes;
|
print '</div>';
|
||||||
print '</div><div style="width: 10%;">';
|
print '<div style="width: 10%;">' . $_bytes . '</div>';
|
||||||
print Byte::stringByteFormat($_bytes);
|
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>";
|
||||||
//
|
//
|
||||||
print "</div>";
|
print "</div>";
|
||||||
@@ -87,13 +99,85 @@ foreach ($bytes as $byte) {
|
|||||||
print "bytes [si]:";
|
print "bytes [si]:";
|
||||||
$_bytes = Byte::humanReadableByteFormat($byte, Byte::BYTE_FORMAT_SI);
|
$_bytes = Byte::humanReadableByteFormat($byte, Byte::BYTE_FORMAT_SI);
|
||||||
print '</div><div style="width: 10%;">' . $_bytes;
|
print '</div><div style="width: 10%;">' . $_bytes;
|
||||||
print '</div><div style="width: 10%;">';
|
print '</div><div style="width: 40%;">';
|
||||||
print Byte::stringByteFormat($_bytes);
|
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>";
|
||||||
//
|
//
|
||||||
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>";
|
print "</body></html>";
|
||||||
|
|
||||||
// __END__
|
// __END__
|
||||||
|
|||||||
@@ -225,6 +225,36 @@ foreach ($intervals as $interval) {
|
|||||||
print "STRINGTOTIME: $reverse_interval: " . DateTime::stringToTime($reverse_interval) . "<br>";
|
print "STRINGTOTIME: $reverse_interval: " . DateTime::stringToTime($reverse_interval) . "<br>";
|
||||||
}
|
}
|
||||||
print "<hr>";
|
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 = [
|
$check_dates = [
|
||||||
'2021-05-01',
|
'2021-05-01',
|
||||||
'2021-05-40'
|
'2021-05-40'
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ function test2(): array
|
|||||||
return DebugSupport::getCallerMethodList(1);
|
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: " . DebugSupport::getCallerMethod(0) . "<br>";
|
||||||
print "S::GETCALLERMETHOD: " . test() . "<br>";
|
print "S::GETCALLERMETHOD: " . test() . "<br>";
|
||||||
print "S::GETCALLERMETHODLIST: <pre>" . print_r(test2(), true) . "</pre><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')
|
$new_log->getLogLevel('debug', 'on')
|
||||||
)) . "<br>";
|
)) . "<br>";
|
||||||
|
|
||||||
echo "<b>CLASS DEBUG CALL</b><br>";
|
echo "<b>CLASS DEBUG CALL LEGACY</b><br>";
|
||||||
|
|
||||||
// @codingStandardsIgnoreLine
|
// @codingStandardsIgnoreLine
|
||||||
class TestL
|
class TestL
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ function mtParseCSV(
|
|||||||
'UTF-8',
|
'UTF-8',
|
||||||
$encoding
|
$encoding
|
||||||
);
|
);
|
||||||
|
if ($string === false) {
|
||||||
|
return $lines;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($flag == 'INTERN') {
|
if ($flag == 'INTERN') {
|
||||||
// split with PHP function
|
// split with PHP function
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ print "Log ERROR: " . $log->prAr($em->getFlagLogError()) . "<br>";
|
|||||||
print "FN: " . ml::fromName('Affe')->name . "<br>";
|
print "FN: " . ml::fromName('Affe')->name . "<br>";
|
||||||
print "NU: " . ml::fromValue(100)->name . "<br>";
|
print "NU: " . ml::fromValue(100)->name . "<br>";
|
||||||
print "NU: " . ml::fromValue(1000)->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');
|
||||||
$em->setErrorMsg('123', 'error', 'msg this is bad, auto logged if debug', 'target-id', 'other-style');
|
$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', 'I have no id set', jump_target:['target' => 'bar-123', 'info' => 'Jump Bar']);
|
||||||
$em->setMessage('error', 'Jump empty', jump_target:['target' => 'bar-empty']);
|
$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 "ErrorsLast: <pre>" . $log->prAr($em->getLastErrorMsg()) . "</pre>";
|
||||||
print "ErrorsIds: <pre>" . $log->prAr($em->getErrorIds()) . "</pre>";
|
print "ErrorsIds: <pre>" . $log->prAr($em->getErrorIds()) . "</pre>";
|
||||||
print "Errors: <pre>" . $log->prAr($em->getErrorMsg()) . "</pre>";
|
print "Errors: <pre>" . $log->prAr($em->getErrorMsg()) . "</pre>";
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ $json = '["f: {b"""ar}]';
|
|||||||
$output = Json::jsonConvertToArray($json);
|
$output = Json::jsonConvertToArray($json);
|
||||||
print "S::E-JSON: $json: " . DgS::printAr($output) . "<br>";
|
print "S::E-JSON: $json: " . DgS::printAr($output) . "<br>";
|
||||||
print "S::E-JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
|
print "S::E-JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
|
||||||
|
print "S::E Validate: " . Json::jsonValidate($json) . ": " . Json::jsonGetLastError(true) . "<br>";
|
||||||
|
|
||||||
// direct
|
// direct
|
||||||
$json = '{"direct": "static function call"}';
|
$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: $json: " . DgS::printAr($output) . "<br>";
|
||||||
print "J/S::E-JSON ERROR: " . $json_class::jsonGetLastError() . ": " . $json_class::jsonGetLastError(true) . "<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"}';
|
// $json = '{"foo": "bar"}';
|
||||||
// $output = Jason::jsonConvertToArray($json);
|
// $output = Jason::jsonConvertToArray($json);
|
||||||
// print "S::JSON: $json: " . DgS::printAr($output) . "<br>";
|
// print "S::JSON: $json: " . DgS::printAr($output) . "<br>";
|
||||||
@@ -67,6 +86,8 @@ print "J/S::E-JSON ERROR: " . $json_class::jsonGetLastError() . ": " . $json_cla
|
|||||||
$array = ['foo' => 'bar'];
|
$array = ['foo' => 'bar'];
|
||||||
$output = Json::jsonConvertArrayTo($array);
|
$output = Json::jsonConvertArrayTo($array);
|
||||||
print "S::JSON: " . DgS::printAr($array) . " => " . $output . "<br>";
|
print "S::JSON: " . DgS::printAr($array) . " => " . $output . "<br>";
|
||||||
|
$array = ['foo' => 'bar', 'sub' => ['other' => 'this', 'foo' => 'bar', 'set' => [12, 34, true]]];
|
||||||
|
print "Pretty: <pre>" . Json::jsonPrettyPrint($array) . "</pre><br>";
|
||||||
|
|
||||||
print "</body></html>";
|
print "</body></html>";
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ $log->error('Cannot process data', ['error' => 'log']);
|
|||||||
$log->critical('Critical message', ['critical' => 'log']);
|
$log->critical('Critical message', ['critical' => 'log']);
|
||||||
$log->alert('Alert message', ['Alert' => 'log']);
|
$log->alert('Alert message', ['Alert' => 'log']);
|
||||||
$log->emergency('Emergency message', ['Emergency' => 'log']);
|
$log->emergency('Emergency message', ['Emergency' => 'log']);
|
||||||
|
error_log('TRIGGER ERROR LOG MANUAL: Emergency');
|
||||||
print "Log File: " . $log->getLogFile() . "<br>";
|
print "Log File: " . $log->getLogFile() . "<br>";
|
||||||
|
|
||||||
$log->setLogFlag(Flag::per_run);
|
$log->setLogFlag(Flag::per_run);
|
||||||
@@ -120,6 +121,12 @@ Class TestP
|
|||||||
public function test(): void
|
public function test(): void
|
||||||
{
|
{
|
||||||
$this->log->info('TestL::test call');
|
$this->log->info('TestL::test call');
|
||||||
|
$this->subCall();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function subCall(): void
|
||||||
|
{
|
||||||
|
$this->log->info('TestL::sub_call call');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ $log = new CoreLibs\Logging\Logging([
|
|||||||
'log_per_date' => true,
|
'log_per_date' => true,
|
||||||
]);
|
]);
|
||||||
$db = new CoreLibs\DB\IO(DB_CONFIG, $log);
|
$db = new CoreLibs\DB\IO(DB_CONFIG, $log);
|
||||||
|
$log->setLogFileId('classTest-login-override');
|
||||||
$login = new CoreLibs\ACL\Login(
|
$login = new CoreLibs\ACL\Login(
|
||||||
$db,
|
$db,
|
||||||
$log,
|
$log,
|
||||||
@@ -45,6 +46,7 @@ $login = new CoreLibs\ACL\Login(
|
|||||||
'locale_path' => BASE . INCLUDES . LOCALE,
|
'locale_path' => BASE . INCLUDES . LOCALE,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
$log->setLogFileId($LOG_FILE_ID);
|
||||||
ob_end_flush();
|
ob_end_flush();
|
||||||
$login->loginMainCall();
|
$login->loginMainCall();
|
||||||
|
|
||||||
@@ -158,5 +160,6 @@ if (is_string($edit_access_cuid)) {
|
|||||||
print "EA ID: " . $edit_access_id . "<br>";
|
print "EA ID: " . $edit_access_id . "<br>";
|
||||||
print "EA CUID: " . $log->prAr($edit_access_cuid) . "<br>";
|
print "EA CUID: " . $log->prAr($edit_access_cuid) . "<br>";
|
||||||
print "REV EA CUID: " . $log->prAr($edit_access_id_rev) . "<br>";
|
print "REV EA CUID: " . $log->prAr($edit_access_id_rev) . "<br>";
|
||||||
|
$log->info('This is a test');
|
||||||
|
|
||||||
print "</body></html>";
|
print "</body></html>";
|
||||||
|
|||||||
@@ -34,17 +34,40 @@ print "<body>";
|
|||||||
print '<div><a href="class_test.php">Class Test Master</a></div>';
|
print '<div><a href="class_test.php">Class Test Master</a></div>';
|
||||||
print '<div><h1>' . $PAGE_NAME . '</h1></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 = 10;
|
||||||
$key_length_b = 5;
|
$key_length_b = 5;
|
||||||
$key_lenght_long = 64;
|
$key_lenght_long = 64;
|
||||||
print "S::RANDOMKEYGEN(auto): " . RandomKey::randomKeyGen() . "<br>";
|
print "S::RANDOMKEYGEN(auto): " . RandomKey::randomKeyGen() . "<br>";
|
||||||
print "S::SETRANDOMKEYLENGTH($key_length): " . RandomKey::setRandomKeyLength($key_length) . "<br>";
|
// print "S::SETRANDOMKEYLENGTH($key_length): " . RandomKey::setRandomKeyLength($key_length) . "<br>";
|
||||||
print "S::RANDOMKEYGEN($key_length): " . RandomKey::randomKeyGen() . "<br>";
|
print "S::RANDOMKEYGEN($key_length): " . RandomKey::randomKeyGen($key_length) . "<br>";
|
||||||
print "S::RANDOMKEYGEN($key_length_b): " . RandomKey::randomKeyGen($key_length_b) . "<br>";
|
print "S::RANDOMKEYGEN($key_length_b): " . RandomKey::randomKeyGen($key_length_b) . "<br>";
|
||||||
print "S::RANDOMKEYGEN($key_length): " . RandomKey::randomKeyGen() . "<br>";
|
print "S::RANDOMKEYGEN($key_length): " . RandomKey::randomKeyGen($key_length) . "<br>";
|
||||||
print "S::RANDOMKEYGEN($key_lenght_long): " . RandomKey::randomKeyGen($key_lenght_long) . "<br>";
|
print "S::RANDOMKEYGEN($key_lenght_long): " . RandomKey::randomKeyGen($key_lenght_long) . "<br>";
|
||||||
|
print "S::RANDOMKEYGEN($key_lenght_long, list data): "
|
||||||
|
. RandomKey::randomKeyGen($key_lenght_long, ['A', 'B', 'C'], ['7', '8', '9']) . "<br>";
|
||||||
|
print "S::RANDOMKEYGEN(auto): " . RandomKey::randomKeyGen() . "<br>";
|
||||||
|
print "===<Br>";
|
||||||
$_array = new CoreLibs\Create\RandomKey();
|
$_array = new CoreLibs\Create\RandomKey();
|
||||||
print "C->RANDOMKEYGEN(auto): " . $_array->randomKeyGen() . "<br>";
|
print "C->RANDOMKEYGEN(default): " . $_array->randomKeyGen() . "<br>";
|
||||||
|
print "===<Br>";
|
||||||
|
// CHANGE key characters
|
||||||
|
$_array = new CoreLibs\Create\RandomKey(['A', 'F', 'B'], ['1', '5', '9']);
|
||||||
|
print "C->RANDOMKEYGEN(pre set): " . $_array->randomKeyGen() . "<br>";
|
||||||
|
|
||||||
|
|
||||||
print "</body></html>";
|
print "</body></html>";
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ require 'config.php';
|
|||||||
$LOG_FILE_ID = 'classTest-string';
|
$LOG_FILE_ID = 'classTest-string';
|
||||||
ob_end_flush();
|
ob_end_flush();
|
||||||
|
|
||||||
|
use CoreLibs\Convert\Strings;
|
||||||
|
use CoreLibs\Debug\Support as DgS;
|
||||||
|
|
||||||
$log = new CoreLibs\Logging\Logging([
|
$log = new CoreLibs\Logging\Logging([
|
||||||
'log_folder' => BASE . LOG,
|
'log_folder' => BASE . LOG,
|
||||||
'log_file_id' => $LOG_FILE_ID,
|
'log_file_id' => $LOG_FILE_ID,
|
||||||
@@ -29,6 +32,7 @@ print '<div><a href="class_test.php">Class Test Master</a></div>';
|
|||||||
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
||||||
|
|
||||||
$split = '4-4-4';
|
$split = '4-4-4';
|
||||||
|
$split_length = 4;
|
||||||
$test_strings = [
|
$test_strings = [
|
||||||
'13',
|
'13',
|
||||||
'1234',
|
'1234',
|
||||||
@@ -40,20 +44,59 @@ $test_strings = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($test_strings as $string) {
|
foreach ($test_strings as $string) {
|
||||||
print "Convert: $string with $split to: "
|
print "A) Convert: $string with $split to: "
|
||||||
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
|
. Strings::splitFormatString($string, $split)
|
||||||
. "<br>";
|
. "<br>";
|
||||||
|
try {
|
||||||
|
print "B) Convert: $string with $split_length to: "
|
||||||
|
. Strings::splitFormatStringFixed($string, $split_length)
|
||||||
|
. "<br>";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
print "Split not possible: " . $e->getMessage() . "<br>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$split = '2_2';
|
$split = '2_2';
|
||||||
|
$split_length = 2;
|
||||||
$string = '1234';
|
$string = '1234';
|
||||||
print "Convert: $string with $split to: "
|
print "A) Convert: $string with $split to: "
|
||||||
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
|
. Strings::splitFormatString($string, $split)
|
||||||
|
. "<br>";
|
||||||
|
print "B) Convert: $string with $split_length to: "
|
||||||
|
. Strings::splitFormatStringFixed($string, $split_length, "_")
|
||||||
. "<br>";
|
. "<br>";
|
||||||
$split = '2-2';
|
$split = '2-2';
|
||||||
$string = 'あいうえ';
|
$string = 'あいうえ';
|
||||||
print "Convert: $string with $split to: "
|
try {
|
||||||
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
|
print "Convert: $string with $split to: "
|
||||||
|
. Strings::splitFormatString($string, $split)
|
||||||
|
. "<br>";
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
print "Cannot split string: " . $e->getMessage() . "<br>";
|
||||||
|
}
|
||||||
|
print "B) Convert: $string with $split_length to: "
|
||||||
|
. Strings::splitFormatStringFixed($string, $split_length, "-")
|
||||||
|
. "<br>";
|
||||||
|
|
||||||
|
$string = 'ABCD12345568ABC13';
|
||||||
|
$format = '2-4_5-2#4';
|
||||||
|
$output = 'AB-CD12_34556-8A#BC13';
|
||||||
|
print "A) Convert: $string with $format to: "
|
||||||
|
. Strings::splitFormatString($string, $format)
|
||||||
|
. "<br>";
|
||||||
|
|
||||||
|
// try other split calls
|
||||||
|
$string = "ABCDE";
|
||||||
|
$split_length = 2;
|
||||||
|
$split_char = "-=-";
|
||||||
|
print "Convert: $string with $split_length / $split_char to: "
|
||||||
|
. Strings::splitFormatStringFixed($string, $split_length, $split_char)
|
||||||
|
. "<br>";
|
||||||
|
$string = "あいうえお";
|
||||||
|
$split_length = 2;
|
||||||
|
$split_char = "-=-";
|
||||||
|
print "Convert: $string with $split_length / $split_char to: "
|
||||||
|
. Strings::splitFormatStringFixed($string, $split_length, $split_char)
|
||||||
. "<br>";
|
. "<br>";
|
||||||
|
|
||||||
$test_splits = [
|
$test_splits = [
|
||||||
@@ -63,7 +106,70 @@ $test_splits = [
|
|||||||
'2-3-4',
|
'2-3-4',
|
||||||
];
|
];
|
||||||
foreach ($test_splits as $split) {
|
foreach ($test_splits as $split) {
|
||||||
print "$split with count: " . \CoreLibs\Convert\Strings::countSplitParts($split) . "<br>";
|
print "$split with count: " . Strings::countSplitParts($split) . "<br>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// check char list in list
|
||||||
|
$needle = "abc";
|
||||||
|
$haystack = "abcdefg";
|
||||||
|
print "Needle: " . $needle . ", Haysteck: " . $haystack . ": "
|
||||||
|
. DgS::prBl(Strings::allCharsInSet($needle, $haystack)) . "<br>";
|
||||||
|
$needle = "abcz";
|
||||||
|
print "Needle: " . $needle . ", Haysteck: " . $haystack . ": "
|
||||||
|
. DgS::prBl(Strings::allCharsInSet($needle, $haystack)) . "<br>";
|
||||||
|
|
||||||
|
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>";
|
||||||
|
print "Unique: " . Strings::removeDuplicates(strtolower($input_string)) . "<br>";
|
||||||
|
|
||||||
|
$regex_string = "/^[A-z]$/";
|
||||||
|
print "Regex is: " . $regex_string . ": " . DgS::prBl(Strings::isValidRegex($regex_string)) . "<br>";
|
||||||
|
$regex_string = "'//test{//'";
|
||||||
|
print "Regex is: " . $regex_string . ": " . DgS::prBl(Strings::isValidRegex($regex_string)) . "<br>";
|
||||||
|
print "Regex is: " . $regex_string . ": " . DgS::printAr(Strings::validateRegex($regex_string)) . "<br>";
|
||||||
|
$regex_string = "/^[A-z";
|
||||||
|
print "Regex is: " . $regex_string . ": " . DgS::prBl(Strings::isValidRegex($regex_string)) . "<br>";
|
||||||
|
print "[A] LAST PREGE ERROR: " . preg_last_error() . " -> "
|
||||||
|
. (Strings::PREG_ERROR_MESSAGES[preg_last_error()] ?? '-') . "<br>";
|
||||||
|
$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>";
|
print "</body></html>";
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ print "UNIQU ID LONG : " . Uids::uniqIdLong() . "<br>";
|
|||||||
$uuidv4 = Uids::uuidv4();
|
$uuidv4 = Uids::uuidv4();
|
||||||
if (!Uids::validateUuuidv4($uuidv4)) {
|
if (!Uids::validateUuuidv4($uuidv4)) {
|
||||||
print "Invalid UUIDv4: " . $uuidv4 . "<br>";
|
print "Invalid UUIDv4: " . $uuidv4 . "<br>";
|
||||||
|
} else {
|
||||||
|
print "Valid UUIDv4: " . $uuidv4 . "<br>";
|
||||||
}
|
}
|
||||||
if (!Uids::validateUuuidv4("foobar")) {
|
if (!Uids::validateUuuidv4("foobar")) {
|
||||||
print "Invalid UUIDv4: hard coded<Br>";
|
print "Invalid UUIDv4: hard coded<Br>";
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ function setCenter(id, left, top)
|
|||||||
{
|
{
|
||||||
// get size of id
|
// get size of id
|
||||||
var dimensions = {
|
var dimensions = {
|
||||||
height: $('#' + id).height(),
|
height: $('#' + id).height() ?? 0,
|
||||||
width: $('#' + id).width()
|
width: $('#' + id).width() ?? 0
|
||||||
};
|
};
|
||||||
var type = $('#' + id).css('position');
|
var type = $('#' + id).css('position');
|
||||||
var viewport = getWindowSize();
|
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)));
|
// 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) {
|
if (left) {
|
||||||
$('#' + id).css({
|
$('#' + 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 (top) {
|
||||||
// if we have fixed, we do not add the offset, else it moves out of the screen
|
// if we have fixed, we do not add the offset, else it moves out of the screen
|
||||||
var top_pos = type == 'fixed' ?
|
var top_pos = type == 'fixed' ?
|
||||||
parseInt((viewport.height / 2) - (dimensions.height / 2)) :
|
(viewport.height / 2) - (dimensions.height / 2) :
|
||||||
parseInt((viewport.height / 2) - (dimensions.height / 2) + offset.top);
|
(viewport.height / 2) - (dimensions.height / 2) + offset.top;
|
||||||
$('#' + id).css({
|
$('#' + id).css({
|
||||||
top: top_pos + 'px'
|
top: top_pos + 'px'
|
||||||
});
|
});
|
||||||
|
|||||||
9680
www/admin/layout/javascript/jquery-4.0.0.js
vendored
Normal file
9680
www/admin/layout/javascript/jquery-4.0.0.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
www/admin/layout/javascript/jquery-4.0.0.min.js
vendored
Normal file
2
www/admin/layout/javascript/jquery-4.0.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -34,11 +34,27 @@ function executeFunctionByName(functionName, context) {
|
|||||||
}
|
}
|
||||||
return context[func].apply(context, args);
|
return context[func].apply(context, args);
|
||||||
}
|
}
|
||||||
function isObject(val) {
|
function runFunction(name) {
|
||||||
if (val === null) {
|
var args = Array.prototype.slice.call(arguments, 1);
|
||||||
return false;
|
runFunctionArgsArray(name, args);
|
||||||
|
}
|
||||||
|
function runFunctionArgsArray(name, args) {
|
||||||
|
var fn = window[name];
|
||||||
|
if (typeof fn !== "function") {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return typeof val === "function" || typeof val === "object";
|
fn.apply(window, args);
|
||||||
|
}
|
||||||
|
function isObject(val) {
|
||||||
|
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) {
|
function getObjectCount(object) {
|
||||||
if (!isObject(object)) {
|
if (!isObject(object)) {
|
||||||
@@ -147,7 +163,7 @@ var HtmlElementCreator = class {
|
|||||||
if (base.id == id) {
|
if (base.id == id) {
|
||||||
base.sub.push(deepCopyFunction(attach));
|
base.sub.push(deepCopyFunction(attach));
|
||||||
} else {
|
} 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++) {
|
for (var i = 0; i < base.sub.length; i++) {
|
||||||
this.ael(base.sub[i], attach, id);
|
this.ael(base.sub[i], attach, id);
|
||||||
}
|
}
|
||||||
@@ -286,7 +302,7 @@ var HtmlElementCreator = class {
|
|||||||
line += ' name="' + (tree.name ? tree.name : tree.id) + '"';
|
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="';
|
line += ' class="';
|
||||||
for (i = 0; i < tree.css.length; i++) {
|
for (i = 0; i < tree.css.length; i++) {
|
||||||
line += tree.css[i] + " ";
|
line += tree.css[i] + " ";
|
||||||
@@ -303,7 +319,7 @@ var HtmlElementCreator = class {
|
|||||||
}
|
}
|
||||||
line += ">";
|
line += ">";
|
||||||
content.push(line);
|
content.push(line);
|
||||||
if (isObject(tree.sub) && tree.sub.length > 0) {
|
if (isArray(tree.sub) && tree.sub.length > 0) {
|
||||||
if (tree.content) {
|
if (tree.content) {
|
||||||
content.push(tree.content);
|
content.push(tree.content);
|
||||||
}
|
}
|
||||||
@@ -659,6 +675,31 @@ function getQueryStringParam(search = "", query = "", single = false) {
|
|||||||
}
|
}
|
||||||
return param;
|
return param;
|
||||||
}
|
}
|
||||||
|
function hasUrlParameter(key) {
|
||||||
|
var urlParams = new URLSearchParams(window.location.search);
|
||||||
|
return urlParams.has(key);
|
||||||
|
}
|
||||||
|
function getUrlParameter(key) {
|
||||||
|
var urlParams = new URLSearchParams(window.location.search);
|
||||||
|
return urlParams.get(key);
|
||||||
|
}
|
||||||
|
function updateUrlParameter(key, value, reload = false) {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.set(key, value);
|
||||||
|
const newUrl = url.toString();
|
||||||
|
window.history.pushState({ path: newUrl }, "", newUrl);
|
||||||
|
if (reload) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function removeUrlParameter(key, reload = false) {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.delete(key);
|
||||||
|
window.history.pushState({}, "", url.toString());
|
||||||
|
if (reload) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// src/utils/LoginLogout.mjs
|
// src/utils/LoginLogout.mjs
|
||||||
function loginLogout() {
|
function loginLogout() {
|
||||||
@@ -979,7 +1020,7 @@ var ActionBox = class {
|
|||||||
$("#overlayBox").css("zIndex", this.zIndex.base);
|
$("#overlayBox").css("zIndex", this.zIndex.base);
|
||||||
}
|
}
|
||||||
$("#overlayBox").show();
|
$("#overlayBox").show();
|
||||||
if (!keyInObject(target_id, this.zIndex.boxes)) {
|
if (!objectKeyExists(this.zIndex.boxes, target_id)) {
|
||||||
this.zIndex.boxes[target_id] = this.zIndex.max;
|
this.zIndex.boxes[target_id] = this.zIndex.max;
|
||||||
this.zIndex.max += 10;
|
this.zIndex.max += 10;
|
||||||
} else if (this.zIndex.boxes[target_id] + 10 < this.zIndex.max) {
|
} else if (this.zIndex.boxes[target_id] + 10 < this.zIndex.max) {
|
||||||
@@ -1005,7 +1046,7 @@ var ActionBox = class {
|
|||||||
if (!exists(target_id)) {
|
if (!exists(target_id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (keyInObject(target_id, this.action_box_storage) && clean === true) {
|
if (objectKeyExists(this.action_box_storage, target_id) && clean === true) {
|
||||||
this.action_box_storage[target_id] = {};
|
this.action_box_storage[target_id] = {};
|
||||||
}
|
}
|
||||||
if (clean === true) {
|
if (clean === true) {
|
||||||
@@ -1043,15 +1084,15 @@ var ActionBox = class {
|
|||||||
* @param {Object} [settings={}] Optional settings, eg style sheets
|
* @param {Object} [settings={}] Optional settings, eg style sheets
|
||||||
*/
|
*/
|
||||||
createActionBox(target_id = "actionBox", title = "", contents = {}, headers = {}, settings = {}, show_close = true) {
|
createActionBox(target_id = "actionBox", title = "", contents = {}, headers = {}, settings = {}, show_close = true) {
|
||||||
if (!keyInObject(target_id, this.action_box_storage)) {
|
if (!objectKeyExists(this.action_box_storage, target_id)) {
|
||||||
this.action_box_storage[target_id] = {};
|
this.action_box_storage[target_id] = {};
|
||||||
}
|
}
|
||||||
let header_css = [];
|
let header_css = [];
|
||||||
if (keyInObject("header_css", settings)) {
|
if (objectKeyExists(settings, "header_css")) {
|
||||||
header_css = settings.header_css;
|
header_css = settings.header_css;
|
||||||
}
|
}
|
||||||
let action_box_css = [];
|
let action_box_css = [];
|
||||||
if (keyInObject("action_box_css", settings)) {
|
if (objectKeyExists(settings, "action_box_css")) {
|
||||||
action_box_css = settings.action_box_css;
|
action_box_css = settings.action_box_css;
|
||||||
}
|
}
|
||||||
let elements = [];
|
let elements = [];
|
||||||
@@ -1082,14 +1123,14 @@ var ActionBox = class {
|
|||||||
)
|
)
|
||||||
));
|
));
|
||||||
if (getObjectCount(headers) > 0) {
|
if (getObjectCount(headers) > 0) {
|
||||||
if (keyInObject("raw_string", headers)) {
|
if (objectKeyExists(headers, "raw_string")) {
|
||||||
elements.push(headers.raw_string);
|
elements.push(headers.raw_string);
|
||||||
} else {
|
} else {
|
||||||
elements.push(this.hec.phfo(headers));
|
elements.push(this.hec.phfo(headers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (getObjectCount(contents) > 0) {
|
if (getObjectCount(contents) > 0) {
|
||||||
if (keyInObject("raw_string", contents)) {
|
if (objectKeyExists(contents, "raw_string")) {
|
||||||
elements.push(contents.raw_string);
|
elements.push(contents.raw_string);
|
||||||
} else {
|
} else {
|
||||||
elements.push(this.hec.phfo(contents));
|
elements.push(this.hec.phfo(contents));
|
||||||
@@ -1295,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
|
// src/utils.mjs
|
||||||
var aiob = new ActionIndicatorOverlayBox();
|
var aiob = new ActionIndicatorOverlayBox();
|
||||||
var hec = new HtmlElementCreator();
|
var hec = new HtmlElementCreator();
|
||||||
@@ -1397,6 +1449,12 @@ function executeFunctionByName2(functionName, context) {
|
|||||||
function isObject2(val) {
|
function isObject2(val) {
|
||||||
return isObject(val);
|
return isObject(val);
|
||||||
}
|
}
|
||||||
|
function isArray2(val) {
|
||||||
|
return isArray(val);
|
||||||
|
}
|
||||||
|
function isIterable2(val) {
|
||||||
|
return isIterable(val);
|
||||||
|
}
|
||||||
function getObjectCount2(object) {
|
function getObjectCount2(object) {
|
||||||
return getObjectCount(object);
|
return getObjectCount(object);
|
||||||
}
|
}
|
||||||
@@ -1517,12 +1575,24 @@ function html_options_block2(name, data, selected = "", multiple = 0, options_on
|
|||||||
function html_options_refill2(name, data, sort = "") {
|
function html_options_refill2(name, data, sort = "") {
|
||||||
html_options_refill(name, data, sort);
|
html_options_refill(name, data, sort);
|
||||||
}
|
}
|
||||||
function parseQueryString2(query = "", return_key = "") {
|
function parseQueryString2(query = "", return_key = "", single = false) {
|
||||||
return parseQueryString(query, return_key);
|
return parseQueryString(query, return_key, single);
|
||||||
}
|
}
|
||||||
function getQueryStringParam2(search = "", query = "", single = false) {
|
function getQueryStringParam2(search = "", query = "", single = false) {
|
||||||
return getQueryStringParam(search, query, single);
|
return getQueryStringParam(search, query, single);
|
||||||
}
|
}
|
||||||
|
function updateUrlParameter2(key, value, reload = false) {
|
||||||
|
return updateUrlParameter(key, value, reload);
|
||||||
|
}
|
||||||
|
function removeUrlParameter2(key, reload = false) {
|
||||||
|
return removeUrlParameter(key, reload);
|
||||||
|
}
|
||||||
|
function hasUrlParameter2(key) {
|
||||||
|
return hasUrlParameter(key);
|
||||||
|
}
|
||||||
|
function getUrlParameter2(key) {
|
||||||
|
return getUrlParameter(key);
|
||||||
|
}
|
||||||
function loginLogout2() {
|
function loginLogout2() {
|
||||||
loginLogout();
|
loginLogout();
|
||||||
}
|
}
|
||||||
@@ -1565,3 +1635,12 @@ function createActionBox(target_id = "actionBox", title = "", contents = {}, hea
|
|||||||
function adjustActionBoxHeight(target_id = "actionBox", override = 0, content_override = 0) {
|
function adjustActionBoxHeight(target_id = "actionBox", override = 0, content_override = 0) {
|
||||||
ab.adjustActionBoxHeight(target_id, override, content_override);
|
ab.adjustActionBoxHeight(target_id, override, content_override);
|
||||||
}
|
}
|
||||||
|
function isWebkit2() {
|
||||||
|
return isWebkit();
|
||||||
|
}
|
||||||
|
function isMobileWebKit2() {
|
||||||
|
return isMobileWebKit();
|
||||||
|
}
|
||||||
|
function isDesktopWebKit2() {
|
||||||
|
return isDesktopWebKit();
|
||||||
|
}
|
||||||
|
|||||||
4
www/admin/layout/javascript/utils.min.js
vendored
4
www/admin/layout/javascript/utils.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -383,7 +383,8 @@ class Basic
|
|||||||
public function initRandomKeyLength(int $key_length): bool
|
public function initRandomKeyLength(int $key_length): bool
|
||||||
{
|
{
|
||||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\RandomKey::setRandomKeyLength()', E_USER_DEPRECATED);
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\RandomKey::setRandomKeyLength()', E_USER_DEPRECATED);
|
||||||
return \CoreLibs\Create\RandomKey::setRandomKeyLength($key_length);
|
// no op, we do no longer pre set the random key length
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -988,10 +989,10 @@ class Basic
|
|||||||
* @param bool $auto_check default true, if source encoding is set
|
* @param bool $auto_check default true, if source encoding is set
|
||||||
* check that the source is actually matching
|
* check that the source is actually matching
|
||||||
* to what we sav the source is
|
* to what we sav the source is
|
||||||
* @return string encoding converted string
|
* @return string|false encoding converted string
|
||||||
* @deprecated use \CoreLibs\Convert\Encoding::convertEncoding() instead
|
* @deprecated use \CoreLibs\Convert\Encoding::convertEncoding() instead
|
||||||
*/
|
*/
|
||||||
public static function convertEncoding(string $string, string $to_encoding, string $source_encoding = '', bool $auto_check = true): string
|
public static function convertEncoding(string $string, string $to_encoding, string $source_encoding = '', bool $auto_check = true): string|false
|
||||||
{
|
{
|
||||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Encoding::convertEncoding()', E_USER_DEPRECATED);
|
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Encoding::convertEncoding()', E_USER_DEPRECATED);
|
||||||
return \CoreLibs\Convert\Encoding::convertEncoding($string, $to_encoding, $source_encoding, $auto_check);
|
return \CoreLibs\Convert\Encoding::convertEncoding($string, $to_encoding, $source_encoding, $auto_check);
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ class Email
|
|||||||
/** @var array<int,string> */
|
/** @var array<int,string> */
|
||||||
private static array $email_regex_check = [
|
private static array $email_regex_check = [
|
||||||
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
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 @
|
1 => "@(.*)@(.*)", // double @
|
||||||
2 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@", // wrong part before @
|
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 @
|
// 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
|
3 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // wrong part after @
|
||||||
5 => "\.([a-zA-Z]{2,6}){1}$", // wrong top level part
|
// 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
|
6 => "@(.*)\.{2,}", // double .. in domain name part
|
||||||
7 => "@.*\.$" // ends with a dot, top level, domain missing
|
7 => "@.*\.$" // ends with a dot, top level, domain missing
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -56,7 +56,11 @@ class Encoding
|
|||||||
{
|
{
|
||||||
// return mb_substitute_character();
|
// return mb_substitute_character();
|
||||||
if ($return_substitute_func === true) {
|
if ($return_substitute_func === true) {
|
||||||
return mb_substitute_character();
|
// if false abort with error
|
||||||
|
if (($return = mb_substitute_character()) === false) {
|
||||||
|
return self::$mb_error_char;
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
} else {
|
} else {
|
||||||
return self::$mb_error_char;
|
return self::$mb_error_char;
|
||||||
}
|
}
|
||||||
@@ -88,7 +92,13 @@ class Encoding
|
|||||||
): array|false {
|
): array|false {
|
||||||
// convert to target encoding and convert back
|
// convert to target encoding and convert back
|
||||||
$temp = mb_convert_encoding($string, $to_encoding, $from_encoding);
|
$temp = mb_convert_encoding($string, $to_encoding, $from_encoding);
|
||||||
|
if ($temp === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$compare = mb_convert_encoding($temp, $from_encoding, $to_encoding);
|
$compare = mb_convert_encoding($temp, $from_encoding, $to_encoding);
|
||||||
|
if ($compare === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// if string does not match anymore we have a convert problem
|
// if string does not match anymore we have a convert problem
|
||||||
if ($string == $compare) {
|
if ($string == $compare) {
|
||||||
return false;
|
return false;
|
||||||
@@ -104,7 +114,7 @@ class Encoding
|
|||||||
(($char != $r_char && (!self::$mb_error_char ||
|
(($char != $r_char && (!self::$mb_error_char ||
|
||||||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
|
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
|
||||||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
|
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
|
||||||
ord($char) != 194
|
ord($char[0]) != 194
|
||||||
) {
|
) {
|
||||||
$failed[] = $char;
|
$failed[] = $char;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace CoreLibs\Combined;
|
|||||||
|
|
||||||
class ArrayHandler
|
class ArrayHandler
|
||||||
{
|
{
|
||||||
|
public const string DATA_SEPARATOR = ':';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* searches key = value in an array / array
|
* searches key = value in an array / array
|
||||||
* only returns the first one found
|
* only returns the first one found
|
||||||
@@ -148,28 +150,32 @@ class ArrayHandler
|
|||||||
* array search simple. looks for key, value combination, if found, returns true
|
* 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
|
* 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 $key key (key to search in)
|
||||||
* @param string|int|bool $value value (what to find)
|
* @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
|
* @param bool $strict [false], if set to true, will strict check key/value
|
||||||
* @return bool true on found, false on not found
|
* @return bool true on found, false on not found
|
||||||
*/
|
*/
|
||||||
public static function arraySearchSimple(
|
public static function arraySearchSimple(
|
||||||
array $array,
|
array $in_array,
|
||||||
string|int $key,
|
string|int $key,
|
||||||
string|int|bool $value,
|
string|int|bool|array $value,
|
||||||
bool $strict = false
|
bool $strict = false
|
||||||
): bool {
|
): bool {
|
||||||
foreach ($array as $_key => $_value) {
|
// convert to array
|
||||||
|
if (!is_array($value)) {
|
||||||
|
$value = [$value];
|
||||||
|
}
|
||||||
|
foreach ($in_array as $_key => $_value) {
|
||||||
// if value is an array, we search
|
// if value is an array, we search
|
||||||
if (is_array($_value)) {
|
if (is_array($_value)) {
|
||||||
// call recursive, and return result if it is true, else continue
|
// call recursive, and return result if it is true, else continue
|
||||||
if (($result = self::arraySearchSimple($_value, $key, $value, $strict)) !== false) {
|
if (($result = self::arraySearchSimple($_value, $key, $value, $strict)) !== false) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
} elseif ($strict === false && $_key == $key && $_value == $value) {
|
} elseif ($strict === false && $_key == $key && in_array($_value, $value)) {
|
||||||
return true;
|
return true;
|
||||||
} elseif ($strict === true && $_key === $key && $_value === $value) {
|
} elseif ($strict === true && $_key === $key && in_array($_value, $value, true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,19 +189,19 @@ class ArrayHandler
|
|||||||
* If prefix is turned on each found group will be prefixed with the
|
* If prefix is turned on each found group will be prefixed with the
|
||||||
* search key
|
* 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 array<mixed> $needles keys to find in array
|
||||||
* @param bool $flat [false] Turn on flat output
|
* @param bool $flat [false] Turn on flat output
|
||||||
* @param bool $prefix [false] Prefix found with needle key
|
* @param bool $prefix [false] Prefix found with needle key
|
||||||
* @return array<mixed> Found values
|
* @return array<mixed> Found values
|
||||||
*/
|
*/
|
||||||
public static function arraySearchKey(
|
public static function arraySearchKey(
|
||||||
array $array,
|
array $in_array,
|
||||||
array $needles,
|
array $needles,
|
||||||
bool $flat = false,
|
bool $flat = false,
|
||||||
bool $prefix = false
|
bool $prefix = false
|
||||||
): array {
|
): array {
|
||||||
$iterator = new \RecursiveArrayIterator($array);
|
$iterator = new \RecursiveArrayIterator($in_array);
|
||||||
$recursive = new \RecursiveIteratorIterator(
|
$recursive = new \RecursiveIteratorIterator(
|
||||||
$iterator,
|
$iterator,
|
||||||
\RecursiveIteratorIterator::SELF_FIRST
|
\RecursiveIteratorIterator::SELF_FIRST
|
||||||
@@ -236,17 +242,171 @@ class ArrayHandler
|
|||||||
return $hit_list;
|
return $hit_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search in an array for value with or without key and
|
||||||
|
* check in the same array block for the required key
|
||||||
|
* If not found return an array with the array block there the required key is missing,
|
||||||
|
* the path as string with seperator block set and the missing key entry
|
||||||
|
*
|
||||||
|
* @param array<mixed> $in_array
|
||||||
|
* @param string|int|float|bool $search_value
|
||||||
|
* @param string|array<string> $required_key
|
||||||
|
* @param ?string $search_key [null]
|
||||||
|
* @param string $path_separator [DATA_SEPARATOR]
|
||||||
|
* @param string $current_path
|
||||||
|
* @return array<array{content?:array<mixed>,path?:string,missing_key?:array<string>}>
|
||||||
|
*/
|
||||||
|
public static function findArraysMissingKey(
|
||||||
|
array $in_array,
|
||||||
|
string|int|float|bool $search_value,
|
||||||
|
string|array $required_key,
|
||||||
|
?string $search_key = null,
|
||||||
|
string $path_separator = self::DATA_SEPARATOR,
|
||||||
|
string $current_path = ''
|
||||||
|
): array {
|
||||||
|
$results = [];
|
||||||
|
foreach ($in_array as $key => $value) {
|
||||||
|
$path = $current_path ? $current_path . $path_separator . $key : $key;
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
// Check if this array contains the search value
|
||||||
|
// either any value match or with key
|
||||||
|
if ($search_key === null) {
|
||||||
|
$containsValue = in_array($search_value, $value, true);
|
||||||
|
} else {
|
||||||
|
$containsValue = array_key_exists($search_key, $value) && $value[$search_key] === $search_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it contains the value but doesn't have the required key
|
||||||
|
if (
|
||||||
|
$containsValue &&
|
||||||
|
(
|
||||||
|
(
|
||||||
|
is_string($required_key) &&
|
||||||
|
!array_key_exists($required_key, $value)
|
||||||
|
) || (
|
||||||
|
is_array($required_key) &&
|
||||||
|
count(array_intersect($required_key, array_keys($value))) !== count($required_key)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$results[] = [
|
||||||
|
'content' => $value,
|
||||||
|
'path' => $path,
|
||||||
|
'missing_key' => is_array($required_key) ?
|
||||||
|
array_values(array_diff($required_key, array_keys($value))) :
|
||||||
|
[$required_key]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively search nested arrays
|
||||||
|
$results = array_merge(
|
||||||
|
$results,
|
||||||
|
self::findArraysMissingKey(
|
||||||
|
$value,
|
||||||
|
$search_value,
|
||||||
|
$required_key,
|
||||||
|
$search_key,
|
||||||
|
$path_separator,
|
||||||
|
$path
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find key => value entry and return set with key for all matching
|
||||||
|
* Can search recursively through nested arrays if recursive flag is set
|
||||||
|
*
|
||||||
|
* @param array<mixed> $in_array
|
||||||
|
* @param string $lookup
|
||||||
|
* @param int|string|float|bool $search
|
||||||
|
* @param bool $strict [false]
|
||||||
|
* @param bool $case_insensitive [false]
|
||||||
|
* @param bool $recursive [false]
|
||||||
|
* @param bool $flat_result [true] If set to false and recursive is on the result is a nested array
|
||||||
|
* @param string $flat_separator [DATA_SEPARATOR] if flat result is true, can be any string
|
||||||
|
* @return array<mixed>
|
||||||
|
*/
|
||||||
|
public static function selectArrayFromOption(
|
||||||
|
array $in_array,
|
||||||
|
string $lookup,
|
||||||
|
int|string|float|bool $search,
|
||||||
|
bool $strict = false,
|
||||||
|
bool $case_insensitive = false,
|
||||||
|
bool $recursive = false,
|
||||||
|
bool $flat_result = true,
|
||||||
|
string $flat_separator = self::DATA_SEPARATOR
|
||||||
|
): array {
|
||||||
|
// skip on empty
|
||||||
|
if ($in_array == []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// init return result
|
||||||
|
$result = [];
|
||||||
|
// case sensitive convert if string
|
||||||
|
if ($case_insensitive && is_string($search)) {
|
||||||
|
$search = strtolower($search);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($in_array as $key => $value) {
|
||||||
|
// Handle current level search
|
||||||
|
if (isset($value[$lookup])) {
|
||||||
|
$compareValue = $value[$lookup];
|
||||||
|
|
||||||
|
if ($case_insensitive && is_string($compareValue)) {
|
||||||
|
$compareValue = strtolower($compareValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
($strict && $search === $compareValue) ||
|
||||||
|
(!$strict && $search == $compareValue)
|
||||||
|
) {
|
||||||
|
$result[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle recursive search if flag is set
|
||||||
|
if ($recursive && is_array($value)) {
|
||||||
|
$recursiveResults = self::selectArrayFromOption(
|
||||||
|
$value,
|
||||||
|
$lookup,
|
||||||
|
$search,
|
||||||
|
$strict,
|
||||||
|
$case_insensitive,
|
||||||
|
true,
|
||||||
|
$flat_result,
|
||||||
|
$flat_separator
|
||||||
|
);
|
||||||
|
|
||||||
|
// Merge recursive results with current results
|
||||||
|
// Preserve keys by using array_merge with string keys or + operator
|
||||||
|
foreach ($recursiveResults as $recKey => $recValue) {
|
||||||
|
if ($flat_result) {
|
||||||
|
$result[$key . $flat_separator . $recKey] = $recValue;
|
||||||
|
} else {
|
||||||
|
$result[$key][$recKey] = $recValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main wrapper function for next/prev key
|
* 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 int|string $key key for next/prev
|
||||||
* @param bool $next [=true] if to search next or prev
|
* @param bool $next [=true] if to search next or prev
|
||||||
* @return int|string|null Next/prev key or null for end/first
|
* @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) {
|
if (($position = array_search($key, $keys, true)) === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -262,26 +422,26 @@ class ArrayHandler
|
|||||||
* Get previous array key from an array
|
* Get previous array key from an array
|
||||||
* null on not found
|
* null on not found
|
||||||
*
|
*
|
||||||
* @param array<mixed> $array
|
* @param array<mixed> $in_array
|
||||||
* @param int|string $key
|
* @param int|string $key
|
||||||
* @return int|string|null Next key, or null for not found
|
* @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
|
* Get next array key from an array
|
||||||
* null on not found
|
* null on not found
|
||||||
*
|
*
|
||||||
* @param array<mixed> $array
|
* @param array<mixed> $in_array
|
||||||
* @param int|string $key
|
* @param int|string $key
|
||||||
* @return int|string|null Next key, or null for not found
|
* @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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -303,27 +463,27 @@ class ArrayHandler
|
|||||||
}
|
}
|
||||||
// default key is not string
|
// default key is not string
|
||||||
$key_is_string = false;
|
$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 last is not array, then assume it is trigger for key is always string
|
||||||
if (!is_array(end($arrays))) {
|
if (!is_array(end($in_arrays))) {
|
||||||
if (array_pop($arrays)) {
|
if (array_pop($in_arrays)) {
|
||||||
$key_is_string = true;
|
$key_is_string = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check that arrays count is at least two, else we don't have enough to do anything
|
// 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');
|
throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments');
|
||||||
}
|
}
|
||||||
$merged = [];
|
$merged = [];
|
||||||
while ($arrays) {
|
while ($in_arrays) {
|
||||||
$array = array_shift($arrays);
|
$in_array = array_shift($in_arrays);
|
||||||
if (!is_array($array)) {
|
if (!is_array($in_array)) {
|
||||||
throw new \TypeError(__FUNCTION__ . ' encountered a non array argument');
|
throw new \TypeError(__FUNCTION__ . ' encountered a non array argument');
|
||||||
}
|
}
|
||||||
if (!$array) {
|
if (!$in_array) {
|
||||||
continue;
|
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
|
// if string or if key is assumed to be string do key match
|
||||||
// else add new entry
|
// else add new entry
|
||||||
if (is_string($key) || $key_is_string === false) {
|
if (is_string($key) || $key_is_string === false) {
|
||||||
@@ -429,14 +589,14 @@ class ArrayHandler
|
|||||||
* converts multi dimensional array to a flat array
|
* converts multi dimensional array to a flat array
|
||||||
* does NOT preserve keys
|
* does NOT preserve keys
|
||||||
*
|
*
|
||||||
* @param array<mixed> $array multi dimensionial array
|
* @param array<mixed> $in_array multi dimensionial array
|
||||||
* @return array<mixed> flattened array
|
* @return array<mixed> flattened array
|
||||||
*/
|
*/
|
||||||
public static function flattenArray(array $array): array
|
public static function flattenArray(array $in_array): array
|
||||||
{
|
{
|
||||||
$return = [];
|
$return = [];
|
||||||
array_walk_recursive(
|
array_walk_recursive(
|
||||||
$array,
|
$in_array,
|
||||||
function ($value) use (&$return) {
|
function ($value) use (&$return) {
|
||||||
$return[] = $value;
|
$return[] = $value;
|
||||||
}
|
}
|
||||||
@@ -447,13 +607,13 @@ class ArrayHandler
|
|||||||
/**
|
/**
|
||||||
* will loop through an array recursivly and write the array keys back
|
* 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
|
* @param array<mixed> $return recoursive pass on array of keys
|
||||||
* @return array<mixed> flattened keys array
|
* @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;
|
$return[] = $key;
|
||||||
if (is_array($sub) && count($sub) > 0) {
|
if (is_array($sub) && count($sub) > 0) {
|
||||||
$return = self::flattenArrayKey($sub, $return);
|
$return = self::flattenArrayKey($sub, $return);
|
||||||
@@ -466,14 +626,14 @@ class ArrayHandler
|
|||||||
* as above will flatten an array, but in this case only the outmost
|
* as above will flatten an array, but in this case only the outmost
|
||||||
* leave nodes, all other keyswill be skipped
|
* 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
|
* @return array<mixed> flattened keys array
|
||||||
*/
|
*/
|
||||||
public static function flattenArrayKeyLeavesOnly(array $array): array
|
public static function flattenArrayKeyLeavesOnly(array $in_array): array
|
||||||
{
|
{
|
||||||
$return = [];
|
$return = [];
|
||||||
array_walk_recursive(
|
array_walk_recursive(
|
||||||
$array,
|
$in_array,
|
||||||
function ($value, $key) use (&$return) {
|
function ($value, $key) use (&$return) {
|
||||||
$return[] = $key;
|
$return[] = $key;
|
||||||
}
|
}
|
||||||
@@ -485,14 +645,14 @@ class ArrayHandler
|
|||||||
* searches for key -> value in an array tree and writes the value one level up
|
* searches for key -> value in an array tree and writes the value one level up
|
||||||
* this will remove this leaf will all other values
|
* 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
|
* @param string|int $search key to find that has no sub leaf
|
||||||
* and will be pushed up
|
* and will be pushed up
|
||||||
* @return array<mixed> modified, flattened array
|
* @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 it is not an array do just nothing
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -500,14 +660,14 @@ class ArrayHandler
|
|||||||
// probe it has search key
|
// probe it has search key
|
||||||
if (isset($value[$search])) {
|
if (isset($value[$search])) {
|
||||||
// set as current
|
// set as current
|
||||||
$array[$key] = $value[$search];
|
$in_array[$key] = $value[$search];
|
||||||
} else {
|
} else {
|
||||||
// call up next node down
|
// call up next node down
|
||||||
// $array[$key] = call_user_func(__METHOD__, $value, $search);
|
// $in_array[$key] = call_user_func(__METHOD__, $value, $search);
|
||||||
$array[$key] = self::arrayFlatForKey($value, $search);
|
$in_array[$key] = self::arrayFlatForKey($value, $search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $array;
|
return $in_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -517,13 +677,13 @@ class ArrayHandler
|
|||||||
*
|
*
|
||||||
* https://stackoverflow.com/a/369608
|
* 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
|
* @param array<mixed> $remove Elements to remove
|
||||||
* @return array<mixed> Array with $remove elements removed
|
* @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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -533,24 +693,124 @@ class ArrayHandler
|
|||||||
* key list is a list[string]
|
* key list is a list[string]
|
||||||
* if key list is empty, return array as is
|
* 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
|
* @param array<string> $key_list
|
||||||
* @return array<string,mixed>
|
* @return array<string,mixed>
|
||||||
*/
|
*/
|
||||||
public static function arrayReturnMatchingKeyOnly(
|
public static function arrayReturnMatchingKeyOnly(
|
||||||
array $array,
|
array $in_array,
|
||||||
array $key_list
|
array $key_list
|
||||||
): array {
|
): array {
|
||||||
// on empty return as is
|
// on empty return as is
|
||||||
if (empty($key_list)) {
|
if (empty($key_list)) {
|
||||||
return $array;
|
return $in_array;
|
||||||
}
|
}
|
||||||
return array_filter(
|
return array_filter(
|
||||||
$array,
|
$in_array,
|
||||||
fn($key) => in_array($key, $key_list),
|
fn($key) => in_array($key, $key_list),
|
||||||
ARRAY_FILTER_USE_KEY
|
ARRAY_FILTER_USE_KEY
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifieds the key of an array with a prefix and/or suffix and
|
||||||
|
* returns it with the original value
|
||||||
|
* does not change order in array
|
||||||
|
*
|
||||||
|
* @param array<string|int,mixed> $in_array
|
||||||
|
* @param string $key_mod_prefix [''] key prefix string
|
||||||
|
* @param string $key_mod_suffix [''] key suffix string
|
||||||
|
* @return array<string|int,mixed>
|
||||||
|
*/
|
||||||
|
public static function arrayModifyKey(
|
||||||
|
array $in_array,
|
||||||
|
string $key_mod_prefix = '',
|
||||||
|
string $key_mod_suffix = ''
|
||||||
|
): array {
|
||||||
|
// skip if array is empty or neither prefix or suffix are set
|
||||||
|
if (
|
||||||
|
$in_array == [] ||
|
||||||
|
($key_mod_prefix == '' && $key_mod_suffix == '')
|
||||||
|
) {
|
||||||
|
return $in_array;
|
||||||
|
}
|
||||||
|
return array_combine(
|
||||||
|
array_map(
|
||||||
|
fn($key) => $key_mod_prefix . $key . $key_mod_suffix,
|
||||||
|
array_keys($in_array)
|
||||||
|
),
|
||||||
|
array_values($in_array)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sort array and return in same call
|
||||||
|
* sort ascending or descending with or without lower case convert
|
||||||
|
* value only, will loose key connections unless preserve_keys is set to true
|
||||||
|
*
|
||||||
|
* @param array<mixed> $in_array Array to sort by values
|
||||||
|
* @param 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 $in_array,
|
||||||
|
bool $case_insensitive = false,
|
||||||
|
bool $reverse = false,
|
||||||
|
bool $maintain_keys = false,
|
||||||
|
int $flag = SORT_REGULAR
|
||||||
|
): array {
|
||||||
|
$fk_sort_lower_case = function (string $a, string $b): int {
|
||||||
|
return strtolower($a) <=> strtolower($b);
|
||||||
|
};
|
||||||
|
$fk_sort_lower_case_reverse = function (string $a, string $b): int {
|
||||||
|
return strtolower($b) <=> strtolower($a);
|
||||||
|
};
|
||||||
|
$case_insensitive ? (
|
||||||
|
$maintain_keys ?
|
||||||
|
(uasort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
|
||||||
|
(usort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
|
||||||
|
) :
|
||||||
|
(
|
||||||
|
$maintain_keys ?
|
||||||
|
($reverse ? arsort($in_array, $flag) : asort($in_array, $flag)) :
|
||||||
|
($reverse ? rsort($in_array, $flag) : sort($in_array, $flag))
|
||||||
|
);
|
||||||
|
return $in_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sort by key ascending or descending and return
|
||||||
|
*
|
||||||
|
* @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 $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);
|
||||||
|
};
|
||||||
|
$fk_sort_lower_case_reverse = function (string $a, string $b): int {
|
||||||
|
return strtolower($b) <=> strtolower($a);
|
||||||
|
};
|
||||||
|
$fk_sort = function (string $a, string $b): int {
|
||||||
|
return $a <=> $b;
|
||||||
|
};
|
||||||
|
$fk_sort_reverse = function (string $a, string $b): int {
|
||||||
|
return $b <=> $a;
|
||||||
|
};
|
||||||
|
uksort(
|
||||||
|
$in_array,
|
||||||
|
$case_insensitive ?
|
||||||
|
($reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case) :
|
||||||
|
($reverse ? $fk_sort_reverse : $fk_sort)
|
||||||
|
);
|
||||||
|
return $in_array;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// __END__
|
// __END__
|
||||||
|
|||||||
@@ -395,39 +395,68 @@ class DateTime
|
|||||||
* does a reverse of the timeStringFormat and converts the string from
|
* does a reverse of the timeStringFormat and converts the string from
|
||||||
* xd xh xm xs xms to a timestamp.microtime format
|
* xd xh xm xs xms to a timestamp.microtime format
|
||||||
*
|
*
|
||||||
* @param string|int|float $timestring formatted interval
|
* @param string|int|float $timestring formatted interval
|
||||||
* @return string|int|float converted float interval, or string as is
|
* @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;
|
$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 = [];
|
$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
|
// preg match: 0: full string
|
||||||
// 2, 4, 6, 8 are the to need values
|
// 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 .
|
// multiply the returned matches and sum them up. the last one (ms) is added with .
|
||||||
foreach ($timegroups as $i => $time_multiply) {
|
foreach ($timegroups as $i => $time_multiply) {
|
||||||
if (isset($matches[$i]) && is_numeric($matches[$i])) {
|
if (isset($matches[$i]) && is_numeric($matches[$i])) {
|
||||||
$timestamp += (float)$matches[$i] * $time_multiply;
|
$timestamp += (float)$matches[$i] * $time_multiply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isset($matches[10]) && is_numeric($matches[10])) {
|
if (isset($matches[11]) && is_numeric($matches[11])) {
|
||||||
$timestamp .= '.' . $matches[10];
|
// for milliseconds, we need to divide by 1000 and add them
|
||||||
|
$timestamp += (float)($matches[11] / 1000);
|
||||||
}
|
}
|
||||||
if ($negative) {
|
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;
|
$timestamp = (float)$timestamp * -1;
|
||||||
}
|
}
|
||||||
return $timestamp;
|
return $timestamp;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class Byte
|
|||||||
public const BYTE_FORMAT_NOSPACE = 1;
|
public const BYTE_FORMAT_NOSPACE = 1;
|
||||||
public const BYTE_FORMAT_ADJUST = 2;
|
public const BYTE_FORMAT_ADJUST = 2;
|
||||||
public const BYTE_FORMAT_SI = 4;
|
public const BYTE_FORMAT_SI = 4;
|
||||||
|
public const RETURN_AS_STRING = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function replaces the old byteStringFormat
|
* This function replaces the old byteStringFormat
|
||||||
@@ -77,7 +78,7 @@ class Byte
|
|||||||
// labels in order of size [Y, Z]
|
// labels in order of size [Y, Z]
|
||||||
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
|
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
|
||||||
// exp position calculation
|
// exp position calculation
|
||||||
$exp = floor(log($abs_bytes, $unit));
|
$exp = (int)floor(log($abs_bytes, $unit));
|
||||||
// avoid printing out anything larger than max labels
|
// avoid printing out anything larger than max labels
|
||||||
if ($exp >= count($labels)) {
|
if ($exp >= count($labels)) {
|
||||||
$exp = count($labels) - 1;
|
$exp = count($labels) - 1;
|
||||||
@@ -119,7 +120,9 @@ class Byte
|
|||||||
* @param int $flags bitwise flag with use space turned on
|
* @param int $flags bitwise flag with use space turned on
|
||||||
* BYTE_FORMAT_SI: use 1000 instead of 1024
|
* BYTE_FORMAT_SI: use 1000 instead of 1024
|
||||||
* @return string|int|float converted value or original value
|
* @return string|int|float converted value or original value
|
||||||
* @throws \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
|
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
|
||||||
{
|
{
|
||||||
@@ -129,7 +132,12 @@ class Byte
|
|||||||
} else {
|
} else {
|
||||||
$si = false;
|
$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);
|
throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
|
||||||
}
|
}
|
||||||
// matches in regex
|
// matches in regex
|
||||||
@@ -142,6 +150,10 @@ class Byte
|
|||||||
strtolower((string)$number),
|
strtolower((string)$number),
|
||||||
$matches
|
$matches
|
||||||
);
|
);
|
||||||
|
$number_negative = false;
|
||||||
|
if (!empty($matches[1])) {
|
||||||
|
$number_negative = true;
|
||||||
|
}
|
||||||
if (isset($matches[2]) && isset($matches[3])) {
|
if (isset($matches[2]) && isset($matches[3])) {
|
||||||
// remove all non valid characters from the number
|
// remove all non valid characters from the number
|
||||||
$number = preg_replace('/[^0-9\.]/', '', $matches[2]);
|
$number = preg_replace('/[^0-9\.]/', '', $matches[2]);
|
||||||
@@ -152,12 +164,48 @@ class Byte
|
|||||||
if ($unit) {
|
if ($unit) {
|
||||||
$number = $number * pow($si ? 1000 : 1024, stripos($valid_units_, $unit[0]) ?: 0);
|
$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
|
// convert to INT to avoid +E output
|
||||||
$number = (int)round($number);
|
$number = (int)round($number);
|
||||||
// if negative input, keep nnegative
|
// if negative input, keep nnegative
|
||||||
if (!empty($matches[1])) {
|
if ($number_negative) {
|
||||||
$number *= -1;
|
$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
|
// if not matching return as is
|
||||||
return $number;
|
return $number;
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ class Encoding
|
|||||||
* @param bool $auto_check default true, if source encoding is set
|
* @param bool $auto_check default true, if source encoding is set
|
||||||
* check that the source is actually matching
|
* check that the source is actually matching
|
||||||
* to what we sav the source is
|
* to what we sav the source is
|
||||||
* @return string encoding converted string
|
* @return string|false encoding converted string or false on error
|
||||||
*/
|
*/
|
||||||
public static function convertEncoding(
|
public static function convertEncoding(
|
||||||
string $string,
|
string $string,
|
||||||
string $to_encoding,
|
string $to_encoding,
|
||||||
string $source_encoding = '',
|
string $source_encoding = '',
|
||||||
bool $auto_check = true
|
bool $auto_check = true
|
||||||
): string {
|
): string|false {
|
||||||
// set if not given
|
// set if not given
|
||||||
if (!$source_encoding) {
|
if (!$source_encoding) {
|
||||||
$source_encoding = mb_detect_encoding($string);
|
$source_encoding = mb_detect_encoding($string);
|
||||||
|
|||||||
@@ -27,10 +27,14 @@ class Json
|
|||||||
* set original value as array
|
* set original value as array
|
||||||
* @return array<mixed> returns an array from the json values
|
* @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) {
|
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 (self::$json_last_error = json_last_error()) {
|
||||||
if ($override == true) {
|
if ($override == true) {
|
||||||
// init return as array with original as element
|
// init return as array with original as element
|
||||||
@@ -55,16 +59,31 @@ class Json
|
|||||||
* Deos not throw errors
|
* Deos not throw errors
|
||||||
*
|
*
|
||||||
* @param array<mixed> $data
|
* @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
|
* @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) ?: '{}';
|
$json_string = json_encode($data, $flags) ?: '{}';
|
||||||
self::$json_last_error = json_last_error();
|
self::$json_last_error = json_last_error();
|
||||||
return (string)$json_string;
|
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
|
* returns human readable string for json errors thrown in jsonConvertToArray
|
||||||
* Source: https://www.php.net/manual/en/function.json-last-error.php
|
* Source: https://www.php.net/manual/en/function.json-last-error.php
|
||||||
@@ -119,6 +138,23 @@ class Json
|
|||||||
}
|
}
|
||||||
return $return_string === true ? $json_error_string : self::$json_last_error;
|
return $return_string === true ? $json_error_string : self::$json_last_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wrapper to call convert array to json with pretty print
|
||||||
|
*
|
||||||
|
* @param array<mixed> $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function jsonPrettyPrint(array $data): string
|
||||||
|
{
|
||||||
|
return self::jsonConvertArrayTo(
|
||||||
|
$data,
|
||||||
|
JSON_PRETTY_PRINT |
|
||||||
|
JSON_UNESCAPED_LINE_TERMINATORS |
|
||||||
|
JSON_UNESCAPED_SLASHES |
|
||||||
|
JSON_UNESCAPED_UNICODE
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// __END__
|
// __END__
|
||||||
|
|||||||
@@ -62,10 +62,15 @@ class Math
|
|||||||
*
|
*
|
||||||
* @param float $number Number to cubic root
|
* @param float $number Number to cubic root
|
||||||
* @return float Calculated value
|
* @return float Calculated value
|
||||||
|
* @throws \InvalidArgumentException if $number is negative
|
||||||
*/
|
*/
|
||||||
public static function cbrt(float|int $number): float
|
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) ?
|
callback: fn ($col) => is_array($row) ?
|
||||||
array_reduce(
|
array_reduce(
|
||||||
array: $row,
|
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
|
// 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,
|
initial: 0,
|
||||||
) :
|
) :
|
||||||
array_reduce(
|
array_reduce(
|
||||||
array: $col,
|
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,
|
initial: 0,
|
||||||
),
|
),
|
||||||
array: $bCols,
|
array: $bCols,
|
||||||
|
|||||||
@@ -8,8 +8,20 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert;
|
namespace CoreLibs\Convert;
|
||||||
|
|
||||||
|
use CoreLibs\Combined\ArrayHandler;
|
||||||
|
|
||||||
class Strings
|
class Strings
|
||||||
{
|
{
|
||||||
|
/** @var array<int,string> all the preg error messages */
|
||||||
|
public const array PREG_ERROR_MESSAGES = [
|
||||||
|
PREG_NO_ERROR => 'No error',
|
||||||
|
PREG_INTERNAL_ERROR => 'Internal PCRE error',
|
||||||
|
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit exhausted',
|
||||||
|
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit exhausted',
|
||||||
|
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
|
||||||
|
PREG_BAD_UTF8_OFFSET_ERROR => 'Bad UTF-8 offset',
|
||||||
|
PREG_JIT_STACKLIMIT_ERROR => 'JIT stack limit exhausted'
|
||||||
|
];
|
||||||
/**
|
/**
|
||||||
* return the number of elements in the split list
|
* return the number of elements in the split list
|
||||||
* 0 if nothing / invalid split
|
* 0 if nothing / invalid split
|
||||||
@@ -52,29 +64,42 @@ class Strings
|
|||||||
* Note a string LONGER then the maxium will be attached with the LAST
|
* Note a string LONGER then the maxium will be attached with the LAST
|
||||||
* split character. In above exmaple
|
* split character. In above exmaple
|
||||||
* ABCD1234EFGHTOOLONG will be ABCD-1234-EFGH-TOOLONG
|
* ABCD1234EFGHTOOLONG will be ABCD-1234-EFGH-TOOLONG
|
||||||
|
* If the characters are NOT ASCII it will return the string as is
|
||||||
*
|
*
|
||||||
* @param string $value string value to split
|
* @param string $string string value to split
|
||||||
* @param string $split_format split format
|
* @param string $split_format split format
|
||||||
* @param string $split_characters list of charcters with which we split
|
|
||||||
* if not set uses dash ('-')
|
|
||||||
* @return string split formatted string or original value if not chnaged
|
* @return string split formatted string or original value if not chnaged
|
||||||
|
* @throws \InvalidArgumentException for empty split format, invalid values, split characters or split format
|
||||||
*/
|
*/
|
||||||
public static function splitFormatString(
|
public static function splitFormatString(
|
||||||
string $value,
|
string $string,
|
||||||
string $split_format,
|
string $split_format,
|
||||||
string $split_characters = '-'
|
|
||||||
): string {
|
): string {
|
||||||
if (
|
// skip if string or split format is empty is empty
|
||||||
// abort if split format is empty
|
if (empty($string) || empty($split_format)) {
|
||||||
empty($split_format) ||
|
return $string;
|
||||||
// if not in the valid ASCII character range for any of the strings
|
}
|
||||||
preg_match('/[^\x20-\x7e]/', $value) ||
|
if (preg_match('/[^\x20-\x7e]/', $string)) {
|
||||||
// preg_match('/[^\x20-\x7e]/', $split_format) ||
|
throw new \InvalidArgumentException(
|
||||||
preg_match('/[^\x20-\x7e]/', $split_characters) ||
|
"The string to split can only be ascii characters: " . $string
|
||||||
// only numbers and split characters in split_format
|
);
|
||||||
!preg_match("/[0-9" . $split_characters . "]/", $split_format)
|
}
|
||||||
) {
|
// get the split characters that are not numerical and check they are ascii
|
||||||
return $value;
|
$split_characters = self::removeDuplicates(preg_replace('/[0-9]/', '', $split_format) ?: '');
|
||||||
|
if (empty($split_characters)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"A split character must exist in the format string: " . $split_format
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (preg_match('/[^\x20-\x7e]/', $split_characters)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"The split character has to be a valid ascii character: " . $split_characters
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!preg_match("/^[0-9" . $split_characters . "]+$/", $split_format)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"The split format can only be numbers and the split characters: " . $split_format
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// split format list
|
// split format list
|
||||||
$split_list = preg_split(
|
$split_list = preg_split(
|
||||||
@@ -86,14 +111,14 @@ class Strings
|
|||||||
);
|
);
|
||||||
// if this is false, or only one array, abort split
|
// if this is false, or only one array, abort split
|
||||||
if (!is_array($split_list) || count($split_list) == 1) {
|
if (!is_array($split_list) || count($split_list) == 1) {
|
||||||
return $value;
|
return $string;
|
||||||
}
|
}
|
||||||
$out = '';
|
$out = '';
|
||||||
$pos = 0;
|
$pos = 0;
|
||||||
$last_split = '';
|
$last_split = '';
|
||||||
foreach ($split_list as $offset) {
|
foreach ($split_list as $offset) {
|
||||||
if (is_numeric($offset)) {
|
if (is_numeric($offset)) {
|
||||||
$_part = substr($value, $pos, (int)$offset);
|
$_part = substr($string, $pos, (int)$offset);
|
||||||
if (empty($_part)) {
|
if (empty($_part)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -104,8 +129,8 @@ class Strings
|
|||||||
$last_split = $offset;
|
$last_split = $offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!empty($out) && $pos < strlen($value)) {
|
if (!empty($out) && $pos < strlen($string)) {
|
||||||
$out .= $last_split . substr($value, $pos);
|
$out .= $last_split . substr($string, $pos);
|
||||||
}
|
}
|
||||||
// if last is not alphanumeric remove, remove
|
// if last is not alphanumeric remove, remove
|
||||||
if (!strcspn(substr($out, -1, 1), $split_characters)) {
|
if (!strcspn(substr($out, -1, 1), $split_characters)) {
|
||||||
@@ -115,10 +140,49 @@ class Strings
|
|||||||
if (!empty($out)) {
|
if (!empty($out)) {
|
||||||
return $out;
|
return $out;
|
||||||
} else {
|
} else {
|
||||||
return $value;
|
return $string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a string into n-length blocks with a split character inbetween
|
||||||
|
* This is simplified version from splitFormatString that uses
|
||||||
|
* fixed split length with a characters, this evenly splits the string out into the
|
||||||
|
* given length
|
||||||
|
* This works with non ASCII characters too
|
||||||
|
*
|
||||||
|
* @param string $string string to split
|
||||||
|
* @param int $split_length split length, must be smaller than string and larger than 0
|
||||||
|
* @param string $split_characters [default=-] the character to split, can be more than one
|
||||||
|
* @return string
|
||||||
|
* @throws \InvalidArgumentException Thrown if split length style is invalid
|
||||||
|
*/
|
||||||
|
public static function splitFormatStringFixed(
|
||||||
|
string $string,
|
||||||
|
int $split_length,
|
||||||
|
string $split_characters = '-'
|
||||||
|
): string {
|
||||||
|
// if empty string or if split lenght is 0 or empty split characters
|
||||||
|
// then we skip any splitting
|
||||||
|
if (empty($string) || $split_length == 0 || empty($split_characters)) {
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
$return_string = '';
|
||||||
|
$string_length = mb_strlen($string);
|
||||||
|
// check that the length is not too short
|
||||||
|
if ($split_length < 1 || $split_length >= $string_length) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"The split length must be at least 1 character and less than the string length to split. "
|
||||||
|
. "Split length: " . $split_length . ", string length: " . $string_length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for ($i = 0; $i < $string_length; $i += $split_length) {
|
||||||
|
$return_string .= mb_substr($string, $i, $split_length) . $split_characters;
|
||||||
|
}
|
||||||
|
// remove last trailing character which is always the split char length
|
||||||
|
return mb_substr($return_string, 0, -1 * mb_strlen($split_characters));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strip any duplicated slahes from a path
|
* Strip any duplicated slahes from a path
|
||||||
* eg: //foo///bar/foo.inc -> /foo/bar/foo.inc
|
* eg: //foo///bar/foo.inc -> /foo/bar/foo.inc
|
||||||
@@ -146,6 +210,165 @@ class Strings
|
|||||||
{
|
{
|
||||||
return trim($text, pack('H*', 'EFBBBF'));
|
return trim($text, pack('H*', 'EFBBBF'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make as string of characters unique
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function removeDuplicates(string $string): string
|
||||||
|
{
|
||||||
|
// combine again
|
||||||
|
$result = implode(
|
||||||
|
'',
|
||||||
|
// unique list
|
||||||
|
array_unique(
|
||||||
|
// split into array
|
||||||
|
mb_str_split($string)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if all characters are in set
|
||||||
|
*
|
||||||
|
* @param string $needle Needle to search
|
||||||
|
* @param string $haystack Haystack to search in
|
||||||
|
* @return bool True on found, False if not in haystack
|
||||||
|
*/
|
||||||
|
public static function allCharsInSet(string $needle, string $haystack): bool
|
||||||
|
{
|
||||||
|
$input_length = strlen($needle);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $input_length; $i++) {
|
||||||
|
if (strpos($haystack, $needle[$i]) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* converts a list of arrays of strings into a string of unique entries
|
||||||
|
* input arrays can be nested, only values are used
|
||||||
|
*
|
||||||
|
* @param array<mixed> ...$char_lists
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function buildCharStringFromLists(array ...$char_lists): string
|
||||||
|
{
|
||||||
|
return implode('', array_unique(
|
||||||
|
ArrayHandler::flattenArray(
|
||||||
|
array_merge(...$char_lists)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* @param string $pattern Any regex string
|
||||||
|
* @return bool False on invalid regex
|
||||||
|
*/
|
||||||
|
public static function isValidRegex(string $pattern): bool
|
||||||
|
{
|
||||||
|
preg_last_error();
|
||||||
|
try {
|
||||||
|
$var = '';
|
||||||
|
@preg_match($pattern, $var);
|
||||||
|
return preg_last_error() === PREG_NO_ERROR;
|
||||||
|
} catch (\Error $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last preg error messages as string
|
||||||
|
* all messages are defined in PREG_ERROR_MESSAGES
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getLastRegexErrorString(): string
|
||||||
|
{
|
||||||
|
return self::PREG_ERROR_MESSAGES[preg_last_error()] ?? 'Unknown error';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if a regex is invalid, returns array with flag and error string
|
||||||
|
*
|
||||||
|
* @param string $pattern
|
||||||
|
* @return array{valid:bool,preg_error:int,error:null|string,pcre_error:null|string}
|
||||||
|
*/
|
||||||
|
public static function validateRegex(string $pattern): array
|
||||||
|
{
|
||||||
|
// Clear any previous PCRE errors
|
||||||
|
preg_last_error();
|
||||||
|
$var = '';
|
||||||
|
if (@preg_match($pattern, $var) === false) {
|
||||||
|
$error = preg_last_error();
|
||||||
|
return [
|
||||||
|
'valid' => false,
|
||||||
|
'preg_error' => $error,
|
||||||
|
'error' => self::PREG_ERROR_MESSAGES[$error] ?? 'Unknown error',
|
||||||
|
'pcre_error' => preg_last_error_msg(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['valid' => true, 'preg_error' => PREG_NO_ERROR, 'error' => null, 'pcre_error' => null];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// __END__
|
// __END__
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class Email
|
|||||||
* @param string $encoding Encoding, if not set UTF-8
|
* @param string $encoding Encoding, if not set UTF-8
|
||||||
* @param bool $kv_folding If set to true and a valid encoding, do KV folding
|
* @param bool $kv_folding If set to true and a valid encoding, do KV folding
|
||||||
* @return string Correctly encoded and build email string
|
* @return string Correctly encoded and build email string
|
||||||
|
* @throws \IntlException if email name cannot be converted to UTF-8
|
||||||
*/
|
*/
|
||||||
public static function encodeEmailName(
|
public static function encodeEmailName(
|
||||||
string $email,
|
string $email,
|
||||||
@@ -52,6 +53,10 @@ class Email
|
|||||||
if ($encoding != 'UTF-8') {
|
if ($encoding != 'UTF-8') {
|
||||||
$email_name = mb_convert_encoding($email_name, $encoding, 'UTF-8');
|
$email_name = mb_convert_encoding($email_name, $encoding, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
// if we cannot transcode the name, return just the email
|
||||||
|
if ($email_name === false) {
|
||||||
|
throw new \IntlException('Cannot convert email_name to UTF-8');
|
||||||
|
}
|
||||||
$email_name =
|
$email_name =
|
||||||
mb_encode_mimeheader(
|
mb_encode_mimeheader(
|
||||||
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
|
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
|
||||||
@@ -77,6 +82,8 @@ class Email
|
|||||||
* @param bool $kv_folding If set to true and a valid encoding,
|
* @param bool $kv_folding If set to true and a valid encoding,
|
||||||
* do KV folding
|
* do KV folding
|
||||||
* @return array<string> Pos 0: Subject, Pos 1: Body
|
* @return array<string> Pos 0: Subject, Pos 1: Body
|
||||||
|
* @throws \IntlException if subject cannot be converted to UTF-8
|
||||||
|
* @throws \IntlException if body cannot be converted to UTF-8
|
||||||
*/
|
*/
|
||||||
private static function replaceContent(
|
private static function replaceContent(
|
||||||
string $subject,
|
string $subject,
|
||||||
@@ -102,6 +109,12 @@ class Email
|
|||||||
$subject = mb_convert_encoding($subject, $encoding, 'UTF-8');
|
$subject = mb_convert_encoding($subject, $encoding, 'UTF-8');
|
||||||
$body = mb_convert_encoding($body, $encoding, 'UTF-8');
|
$body = mb_convert_encoding($body, $encoding, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
if ($subject === false) {
|
||||||
|
throw new \IntlException('Cannot convert subject to UTF-8');
|
||||||
|
}
|
||||||
|
if ($body === false) {
|
||||||
|
throw new \IntlException('Cannot convert body to UTF-8');
|
||||||
|
}
|
||||||
// we need to encodde the subject
|
// we need to encodde the subject
|
||||||
$subject = mb_encode_mimeheader(
|
$subject = mb_encode_mimeheader(
|
||||||
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
|
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
|
||||||
|
|||||||
@@ -8,39 +8,97 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Create;
|
namespace CoreLibs\Create;
|
||||||
|
|
||||||
|
use CoreLibs\Convert\Strings;
|
||||||
|
|
||||||
class RandomKey
|
class RandomKey
|
||||||
{
|
{
|
||||||
|
/** @var int set the default key length it nothing else is set */
|
||||||
|
public const int KEY_LENGTH_DEFAULT = 4;
|
||||||
|
/** @var int the maximum key length allowed */
|
||||||
|
public const int KEY_LENGTH_MAX = 256;
|
||||||
|
/** @var string the default characters in the key range */
|
||||||
|
public const string KEY_CHARACTER_RANGE_DEFAULT =
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
. 'abcdefghijklmnopqrstuvwxyz'
|
||||||
|
. '0123456789';
|
||||||
// key generation
|
// key generation
|
||||||
/** @var string */
|
/** @var string all the characters that are int he current radnom key range */
|
||||||
private static string $key_range = '';
|
private static string $key_character_range = '';
|
||||||
/** @var int */
|
/** @var int character count in they key character range */
|
||||||
private static int $one_key_length;
|
private static int $key_character_range_length = 0;
|
||||||
/** @var int */
|
/** @var int default key lenghth */
|
||||||
private static int $key_length = 4; // default key length
|
/** @deprecated Will be removed, as setting has moved to randomKeyGen */
|
||||||
/** @var int */
|
private static int $key_length = 4;
|
||||||
private static int $max_key_length = 256; // max allowed length
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if launched as class, init random key data first
|
* if launched as class, init random key data first
|
||||||
|
*
|
||||||
|
* @param array<string> ...$key_range
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct(array ...$key_range)
|
||||||
{
|
{
|
||||||
$this->initRandomKeyData();
|
$this->setRandomKeyData(...$key_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal key range validation
|
||||||
|
*
|
||||||
|
* @param array<string> ...$key_range
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function validateRandomKeyData(array ...$key_range): string
|
||||||
|
{
|
||||||
|
$key_character_range = Strings::buildCharStringFromLists(...$key_range);
|
||||||
|
if (strlen($key_character_range) <= 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return $key_character_range;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the random key range with the default values
|
* sets the random key range with the default values
|
||||||
*
|
*
|
||||||
|
* @param array<string> $key_range a list of key ranges as array
|
||||||
* @return void has no return
|
* @return void has no return
|
||||||
|
* @throws \LengthException If the string length is only 1 abort
|
||||||
*/
|
*/
|
||||||
private static function initRandomKeyData(): void
|
public static function setRandomKeyData(array ...$key_range): void
|
||||||
{
|
{
|
||||||
// random key generation base string
|
// if key range is not set
|
||||||
self::$key_range = join('', array_merge(
|
if (!count($key_range)) {
|
||||||
range('A', 'Z'),
|
self::$key_character_range = self::KEY_CHARACTER_RANGE_DEFAULT;
|
||||||
range('a', 'z'),
|
} else {
|
||||||
range('0', '9')
|
self::$key_character_range = self::validateRandomKeyData(...$key_range);
|
||||||
));
|
// random key generation base string
|
||||||
self::$one_key_length = strlen(self::$key_range);
|
}
|
||||||
|
self::$key_character_range_length = strlen(self::$key_character_range);
|
||||||
|
if (self::$key_character_range_length <= 1) {
|
||||||
|
throw new \LengthException(
|
||||||
|
"The given key character range '" . self::$key_character_range . "' "
|
||||||
|
. "is too small, must be at lest two characters: "
|
||||||
|
. self::$key_character_range_length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the characters for the current key characters
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getRandomKeyData(): string
|
||||||
|
{
|
||||||
|
return self::$key_character_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the length of all random characters
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function getRandomKeyDataLength(): int
|
||||||
|
{
|
||||||
|
return self::$key_character_range_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,11 +107,11 @@ class RandomKey
|
|||||||
* @param int $key_length key length
|
* @param int $key_length key length
|
||||||
* @return bool true for valid, false for invalid 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 (
|
if (
|
||||||
$key_length > 0 &&
|
$key_length > 0 &&
|
||||||
$key_length <= self::$max_key_length
|
$key_length <= self::KEY_LENGTH_MAX
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -67,11 +125,12 @@ class RandomKey
|
|||||||
*
|
*
|
||||||
* @param int $key_length key length
|
* @param int $key_length key length
|
||||||
* @return bool true/false for set status
|
* @return bool true/false for set status
|
||||||
|
* @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
|
public static function setRandomKeyLength(int $key_length): bool
|
||||||
{
|
{
|
||||||
// only if valid int key with valid length
|
// 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;
|
self::$key_length = $key_length;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -83,6 +142,7 @@ class RandomKey
|
|||||||
* get the current set random key length
|
* get the current set random key length
|
||||||
*
|
*
|
||||||
* @return int Current set key length
|
* @return int Current set key length
|
||||||
|
* @deprecated Key length is set during randomKeyGen call, this nethid is deprecated
|
||||||
*/
|
*/
|
||||||
public static function getRandomKeyLength(): int
|
public static function getRandomKeyLength(): int
|
||||||
{
|
{
|
||||||
@@ -94,28 +154,37 @@ class RandomKey
|
|||||||
* if override key length is set, it will check on valid key and use this
|
* if override key length is set, it will check on valid key and use this
|
||||||
* this will not set the class key length variable
|
* this will not set the class key length variable
|
||||||
*
|
*
|
||||||
* @param int $key_length key length override, -1 for use default
|
* @param int $key_length [default=-1] key length override,
|
||||||
* @return string random key
|
* if not set use default [LEGACY]
|
||||||
|
* @param array<string> $key_range a list of key ranges as array,
|
||||||
|
* if not set use previous set data
|
||||||
|
* @return string random key
|
||||||
*/
|
*/
|
||||||
public static function randomKeyGen(int $key_length = -1): string
|
public static function randomKeyGen(
|
||||||
{
|
int $key_length = self::KEY_LENGTH_DEFAULT,
|
||||||
// init random key strings if not set
|
array ...$key_range
|
||||||
if (
|
): string {
|
||||||
!isset(self::$one_key_length)
|
$key_character_range = '';
|
||||||
) {
|
if (count($key_range)) {
|
||||||
self::initRandomKeyData();
|
$key_character_range = self::validateRandomKeyData(...$key_range);
|
||||||
}
|
$key_character_range_length = strlen($key_character_range);
|
||||||
$use_key_length = 0;
|
|
||||||
// only if valid int key with valid length
|
|
||||||
if (self::validateRandomKeyLenght($key_length) === true) {
|
|
||||||
$use_key_length = $key_length;
|
|
||||||
} else {
|
} else {
|
||||||
$use_key_length = self::$key_length;
|
if (!self::$key_character_range_length) {
|
||||||
|
self::setRandomKeyData();
|
||||||
|
}
|
||||||
|
$key_character_range = self::getRandomKeyData();
|
||||||
|
$key_character_range_length = self::getRandomKeyDataLength();
|
||||||
|
}
|
||||||
|
// if not valid key length, fallback to default
|
||||||
|
if (!self::validateRandomKeyLength($key_length)) {
|
||||||
|
$key_length = self::KEY_LENGTH_DEFAULT;
|
||||||
}
|
}
|
||||||
// create random string
|
// create random string
|
||||||
$random_string = '';
|
$random_string = '';
|
||||||
for ($i = 1; $i <= $use_key_length; $i++) {
|
for ($i = 1; $i <= $key_length; $i++) {
|
||||||
$random_string .= self::$key_range[random_int(0, self::$one_key_length - 1)];
|
$random_string .= $key_character_range[
|
||||||
|
random_int(0, $key_character_range_length - 1)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return $random_string;
|
return $random_string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class Uids
|
|||||||
*/
|
*/
|
||||||
public static function validateUuuidv4(string $uuidv4): bool
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ class ArrayIO extends \CoreLibs\DB\IO
|
|||||||
{
|
{
|
||||||
// main calss variables
|
// main calss variables
|
||||||
/** @var array<mixed> */
|
/** @var array<mixed> */
|
||||||
private array $table_array; // the array from the table to work on
|
private array $table_array = []; // the array from the table to work on
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private string $table_name; // the table_name
|
private string $table_name = ''; // the table_name
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private string $pk_name = ''; // the primary key from this table
|
private string $pk_name = ''; // the primary key from this table
|
||||||
/** @var int|string|null */
|
/** @var int|string|null */
|
||||||
@@ -127,9 +127,9 @@ class ArrayIO extends \CoreLibs\DB\IO
|
|||||||
public function getTableArray(bool $reset = false): array
|
public function getTableArray(bool $reset = false): array
|
||||||
{
|
{
|
||||||
if (!$reset) {
|
if (!$reset) {
|
||||||
return $this->table_array ?? [];
|
return $this->table_array;
|
||||||
}
|
}
|
||||||
$table_array = $this->table_array ?? [];
|
$table_array = $this->table_array;
|
||||||
reset($table_array);
|
reset($table_array);
|
||||||
return $table_array;
|
return $table_array;
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ class ArrayIO extends \CoreLibs\DB\IO
|
|||||||
*/
|
*/
|
||||||
public function getTableName(): string
|
public function getTableName(): string
|
||||||
{
|
{
|
||||||
return $this->table_name ?? '';
|
return $this->table_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2544,7 +2544,10 @@ class IO
|
|||||||
} // only go if NO cursor exists
|
} // only go if NO cursor exists
|
||||||
|
|
||||||
// if cursor exists ...
|
// if cursor exists ...
|
||||||
if ($this->cursor_ext[$query_hash]['cursor']) {
|
if (
|
||||||
|
$this->cursor_ext[$query_hash]['cursor'] instanceof \PgSql\Result ||
|
||||||
|
$this->cursor_ext[$query_hash]['cursor'] == 1
|
||||||
|
) {
|
||||||
if ($first_call === true) {
|
if ($first_call === true) {
|
||||||
$this->cursor_ext[$query_hash]['log'][] = 'First call';
|
$this->cursor_ext[$query_hash]['log'][] = 'First call';
|
||||||
// count the rows returned (if select)
|
// count the rows returned (if select)
|
||||||
|
|||||||
@@ -263,11 +263,11 @@ class ConvertPlaceholder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
// 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(
|
throw new \RuntimeException(
|
||||||
'Cannot lookup ' . $match . ' in params lookup list',
|
'Cannot lookup ' . $match . ' in params lookup list',
|
||||||
211
|
211
|
||||||
);
|
)*/;
|
||||||
},
|
},
|
||||||
$converted_placeholders['original']['query']
|
$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
|
// 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(
|
throw new \RuntimeException(
|
||||||
'Cannot lookup ' . $match . ' in params lookup list',
|
'Cannot lookup ' . $match . ' in params lookup list',
|
||||||
231
|
231
|
||||||
);
|
)*/;
|
||||||
},
|
},
|
||||||
$converted_placeholders['original']['query']
|
$converted_placeholders['original']['query']
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,6 +33,36 @@ class Support
|
|||||||
return $string;
|
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
|
* prints a html formatted (pre) data
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ class ErrorMessage
|
|||||||
// set a jump target
|
// set a jump target
|
||||||
$this->setJumpTarget($jump_target['target'] ?? null, $jump_target['info'] ?? null, $level);
|
$this->setJumpTarget($jump_target['target'] ?? null, $jump_target['info'] ?? null, $level);
|
||||||
// write to log for abort/crash
|
// write to log for abort/crash
|
||||||
|
$this->log->setErrorMessageCallSetErrorMsg();
|
||||||
switch ($level) {
|
switch ($level) {
|
||||||
case 'notice':
|
case 'notice':
|
||||||
$this->log->notice($message ?? $str, array_merge([
|
$this->log->notice($message ?? $str, array_merge([
|
||||||
@@ -210,6 +211,7 @@ class ErrorMessage
|
|||||||
?bool $log_error = null,
|
?bool $log_error = null,
|
||||||
?bool $log_warning = null,
|
?bool $log_warning = null,
|
||||||
): void {
|
): void {
|
||||||
|
$this->log->setErrorMessageCallSetMessage();
|
||||||
$this->setErrorMsg(
|
$this->setErrorMsg(
|
||||||
$error_id ?? '',
|
$error_id ?? '',
|
||||||
$level,
|
$level,
|
||||||
@@ -289,7 +291,7 @@ class ErrorMessage
|
|||||||
*/
|
*/
|
||||||
public function getLastErrorMsg(): array
|
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' => '',
|
'level' => '',
|
||||||
'str' => '',
|
'str' => '',
|
||||||
'id' => '',
|
'id' => '',
|
||||||
|
|||||||
@@ -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
|
* @param Level $level
|
||||||
* @return bool
|
* @return bool
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace CoreLibs\Logging\Logger;
|
|||||||
|
|
||||||
enum MessageLevel: int
|
enum MessageLevel: int
|
||||||
{
|
{
|
||||||
|
case noset = 0;
|
||||||
case ok = 100;
|
case ok = 100;
|
||||||
case success = 150; // special for file uploads
|
case success = 150; // special for file uploads
|
||||||
case info = 200;
|
case info = 200;
|
||||||
|
|||||||
@@ -29,12 +29,21 @@ use Stringable;
|
|||||||
class Logging
|
class Logging
|
||||||
{
|
{
|
||||||
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
|
/** @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 */
|
/** @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 */
|
/** @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
|
// NOTE: the second party array{} hs some errors
|
||||||
/** @var array<string,array<string,string|bool|Level>>|array{string:array{type:string,type_info?:string,mandatory:true,alias?:string,default:string|bool|Level,deprecated:bool,use?:string}} */
|
/** @var array<string,array<string,string|bool|Level>>|array{string:array{type:string,type_info?:string,mandatory:true,alias?:string,default:string|bool|Level,deprecated:bool,use?:string}} */
|
||||||
private const OPTIONS = [
|
private const OPTIONS = [
|
||||||
@@ -50,6 +59,7 @@ class Logging
|
|||||||
'type' => 'string', 'mandatory' => false,
|
'type' => 'string', 'mandatory' => false,
|
||||||
'default' => '', 'deprecated' => true, 'use' => 'log_file_id'
|
'default' => '', 'deprecated' => true, 'use' => 'log_file_id'
|
||||||
],
|
],
|
||||||
|
// log level
|
||||||
'log_level' => [
|
'log_level' => [
|
||||||
'type' => 'instance',
|
'type' => 'instance',
|
||||||
'type_info' => '\CoreLibs\Logging\Logger\Level',
|
'type_info' => '\CoreLibs\Logging\Logger\Level',
|
||||||
@@ -57,6 +67,14 @@ class Logging
|
|||||||
'default' => Level::Debug,
|
'default' => Level::Debug,
|
||||||
'deprecated' => false
|
'deprecated' => false
|
||||||
],
|
],
|
||||||
|
// level to trigger write to error_log
|
||||||
|
'error_log_write_level' => [
|
||||||
|
'type' => 'instance',
|
||||||
|
'type_info' => '\CoreLibs\Logging\Logger\Level',
|
||||||
|
'mandatory' => false,
|
||||||
|
'default' => Level::Emergency,
|
||||||
|
'deprecated' => false,
|
||||||
|
],
|
||||||
// options
|
// options
|
||||||
'log_per_run' => [
|
'log_per_run' => [
|
||||||
'type' => 'bool', 'mandatory' => false,
|
'type' => 'bool', 'mandatory' => false,
|
||||||
@@ -86,14 +104,21 @@ class Logging
|
|||||||
'type' => 'bool', 'mandatory' => false,
|
'type' => 'bool', 'mandatory' => false,
|
||||||
'default' => false, 'deprecated' => true, 'use' => 'log_per_date'
|
'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
|
// options
|
||||||
/** @var array<mixed> */
|
/** @var array<mixed> */
|
||||||
private array $options = [];
|
private array $options = [];
|
||||||
|
|
||||||
/** @var Level set level */
|
/** @var Level set logging level */
|
||||||
private Level $log_level;
|
private Level $log_level;
|
||||||
|
/** @var Level set level for writing to error_log, will not write if log level lower than error log write level */
|
||||||
|
private Level $error_log_write_level;
|
||||||
|
|
||||||
// page and host name
|
// page and host name
|
||||||
/** @var string */
|
/** @var string */
|
||||||
@@ -121,6 +146,12 @@ class Logging
|
|||||||
/** @var string Y-m-d file in file name */
|
/** @var string Y-m-d file in file name */
|
||||||
private string $log_file_date = '';
|
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)
|
* 1: create a new log file per run (time stamp + unique ID)
|
||||||
* 2: add Y-m-d and do automatic daily rotation
|
* 2: add Y-m-d and do automatic daily rotation
|
||||||
@@ -145,12 +176,13 @@ class Logging
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init logger
|
* MARK: Init logger
|
||||||
*
|
*
|
||||||
* options array layout
|
* options array layout
|
||||||
* - log_folder:
|
* - log_folder:
|
||||||
* - log_file_id / file_id (will be deprecated):
|
* - log_file_id / file_id (will be deprecated):
|
||||||
* - log_level:
|
* - log_level:
|
||||||
|
* - error_log_write_level: at what level we write to error_log
|
||||||
*
|
*
|
||||||
* - log_per_run:
|
* - log_per_run:
|
||||||
* - log_per_date: (was print_file_date)
|
* - log_per_date: (was print_file_date)
|
||||||
@@ -172,6 +204,8 @@ class Logging
|
|||||||
|
|
||||||
// set log level
|
// set log level
|
||||||
$this->initLogLevel();
|
$this->initLogLevel();
|
||||||
|
// set error log write level
|
||||||
|
$this->initErrorLogWriteLevel();
|
||||||
// set log folder from options
|
// set log folder from options
|
||||||
$this->initLogFolder();
|
$this->initLogFolder();
|
||||||
// set per run UID for logging
|
// set per run UID for logging
|
||||||
@@ -190,8 +224,10 @@ class Logging
|
|||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|
||||||
|
// MARK: options check
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* validate options
|
||||||
*
|
*
|
||||||
* @param array<mixed> $options
|
* @param array<mixed> $options
|
||||||
* @return bool
|
* @return bool
|
||||||
@@ -263,6 +299,8 @@ class Logging
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: init log elvels
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init log level, just a wrapper to auto set from options
|
* init log level, just a wrapper to auto set from options
|
||||||
*
|
*
|
||||||
@@ -280,6 +318,24 @@ class Logging
|
|||||||
$this->setLoggingLevel($this->options['log_level']);
|
$this->setLoggingLevel($this->options['log_level']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init error log write level
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function initErrorLogWriteLevel()
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
empty($this->options['error_log_write_level']) ||
|
||||||
|
!$this->options['error_log_write_level'] instanceof Level
|
||||||
|
) {
|
||||||
|
$this->options['error_log_write_level'] = Level::Emergency;
|
||||||
|
}
|
||||||
|
$this->setErrorLogWriteLevel($this->options['error_log_write_level']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: set log folder
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the log folder
|
* Set the log folder
|
||||||
* If folder is not writeable the script will throw an E_USER_ERROR
|
* If folder is not writeable the script will throw an E_USER_ERROR
|
||||||
@@ -321,6 +377,8 @@ class Logging
|
|||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: set host name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the hostname and port
|
* Set the hostname and port
|
||||||
* If port is not defaul 80 it will be added to the host name
|
* If port is not defaul 80 it will be added to the host name
|
||||||
@@ -337,6 +395,8 @@ class Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: set log file id (file)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set log file prefix id
|
* set log file prefix id
|
||||||
*
|
*
|
||||||
@@ -395,6 +455,8 @@ class Logging
|
|||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK init log flags and levels
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set flags from options and option flags connection internal settings
|
* set flags from options and option flags connection internal settings
|
||||||
*
|
*
|
||||||
@@ -423,6 +485,19 @@ class Logging
|
|||||||
return $this->log_level->includes($level);
|
return $this->log_level->includes($level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that given level is matchins error_log write level
|
||||||
|
*
|
||||||
|
* @param Level $level
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function checkErrorLogWriteLevel(Level $level): bool
|
||||||
|
{
|
||||||
|
return $this->error_log_write_level->includes($level);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: build log ifle name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the file name for writing
|
* Build the file name for writing
|
||||||
*
|
*
|
||||||
@@ -490,6 +565,8 @@ class Logging
|
|||||||
return $fn;
|
return $fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: master write log to file
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* writes error msg data to file for current level
|
* writes error msg data to file for current level
|
||||||
*
|
*
|
||||||
@@ -507,6 +584,10 @@ class Logging
|
|||||||
if (!$this->checkLogLevel($level)) {
|
if (!$this->checkLogLevel($level)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// if we match level then write to error_log
|
||||||
|
if ($this->checkErrorLogWriteLevel($level)) {
|
||||||
|
error_log((string)$message);
|
||||||
|
}
|
||||||
|
|
||||||
// build logging file name
|
// build logging file name
|
||||||
// fn is log folder + file name
|
// fn is log folder + file name
|
||||||
@@ -531,6 +612,8 @@ class Logging
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: master prepare log
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the log message with all needed info blocks:
|
* Prepare the log message with all needed info blocks:
|
||||||
* [timestamp] [host name] [file path + file::row number] [running uid] {class::/->method}
|
* [timestamp] [host name] [file path + file::row number] [running uid] {class::/->method}
|
||||||
@@ -558,31 +641,65 @@ class Logging
|
|||||||
$file_line = '';
|
$file_line = '';
|
||||||
$caller_class_method = '-';
|
$caller_class_method = '-';
|
||||||
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||||
// print "[" . $level->getName() . "] [$message] prepareLog:<br>" . Support::printAr($traces);
|
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
|
||||||
// file + line: call not this but one before (the one that calls this)
|
// set stack trace level +1 if called from ErrorMessage::setMessage
|
||||||
// start from this level, if unset fall down until we are at null
|
if ($this->error_message_call_set_message) {
|
||||||
$start_trace_level = 2;
|
$stack_trace_start_level_line = 3;
|
||||||
for ($trace_level = $start_trace_level; $trace_level >= 0; $trace_level--) {
|
} elseif ($this->error_message_call_set_error_msg) {
|
||||||
if (isset($traces[$trace_level])) {
|
$stack_trace_start_level_line = 2;
|
||||||
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
|
}
|
||||||
. ':' . ($traces[$trace_level]['line'] ?? '-');
|
// if we have line > default, then check if valid, else reset to default
|
||||||
// as namespace\class->method
|
if ($stack_trace_start_level_line > self::DEFAULT_STACK_TRACE_LEVEL_LINE) {
|
||||||
$caller_class_method =
|
// check if function at level is one of the override checks
|
||||||
// get the last call before we are in the Logging class
|
$fn_check = $traces[$stack_trace_start_level_line]['function'] ?? '';
|
||||||
($traces[$trace_level]['class'] ?? '')
|
if (
|
||||||
// connector, if unkown use ==
|
!isset(self::STACK_OVERRIDE_CHECK[$fn_check]) ||
|
||||||
. ($traces[$trace_level]['type'] ?? '')
|
self::STACK_OVERRIDE_CHECK[$fn_check] != $stack_trace_start_level_line
|
||||||
// method/function: prepareLog->(debug|info|...)->[THIS]
|
) {
|
||||||
. $traces[$trace_level]['function'];
|
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$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)) {
|
if (empty($file_line)) {
|
||||||
$file_line = System::getPageName(System::FULL_PATH);
|
$file_line = System::getPageName(System::FULL_PATH);
|
||||||
}
|
}
|
||||||
// print "CLASS: " . $class . "<br>";
|
// print "CLASS: " . $class . "<br>";
|
||||||
// get timestamp
|
// 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
|
// if group id is empty replace it with current level
|
||||||
$group_str = $level->getName();
|
$group_str = $level->getName();
|
||||||
@@ -610,6 +727,7 @@ class Logging
|
|||||||
// PUBLIC STATIC METHJODS
|
// PUBLIC STATIC METHJODS
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|
||||||
|
// MARK: set log level
|
||||||
/**
|
/**
|
||||||
* set the log level
|
* set the log level
|
||||||
*
|
*
|
||||||
@@ -670,7 +788,7 @@ class Logging
|
|||||||
|
|
||||||
// **** GET/SETTER
|
// **** GET/SETTER
|
||||||
|
|
||||||
// log level set and get
|
// MARK: log level
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set new log level
|
* set new log level
|
||||||
@@ -705,7 +823,30 @@ class Logging
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// log file id set (file name prefix)
|
// MARK: error log write level
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the error_log write level
|
||||||
|
*
|
||||||
|
* @param string|int|Level $level
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setErrorLogWriteLevel(string|int|Level $level): void
|
||||||
|
{
|
||||||
|
$this->error_log_write_level = $this->processLogLevel($level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the current level for error_log write
|
||||||
|
*
|
||||||
|
* @return Level
|
||||||
|
*/
|
||||||
|
public function getErrorLogWriteLevel(): Level
|
||||||
|
{
|
||||||
|
return $this->error_log_write_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: log file id set (file name prefix)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the internal log file prefix id
|
* sets the internal log file prefix id
|
||||||
@@ -733,7 +874,7 @@ class Logging
|
|||||||
return $this->log_file_id;
|
return $this->log_file_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// log unique id set (for per run)
|
// MARK: log unique id set (for per run)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a unique id based on current date (y/m/d, h:i:s) and a unique id (8 chars)
|
* Sets a unique id based on current date (y/m/d, h:i:s) and a unique id (8 chars)
|
||||||
@@ -768,7 +909,7 @@ class Logging
|
|||||||
return $this->log_file_unique_id;
|
return $this->log_file_unique_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// general log date
|
// MARK: general log date
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the log file date to Y-m-d
|
* set the log file date to Y-m-d
|
||||||
@@ -791,7 +932,7 @@ class Logging
|
|||||||
return $this->log_file_date;
|
return $this->log_file_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// general flag set
|
// MARK: general flag set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set one of the basic flags
|
* set one of the basic flags
|
||||||
@@ -846,7 +987,7 @@ class Logging
|
|||||||
return $this->log_flags;
|
return $this->log_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
// log folder/file
|
// MARK: log folder/file
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set new log folder, check that folder is writeable
|
* set new log folder, check that folder is writeable
|
||||||
@@ -890,7 +1031,7 @@ class Logging
|
|||||||
return $this->log_file_name;
|
return $this->log_file_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// max log file size
|
// MARK: max log file size
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set mag log file size
|
* set mag log file size
|
||||||
@@ -921,7 +1062,31 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
// OPTIONS CALLS
|
// 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
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -939,6 +1104,8 @@ class Logging
|
|||||||
// MAIN CALLS
|
// MAIN CALLS
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|
||||||
|
// MARK: main log call
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commong log interface
|
* Commong log interface
|
||||||
*
|
*
|
||||||
@@ -976,7 +1143,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEBUG: 100
|
* MARK: DEBUG: 100
|
||||||
*
|
*
|
||||||
* write debug data to error_msg array
|
* write debug data to error_msg array
|
||||||
*
|
*
|
||||||
@@ -1008,7 +1175,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INFO: 200
|
* MARK: INFO: 200
|
||||||
*
|
*
|
||||||
* @param string|Stringable $message
|
* @param string|Stringable $message
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
@@ -1027,7 +1194,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTICE: 250
|
* MARK: NOTICE: 250
|
||||||
*
|
*
|
||||||
* @param string|Stringable $message
|
* @param string|Stringable $message
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
@@ -1046,7 +1213,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WARNING: 300
|
* MARK: WARNING: 300
|
||||||
*
|
*
|
||||||
* @param string|Stringable $message
|
* @param string|Stringable $message
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
@@ -1065,7 +1232,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ERROR: 400
|
* MARK: ERROR: 400
|
||||||
*
|
*
|
||||||
* @param string|Stringable $message
|
* @param string|Stringable $message
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
@@ -1084,7 +1251,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CTRITICAL: 500
|
* MARK: CTRITICAL: 500
|
||||||
*
|
*
|
||||||
* @param string|Stringable $message
|
* @param string|Stringable $message
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
@@ -1103,7 +1270,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ALERT: 550
|
* MARK: ALERT: 550
|
||||||
*
|
*
|
||||||
* @param string|Stringable $message
|
* @param string|Stringable $message
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
@@ -1122,7 +1289,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EMERGENCY: 600
|
* MARK: EMERGENCY: 600
|
||||||
*
|
*
|
||||||
* @param string|Stringable $message
|
* @param string|Stringable $message
|
||||||
* @param mixed[] $context
|
* @param mixed[] $context
|
||||||
@@ -1141,7 +1308,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
// DEPRECATED SUPPORT CALLS
|
// MARK: DEPRECATED SUPPORT CALLS
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|
||||||
// legacy, but there are too many implemented
|
// legacy, but there are too many implemented
|
||||||
@@ -1199,7 +1366,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
// DEPRECATED METHODS
|
// MARK: DEPRECATED METHODS
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1365,7 +1532,7 @@ class Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
// DEBUG METHODS
|
// MARK: DEBUG METHODS
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1385,19 +1552,21 @@ class Logging
|
|||||||
Level::Error, Level::Critical, Level::Alert, Level::Emergency
|
Level::Error, Level::Critical, Level::Alert, Level::Emergency
|
||||||
] as $l
|
] as $l
|
||||||
) {
|
) {
|
||||||
|
print "Check: " . $this->log_level->getName() . " | " . $l->getName() . "<br>";
|
||||||
if ($this->log_level->isHigherThan($l)) {
|
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)) {
|
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)) {
|
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>";
|
echo "<br>";
|
||||||
}
|
}
|
||||||
// back to options level
|
// back to options level
|
||||||
$this->initLogLevel();
|
$this->initLogLevel();
|
||||||
|
$this->initErrorLogWriteLevel();
|
||||||
print "OPT set level: " . $this->getLoggingLevel()->getName() . "<br>";
|
print "OPT set level: " . $this->getLoggingLevel()->getName() . "<br>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -365,9 +365,6 @@ class Image
|
|||||||
imagepng($thumb, $thumbnail_write_path . $thumbnail);
|
imagepng($thumb, $thumbnail_write_path . $thumbnail);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// free up resources (in case we are called in a loop)
|
|
||||||
imagedestroy($source);
|
|
||||||
imagedestroy($thumb);
|
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException(
|
throw new \RuntimeException(
|
||||||
'Invalid source image file. Only JPEG/PNG are allowed: ' . $filename,
|
'Invalid source image file. Only JPEG/PNG are allowed: ' . $filename,
|
||||||
@@ -543,8 +540,6 @@ class Image
|
|||||||
imagepng($img, $filename);
|
imagepng($img, $filename);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// clean up image if we have an image
|
|
||||||
imagedestroy($img);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -599,7 +599,7 @@ class Curl implements Interface\RequestsInterface
|
|||||||
// for post we set POST option
|
// for post we set POST option
|
||||||
if ($type == "post") {
|
if ($type == "post") {
|
||||||
curl_setopt($handle, CURLOPT_POST, true);
|
curl_setopt($handle, CURLOPT_POST, true);
|
||||||
} elseif (!empty($type) && in_array($type, self::CUSTOM_REQUESTS)) {
|
} elseif (in_array($type, self::CUSTOM_REQUESTS)) {
|
||||||
curl_setopt($handle, CURLOPT_CUSTOMREQUEST, strtoupper($type));
|
curl_setopt($handle, CURLOPT_CUSTOMREQUEST, strtoupper($type));
|
||||||
}
|
}
|
||||||
// set body data if not null, will send empty [] for empty data
|
// set body data if not null, will send empty [] for empty data
|
||||||
@@ -614,8 +614,6 @@ class Curl implements Interface\RequestsInterface
|
|||||||
// print "CURLINFO_HEADER_OUT: <pre>" . curl_getinfo($handle, CURLINFO_HEADER_OUT) . "</pre>";
|
// print "CURLINFO_HEADER_OUT: <pre>" . curl_getinfo($handle, CURLINFO_HEADER_OUT) . "</pre>";
|
||||||
// get response code and bail on not authorized
|
// get response code and bail on not authorized
|
||||||
$http_response = $this->handleCurlResponse($handle, $http_result, $options['http_errors']);
|
$http_response = $this->handleCurlResponse($handle, $http_result, $options['http_errors']);
|
||||||
// close handler
|
|
||||||
$this->handleCurlClose($handle);
|
|
||||||
// return response and result
|
// return response and result
|
||||||
return [
|
return [
|
||||||
'code' => (string)$http_response,
|
'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
|
// MARK: PUBLIC METHODS
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
|
|||||||
Reference in New Issue
Block a user