Compare commits
102 Commits
v9.26.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 | ||
|
|
8134da349f | ||
|
|
8396f7856b | ||
|
|
b18866077e | ||
|
|
a66cc09095 | ||
|
|
1cfdc45107 | ||
|
|
07e46c91ab | ||
|
|
8aee448c59 | ||
|
|
37367db878 | ||
|
|
2d30d1d160 | ||
|
|
531229e8b7 | ||
|
|
d09c20ff9d | ||
|
|
f4ddc5a5fc | ||
|
|
1791ec3908 | ||
|
|
3d13f55c35 | ||
|
|
cf1989819a | ||
|
|
b302fb4053 | ||
|
|
32decdd037 | ||
|
|
46cda40d37 | ||
|
|
e71df90144 | ||
|
|
bbcc642fde | ||
|
|
558694aa6c | ||
|
|
f3bd09529a | ||
|
|
816bb7c9ee | ||
|
|
fc7b705355 | ||
|
|
7b96c1f9ca | ||
|
|
26c6ebcea7 | ||
|
|
32dee1692e | ||
|
|
6291ed88c0 | ||
|
|
5e21ead6fa | ||
|
|
07fbd13213 | ||
|
|
44b825310a | ||
|
|
2c234ccef6 | ||
|
|
b493b3c4fd | ||
|
|
e7dd96b5d9 | ||
|
|
bcde36ac17 | ||
|
|
8bde34ec7d | ||
|
|
a345d71306 | ||
|
|
0ff6294faa | ||
|
|
757d7ae01d | ||
|
|
4e78b21c67 | ||
|
|
d7e6434808 | ||
|
|
443cc2751d | ||
|
|
cf6500b55a | ||
|
|
09c2ec653f | ||
|
|
fc105f9295 | ||
|
|
545279b9fe |
43
.eslintrc.js
43
.eslintrc.js
@@ -1,43 +0,0 @@
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'commonjs': true,
|
||||
'jquery': true
|
||||
},
|
||||
'extends': 'eslint:recommended',
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 6
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
'tab',
|
||||
{
|
||||
'SwitchCase': 1
|
||||
}
|
||||
],
|
||||
'linebreak-style': [
|
||||
'error',
|
||||
'unix'
|
||||
],
|
||||
'quotes': [
|
||||
'error',
|
||||
'single'
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'no-console': 'off',
|
||||
'no-unused-vars': [
|
||||
'error', {
|
||||
'vars': 'all',
|
||||
'args': 'after-used',
|
||||
'ignoreRestSiblings': false
|
||||
}
|
||||
],
|
||||
// Requires eslint >= v8.14.0
|
||||
'no-constant-binary-expression': 'error'
|
||||
}
|
||||
};
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,3 +5,6 @@ vendor/
|
||||
tools/
|
||||
www/composer.lock
|
||||
www/vendor
|
||||
**/.env
|
||||
**/.target
|
||||
package-lock.json
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
use Phan\Config;
|
||||
|
||||
return [
|
||||
// "target_php_version" => "8.2",
|
||||
"minimum_target_php_version" => "8.2",
|
||||
// "target_php_version" => "8.3",
|
||||
"minimum_target_php_version" => "8.3",
|
||||
// turn color on (-C)
|
||||
"color_issue_messages_if_supported" => true,
|
||||
// If true, missing properties will be created when
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phive xmlns="https://phar.io/phive">
|
||||
<phar name="phpunit" version="^10.3.5" installed="10.3.5" location="./tools/phpunit" copy="false"/>
|
||||
<phar name="phpcbf" version="^3.7.2" installed="3.10.3" location="./tools/phpcbf" copy="false"/>
|
||||
<phar name="phpcs" version="^3.10.3" installed="3.10.3" location="./tools/phpcs" copy="false"/>
|
||||
<phar name="phpstan" version="^2.0" installed="2.0.4" location="./tools/phpstan" copy="false"/>
|
||||
<phar name="phan" version="^5.4.3" installed="5.4.3" location="./tools/phan" copy="false"/>
|
||||
<phar name="psalm" version="^5.15.0" installed="5.24.0" location="./tools/psalm" copy="false"/>
|
||||
<phar name="phpunit" version="~9.6" installed="9.6.31" location="./tools/phpunit" copy="false"/>
|
||||
<phar name="phpcbf" version="4" installed="4.0.1" location="./tools/phpcbf" copy="false"/>
|
||||
<phar name="phpcs" version="4" installed="4.0.1" location="./tools/phpcs" copy="false"/>
|
||||
<phar name="phpstan" version="^2.0" installed="2.1.33" location="./tools/phpstan" copy="false"/>
|
||||
<phar name="phan" version="^5.4.3" installed="5.5.2" location="./tools/phan" copy="false"/>
|
||||
<phar name="psalm" version="^5.26.1" installed="5.26.1" location="./tools/psalm" copy="false"/>
|
||||
<phar name="phpdox" version="^0.12.0" installed="0.12.0" location="./tools/phpdox" copy="false"/>
|
||||
<phar name="phpdocumentor" version="^3.4.2" installed="3.4.3" location="./tools/phpDocumentor" copy="false"/>
|
||||
<phar name="php-cs-fixer" version="^3.34.1" installed="3.57.2" location="./tools/php-cs-fixer" copy="false"/>
|
||||
<phar name="phpdocumentor" version="^3.4.2" installed="3.9.1" location="./tools/phpDocumentor" copy="false"/>
|
||||
<phar name="php-cs-fixer" version="^3.34.1" installed="3.92.4" location="./tools/php-cs-fixer" copy="false"/>
|
||||
</phive>
|
||||
|
||||
@@ -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}
|
||||
cd $base || exit;
|
||||
${base}tools/phan --progress-bar -C --analyze-twice;
|
||||
cd "$BASE_PATH" || exit;
|
||||
export PHAN_DISABLE_XDEBUG_WARN=1;
|
||||
PHAN_CALL=(
|
||||
"${php_bin}"
|
||||
);
|
||||
if [ "${use_composer}" -eq 1 ]; then
|
||||
PHAN_CALL+=("${BASE_PATH}vendor/bin/phan");
|
||||
else
|
||||
PHAN_CALL+=("${BASE_PATH}tools/phan");
|
||||
fi;
|
||||
PHAN_CALL+=(
|
||||
"--progress-bar"
|
||||
"-C"
|
||||
"--analyze-twice"
|
||||
)
|
||||
"${PHAN_CALL[@]}";
|
||||
if [ "${no_php_version}" -eq 0 ]; then
|
||||
echo "*** CALLED WITH PHP ${php_bin} ***";
|
||||
${php_bin} --version;
|
||||
else
|
||||
echo "Default PHP used: $(php --version)";
|
||||
fi;
|
||||
cd ~ || exit;
|
||||
|
||||
# __END__
|
||||
|
||||
@@ -1,5 +1,92 @@
|
||||
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
||||
# must be run in ${base}
|
||||
cd $base || exit;
|
||||
${base}tools/phpstan;
|
||||
#!/bin/env bash
|
||||
|
||||
function error() {
|
||||
if [ -t 1 ]; then echo "[MAK] ERROR: $*" >&2; fi; exit 0;
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-h | --help] [-p | --php VERSION] [-c | --composer]
|
||||
|
||||
Runs phan static analyzer.
|
||||
|
||||
If -p is not set, the default intalled PHP is used.
|
||||
|
||||
Available options:
|
||||
|
||||
-h, --help Print this help and exit
|
||||
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
|
||||
-c, --composer Use composer version and not the default phives bundle
|
||||
EOF
|
||||
exit
|
||||
}
|
||||
|
||||
BASE_PATH=$(pwd)"/";
|
||||
PHP_BIN_PATH=$(which php);
|
||||
if [ -z "${PHP_BIN_PATH}" ]; then
|
||||
echo "Cannot find php binary";
|
||||
exit;
|
||||
fi;
|
||||
DEFAULT_PHP_VERSION=$(${PHP_BIN_PATH} -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;");
|
||||
if [ -z "${DEFAULT_PHP_VERSION}" ]; then
|
||||
echo "Cannot set default PHP version";
|
||||
exit;
|
||||
fi;
|
||||
php_version="";
|
||||
no_php_version=0;
|
||||
use_composer=0;
|
||||
while [ -n "${1-}" ]; do
|
||||
case "${1}" in
|
||||
-p | --php)
|
||||
php_version="${2-}";
|
||||
shift
|
||||
;;
|
||||
-c | --composer)
|
||||
use_composer=1;
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
;;
|
||||
# invalid option
|
||||
-?*)
|
||||
error "[!] Unknown option: '$1'."
|
||||
;;
|
||||
esac
|
||||
shift;
|
||||
done;
|
||||
if [ -z "${php_version}" ]; then
|
||||
php_version="${DEFAULT_PHP_VERSION}";
|
||||
no_php_version=1;
|
||||
fi;
|
||||
php_bin="${PHP_BIN_PATH}${php_version}";
|
||||
echo "Use PHP Version: ${php_version}";
|
||||
if [ "${use_composer}" -eq 1 ]; then
|
||||
echo "Use composer installed";
|
||||
else
|
||||
echo "Use phive installed";
|
||||
fi;
|
||||
|
||||
if [ ! -f "${php_bin}" ]; then
|
||||
echo "Set php ${php_bin} does not exist";
|
||||
exit;
|
||||
fi;
|
||||
|
||||
BASE_PATH=$(pwd)"/";
|
||||
cd "$BASE_PATH" || exit;
|
||||
PHPSTAN_CALL=(
|
||||
"${php_bin}"
|
||||
);
|
||||
if [ "${use_composer}" -eq 1 ]; then
|
||||
PHPSTAN_CALL+=("${BASE_PATH}vendor/bin/phpstan");
|
||||
else
|
||||
PHPSTAN_CALL+=("${BASE_PATH}tools/phpstan");
|
||||
fi;
|
||||
"${PHPSTAN_CALL[@]}";
|
||||
if [ "${no_php_version}" -eq 0 ]; then
|
||||
echo "*** CALLED WITH PHP ${php_bin} ***";
|
||||
${php_bin} --version;
|
||||
else
|
||||
echo "Default PHP used: $(php --version)";
|
||||
fi;
|
||||
cd ~ || exit;
|
||||
|
||||
@@ -6,7 +6,7 @@ function error() {
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-t] [-v] [-p VERSION]
|
||||
Usage: $(basename "${BASH_SOURCE[0]}") [-h | --help] [-p | --php VERSION] [-c | --composer] [-t | --testdox] [-v | --verbose]
|
||||
|
||||
Runs all the PHP unit tests.
|
||||
|
||||
@@ -15,15 +15,16 @@ If -p is not set, the default intalled PHP is used.
|
||||
Available options:
|
||||
|
||||
-h, --help Print this help and exit
|
||||
-t, --testdox Enable testdox output for phpunit
|
||||
-t, --testdox Enable testdox output for PHPunit
|
||||
-v, --verbose Enable verbose output for PHPunit
|
||||
-c, --composer Use composer version and not the default phives bundle
|
||||
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
|
||||
EOF
|
||||
exit
|
||||
}
|
||||
|
||||
# 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";
|
||||
PHP_BIN_PATH=$(which php);
|
||||
if [ -z "${PHP_BIN_PATH}" ]; then
|
||||
@@ -45,6 +46,7 @@ opt_testdox="";
|
||||
opt_verbose="";
|
||||
php_version="";
|
||||
no_php_version=0;
|
||||
use_composer=0;
|
||||
while [ -n "${1-}" ]; do
|
||||
case "${1}" in
|
||||
-t | --testdox)
|
||||
@@ -53,6 +55,10 @@ while [ -n "${1-}" ]; do
|
||||
-v | --verbose)
|
||||
opt_verbose="--verbose";
|
||||
;;
|
||||
-c | --composer)
|
||||
use_composer=1;
|
||||
shift
|
||||
;;
|
||||
-p | --php)
|
||||
php_version="${2-}";
|
||||
shift
|
||||
@@ -74,21 +80,38 @@ if [ -z "${php_version}" ]; then
|
||||
fi;
|
||||
php_bin="${PHP_BIN_PATH}${php_version}";
|
||||
echo "Use PHP Version: ${php_version}";
|
||||
if [ "${use_composer}" -eq 1 ]; then
|
||||
echo "Use composer installed";
|
||||
else
|
||||
echo "Use phive installed";
|
||||
fi;
|
||||
|
||||
if [ ! -f "${php_bin}" ]; then
|
||||
echo "Set php ${php_bin} does not exist";
|
||||
exit;
|
||||
fi;
|
||||
php_bin="${php_bin} ";
|
||||
|
||||
# Note 4dev/tests/bootstrap.php has to be set as bootstrap file in phpunit.xml
|
||||
phpunit_call="${php_bin}${BASE_PATH}vendor/bin/phpunit ${opt_testdox} ${opt_verbose} -c ${PHPUNIT_CONFIG} ${BASE_PATH}4dev/tests/";
|
||||
|
||||
${phpunit_call};
|
||||
PHPUNIT_CALL=(
|
||||
"${php_bin}"
|
||||
);
|
||||
if [ "${use_composer}" -eq 1 ]; then
|
||||
PHPUNIT_CALL+=("${BASE_PATH}vendor/bin/phpunit");
|
||||
else
|
||||
PHPUNIT_CALL+=("${BASE_PATH}tools/phpunit");
|
||||
fi;
|
||||
PHPUNIT_CALL+=(
|
||||
"${opt_testdox}"
|
||||
"${opt_verbose}"
|
||||
"-c" "${PHPUNIT_CONFIG}"
|
||||
"${BASE_PATH}4dev/tests/"
|
||||
);
|
||||
"${PHPUNIT_CALL[@]}" || exit;
|
||||
|
||||
echo -e "\nPHPUnit Config: ${PHPUNIT_CONFIG}";
|
||||
if [ "${no_php_version}" -eq 0 ]; then
|
||||
echo "CALLED WITH PHP: ${php_bin}$(${php_bin} --version)";
|
||||
echo "*** CALLED WITH PHP ${php_bin} ***";
|
||||
${php_bin} --version;
|
||||
else
|
||||
echo "Default PHP used: $(php --version)";
|
||||
fi;
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
CREATE TABLE generic (
|
||||
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
|
||||
date_updated TIMESTAMP WITHOUT TIME ZONE,
|
||||
uuid UUID DEFAULT gen_random_uuid(),
|
||||
uid VARCHAR
|
||||
);
|
||||
|
||||
@@ -48,7 +48,7 @@ header("Content-Type: application/json; charset=UTF-8");
|
||||
if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) {
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// if server request type is get set file_get to null -> no body
|
||||
@@ -57,7 +57,7 @@ if ($_SERVER['REQUEST_METHOD'] == "GET") {
|
||||
} elseif (($file_get = file_get_contents('php://input')) === false) {
|
||||
header("HTTP/1.1 404 Not Found");
|
||||
print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
print buildContent($http_headers, $file_get);
|
||||
|
||||
@@ -12,6 +12,8 @@ Not yet covered tests:
|
||||
- loginGetLocale
|
||||
- loginGetHeaderColor
|
||||
- loginGetPages
|
||||
- loginGetPageLookupList
|
||||
- loginPageAccessAllowed
|
||||
- loginGetEuid
|
||||
*/
|
||||
|
||||
@@ -152,7 +154,6 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
// TARGET
|
||||
define('TARGET', 'test');
|
||||
// LOGIN DB SCHEMA
|
||||
// define('LOGIN_DB_SCHEMA', '');
|
||||
|
||||
// SHOULD SET
|
||||
// DEFAULT_ACL_LEVEL (d80)
|
||||
@@ -1531,6 +1532,12 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_uid']),
|
||||
'Assert check access uid to cuid valid'
|
||||
);
|
||||
// - loginGetEditAccessCuidFromId
|
||||
$this->assertEquals(
|
||||
$expected['check_access_cuid'],
|
||||
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_id']),
|
||||
'Assert check access id to cuid valid'
|
||||
);
|
||||
// Deprecated
|
||||
// - loginCheckEditAccess
|
||||
$this->assertEquals(
|
||||
|
||||
@@ -24,12 +24,12 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
'get email regex invalid -1, will be 0' => [
|
||||
-1,
|
||||
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
],
|
||||
'get email regex invalid 10, will be 0' => [
|
||||
10,
|
||||
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
],
|
||||
'get email regex valid 1, will be 1' => [
|
||||
1,
|
||||
@@ -157,7 +157,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
'error' => 0,
|
||||
'message' => 'Invalid email address',
|
||||
'regex' => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
]
|
||||
],
|
||||
'error 1 will return double @ error' => [
|
||||
@@ -181,7 +181,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
[
|
||||
'error' => 3,
|
||||
'message' => 'Invalid domain part after @ sign',
|
||||
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$"
|
||||
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
|
||||
]
|
||||
],
|
||||
'error 4 will be invalid domain' => [
|
||||
@@ -189,7 +189,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
[
|
||||
'error' => 4,
|
||||
'message' => 'Invalid domain name part',
|
||||
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\."
|
||||
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\."
|
||||
]
|
||||
],
|
||||
'error 5 will be invalid domain top level only' => [
|
||||
@@ -197,7 +197,7 @@ final class CoreLibsCheckEmailTest extends TestCase
|
||||
[
|
||||
'error' => 5,
|
||||
'message' => 'Wrong domain top level part',
|
||||
'regex' => "\.([a-zA-Z]{2,6}){1}$"
|
||||
'regex' => "\.[a-zA-Z]{2,6}$"
|
||||
]
|
||||
],
|
||||
'error 6 will be domain double dot' => [
|
||||
|
||||
@@ -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' => [
|
||||
'18999d 0h 38m 10s 1235ms',
|
||||
1641515890.1235,
|
||||
1641515891.235,
|
||||
],
|
||||
'micro interval with microtime' => [
|
||||
'18999d 0h 38m 10s 1234567890ms',
|
||||
1641515890.1234567,
|
||||
1642750457.89,
|
||||
],
|
||||
'negative interval no microtime' => [
|
||||
'-18999d 0h 38m 10s',
|
||||
@@ -503,23 +503,246 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
// short for mini tests
|
||||
'microtime only' => [
|
||||
'0s 1235ms',
|
||||
0.1235,
|
||||
1.235,
|
||||
],
|
||||
'seconds only' => [
|
||||
'30s 1235ms',
|
||||
30.1235,
|
||||
31.235,
|
||||
],
|
||||
'minutes only' => [
|
||||
'1m 30s 1235ms',
|
||||
90.1235,
|
||||
91.235,
|
||||
],
|
||||
'hours only' => [
|
||||
'1h 1m 30s 1235ms',
|
||||
3690.1235,
|
||||
3691.235,
|
||||
],
|
||||
'days only' => [
|
||||
'1d 1h 1m 30s 1235ms',
|
||||
90090.1235,
|
||||
90091.235,
|
||||
],
|
||||
'days only with long name' => [
|
||||
'1day 1hour 1min 30second 1235millisecond',
|
||||
90091.235,
|
||||
],
|
||||
// Test day variations
|
||||
'day singular' => [
|
||||
'5day',
|
||||
432000,
|
||||
],
|
||||
'days plural' => [
|
||||
'3days',
|
||||
259200,
|
||||
],
|
||||
'days with space' => [
|
||||
'2days 5h',
|
||||
190800,
|
||||
],
|
||||
'day without space' => [
|
||||
'1day1h',
|
||||
90000,
|
||||
],
|
||||
// Test hour variations
|
||||
'hour singular' => [
|
||||
'2hour',
|
||||
7200,
|
||||
],
|
||||
'hours plural' => [
|
||||
'4hours',
|
||||
14400,
|
||||
],
|
||||
'hours with space' => [
|
||||
'3hours 30m',
|
||||
12600,
|
||||
],
|
||||
'hour without space' => [
|
||||
'1hour30m',
|
||||
5400,
|
||||
],
|
||||
// Test minute variations
|
||||
'min short' => [
|
||||
'45min',
|
||||
2700,
|
||||
],
|
||||
'minute singular' => [
|
||||
'1minute',
|
||||
60,
|
||||
],
|
||||
'minutes plural' => [
|
||||
'10minutes',
|
||||
600,
|
||||
],
|
||||
'minutes with space' => [
|
||||
'5minutes 20s',
|
||||
320,
|
||||
],
|
||||
'min without space' => [
|
||||
'2min30s',
|
||||
150,
|
||||
],
|
||||
// Test second variations
|
||||
'sec short' => [
|
||||
'30sec',
|
||||
30,
|
||||
],
|
||||
'second singular' => [
|
||||
'1second',
|
||||
1,
|
||||
],
|
||||
'seconds plural' => [
|
||||
'45seconds',
|
||||
45,
|
||||
],
|
||||
'seconds with space' => [
|
||||
'15seconds 500ms',
|
||||
15.5,
|
||||
],
|
||||
'sec without space' => [
|
||||
'10sec250ms',
|
||||
10.25,
|
||||
],
|
||||
// Test millisecond variations
|
||||
'ms short' => [
|
||||
'500ms',
|
||||
0.5,
|
||||
],
|
||||
'millis short' => [
|
||||
'250millis',
|
||||
0.25,
|
||||
],
|
||||
'millisec medium singular' => [
|
||||
'250millisec',
|
||||
0.25,
|
||||
],
|
||||
'millisecs medium plural' => [
|
||||
'250millisecs',
|
||||
0.25,
|
||||
],
|
||||
'misec medium singular' => [
|
||||
'250millisec',
|
||||
0.25,
|
||||
],
|
||||
'msecs medium plural' => [
|
||||
'250millisecs',
|
||||
0.25,
|
||||
],
|
||||
'millisecond long singular' => [
|
||||
'1millisecond',
|
||||
0.001,
|
||||
],
|
||||
'milliseconds long plural' => [
|
||||
'999milliseconds',
|
||||
0.999,
|
||||
],
|
||||
// Test negative values
|
||||
'negative days' => [
|
||||
'-5d',
|
||||
-432000,
|
||||
],
|
||||
'negative hours' => [
|
||||
'-3h',
|
||||
-10800,
|
||||
],
|
||||
'negative minutes' => [
|
||||
'-45m',
|
||||
-2700,
|
||||
],
|
||||
'negative seconds' => [
|
||||
'-30s',
|
||||
-30,
|
||||
],
|
||||
'negative milliseconds' => [
|
||||
'-500ms',
|
||||
-0.5,
|
||||
],
|
||||
'negative complex' => [
|
||||
'-2days 3hours 15minutes 30seconds 250milliseconds',
|
||||
-184530.25,
|
||||
],
|
||||
// Test combined formats
|
||||
'all components short' => [
|
||||
'1d 2h 3m 4s 5ms',
|
||||
93784.005,
|
||||
],
|
||||
'all components long' => [
|
||||
'2days 3hours 4minutes 5seconds 678milliseconds',
|
||||
183845.678,
|
||||
],
|
||||
'mixed short and long' => [
|
||||
'1day 2h 3minutes 4sec 100ms',
|
||||
93784.1,
|
||||
],
|
||||
'no spaces between components' => [
|
||||
'1d2h3m4s5ms',
|
||||
93784.005,
|
||||
],
|
||||
'only days and milliseconds' => [
|
||||
'5d 123ms',
|
||||
432000.123,
|
||||
],
|
||||
'only hours and seconds' => [
|
||||
'2h 45s',
|
||||
7245,
|
||||
],
|
||||
'only minutes and milliseconds' => [
|
||||
'30m 500ms',
|
||||
1800.5,
|
||||
],
|
||||
// Test zero values
|
||||
'zero seconds' => [
|
||||
'0s',
|
||||
0,
|
||||
],
|
||||
'zero with milliseconds' => [
|
||||
'0s 123ms',
|
||||
0.123,
|
||||
],
|
||||
// Test large values
|
||||
'large days' => [
|
||||
'365days',
|
||||
31536000,
|
||||
],
|
||||
'large hours' => [
|
||||
'48hours',
|
||||
172800,
|
||||
],
|
||||
'large minutes' => [
|
||||
'1440minutes',
|
||||
86400,
|
||||
],
|
||||
'large seconds' => [
|
||||
'86400seconds',
|
||||
86400,
|
||||
],
|
||||
// Test edge cases with spaces
|
||||
'extra spaces' => [
|
||||
'1d 2h 3m 4s 5ms',
|
||||
93784.005,
|
||||
],
|
||||
'mixed spaces and no spaces' => [
|
||||
'1d 2h3m 4s5ms',
|
||||
93784.005,
|
||||
],
|
||||
// Test single component each
|
||||
'only days short' => [
|
||||
'7d',
|
||||
604800,
|
||||
],
|
||||
'only hours short' => [
|
||||
'12h',
|
||||
43200,
|
||||
],
|
||||
'only minutes short' => [
|
||||
'90m',
|
||||
5400,
|
||||
],
|
||||
'only seconds short' => [
|
||||
'120s',
|
||||
120,
|
||||
],
|
||||
'only milliseconds short' => [
|
||||
'1500ms',
|
||||
1.5,
|
||||
],
|
||||
'already set' => [
|
||||
1641515890,
|
||||
@@ -529,10 +752,18 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
'xyz',
|
||||
'xyz',
|
||||
],
|
||||
'empty data' => [
|
||||
' ',
|
||||
' ',
|
||||
],
|
||||
'out of bound data' => [
|
||||
'99999999999999999999d',
|
||||
8.64E+24
|
||||
],
|
||||
'spaces inbetween' => [
|
||||
' - 9 d 2h 58minutes 35 seconds 123 ms ',
|
||||
-788315.123,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -555,6 +786,36 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::stringToTime
|
||||
* @testdox stringToTime invalid input will throw exception if requested
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testStringToTimeException(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches("/^Invalid time string format, cannot parse: /");
|
||||
\CoreLibs\Combined\DateTime::stringToTime('1x 2y 3z', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::stringToTime
|
||||
* @testdox stringToTime empty input will throw exception if requested
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testStringToTimeExceptionEmpty(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches("/^Invalid time string format, no interval value found: /");
|
||||
\CoreLibs\Combined\DateTime::stringToTime(' ', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -926,48 +1187,114 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
public function daysIntervalProvider(): array
|
||||
{
|
||||
return [
|
||||
'valid interval /, not named array' => [
|
||||
'2020/1/1',
|
||||
'2020/1/30',
|
||||
false,
|
||||
[29, 22, 8],
|
||||
// normal and format tests
|
||||
'valid interval / not named array' => [
|
||||
'input_a' => '2020/1/1',
|
||||
'input_b' => '2020/1/30',
|
||||
'return_named' => false, // return_named
|
||||
'include_end_date' => true, // include_end_date
|
||||
'exclude_start_date' => false, // exclude_start_date
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
'valid interval /, named array' => [
|
||||
'2020/1/1',
|
||||
'2020/1/30',
|
||||
true,
|
||||
['overall' => 29, 'weekday' => 22, 'weekend' => 8],
|
||||
'valid interval / named array' => [
|
||||
'input_a' => '2020/1/1',
|
||||
'input_b' => '2020/1/30',
|
||||
'return_named' => true,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => ['overall' => 30, 'weekday' => 22, 'weekend' => 8, 'reverse' => false],
|
||||
],
|
||||
'valid interval -' => [
|
||||
'2020-1-1',
|
||||
'2020-1-30',
|
||||
false,
|
||||
[29, 22, 8],
|
||||
],
|
||||
'valid interval switched' => [
|
||||
'2020/1/30',
|
||||
'2020/1/1',
|
||||
false,
|
||||
[28, 0, 0],
|
||||
'valid interval with "-"' => [
|
||||
'input_a' => '2020-1-1',
|
||||
'input_b' => '2020-1-30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
'valid interval with time' => [
|
||||
'2020/1/1 12:12:12',
|
||||
'2020/1/30 13:13:13',
|
||||
false,
|
||||
[28, 21, 8],
|
||||
'input_a' => '2020/1/1 12:12:12',
|
||||
'input_b' => '2020/1/30 13:13:13',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
// invalid
|
||||
'invalid dates' => [
|
||||
'abc',
|
||||
'xyz',
|
||||
false,
|
||||
[0, 0, 0]
|
||||
'input_a' => 'abc',
|
||||
'input_b' => 'xyz',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [0, 0, 0, false]
|
||||
],
|
||||
// this test will take a long imte
|
||||
// this test will take a long time
|
||||
'out of bound dates' => [
|
||||
'1900-1-1',
|
||||
'9999-12-31',
|
||||
false,
|
||||
[2958463,2113189,845274],
|
||||
'input_a' => '1900-1-1',
|
||||
'input_b' => '9999-12-31',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [2958463, 2113189, 845274, false],
|
||||
],
|
||||
// tests for include/exclude
|
||||
'exclude end date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [29, 21, 8, false],
|
||||
],
|
||||
'exclude start date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [29, 21, 8, false],
|
||||
],
|
||||
'exclude start and end date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [28, 20, 8, false],
|
||||
],
|
||||
// reverse
|
||||
'reverse: valid interval' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, true],
|
||||
],
|
||||
'reverse: exclude end date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [29, 21, 8, true],
|
||||
],
|
||||
'reverse: exclude start date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [29, 21, 8, true],
|
||||
],
|
||||
'reverse: exclude start and end date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [28, 20, 8, true],
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -982,20 +1309,52 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
*
|
||||
* @param string $input_a
|
||||
* @param string $input_b
|
||||
* @param bool $flag
|
||||
* @param array $expected
|
||||
* @param bool $return_named
|
||||
* @param array $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testCalcDaysInterval(
|
||||
string $input_a,
|
||||
string $input_b,
|
||||
bool $flag,
|
||||
bool $return_named,
|
||||
bool $include_end_date,
|
||||
bool $exclude_start_date,
|
||||
$expected
|
||||
): void {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysInterval($input_a, $input_b, $flag)
|
||||
\CoreLibs\Combined\DateTime::calcDaysInterval(
|
||||
$input_a,
|
||||
$input_b,
|
||||
return_named:$return_named,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysInterval'
|
||||
);
|
||||
if ($return_named) {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysIntervalNamedIndex(
|
||||
$input_a,
|
||||
$input_b,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysIntervalNamedIndex'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysIntervalNumIndex(
|
||||
$input_a,
|
||||
$input_b,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysIntervalNamedIndex'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1187,7 +1546,38 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
'2023-07-03',
|
||||
'2023-07-27',
|
||||
true
|
||||
]
|
||||
],
|
||||
// reverse
|
||||
'reverse: no weekend' => [
|
||||
'2023-07-04',
|
||||
'2023-07-03',
|
||||
false
|
||||
],
|
||||
'reverse: start weekend sat' => [
|
||||
'2023-07-04',
|
||||
'2023-07-01',
|
||||
true
|
||||
],
|
||||
'reverse: start weekend sun' => [
|
||||
'2023-07-04',
|
||||
'2023-07-02',
|
||||
true
|
||||
],
|
||||
'reverse: end weekend sat' => [
|
||||
'2023-07-08',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
'reverse: end weekend sun' => [
|
||||
'2023-07-09',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
'reverse: long period > 6 days' => [
|
||||
'2023-07-27',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
4 => '1.00 KB',
|
||||
5 => '1.02KiB',
|
||||
],
|
||||
'invalud string number' => [
|
||||
'invalid string number' => [
|
||||
0 => '1024 MB',
|
||||
1 => '1024 MB',
|
||||
2 => '1024 MB',
|
||||
@@ -123,47 +123,6 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function byteStringProvider(): array
|
||||
{
|
||||
return [
|
||||
'negative number' => [
|
||||
0 => '-117.42 MB',
|
||||
1 => -123123794,
|
||||
2 => -117420000,
|
||||
],
|
||||
'megabyte' => [
|
||||
0 => '242.98 MB',
|
||||
1 => 254782996,
|
||||
2 => 242980000
|
||||
],
|
||||
'megabyte si' => [
|
||||
0 => '254.78 MiB',
|
||||
1 => 267156193,
|
||||
2 => 254780000
|
||||
],
|
||||
'petabyte' => [
|
||||
0 => '1 EiB',
|
||||
1 => 1152921504606846976,
|
||||
2 => 1000000000000000000,
|
||||
],
|
||||
'max int' => [
|
||||
0 => '8 EB',
|
||||
1 => -9223372036854775807 - 1,
|
||||
2 => 8000000000000000000,
|
||||
],
|
||||
'exabyte, overflow' => [
|
||||
0 => '867.36EB',
|
||||
1 => 3873816255479021568,
|
||||
2 => 363028535651074048,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -180,7 +139,7 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
* @return void
|
||||
*/
|
||||
public function testHumanReadableByteFormat(
|
||||
$input,
|
||||
string|int|float $input,
|
||||
string $expected,
|
||||
string $expected_si,
|
||||
string $expected_no_space,
|
||||
@@ -217,6 +176,73 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function byteStringProvider(): array
|
||||
{
|
||||
return [
|
||||
'negative number' => [
|
||||
0 => '-117.42 MB',
|
||||
1 => -123123794,
|
||||
2 => -117420000,
|
||||
3 => "-123123793",
|
||||
4 => "-117420000",
|
||||
5 => null,
|
||||
],
|
||||
'megabyte' => [
|
||||
0 => '242.98 MB',
|
||||
1 => 254782996,
|
||||
2 => 242980000,
|
||||
3 => "254782996",
|
||||
4 => "242980000",
|
||||
5 => null,
|
||||
],
|
||||
'megabyte si' => [
|
||||
0 => '254.78 MiB',
|
||||
1 => 267156193,
|
||||
2 => 254780000,
|
||||
3 => "267156193",
|
||||
4 => "254780000",
|
||||
5 => null,
|
||||
],
|
||||
'petabyte' => [
|
||||
0 => '1 EiB',
|
||||
1 => 1152921504606846976,
|
||||
2 => 1000000000000000000,
|
||||
3 => "1152921504606846976",
|
||||
4 => "1000000000000000000",
|
||||
5 => null,
|
||||
],
|
||||
'max int' => [
|
||||
0 => '8 EB',
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => "9223372036854775808",
|
||||
4 => "8000000000000000000",
|
||||
5 => \LengthException::class,
|
||||
],
|
||||
'exabyte, overflow' => [
|
||||
0 => '867.36EB',
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => "999997996235794808832",
|
||||
4 => "867360000000000000000",
|
||||
5 => \LengthException::class,
|
||||
],
|
||||
'huge exabyte, overflow' => [
|
||||
0 => '1000EB',
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => "1152921504606846976000",
|
||||
4 => "1000000000000000000000",
|
||||
5 => \LengthException::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -227,10 +253,22 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
* @param string|int|float $input
|
||||
* @param string|int|float $expected
|
||||
* @param string|int|float $expected_si
|
||||
* @param string|int|float $expected_string
|
||||
* @param string|int|float $expected_string_si
|
||||
* @param ?string $exception
|
||||
* @return void
|
||||
*/
|
||||
public function testStringByteFormat($input, $expected, $expected_si): void
|
||||
{
|
||||
public function testStringByteFormat(
|
||||
string|int|float $input,
|
||||
string|int|float $expected,
|
||||
string|int|float $expected_si,
|
||||
string|int|float $expected_string,
|
||||
string|int|float $expected_string_si,
|
||||
?string $exception
|
||||
): void {
|
||||
if ($exception !== null) {
|
||||
$this->expectException($exception);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat($input)
|
||||
@@ -239,6 +277,17 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
$expected_si,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::BYTE_FORMAT_SI)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_string,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::RETURN_AS_STRING)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_string_si,
|
||||
\CoreLibs\Convert\Byte::stringByteFormat(
|
||||
$input,
|
||||
\CoreLibs\Convert\Byte::BYTE_FORMAT_SI | \CoreLibs\Convert\Byte::RETURN_AS_STRING
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -164,6 +164,51 @@ final class CoreLibsConvertJsonTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test with flags
|
||||
*
|
||||
* @covers ::jsonConvertToArray
|
||||
* @testdox jsonConvertToArray flag test, if flag is used
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testJsonConvertToArrayWithFlags(): void
|
||||
{
|
||||
$input = '{"valid":"json","invalid":"\xB1\x31"}';
|
||||
/* $expected_without_flag = [
|
||||
'valid' => 'json'
|
||||
];
|
||||
$expected_with_flag = [
|
||||
'valid' => 'json',
|
||||
'invalid' => "\xB1\x31"
|
||||
]; */
|
||||
// no idea why in both it throws an erro
|
||||
$expected_without_flag = [];
|
||||
$expected_with_flag = [];
|
||||
$this->assertEquals(
|
||||
$expected_without_flag,
|
||||
\CoreLibs\Convert\Json::jsonConvertToArray($input)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_with_flag,
|
||||
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_INVALID_UTF8_IGNORE)
|
||||
);
|
||||
}
|
||||
|
||||
public function testJsonConvertToArrayRemoveThrowFlag(): void
|
||||
{
|
||||
$input = '{"valid":"json","invalid":"\xB1\x31"}';
|
||||
// show NOT throw an exception
|
||||
try {
|
||||
$this->assertEquals(
|
||||
[],
|
||||
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_THROW_ON_ERROR)
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->fail('Exception was thrown despite flag removal');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test json error states
|
||||
*
|
||||
@@ -189,6 +234,49 @@ final class CoreLibsConvertJsonTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test json error states
|
||||
*
|
||||
* @covers ::jsonValidate
|
||||
* @dataProvider jsonErrorProvider
|
||||
* @testdox jsonValidate $input will be $expected_i/$expected_s [$_dataName]
|
||||
*
|
||||
* @param string|null $input
|
||||
* @param int $expected_i
|
||||
* @param string $expected_s
|
||||
* @return void
|
||||
*/
|
||||
public function testJsonValidateGetLastError(?string $input, int $expected_i, string $expected_s): void
|
||||
{
|
||||
\CoreLibs\Convert\Json::jsonValidate($input);
|
||||
$this->assertEquals(
|
||||
$expected_i,
|
||||
\CoreLibs\Convert\Json::jsonGetLastError()
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_s,
|
||||
\CoreLibs\Convert\Json::jsonGetLastError(true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test json validation
|
||||
*
|
||||
* @covers ::jsonValidate
|
||||
* @testdox jsonValidate test valid and invalid json
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testJsonValidate(): void
|
||||
{
|
||||
$this->assertTrue(
|
||||
\CoreLibs\Convert\Json::jsonValidate('{"valid": "json"}')
|
||||
);
|
||||
$this->assertFalse(
|
||||
\CoreLibs\Convert\Json::jsonValidate('not valid json')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
|
||||
@@ -122,9 +122,9 @@ final class CoreLibsConvertMathTest extends TestCase
|
||||
public function providerCbrt(): array
|
||||
{
|
||||
return [
|
||||
'cube root of 2' => [2, 1.25992, 5],
|
||||
'cube root of 3' => [3, 1.44225, 5],
|
||||
'cube root of -1' => [-1, 'NAN', 0],
|
||||
'cube root of 2' => [2, 1.25992, 5, null],
|
||||
'cube root of 3' => [3, 1.44225, 5, null],
|
||||
'cube root of -1' => [-1, 'NAN', 0, \InvalidArgumentException::class],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -138,10 +138,14 @@ final class CoreLibsConvertMathTest extends TestCase
|
||||
* @param float|int $number
|
||||
* @param float $expected
|
||||
* @param int $round_to
|
||||
* @param ?string $exception
|
||||
* @return void
|
||||
*/
|
||||
public function testCbrt(float|int $number, float|string $expected, int $round_to): void
|
||||
public function testCbrt(float|int $number, float|string $expected, int $round_to, ?string $exception): void
|
||||
{
|
||||
if ($exception !== null) {
|
||||
$this->expectException($exception);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)
|
||||
|
||||
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
|
||||
// 1: format
|
||||
// 2: split characters as string, null for default
|
||||
// 3: expected
|
||||
return [
|
||||
'all empty string' => [
|
||||
'',
|
||||
'',
|
||||
null,
|
||||
''
|
||||
],
|
||||
'empty input string' => [
|
||||
'',
|
||||
'2-2',
|
||||
null,
|
||||
''
|
||||
],
|
||||
'empty format string string' => [
|
||||
'1234',
|
||||
'',
|
||||
null,
|
||||
'1234'
|
||||
],
|
||||
'string format match' => [
|
||||
'1234',
|
||||
'2-2',
|
||||
null,
|
||||
'12-34'
|
||||
],
|
||||
'string format trailing match' => [
|
||||
'1234',
|
||||
'2-2-',
|
||||
null,
|
||||
'12-34'
|
||||
],
|
||||
'string format leading match' => [
|
||||
'1234',
|
||||
'-2-2',
|
||||
null,
|
||||
'12-34'
|
||||
],
|
||||
'string format double inside match' => [
|
||||
'1234',
|
||||
'2--2',
|
||||
null,
|
||||
'12--34',
|
||||
],
|
||||
'string format short first' => [
|
||||
'1',
|
||||
'2-2',
|
||||
null,
|
||||
'1'
|
||||
],
|
||||
'string format match first' => [
|
||||
'12',
|
||||
'2-2',
|
||||
null,
|
||||
'12'
|
||||
],
|
||||
'string format short second' => [
|
||||
'123',
|
||||
'2-2',
|
||||
null,
|
||||
'12-3'
|
||||
],
|
||||
'string format too long' => [
|
||||
'1234567',
|
||||
'2-2',
|
||||
null,
|
||||
'12-34-567'
|
||||
],
|
||||
'string format invalid format string' => [
|
||||
'1234',
|
||||
'2_2',
|
||||
null,
|
||||
'1234'
|
||||
],
|
||||
'different split character' => [
|
||||
'1234',
|
||||
'2_2',
|
||||
'_',
|
||||
'12_34'
|
||||
],
|
||||
'mixed split characters' => [
|
||||
'123456',
|
||||
'2-2_2',
|
||||
'-_',
|
||||
'12-34_56'
|
||||
],
|
||||
'length mixed' => [
|
||||
'ABCD12345568ABC13',
|
||||
'2-4_5-2#4',
|
||||
'-_#',
|
||||
'AB-CD12_34556-8A#BC13'
|
||||
],
|
||||
'split with split chars in string' => [
|
||||
'12-34',
|
||||
'2-2',
|
||||
null,
|
||||
'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
|
||||
* @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 $format
|
||||
* @param string|null $split_characters
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testSplitFormatString(
|
||||
string $input,
|
||||
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 $expected
|
||||
): void {
|
||||
if ($split_characters === null) {
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatStringFixed(
|
||||
$input,
|
||||
$format
|
||||
$split_length
|
||||
);
|
||||
} else {
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatString(
|
||||
$output = \CoreLibs\Convert\Strings::splitFormatStringFixed(
|
||||
$input,
|
||||
$format,
|
||||
$split_length,
|
||||
$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
|
||||
*
|
||||
@@ -378,6 +482,305 @@ final class CoreLibsConvertStringsTest extends TestCase
|
||||
\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__
|
||||
|
||||
@@ -21,8 +21,10 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
public function hashData(): array
|
||||
{
|
||||
return [
|
||||
'any string' => [
|
||||
'hash tests' => [
|
||||
// this is the string
|
||||
'text' => 'Some String Text',
|
||||
// hash list special
|
||||
'crc32b_reverse' => 'c5c21d91', // crc32b (in revere)
|
||||
'sha1Short' => '4d2bc9ba0', // sha1Short
|
||||
// via hash
|
||||
@@ -31,6 +33,8 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
'fnv132' => '9df444f9', // hash: fnv132
|
||||
'fnv1a32' => '2c5f91b9', // hash: fnv1a32
|
||||
'joaat' => '50dab846', // hash: joaat
|
||||
'ripemd160' => 'aeae3f041b20136451519edd9361570909300342', // hash: ripemd160,
|
||||
'sha256' => '9055080e022f224fa835929b80582b3c71c672206fa3a49a87412c25d9d42ceb', // hash: sha256
|
||||
]
|
||||
];
|
||||
}
|
||||
@@ -81,7 +85,7 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->hashData() as $name => $values) {
|
||||
foreach ([null, 'crc32b', 'adler32', 'fnv132', 'fnv1a32', 'joaat'] as $_hash_type) {
|
||||
foreach ([null, 'crc32b', 'adler32', 'fnv132', 'fnv1a32', 'joaat', 'ripemd160', 'sha256'] as $_hash_type) {
|
||||
// default value test
|
||||
if ($_hash_type === null) {
|
||||
$hash_type = \CoreLibs\Create\Hash::STANDARD_HASH_SHORT;
|
||||
@@ -114,6 +118,22 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function hashStandardProvider(): array
|
||||
{
|
||||
$hash_source = 'Some String Text';
|
||||
return [
|
||||
'Long Hash check: ' . \CoreLibs\Create\Hash::STANDARD_HASH => [
|
||||
$hash_source,
|
||||
hash(\CoreLibs\Create\Hash::STANDARD_HASH, $hash_source)
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -136,9 +156,13 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* phpcs:disable Generic.Files.LineLength
|
||||
* @covers ::__sha1Short
|
||||
* @covers ::__crc32b
|
||||
* @covers ::sha1Short
|
||||
* @dataProvider sha1ShortProvider
|
||||
* @testdox __sha1Short $input will be $expected (crc32b) and $expected_sha1 (sha1 short) [$_dataName]
|
||||
* @testdox __sha1Short/__crc32b/sha1short $input will be $expected (crc32b) and $expected_sha1 (sha1 short) [$_dataName]
|
||||
* phpcs:enable Generic.Files.LineLength
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
@@ -149,16 +173,29 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
// uses crc32b
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__sha1Short($input)
|
||||
\CoreLibs\Create\Hash::__sha1Short($input),
|
||||
'__sha1Short depreacted'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, false)
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, false),
|
||||
'__sha1Short (false) depreacted'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__crc32b($input),
|
||||
'__crc32b'
|
||||
);
|
||||
// sha1 type
|
||||
$this->assertEquals(
|
||||
$expected_sha1,
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, true)
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, true),
|
||||
'__sha1Short (true) depreacted'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_sha1,
|
||||
\CoreLibs\Create\Hash::sha1Short($input),
|
||||
'sha1Short'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,8 +203,10 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__hash
|
||||
* @covers ::hashShort
|
||||
* @covers ::hashShort
|
||||
* @dataProvider hashProvider
|
||||
* @testdox __hash $input with $hash_type will be $expected [$_dataName]
|
||||
* @testdox __hash/hashShort/hash $input with $hash_type will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string|null $hash_type
|
||||
@@ -179,12 +218,24 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
if ($hash_type === null) {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__hash($input)
|
||||
\CoreLibs\Create\Hash::__hash($input),
|
||||
'__hash'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashShort($input),
|
||||
'hashShort'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__hash($input, $hash_type)
|
||||
\CoreLibs\Create\Hash::__hash($input, $hash_type),
|
||||
'__hash with hash type'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hash($input, $hash_type),
|
||||
'hash with hash type'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -193,8 +244,9 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__hashLong
|
||||
* @covers ::hashLong
|
||||
* @dataProvider hashLongProvider
|
||||
* @testdox __hashLong $input will be $expected [$_dataName]
|
||||
* @testdox __hashLong/hashLong $input will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
@@ -206,6 +258,168 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__hashLong($input)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashLong($input)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::hash
|
||||
* @covers ::hashStd
|
||||
* @dataProvider hashStandardProvider
|
||||
* @testdox hash/hashStd $input will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testHashStandard(string $input, string $expected): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashStd($input)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hash($input)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::hash
|
||||
* @testdox hash with invalid type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidHashType(): void
|
||||
{
|
||||
$hash_source = 'Some String Text';
|
||||
$expected = hash(\CoreLibs\Create\Hash::STANDARD_HASH, $hash_source);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hash($hash_source, 'DOES_NOT_EXIST')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this only tests default sha256
|
||||
*
|
||||
* @covers ::hashHmac
|
||||
* @testdox hash hmac test
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testHashMac(): void
|
||||
{
|
||||
$hash_key = 'FIX KEY';
|
||||
$hash_source = 'Some String Text';
|
||||
$expected = '16479b3ef6fa44e1cdd8b2dcfaadf314d1a7763635e8738f1e7996d714d9b6bf';
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashHmac($hash_source, $hash_key)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::hashHmac
|
||||
* @testdox hash hmac with invalid type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidHashMacType(): void
|
||||
{
|
||||
$hash_key = 'FIX KEY';
|
||||
$hash_source = 'Some String Text';
|
||||
$expected = hash_hmac(\CoreLibs\Create\Hash::STANDARD_HASH, $hash_source, $hash_key);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashHmac($hash_source, $hash_key, 'DOES_NOT_EXIST')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function providerHashTypes(): array
|
||||
{
|
||||
return [
|
||||
'Hash crc32b' => [
|
||||
'crc32b',
|
||||
true,
|
||||
false,
|
||||
],
|
||||
'Hash adler32' => [
|
||||
'adler32',
|
||||
true,
|
||||
false,
|
||||
],
|
||||
'HAsh fnv132' => [
|
||||
'fnv132',
|
||||
true,
|
||||
false,
|
||||
],
|
||||
'Hash fnv1a32' => [
|
||||
'fnv1a32',
|
||||
true,
|
||||
false,
|
||||
],
|
||||
'Hash: joaat' => [
|
||||
'joaat',
|
||||
true,
|
||||
false,
|
||||
],
|
||||
'Hash: ripemd160' => [
|
||||
'ripemd160',
|
||||
true,
|
||||
true,
|
||||
],
|
||||
'Hash: sha256' => [
|
||||
'sha256',
|
||||
true,
|
||||
true,
|
||||
],
|
||||
'Hash: invalid' => [
|
||||
'invalid',
|
||||
false,
|
||||
false
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::isValidHashType
|
||||
* @covers ::isValidHashHmacType
|
||||
* @dataProvider providerHashTypes
|
||||
* @testdox check if $hash_type is valid for hash $hash_ok and hash hmac $hash_hmac_ok [$_dataName]
|
||||
*
|
||||
* @param string $hash_type
|
||||
* @param bool $hash_ok
|
||||
* @param bool $hash_hmac_ok
|
||||
* @return void
|
||||
*/
|
||||
public function testIsValidHashAndHashHmacTypes(string $hash_type, bool $hash_ok, bool $hash_hmac_ok): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$hash_ok,
|
||||
\CoreLibs\Create\Hash::isValidHashType($hash_type),
|
||||
'hash valid'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$hash_hmac_ok,
|
||||
\CoreLibs\Create\Hash::isValidHashHmacType($hash_type),
|
||||
'hash hmac valid'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,32 +13,6 @@ use PHPUnit\Framework\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
|
||||
*
|
||||
@@ -47,109 +21,67 @@ final class CoreLibsCreateRandomKeyTest extends TestCase
|
||||
public function randomKeyGenProvider(): array
|
||||
{
|
||||
return [
|
||||
'default key length' => [
|
||||
// just key length
|
||||
'default key length, default char set' => [
|
||||
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,
|
||||
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,
|
||||
1 => 4,
|
||||
1 => \CoreLibs\Create\RandomKey::KEY_LENGTH_DEFAULT,
|
||||
],
|
||||
'set override key lenght' => [
|
||||
'set override key lenght, default char set' => [
|
||||
0 => 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 [
|
||||
'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,
|
||||
]
|
||||
];
|
||||
}
|
||||
$inputLength = strlen($input);
|
||||
|
||||
/**
|
||||
* run before each test and reset to default 4
|
||||
*
|
||||
* @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;
|
||||
for ($i = 0; $i < $inputLength; $i++) {
|
||||
if (strpos($allowed_chars, $input[$i]) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->assertEquals(
|
||||
$input,
|
||||
\CoreLibs\Create\RandomKey::getRandomKeyLength()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,43 +95,41 @@ final class CoreLibsCreateRandomKeyTest extends TestCase
|
||||
* @param integer $expected
|
||||
* @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) {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
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(
|
||||
$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__
|
||||
|
||||
@@ -135,6 +135,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
}
|
||||
// check if they already exist, drop them
|
||||
if ($db->dbShowTableMetaData('table_with_primary_key') !== false) {
|
||||
$db->dbExec("CREATE EXTENSION IF NOT EXISTS pgcrypto");
|
||||
$db->dbExec("DROP TABLE table_with_primary_key");
|
||||
$db->dbExec("DROP TABLE table_without_primary_key");
|
||||
$db->dbExec("DROP TABLE test_meta");
|
||||
@@ -3692,7 +3693,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function preparedProviderValue(): array
|
||||
public function providerDbGetPrepareCursorValue(): array
|
||||
{
|
||||
// 1: query (can be empty for do not set)
|
||||
// 2: stm name
|
||||
@@ -3736,7 +3737,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
* test return prepare cursor errors
|
||||
*
|
||||
* @covers ::dbGetPrepareCursorValue
|
||||
* @dataProvider preparedProviderValue
|
||||
* @dataProvider providerDbGetPrepareCursorValue
|
||||
* @testdox prepared query $stm_name with $key expect error id $error_id [$_dataName]
|
||||
*
|
||||
* @param string $query
|
||||
@@ -3769,6 +3770,94 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerDbPreparedCursorStatus(): array
|
||||
{
|
||||
return [
|
||||
'empty statement pararm' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_a',
|
||||
'check_stm_name' => '',
|
||||
'check_query' => '',
|
||||
'expected' => false
|
||||
],
|
||||
'different stm_name' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_b',
|
||||
'check_stm_name' => 'other_name',
|
||||
'check_query' => '',
|
||||
'expected' => 0
|
||||
],
|
||||
'same stm_name' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_c',
|
||||
'check_stm_name' => 'test_stm_c',
|
||||
'check_query' => '',
|
||||
'expected' => 1
|
||||
],
|
||||
'same stm_name and query' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_d',
|
||||
'check_stm_name' => 'test_stm_d',
|
||||
'check_query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'expected' => 2
|
||||
],
|
||||
'same stm_name and different query' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_e',
|
||||
'check_stm_name' => 'test_stm_e',
|
||||
'check_query' => 'SELECT row_int, uid, row_int FROM table_with_primary_key',
|
||||
'expected' => 1
|
||||
],
|
||||
'insert query test' => [
|
||||
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
|
||||
'stm_name' => 'test_stm_f',
|
||||
'check_stm_name' => 'test_stm_f',
|
||||
'check_query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
|
||||
'expected' => 2
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test cursor status for prepared statement
|
||||
*
|
||||
* @covers ::dbPreparedCursorStatus
|
||||
* @dataProvider providerDbPreparedCursorStatus
|
||||
* @testdox Check prepared $stm_name ($check_stm_name) status is $expected [$_dataName]
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $stm_name
|
||||
* @param string $check_stm_name
|
||||
* @param string $check_query
|
||||
* @param bool|int $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testDbPreparedCursorStatus(
|
||||
string $query,
|
||||
string $stm_name,
|
||||
string $check_stm_name,
|
||||
string $check_query,
|
||||
bool|int $expected
|
||||
): void {
|
||||
$db = new \CoreLibs\DB\IO(
|
||||
self::$db_config['valid'],
|
||||
self::$log
|
||||
);
|
||||
$db->dbPrepare($stm_name, $query);
|
||||
// $db->dbExecute($stm_name);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$db->dbPreparedCursorStatus($check_stm_name, $check_query),
|
||||
'check prepared stement cursor status'
|
||||
);
|
||||
unset($db);
|
||||
}
|
||||
|
||||
// - schema set/get tests
|
||||
// dbGetSchema, dbSetSchema
|
||||
|
||||
@@ -4656,7 +4745,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
|
||||
// all hast to be string
|
||||
foreach ($res as $key => $value) {
|
||||
$this->assertIsString($value, 'Aseert string for column: ' . $key);
|
||||
$this->assertIsString($value, 'Assert string for column: ' . $key);
|
||||
}
|
||||
// convert base only
|
||||
$db->dbSetConvertFlag(Convert::on);
|
||||
@@ -4669,10 +4758,10 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
}
|
||||
switch ($type_layout[$name]) {
|
||||
case 'int':
|
||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
||||
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
default:
|
||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
||||
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4686,13 +4775,13 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
}
|
||||
switch ($type_layout[$name]) {
|
||||
case 'int':
|
||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
||||
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
case 'float':
|
||||
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
|
||||
$this->assertIsFloat($value, 'Assert float for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
default:
|
||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
||||
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4706,17 +4795,17 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
}
|
||||
switch ($type_layout[$name]) {
|
||||
case 'int':
|
||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
||||
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
case 'float':
|
||||
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
|
||||
$this->assertIsFloat($value, 'Assert float for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
case 'json':
|
||||
case 'jsonb':
|
||||
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
|
||||
$this->assertIsArray($value, 'Assert array for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
default:
|
||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
||||
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4730,25 +4819,25 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
}
|
||||
switch ($type_layout[$name]) {
|
||||
case 'int':
|
||||
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
|
||||
$this->assertIsInt($value, 'Assert int for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
case 'float':
|
||||
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
|
||||
$this->assertIsFloat($value, 'Assert float for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
case 'json':
|
||||
case 'jsonb':
|
||||
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
|
||||
$this->assertIsArray($value, 'Assert array for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
case 'bytea':
|
||||
// for hex types it must not start with \x
|
||||
$this->assertStringStartsNotWith(
|
||||
'\x',
|
||||
$value,
|
||||
'Aseert bytes not starts with \x for column: ' . $key . '/' . $name
|
||||
'Assert bytes not starts with \x for column: ' . $key . '/' . $name
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
|
||||
$this->assertIsString($value, 'Assert string for column: ' . $key . '/' . $name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -4920,8 +5009,8 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
)
|
||||
),
|
||||
($params === null ?
|
||||
$db->dbGetQueryHash($query) :
|
||||
$db->dbGetQueryHash($query, $params)
|
||||
$db->dbBuildQueryHash($query) :
|
||||
$db->dbBuildQueryHash($query, $params)
|
||||
),
|
||||
'Failed assertdbGetQueryHash '
|
||||
);
|
||||
@@ -5147,6 +5236,9 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
$3
|
||||
-- comment 3
|
||||
, $4
|
||||
-- ignore $5, $6
|
||||
-- $7, $8
|
||||
-- digest($9, 10)
|
||||
)
|
||||
SQL,
|
||||
'count' => 4,
|
||||
@@ -5217,8 +5309,57 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
SQL,
|
||||
'count' => 2,
|
||||
'convert' => false,
|
||||
],
|
||||
// special $$ string case
|
||||
'text string, with $ placehoders that could be seen as $$ string' => [
|
||||
'query' => <<<SQL
|
||||
SELECT row_int
|
||||
FROM table_with_primary_key
|
||||
WHERE
|
||||
row_bytea = digest($3::VARCHAR, $4) OR
|
||||
row_varchar = encode(digest($3, $4), 'hex') OR
|
||||
row_bytea = hmac($3, $5, $4) OR
|
||||
row_varchar = encode(hmac($3, $5, $4), 'hex') OR
|
||||
row_bytea = pgp_sym_encrypt($3, $6) OR
|
||||
row_varchar = encode(pgp_sym_encrypt($1, $6), 'hex') OR
|
||||
row_varchar = CASE WHEN row_int = 1 THEN $1 ELSE $2 END
|
||||
SQL,
|
||||
'count' => 6,
|
||||
'convert' => false,
|
||||
],
|
||||
// NOTE, in SQL heredoc we cannot write $$ strings parts
|
||||
'text string, with $ placehoders are in $$ strings' => [
|
||||
'query' => '
|
||||
SELECT row_int
|
||||
FROM table_with_primary_key
|
||||
WHERE
|
||||
row_varchar = $$some string$$ OR
|
||||
row_varchar = $tag$some string$tag$ OR
|
||||
row_varchar = $btag$some $1 string$btag$ OR
|
||||
row_varchar = $btag$some $1 $subtag$ something $subtag$string$btag$ OR
|
||||
row_varchar = $1
|
||||
',
|
||||
'count' => 1,
|
||||
'convert' => false,
|
||||
],
|
||||
// a text string with escaped quite
|
||||
'text string, with escaped quote' => [
|
||||
'query' => <<<SQL
|
||||
SELECT row_int
|
||||
FROM table_with_primary_key
|
||||
WHERE
|
||||
row_varchar = 'foo bar bar baz $5' OR
|
||||
row_varchar = 'foo bar '' barbar $6' OR
|
||||
row_varchar = E'foo bar \' barbar $7' OR
|
||||
row_varchar = CASE WHEN row_int = 1 THEN $1 ELSE $2 END
|
||||
SQL,
|
||||
'count' => 2,
|
||||
'convert' => false,
|
||||
]
|
||||
];
|
||||
$string = <<<SQL
|
||||
'''
|
||||
SQL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,11 +105,15 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
'log_level' => Level::Error,
|
||||
]);
|
||||
$errorLogTmpfile = tmpfile();
|
||||
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
||||
$em->setMessage(
|
||||
$level,
|
||||
$str
|
||||
);
|
||||
// for exceptions if log level is set to catch them
|
||||
$error_log_content = stream_get_contents($errorLogTmpfile);
|
||||
$this->assertEquals(
|
||||
[
|
||||
'level' => $expected,
|
||||
@@ -377,6 +381,8 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
?bool $log_warning,
|
||||
string $expected
|
||||
): void {
|
||||
$errorLogTmpfile = tmpfile();
|
||||
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testErrorMessagesLogError',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
@@ -392,6 +398,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
log_error: $log_error,
|
||||
log_warning: $log_warning
|
||||
);
|
||||
ini_set('error_log', $errorLogLocationBackup);
|
||||
// for exceptions if log level is set to catch them
|
||||
$error_log_content = stream_get_contents($errorLogTmpfile);
|
||||
$file_content = '';
|
||||
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
||||
$file_content = file_get_contents(
|
||||
@@ -447,6 +456,8 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
'log_level' => Level::Debug,
|
||||
'log_per_run' => true
|
||||
]);
|
||||
$errorLogTmpfile = tmpfile();
|
||||
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
|
||||
$em = new \CoreLibs\Logging\ErrorMessage($log);
|
||||
$em->setErrorMsg(
|
||||
$id,
|
||||
@@ -456,6 +467,9 @@ final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
log_error: $log_error,
|
||||
log_warning: $log_warning
|
||||
);
|
||||
ini_set('error_log', $errorLogLocationBackup);
|
||||
// for exceptions if log level is set to catch them
|
||||
$error_log_content = stream_get_contents($errorLogTmpfile);
|
||||
$file_content = '';
|
||||
if (is_file($log->getLogFolder() . $log->getLogFile())) {
|
||||
$file_content = file_get_contents(
|
||||
|
||||
@@ -18,7 +18,7 @@ use CoreLibs\Logging\Logger\Flag;
|
||||
final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
{
|
||||
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
|
||||
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
|
||||
private const REGEX_BASE = "\[[\d\-\s\.:+T]+\]\s{1}" // date, just basic checks
|
||||
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
|
||||
. "\[(phar:\/\/)?[\w\-\.\/]+:\d+\]\s{1}" // folder/file [note phar:// is for phpunit]
|
||||
. "\[\w+\]\s{1}" // run id
|
||||
@@ -249,7 +249,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$this->assertFalse(
|
||||
$log->loggingLevelIsDebug()
|
||||
);
|
||||
// not set, should be debug]
|
||||
// not set, should be debug
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
'log_file_id' => 'testSetLoggingLevel',
|
||||
'log_folder' => self::LOG_FOLDER,
|
||||
@@ -297,6 +297,71 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$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
|
||||
// getLogFileId
|
||||
|
||||
@@ -395,7 +460,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
}
|
||||
$per_run_id = $log->getLogUniqueId();
|
||||
$this->assertMatchesRegularExpression(
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
|
||||
$per_run_id,
|
||||
'assert per log run id 1st'
|
||||
);
|
||||
@@ -403,7 +468,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$log->setLogUniqueId(true);
|
||||
$per_run_id_2nd = $log->getLogUniqueId();
|
||||
$this->assertMatchesRegularExpression(
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
|
||||
$per_run_id_2nd,
|
||||
'assert per log run id 2nd'
|
||||
);
|
||||
@@ -824,13 +889,13 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
|
||||
$this->assertEquals(
|
||||
$log->getLogFile(),
|
||||
$log->getLogFileId() . '_DEBUG.log'
|
||||
$log->getLogFileId() . '.DEBUG.log'
|
||||
);
|
||||
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
|
||||
$this->assertTrue($log_ok, 'assert ::log (info) OK');
|
||||
$this->assertEquals(
|
||||
$log->getLogFile(),
|
||||
$log->getLogFileId() . '_INFO.log'
|
||||
$log->getLogFileId() . '.INFO.log'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,6 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
continue;
|
||||
}
|
||||
$this->url_basic = $url;
|
||||
// split out the last / part for url set test
|
||||
curl_close($handle);
|
||||
// print "Open: $url\n";
|
||||
break;
|
||||
}
|
||||
@@ -969,13 +967,23 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
"query" => ["foo-get" => "bar"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: get response code not matching");
|
||||
$request_expected = json_decode(
|
||||
<<<JSON
|
||||
{
|
||||
"HEADERS":{
|
||||
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||
"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",
|
||||
"HTTP_HOST":"soba.egplusww.jp"
|
||||
},
|
||||
"REQUEST_TYPE":"GET",
|
||||
"PARAMS":{"foo-get":"bar"},"BODY":null
|
||||
}
|
||||
JSON,
|
||||
true
|
||||
);
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"GET",'
|
||||
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
$request_expected,
|
||||
json_decode($response['content'], true),
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
// post
|
||||
@@ -984,13 +992,25 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
"body" => ["foo-post" => "baz"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: post response code not matching");
|
||||
$request_expected = json_decode(
|
||||
<<<JSON
|
||||
{
|
||||
"HEADERS":{
|
||||
"HTTP_HOST":"soba.egplusww.jp",
|
||||
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||
"HTTP_SECOND_CALL":"post",
|
||||
"HTTP_ACCEPT":"*\/*"
|
||||
},
|
||||
"REQUEST_TYPE":"POST",
|
||||
"PARAMS":[],
|
||||
"BODY":{"foo-post":"baz"}
|
||||
}
|
||||
JSON,
|
||||
true
|
||||
);
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"POST",'
|
||||
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
$request_expected,
|
||||
json_decode($response['content'], true),
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
// delete
|
||||
@@ -998,13 +1018,24 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
"headers" => ["third-call" => "delete"],
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: delete response code not matching");
|
||||
$request_expected = json_decode(
|
||||
<<<JSON
|
||||
{
|
||||
"HEADERS":{
|
||||
"HTTP_HOST":"soba.egplusww.jp",
|
||||
"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",
|
||||
"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"
|
||||
},
|
||||
"REQUEST_TYPE":"DELETE",
|
||||
"PARAMS":[],
|
||||
"BODY":[]
|
||||
}
|
||||
JSON,
|
||||
true
|
||||
);
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"DELETE",'
|
||||
. '"PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
$request_expected,
|
||||
json_decode($response['content'], true),
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
}
|
||||
|
||||
10
README.md
10
README.md
@@ -114,3 +114,13 @@ Add `.libs` to the master .gitingore
|
||||
### Update phpunit
|
||||
|
||||
On a version update the old phpunit folder in .libs has to be removed and the new version extracted again
|
||||
|
||||
## Javascript
|
||||
|
||||
The original edit.js javascript functions are now in utils.js or utils.min.js.
|
||||
|
||||
The development for those files is located in a different repository
|
||||
|
||||
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
|
||||
59
eslint.config.mjs
Normal file
59
eslint.config.mjs
Normal file
@@ -0,0 +1,59 @@
|
||||
import globals from 'globals';
|
||||
import pluginJs from '@eslint/js';
|
||||
|
||||
/*
|
||||
module.exports = {
|
||||
// in globals block
|
||||
'extends': 'eslint:recommended',
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 6
|
||||
},
|
||||
// rules copied
|
||||
};
|
||||
*/
|
||||
|
||||
/** @type {import('eslint').Linter.Config[]} */
|
||||
export default [
|
||||
{languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.jquery
|
||||
}
|
||||
}},
|
||||
pluginJs.configs.recommended,
|
||||
{
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
'tab',
|
||||
{
|
||||
'SwitchCase': 1
|
||||
}
|
||||
],
|
||||
'linebreak-style': [
|
||||
'error',
|
||||
'unix'
|
||||
],
|
||||
// 'quotes': [
|
||||
// 'error',
|
||||
// 'single'
|
||||
// ],
|
||||
'semi': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'no-console': 'off',
|
||||
'no-unused-vars': [
|
||||
'error', {
|
||||
'vars': 'all',
|
||||
'args': 'after-used',
|
||||
'ignoreRestSiblings': false
|
||||
}
|
||||
],
|
||||
// Requires eslint >= v8.14.0
|
||||
'no-constant-binary-expression': 'error'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// __END__
|
||||
@@ -1,9 +1,11 @@
|
||||
// https://www.typescriptlang.org/tsconfig/#compilerOptions
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"target": "ES2020",
|
||||
"jsx": "react",
|
||||
"checkJs": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true
|
||||
|
||||
17
package.json
Normal file
17
package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "core-libraries",
|
||||
"version": "9.26.8",
|
||||
"main": "",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Clemens Schwaighofer",
|
||||
"license": "",
|
||||
"description": "Core Libraries",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.20.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^9.20.1",
|
||||
"globals": "^15.15.0"
|
||||
}
|
||||
}
|
||||
@@ -10,5 +10,6 @@ $_SERVER['HTTP_HOST'] = 'soba.tokyo.tequila.jp';
|
||||
define('BASE_NAME', '');
|
||||
define('SITE_DOMAIN', '');
|
||||
define('HOST_NAME', 'soba.tokyo.tequila.jp');
|
||||
define('DEFAULT_ENCODING', 'en_US.UTF-8');
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -52,7 +52,7 @@ header("Content-Type: application/json; charset=UTF-8");
|
||||
if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) {
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// if server request type is get set file_get to null -> no body
|
||||
@@ -61,7 +61,7 @@ if ($_SERVER['REQUEST_METHOD'] == "GET") {
|
||||
} elseif (($file_get = file_get_contents('php://input')) === false) {
|
||||
header("HTTP/1.1 404 Not Found");
|
||||
print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
// str_replace('\"', '"', trim($file_get, '"'));
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ $test_array = [
|
||||
'element_c' => [
|
||||
'type' => 'email'
|
||||
],
|
||||
'element_d' => [
|
||||
'type' => 'butter'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -60,6 +63,8 @@ echo "ARRAYSEARCHRECURSIVE(email, [array], type): "
|
||||
. DgS::printAr(ArrayHandler::arraySearchRecursive('email', $test_array, 'type')) . "<br>";
|
||||
echo "ARRAYSEARCHRECURSIVE(email, [array]['input'], type): "
|
||||
. 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
|
||||
echo "ARRAYSEARCHRECURSIVEALL(email, [array], type): "
|
||||
. Dgs::printAr((array)ArrayHandler::arraySearchRecursiveAll('email', $test_array, 'type')) . "<br>";
|
||||
@@ -68,7 +73,15 @@ echo "ARRAYSEARCHRECURSIVEALL(email, [array], type): "
|
||||
|
||||
// simple search
|
||||
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 = [
|
||||
'foo' => 'bar'
|
||||
@@ -168,6 +181,31 @@ $data = [
|
||||
$search = ['image', 'result_image', 'nothing', 'EMPTY'];
|
||||
$result = ArrayHandler::arraySearchKey($data, $search);
|
||||
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 = [
|
||||
// 'A' => [
|
||||
@@ -263,6 +301,233 @@ $out = array_intersect_key(
|
||||
);
|
||||
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>";
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -74,9 +74,21 @@ foreach ($bytes as $byte) {
|
||||
print '<div style="width: 35%; text-align: right; padding-right: 2px;">';
|
||||
print "(" . number_format($byte) . "/" . $byte . ") bytes :";
|
||||
$_bytes = Byte::humanReadableByteFormat($byte);
|
||||
print '</div><div style="width: 10%;">' . $_bytes;
|
||||
print '</div><div style="width: 10%;">';
|
||||
print Byte::stringByteFormat($_bytes);
|
||||
print '</div>';
|
||||
print '<div style="width: 10%;">' . $_bytes . '</div>';
|
||||
print '<div style="width: 40%;">';
|
||||
try {
|
||||
print Byte::stringByteFormat($_bytes);
|
||||
} catch (\LengthException $e) {
|
||||
print "LengthException 1: " . $e->getMessage();
|
||||
try {
|
||||
print "<br>S: " . Byte::stringByteFormat($_bytes, Byte::RETURN_AS_STRING);
|
||||
} catch (\LengthException $e) {
|
||||
print "LengthException 2: " . $e->getMessage();
|
||||
} catch (\RuntimeException $e) {
|
||||
print "RuntimeException 1: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
print "</div>";
|
||||
//
|
||||
print "</div>";
|
||||
@@ -87,13 +99,85 @@ foreach ($bytes as $byte) {
|
||||
print "bytes [si]:";
|
||||
$_bytes = Byte::humanReadableByteFormat($byte, Byte::BYTE_FORMAT_SI);
|
||||
print '</div><div style="width: 10%;">' . $_bytes;
|
||||
print '</div><div style="width: 10%;">';
|
||||
print Byte::stringByteFormat($_bytes);
|
||||
print '</div><div style="width: 40%;">';
|
||||
try {
|
||||
print Byte::stringByteFormat($_bytes);
|
||||
} catch (\LengthException $e) {
|
||||
print "LengthException A: " . $e->getMessage();
|
||||
try {
|
||||
print "<br>Ssi: " . Byte::stringByteFormat($_bytes, Byte::RETURN_AS_STRING | Byte::BYTE_FORMAT_SI);
|
||||
} catch (\LengthException $e) {
|
||||
print "LengthException B: " . $e->getMessage();
|
||||
} catch (\RuntimeException $e) {
|
||||
print "RuntimeException A: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
print "</div>";
|
||||
//
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
$string_bytes = [
|
||||
'-117.42 MB',
|
||||
'242.98 MB',
|
||||
'254.78 MiB',
|
||||
'1 EiB',
|
||||
'8 EB',
|
||||
'867.36EB',
|
||||
'1000EB',
|
||||
'10000EB',
|
||||
];
|
||||
print "<b>BYTE STRING TO BYTES TESTS</b><br>";
|
||||
foreach ($string_bytes as $string) {
|
||||
print '<div style="display: flex; border-bottom: 1px dashed gray;">';
|
||||
//
|
||||
print '<div style="width: 35%; text-align: right; padding-right: 2px;">';
|
||||
print "string byte ($string) to bytes :";
|
||||
try {
|
||||
$_bytes = Byte::stringByteFormat($string);
|
||||
} catch (\LengthException $e) {
|
||||
print "<br>LengthException A: " . $e->getMessage();
|
||||
$_bytes = 0;
|
||||
}
|
||||
try {
|
||||
$_bytes_string = Byte::stringByteFormat($string, Byte::RETURN_AS_STRING);
|
||||
} catch (\LengthException $e) {
|
||||
print "<br>LengthException B: " . $e->getMessage();
|
||||
$_bytes_string = '';
|
||||
} catch (\RuntimeException $e) {
|
||||
print "<br>RuntimeException: " . $e->getMessage();
|
||||
$_bytes_string = '';
|
||||
}
|
||||
try {
|
||||
$_bytes_si = Byte::stringByteFormat($string, Byte::BYTE_FORMAT_SI);
|
||||
} catch (\LengthException $e) {
|
||||
print "<br>LengthException A: " . $e->getMessage();
|
||||
$_bytes_si = 0;
|
||||
}
|
||||
try {
|
||||
$_bytes_string_si = Byte::stringByteFormat($string, Byte::RETURN_AS_STRING | Byte::BYTE_FORMAT_SI);
|
||||
} catch (\LengthException $e) {
|
||||
print "<br>LengthException B: " . $e->getMessage();
|
||||
$_bytes_string_si = '';
|
||||
} catch (\RuntimeException $e) {
|
||||
print "<br>RuntimeException: " . $e->getMessage();
|
||||
$_bytes_string_si = '';
|
||||
}
|
||||
print '</div>';
|
||||
print '<div style="width: 20%;">'
|
||||
. "F:" . number_format((int)$_bytes)
|
||||
. '<br>B: ' . $_bytes
|
||||
. '<br>S: ' . $_bytes_string
|
||||
. "<br>Fsi:" . number_format((int)$_bytes_si)
|
||||
. '<br>Bsi: ' . $_bytes_si
|
||||
. '<br>Ssi: ' . $_bytes_string_si;
|
||||
print '</div>';
|
||||
print '<div style="width: 10%;">';
|
||||
print "B: " . Byte::humanReadableByteFormat($_bytes) . "<br>";
|
||||
print "Bsi: " . Byte::humanReadableByteFormat($_bytes_si, Byte::BYTE_FORMAT_SI);
|
||||
print "</div>";
|
||||
print "</div>";
|
||||
}
|
||||
print "</body></html>";
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -225,6 +225,36 @@ foreach ($intervals as $interval) {
|
||||
print "STRINGTOTIME: $reverse_interval: " . DateTime::stringToTime($reverse_interval) . "<br>";
|
||||
}
|
||||
print "<hr>";
|
||||
$interval_strings = [
|
||||
'10d 5h 30m 15s 123456ms',
|
||||
'18999d 0h 38m 10s 1235ms',
|
||||
'18999 d 0 h 38 m 10s 1235ms',
|
||||
'-2h 15m 5s',
|
||||
'45s 500ms',
|
||||
'0s',
|
||||
'0ms',
|
||||
'1s 5ms',
|
||||
'1s 50ms',
|
||||
'1s 500ms',
|
||||
'1s 5000ms',
|
||||
'10day 5hour 30min 15sec 123456millis',
|
||||
'10day 5hour 30min 15sec 123456millisec',
|
||||
'10day 5hour 30min 15sec 123456msec',
|
||||
'-2days 3hours 15minutes 30seconds 250milliseconds',
|
||||
'',
|
||||
' ',
|
||||
'invalid',
|
||||
];
|
||||
foreach ($interval_strings as $interval_string) {
|
||||
print "STRINGTOTIME: $interval_string: " . DateTime::stringToTime($interval_string) . "<br>";
|
||||
try {
|
||||
// test exception
|
||||
DateTime::stringToTime($interval_string, throw_exception:true);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
print "ERROR: " . $e->getMessage() . "<br><pre>" . $e . "</pre><br>";
|
||||
}
|
||||
}
|
||||
print "<hr>";
|
||||
$check_dates = [
|
||||
'2021-05-01',
|
||||
'2021-05-40'
|
||||
@@ -268,7 +298,9 @@ foreach ($compare_datetimes as $compare_datetime) {
|
||||
print "COMPAREDATE: $compare_datetime[0] = $compare_datetime[1]: "
|
||||
. (string)DateTime::compareDateTime($compare_datetime[0], $compare_datetime[1]) . "<br>";
|
||||
}
|
||||
|
||||
print "<hr>";
|
||||
print "<h2>calcDaysInterval</h2>";
|
||||
$compare_dates = [
|
||||
[ '2021-05-01', '2021-05-10', ],
|
||||
[ '2021-05-10', '2021-05-01', ],
|
||||
@@ -279,9 +311,21 @@ foreach ($compare_dates as $compare_date) {
|
||||
print "CALCDAYSINTERVAL: $compare_date[0] = $compare_date[1]: "
|
||||
. DgS::printAr(DateTime::calcDaysInterval($compare_date[0], $compare_date[1])) . "<br>";
|
||||
print "CALCDAYSINTERVAL(named): $compare_date[0] = $compare_date[1]: "
|
||||
. DgS::printAr(DateTime::calcDaysInterval($compare_date[0], $compare_date[1], true)) . "<br>";
|
||||
. DgS::printAr(DateTime::calcDaysInterval($compare_date[0], $compare_date[1], return_named:true)) . "<br>";
|
||||
print "CALCDAYSINTERVAL(EXCLUDE END): $compare_date[0] = $compare_date[1]: "
|
||||
. Dgs::printAr(DateTime::calcDaysInterval($compare_date[0], $compare_date[1], include_end_date:false));
|
||||
print "CALCDAYSINTERVAL(EXCLUDE START): $compare_date[0] = $compare_date[1]: "
|
||||
. Dgs::printAr(DateTime::calcDaysInterval($compare_date[0], $compare_date[1], exclude_start_date:true));
|
||||
print "CALCDAYSINTERVAL(EXCLUDE END, EXCLUDE START): $compare_date[0] = $compare_date[1]: "
|
||||
. Dgs::printAr(DateTime::calcDaysInterval(
|
||||
$compare_date[0],
|
||||
$compare_date[1],
|
||||
include_end_date:false,
|
||||
exclude_start_date:true
|
||||
));
|
||||
}
|
||||
print "<hr>";
|
||||
print "<h2>setWeekdayNameFromIsoDow</h2>";
|
||||
// test date conversion
|
||||
$dow = 2;
|
||||
print "DOW[$dow]: " . DateTime::setWeekdayNameFromIsoDow($dow) . "<br>";
|
||||
@@ -297,26 +341,25 @@ $date = '2022-70-242';
|
||||
print "DATE-dow[$date];invalid: " . DateTime::setWeekdayNameFromDate($date) . "<br>";
|
||||
print "DATE-dow[$date],long;invalid: " . DateTime::setWeekdayNameFromDate($date, true) . "<br>";
|
||||
print "DOW-date[$date];invalid: " . DateTime::setWeekdayNumberFromDate($date) . "<br>";
|
||||
print "<hr>";
|
||||
// check date range includes a weekend
|
||||
// does not:
|
||||
$start_date = '2023-07-03';
|
||||
$end_date = '2023-07-05';
|
||||
print "Has Weekend: " . $start_date . " ~ " . $end_date . ": "
|
||||
. Dgs::prBl(DateTime::dateRangeHasWeekend($start_date, $end_date)) . "<br>";
|
||||
$start_date = '2023-07-03';
|
||||
$end_date = '2023-07-10';
|
||||
print "Has Weekend: " . $start_date . " ~ " . $end_date . ": "
|
||||
. Dgs::prBl(DateTime::dateRangeHasWeekend($start_date, $end_date)) . "<br>";
|
||||
$start_date = '2023-07-03';
|
||||
$end_date = '2023-07-31';
|
||||
print "Has Weekend: " . $start_date . " ~ " . $end_date . ": "
|
||||
. Dgs::prBl(DateTime::dateRangeHasWeekend($start_date, $end_date)) . "<br>";
|
||||
$start_date = '2023-07-01';
|
||||
$end_date = '2023-07-03';
|
||||
print "Has Weekend: " . $start_date . " ~ " . $end_date . ": "
|
||||
. Dgs::prBl(DateTime::dateRangeHasWeekend($start_date, $end_date)) . "<br>";
|
||||
|
||||
print "<hr>";
|
||||
print "<h2>dateRangeHasWeekend</h2>";
|
||||
// check date range includes a weekend
|
||||
$has_weekend_list = [
|
||||
['2023-07-03', '2023-07-05'],
|
||||
['2023-07-03', '2023-07-10'],
|
||||
['2023-07-03', '2023-07-31'],
|
||||
['2023-07-01', '2023-07-03'],
|
||||
['2023-07-01', '2023-07-01'],
|
||||
['2023-07-01', '2023-07-02'],
|
||||
['2023-06-30', '2023-07-01'],
|
||||
['2023-06-30', '2023-06-30'],
|
||||
['2023-07-01', '2023-06-30'],
|
||||
];
|
||||
foreach ($has_weekend_list as $days) {
|
||||
print "Has Weekend: " . $days[0] . " ~ " . $days[1] . ": "
|
||||
. Dgs::prBl(DateTime::dateRangeHasWeekend($days[0], $days[1])) . "<br>";
|
||||
}
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
@@ -460,7 +503,10 @@ function intervalStringFormatDeprecated(
|
||||
// print "-> V: $value | $part, $time_name | I: " . is_int($value) . " | F: " . is_float($value)
|
||||
// . " | " . ($value != 0 ? 'Not zero' : 'ZERO') . "<br>";
|
||||
// var_dump($skip_last_zero);
|
||||
if ($value != 0 || $skip_zero === false || $skip_last_zero === false) {
|
||||
if (
|
||||
is_numeric($value) &&
|
||||
($value != 0 || $skip_zero === false || $skip_last_zero === false)
|
||||
) {
|
||||
if ($part == 'f') {
|
||||
if ($truncate_nanoseconds === true) {
|
||||
$value = round($value, 3);
|
||||
|
||||
@@ -21,6 +21,7 @@ ob_end_flush();
|
||||
|
||||
use CoreLibs\Debug\Support;
|
||||
use CoreLibs\DB\Support\ConvertPlaceholder;
|
||||
use CoreLibs\Convert\Html;
|
||||
|
||||
$log = new CoreLibs\Logging\Logging([
|
||||
'log_folder' => BASE . LOG,
|
||||
@@ -38,10 +39,12 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
||||
print "LOGFILE NAME: " . $log->getLogFile() . "<br>";
|
||||
print "LOGFILE ID: " . $log->getLogFileId() . "<br>";
|
||||
|
||||
print "Lookup Regex: <pre>" . ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS . "</pre>";
|
||||
print "Replace Named Regex: <pre>" . ConvertPlaceholder::REGEX_REPLACE_NAMED . "</pre>";
|
||||
print "Replace Named Regex: <pre>" . ConvertPlaceholder::REGEX_REPLACE_QUESTION_MARK . "</pre>";
|
||||
print "Replace Named Regex: <pre>" . ConvertPlaceholder::REGEX_REPLACE_NUMBERED . "</pre>";
|
||||
print "Lookup Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS) . "</pre>";
|
||||
print "Lookup Numbered Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_LOOKUP_NUMBERED) . "</pre>";
|
||||
print "Replace Named Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_REPLACE_NAMED) . "</pre>";
|
||||
print "Replace Question Mark Regex: <pre>"
|
||||
. Html::htmlent(ConvertPlaceholder::REGEX_REPLACE_QUESTION_MARK) . "</pre>";
|
||||
print "Replace Numbered Regex: <pre>" . Html::htmlent(ConvertPlaceholder::REGEX_REPLACE_NUMBERED) . "</pre>";
|
||||
|
||||
$uniqid = \CoreLibs\Create\Uids::uniqIdShort();
|
||||
// $binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: '');
|
||||
@@ -91,40 +94,63 @@ RETURNING
|
||||
some_binary
|
||||
SQL;
|
||||
|
||||
print "[ALL] Convert: "
|
||||
print "<b>[ALL] Convert</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
$query = "SELECT foo FROM bar WHERE baz = :baz AND buz = :baz AND biz = :biz AND boz = :bez";
|
||||
$params = [':baz' => 'SETBAZ', ':bez' => 'SETBEZ', ':biz' => 'SETBIZ'];
|
||||
print "[NO PARAMS] Convert: "
|
||||
print "<b>[NO PARAMS] Convert</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
$query = "SELECT foo FROM bar WHERE baz = :baz AND buz = :baz AND biz = :biz AND boz = :bez";
|
||||
$params = null;
|
||||
print "[NO PARAMS] Convert: "
|
||||
print "<b>[NO PARAMS] Convert</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
$query = "SELECT row_varchar FROM table_with_primary_key WHERE row_varchar <> :row_varchar";
|
||||
$params = null;
|
||||
print "[NO PARAMS] Convert: "
|
||||
print "<b>[NO PARAMS] Convert</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
$query = "SELECT row_varchar, row_varchar_literal, row_int, row_date FROM table_with_primary_key";
|
||||
$params = null;
|
||||
print "[NO PARAMS] TEST: "
|
||||
print "<b>[NO PARAMS] TEST</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
print "[P-CONV]: "
|
||||
$query = <<<SQL
|
||||
UPDATE table_with_primary_key SET
|
||||
row_int = $1::INT, row_numeric = $1::NUMERIC, row_varchar = $1
|
||||
WHERE
|
||||
row_varchar = $1
|
||||
SQL;
|
||||
$params = [1];
|
||||
print "<b>[All the same params] TEST</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
$query = <<<SQL
|
||||
SELECT row_varchar, row_varchar_literal, row_int, row_date
|
||||
FROM table_with_primary_key
|
||||
WHERE row_varchar = :row_varchar
|
||||
SQL;
|
||||
$params = [':row_varchar' => 1];
|
||||
print "<b>[: param] TEST</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
print "<b>[P-CONV]</b>: "
|
||||
. Support::printAr(
|
||||
ConvertPlaceholder::updateParamList([
|
||||
'original' => [
|
||||
@@ -186,6 +212,13 @@ SQL,
|
||||
'params' => [\CoreLibs\Create\Uids::uniqIdShort(), 'string A-1', 1234],
|
||||
'direction' => 'pg',
|
||||
],
|
||||
'b?' => [
|
||||
'query' => <<<SQL
|
||||
SELECT test FROM test_foo = ?
|
||||
SQL,
|
||||
'params' => [1234],
|
||||
'direction' => 'pg',
|
||||
],
|
||||
'b:' => [
|
||||
'query' => <<<SQL
|
||||
INSERT INTO test_foo (
|
||||
@@ -220,7 +253,7 @@ foreach ($test_queries as $info => $data) {
|
||||
$query = $data['query'];
|
||||
$params = $data['params'];
|
||||
$direction = $data['direction'];
|
||||
print "[$info] Convert: "
|
||||
print "<b>[$info] Convert</b>: "
|
||||
. Support::printAr(ConvertPlaceholder::convertPlaceholderInQuery($query, $params, $direction))
|
||||
. "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
166
www/admin/class_test.db.encryption.php
Normal file
166
www/admin/class_test.db.encryption.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php // phpcs:ignore warning
|
||||
|
||||
/**
|
||||
* @phan-file-suppress PhanTypeSuspiciousStringExpression
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// turn on all error reporting
|
||||
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
|
||||
|
||||
ob_start();
|
||||
|
||||
// basic class test file
|
||||
define('USE_DATABASE', true);
|
||||
// sample config
|
||||
require 'config.php';
|
||||
// for testing encryption compare
|
||||
use OpenPGP\OpenPGP;
|
||||
// define log file id
|
||||
$LOG_FILE_ID = 'classTest-db-query-encryption';
|
||||
ob_end_flush();
|
||||
|
||||
// use CoreLibs\Debug\Support;
|
||||
use CoreLibs\Security\SymmetricEncryption;
|
||||
use CoreLibs\Security\CreateKey;
|
||||
use CoreLibs\Create\Hash;
|
||||
use CoreLibs\Debug\Support;
|
||||
|
||||
$log = new CoreLibs\Logging\Logging([
|
||||
'log_folder' => BASE . LOG,
|
||||
'log_file_id' => $LOG_FILE_ID,
|
||||
'log_per_date' => true,
|
||||
]);
|
||||
// db connection and attach logger
|
||||
$db = new CoreLibs\DB\IO(DB_CONFIG, $log);
|
||||
$db->log->debug('START', '=============================>');
|
||||
|
||||
$PAGE_NAME = 'TEST CLASS: DB QUERY ENCRYPTION';
|
||||
print "<!DOCTYPE html>";
|
||||
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
|
||||
print "<body>";
|
||||
print '<div><a href="class_test.php">Class Test Master</a></div>';
|
||||
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
||||
|
||||
// encryption key
|
||||
$key_new = CreateKey::generateRandomKey();
|
||||
print "Secret Key NEW: " . $key_new . "<br>";
|
||||
// for reproducable test results
|
||||
$key = 'e475c19b9a3c8363feb06b51f5b73f1dc9b6f20757d4ab89509bf5cc70ed30ec';
|
||||
print "Secret Key: " . $key . "<br>";
|
||||
|
||||
// test text
|
||||
$text_string = "I a some deep secret";
|
||||
$text_string = "I a some deep secret ABC";
|
||||
//
|
||||
$crypt = new SymmetricEncryption($key);
|
||||
$encrypted = $crypt->encrypt($text_string);
|
||||
$string_hashed = Hash::hashStd($text_string);
|
||||
$string_hmac = Hash::hashHmac($text_string, $key);
|
||||
$decrypted = $crypt->decrypt($encrypted);
|
||||
|
||||
print "String: " . $text_string . "<br>";
|
||||
print "Encrypted: " . $encrypted . "<br>";
|
||||
print "Hashed: " . $string_hashed . "<br>";
|
||||
print "Hmac: " . $string_hmac . "<br>";
|
||||
|
||||
$db->dbExecParams(
|
||||
<<<SQL
|
||||
INSERT INTO test_encryption (
|
||||
-- for compare
|
||||
plain_text,
|
||||
-- via php encryption
|
||||
hash_text, hmac_text, crypt_text,
|
||||
-- -- in DB encryption
|
||||
pg_digest_bytea, pg_digest_text,
|
||||
pg_hmac_bytea, pg_hmac_text,
|
||||
pg_crypt_bytea, pg_crypt_text
|
||||
) VALUES (
|
||||
$1,
|
||||
$2, $3, $4,
|
||||
digest($1::VARCHAR, $5),
|
||||
encode(digest($1, $5), 'hex'),
|
||||
hmac($1, $6, $5),
|
||||
encode(hmac($1, $6, $5), 'hex'),
|
||||
pgp_sym_encrypt($1, $7),
|
||||
encode(pgp_sym_encrypt($1, $7), 'hex')
|
||||
) RETURNING cuuid
|
||||
SQL,
|
||||
[
|
||||
// 1: original string
|
||||
$text_string,
|
||||
// 2: hashed, 3: hmac, 4: encrypted
|
||||
$string_hashed, $string_hmac, $encrypted,
|
||||
// 5: hash type, 6: hmac secret, 7: pgp secret
|
||||
'sha256', $key, $key
|
||||
]
|
||||
);
|
||||
$cuuid = $db->dbGetReturningExt('cuuid');
|
||||
print "INSERTED: " . print_r($cuuid, true) . "<br>";
|
||||
print "LAST ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||
|
||||
// read back
|
||||
$res = $db->dbReturnRowParams(
|
||||
<<<SQL
|
||||
SELECT
|
||||
-- for compare
|
||||
plain_text,
|
||||
-- via php encryption
|
||||
hash_text, hmac_text, crypt_text,
|
||||
-- in DB encryption
|
||||
pg_digest_bytea, pg_digest_text,
|
||||
pg_hmac_bytea, pg_hmac_text,
|
||||
pg_crypt_bytea, pg_crypt_text,
|
||||
encode(pg_crypt_bytea, 'hex') AS pg_crypt_bytea_hex,
|
||||
pgp_sym_decrypt(pg_crypt_bytea, $2) AS from_pg_crypt_bytea,
|
||||
pgp_sym_decrypt(decode(pg_crypt_text, 'hex'), $2) AS from_pg_crypt_text
|
||||
FROM
|
||||
test_encryption
|
||||
WHERE
|
||||
cuuid = $1
|
||||
SQL,
|
||||
[
|
||||
$cuuid, $key
|
||||
]
|
||||
);
|
||||
|
||||
print "RES: <pre>" . Support::prAr($res) . "</pre><br>";
|
||||
|
||||
if ($res === false) {
|
||||
echo "Failed to run query<br>";
|
||||
} else {
|
||||
if (hash_equals($string_hashed, $res['pg_digest_text'])) {
|
||||
print "libsodium and pgcrypto hash match<br>";
|
||||
}
|
||||
if (hash_equals($string_hmac, $res['pg_hmac_text'])) {
|
||||
print "libsodium and pgcrypto hash hmac match<br>";
|
||||
}
|
||||
// do compare for PHP and pgcrypto settings
|
||||
$encryptedMessage_template = <<<TEXT
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
{BASE64}
|
||||
-----END PGP MESSAGE-----
|
||||
TEXT;
|
||||
$base64_string = base64_encode(hex2bin($res['pg_crypt_text']) ?: '');
|
||||
$encryptedMessage = str_replace(
|
||||
'{BASE64}',
|
||||
$base64_string,
|
||||
$encryptedMessage_template
|
||||
);
|
||||
try {
|
||||
$literalMessage = OpenPGP::decryptMessage($encryptedMessage, passwords: [$key]);
|
||||
$decrypted = $literalMessage->getLiteralData()->getData();
|
||||
print "Pg decrypted PHP: " . $decrypted . "<br>";
|
||||
if ($decrypted == $text_string) {
|
||||
print "Decryption worked<br>";
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
print "Error decrypting message: " . $e->getMessage() . "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
// __END__
|
||||
@@ -76,41 +76,41 @@ $db->dbResetEncoding();
|
||||
|
||||
// empty calls, none of the below should fail
|
||||
//
|
||||
$db->dbGetCursor();
|
||||
$foo = $db->dbGetCursor();
|
||||
//
|
||||
$db->dbGetCursorExt();
|
||||
$foo = $db->dbGetCursorExt();
|
||||
//
|
||||
$db->dbGetCursorPos('SELECT foo', ['bar']);
|
||||
$foo = $db->dbGetCursorPos('SELECT foo', ['bar']);
|
||||
//
|
||||
$db->dbGetCursorNumRows('SELECT foo', ['bar']);
|
||||
$foo = $db->dbGetCursorNumRows('SELECT foo', ['bar']);
|
||||
//
|
||||
$db->dbGetInsertPKName();
|
||||
$foo = $db->dbGetInsertPKName();
|
||||
//
|
||||
$db->dbGetInsertPK();
|
||||
$foo = $db->dbGetInsertPK();
|
||||
//
|
||||
$db->dbGetReturningExt();
|
||||
$db->dbGetReturningExt('foo');
|
||||
$db->dbGetReturningExt('foo', 0);
|
||||
$db->dbGetReturningExt(pos:0);
|
||||
$foo = $db->dbGetReturningExt();
|
||||
$foo = $db->dbGetReturningExt('foo');
|
||||
$foo = $db->dbGetReturningExt('foo', 0);
|
||||
$foo = $db->dbGetReturningExt(pos:0);
|
||||
//
|
||||
$db->dbGetReturningArray();
|
||||
$foo = $db->dbGetReturningArray();
|
||||
//
|
||||
$db->dbGetNumRows();
|
||||
$foo = $db->dbGetNumRows();
|
||||
//
|
||||
$db->dbGetNumFields();
|
||||
$foo = $db->dbGetNumFields();
|
||||
//
|
||||
$db->dbGetFieldNames();
|
||||
$foo = $db->dbGetFieldNames();
|
||||
//
|
||||
$db->dbGetFieldTypes();
|
||||
$foo = $db->dbGetFieldTypes();
|
||||
//
|
||||
$db->dbGetFieldNameTypes();
|
||||
$foo = $db->dbGetFieldNameTypes();
|
||||
//
|
||||
$db->dbGetFieldName(0);
|
||||
$foo = $db->dbGetFieldName(0);
|
||||
//
|
||||
$db->dbGetFieldType(0);
|
||||
$db->dbGetFieldType('foo');
|
||||
$foo = $db->dbGetFieldType(0);
|
||||
$foo = $db->dbGetFieldType('foo');
|
||||
//
|
||||
$db->dbGetPrepareCursorValue('foo', 'bar');
|
||||
$foo = $db->dbGetPrepareCursorValue('foo', 'bar');
|
||||
|
||||
// TEST CACHE READS
|
||||
|
||||
@@ -707,6 +707,17 @@ if (
|
||||
} else {
|
||||
print "[PGB] [3] pgb_sel_test_foo prepare OK<br>";
|
||||
}
|
||||
$stm_status = $db->dbPreparedCursorStatus('');
|
||||
print "[PGB] Empty statement name: " . $log->prAr($stm_status) . "<br>";
|
||||
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foobar');
|
||||
print "[PGB] Prepared name not match status: $stm_status<br>";
|
||||
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo');
|
||||
print "[PGB] Prepared name match status: $stm_status<br>";
|
||||
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo', $q_prep);
|
||||
print "[PGB] prepared exists and query match status: $stm_status<br>";
|
||||
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo', "SELECT * FROM test_foo");
|
||||
print "[PGB] prepared exists and query not match status: $stm_status<br>";
|
||||
|
||||
$db_pgb->dbClose();
|
||||
|
||||
# db write class test
|
||||
|
||||
@@ -54,7 +54,7 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
|
||||
print "NO DB HANDLER<br>";
|
||||
}
|
||||
// REGEX for placeholder count
|
||||
print "Placeholder regex: <pre>" . CoreLibs\DB\Support\ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS . "</pre>";
|
||||
print "Placeholder lookup regex: <pre>" . CoreLibs\DB\Support\ConvertPlaceholder::REGEX_LOOKUP_NUMBERED . "</pre>";
|
||||
|
||||
// turn on debug replace for placeholders
|
||||
$db->dbSetDebugReplacePlaceholder(true);
|
||||
@@ -148,6 +148,7 @@ RETURNING
|
||||
bigint_a, number_real, number_double, numeric_3,
|
||||
uuid_var
|
||||
SQL;
|
||||
print "Placeholders: <pre>" . print_r($db->dbGetQueryParamPlaceholders($query_insert), true) . "<pre>";
|
||||
$status = $db->dbExecParams($query_insert, $query_params);
|
||||
echo "<b>*</b><br>";
|
||||
echo "INSERT ALL COLUMN TYPES: "
|
||||
@@ -326,6 +327,7 @@ SQL,
|
||||
) {
|
||||
print "RES: " . Support::prAr($res) . "<br>";
|
||||
}
|
||||
print "PL: " . Support::PrAr($db->dbGetPlaceholderConverted()) . "<br>";
|
||||
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
@@ -68,6 +68,14 @@ function test2(): array
|
||||
return DebugSupport::getCallerMethodList(1);
|
||||
}
|
||||
|
||||
// date stueff
|
||||
print "printTime(-1): " . DebugSupport::printTime() . "<br>";
|
||||
print "printTime(2): " . DebugSupport::printTime(2) . "<br>";
|
||||
print "printTime(3): " . DebugSupport::printTime(3) . "<br>";
|
||||
print "printTime(5): " . DebugSupport::printTime(5) . "<br>";
|
||||
print "printIsoTime(): " . DebugSupport::printIsoTime() . "<br>";
|
||||
print "printIsoTime(false): " . DebugSupport::printIsoTime(false) . "<br>";
|
||||
|
||||
print "S::GETCALLERMETHOD: " . DebugSupport::getCallerMethod(0) . "<br>";
|
||||
print "S::GETCALLERMETHOD: " . test() . "<br>";
|
||||
print "S::GETCALLERMETHODLIST: <pre>" . print_r(test2(), true) . "</pre><br>";
|
||||
@@ -146,7 +154,7 @@ print "LOG LEVEL: " . DebugSupport::printAr(\CoreLibs\Convert\SetVarType::setAr
|
||||
$new_log->getLogLevel('debug', 'on')
|
||||
)) . "<br>";
|
||||
|
||||
echo "<b>CLASS DEBUG CALL</b><br>";
|
||||
echo "<b>CLASS DEBUG CALL LEGACY</b><br>";
|
||||
|
||||
// @codingStandardsIgnoreLine
|
||||
class TestL
|
||||
|
||||
110
www/admin/class_test.deprecated.helper.php
Normal file
110
www/admin/class_test.deprecated.helper.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php // phpcs:ignore warning
|
||||
|
||||
/**
|
||||
* @phan-file-suppress PhanTypeSuspiciousStringExpression
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
|
||||
|
||||
ob_start();
|
||||
|
||||
// basic class test file
|
||||
define('USE_DATABASE', false);
|
||||
// sample config
|
||||
require 'config.php';
|
||||
// define log file id
|
||||
$LOG_FILE_ID = 'classTest-phpv';
|
||||
ob_end_flush();
|
||||
|
||||
$log = new CoreLibs\Logging\Logging([
|
||||
'log_folder' => BASE . LOG,
|
||||
'log_file_id' => $LOG_FILE_ID,
|
||||
'log_per_date' => true,
|
||||
]);
|
||||
$_phpv = new CoreLibs\Check\PhpVersion();
|
||||
$phpv_class = 'CoreLibs\Check\PhpVersion';
|
||||
|
||||
$PAGE_NAME = 'TEST CLASS: PHP VERSION';
|
||||
print "<!DOCTYPE html>";
|
||||
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
|
||||
print "<body>";
|
||||
print '<div><a href="class_test.php">Class Test Master</a></div>';
|
||||
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
||||
|
||||
// fputcsv
|
||||
print "<h3>\CoreLibs\DeprecatedHelper\Deprecated84::fputcsv()</h3>";
|
||||
$test_csv = BASE . TMP . 'DeprecatedHelper.test.csv';
|
||||
print "File: $test_csv<br>";
|
||||
|
||||
$fp = fopen($test_csv, "w");
|
||||
if (!is_resource($fp)) {
|
||||
die("Cannot open file: $test_csv");
|
||||
}
|
||||
\CoreLibs\DeprecatedHelper\Deprecated84::fputcsv($fp, ["A", "B", "C"]);
|
||||
fclose($fp);
|
||||
|
||||
$fp = fopen($test_csv, "r");
|
||||
if (!is_resource($fp)) {
|
||||
die("Cannot open file: $test_csv");
|
||||
}
|
||||
while ($entry = \CoreLibs\DeprecatedHelper\Deprecated84::fgetcsv($fp)) {
|
||||
print "fgetcsv: <pre>" . print_r($entry, true) . "</pre>";
|
||||
}
|
||||
fclose($fp);
|
||||
|
||||
$out = \CoreLibs\DeprecatedHelper\Deprecated84::str_getcsv("A,B,C");
|
||||
print "str_getcsv: <pre>" . print_r($out, true) . "</pre>";
|
||||
|
||||
/**
|
||||
* temporary different CSV function, because fgetcsv seems to be broken on some systems
|
||||
* (does not read out japanese text)
|
||||
*
|
||||
* @param string $string full line for csv split
|
||||
* @param string $encoding optional, if given, converts string to the internal encoding
|
||||
* before we do anything
|
||||
* @param string $delimiter sepperate character, default ','
|
||||
* @param string $enclosure string line marker, default '"'
|
||||
* @param string $flag INTERN | EXTERN. if INTERN uses the PHP function, else uses explode
|
||||
* @return array<int,string|null> array with split data from input line
|
||||
*/
|
||||
function mtParseCSV(
|
||||
string $string,
|
||||
string $encoding = '',
|
||||
string $delimiter = ',',
|
||||
string $enclosure = '"',
|
||||
string $flag = 'INTERN'
|
||||
): array {
|
||||
$lines = [];
|
||||
if ($encoding) {
|
||||
$string = \CoreLibs\Convert\Encoding::convertEncoding(
|
||||
$string,
|
||||
'UTF-8',
|
||||
$encoding
|
||||
);
|
||||
if ($string === false) {
|
||||
return $lines;
|
||||
}
|
||||
}
|
||||
if ($flag == 'INTERN') {
|
||||
// split with PHP function
|
||||
$lines = str_getcsv($string, $delimiter, $enclosure);
|
||||
} else {
|
||||
// split up with delimiter
|
||||
$lines = explode(',', $string) ?: [];
|
||||
}
|
||||
// strip " from beginning and end of line
|
||||
for ($i = 0; $i < count($lines); $i++) {
|
||||
// remove line breaks
|
||||
$lines[$i] = preg_replace("/\r\n?/", '', (string)$lines[$i]) ?? '';
|
||||
// lingering " at the beginning and end of the line
|
||||
$lines[$i] = preg_replace("/^\"/", '', (string)$lines[$i]) ?? '';
|
||||
$lines[$i] = preg_replace("/\"$/", '', (string)$lines[$i]) ?? '';
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
// __END__
|
||||
@@ -40,6 +40,8 @@ print "Log ERROR: " . $log->prAr($em->getFlagLogError()) . "<br>";
|
||||
print "FN: " . ml::fromName('Affe')->name . "<br>";
|
||||
print "NU: " . ml::fromValue(100)->name . "<br>";
|
||||
print "NU: " . ml::fromValue(1000)->name . "<br>";
|
||||
print "OK.: " . ml::ok->name . "<br>";
|
||||
print "OK^: " . ml::fromName('OK')->name . "<br>";
|
||||
|
||||
$em->setErrorMsg('123', 'error', 'msg this is bad, auto logged if debug');
|
||||
$em->setErrorMsg('123', 'error', 'msg this is bad, auto logged if debug', 'target-id', 'other-style');
|
||||
@@ -56,6 +58,14 @@ $em->setErrorMsg('100-2', 'error', 'Input wring', jump_target:['target' => 'foo-
|
||||
$em->setMessage('error', 'I have no id set', jump_target:['target' => 'bar-123', 'info' => 'Jump Bar']);
|
||||
$em->setMessage('error', 'Jump empty', jump_target:['target' => 'bar-empty']);
|
||||
|
||||
function inLine(\CoreLibs\Logging\ErrorMessage $em): void
|
||||
{
|
||||
$em->log->error('Direct log before from ', context:['function' => __FUNCTION__]);
|
||||
$em->setMessage('error', 'Inline call', context:['test' => 'inLine Function']);
|
||||
$em->log->error('Direct log from ', context:['function' => __FUNCTION__]);
|
||||
}
|
||||
inLine($em);
|
||||
|
||||
print "ErrorsLast: <pre>" . $log->prAr($em->getLastErrorMsg()) . "</pre>";
|
||||
print "ErrorsIds: <pre>" . $log->prAr($em->getErrorIds()) . "</pre>";
|
||||
print "Errors: <pre>" . $log->prAr($em->getErrorMsg()) . "</pre>";
|
||||
|
||||
@@ -19,6 +19,7 @@ $LOG_FILE_ID = 'classTest-hash';
|
||||
ob_end_flush();
|
||||
|
||||
use CoreLibs\Create\Hash;
|
||||
use CoreLibs\Security\CreateKey;
|
||||
|
||||
$log = new CoreLibs\Logging\Logging([
|
||||
'log_folder' => BASE . LOG,
|
||||
@@ -38,28 +39,66 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
||||
|
||||
$to_crc = 'Some text block';
|
||||
// static
|
||||
print "S::__CRC32B: $to_crc: " . $hash_class::__crc32b($to_crc) . "<br>";
|
||||
print "S::__SHA1SHORT(off): $to_crc: " . $hash_class::__sha1short($to_crc) . "<br>";
|
||||
print "S::__SHA1SHORT(on): $to_crc: " . $hash_class::__sha1short($to_crc, true) . "<br>";
|
||||
print "S::__hash(d): " . $to_crc . "/"
|
||||
. Hash::STANDARD_HASH_SHORT . ": " . $hash_class::__hash($to_crc) . "<br>";
|
||||
foreach (['adler32', 'fnv132', 'fnv1a32', 'joaat', 'sha512'] as $__hash_c) {
|
||||
print "S::__hash($__hash_c): $to_crc: " . $hash_class::__hash($to_crc, $__hash_c) . "<br>";
|
||||
print "S::__CRC32B: $to_crc: " . Hash::__crc32b($to_crc) . "<br>";
|
||||
// print "S::__SHA1SHORT(off): $to_crc: " . Hash::__sha1short($to_crc) . "<br>";
|
||||
print "S::hashShort(__sha1Short replace): $to_crc: " . Hash::hashShort($to_crc) . "<br>";
|
||||
// print "S::__SHA1SHORT(on): $to_crc: " . Hash::__sha1short($to_crc, true) . "<br>";
|
||||
print "S::sha1Short(__sha1Short replace): $to_crc: " . Hash::sha1Short($to_crc) . "<br>";
|
||||
// print "S::__hash(d): " . $to_crc . "/"
|
||||
// . Hash::STANDARD_HASH_SHORT . ": " . $hash_class::__hash($to_crc) . "<br>";
|
||||
$to_crc_list = [
|
||||
'Some text block',
|
||||
'Some String Text',
|
||||
'any string',
|
||||
];
|
||||
foreach ($to_crc_list as $__to_crc) {
|
||||
foreach (['adler32', 'fnv132', 'fnv1a32', 'joaat', 'ripemd160', 'sha256', 'sha512'] as $__hash_c) {
|
||||
print "Hash::hash($__hash_c): $__to_crc: " . Hash::hash($to_crc, $__hash_c) . "<br>";
|
||||
}
|
||||
}
|
||||
// static use
|
||||
print "U-S::__CRC32B: $to_crc: " . Hash::__crc32b($to_crc) . "<br>";
|
||||
|
||||
echo "<hr>";
|
||||
$text = 'Some String Text';
|
||||
// $text = 'any string';
|
||||
$type = 'crc32b';
|
||||
print "Hash: " . $type . ": " . hash($type, $text) . "<br>";
|
||||
print "Class: " . $type . ": " . Hash::__hash($text, $type) . "<br>";
|
||||
// print "Class (old): " . $type . ": " . Hash::__hash($text, $type) . "<br>";
|
||||
print "Class (new): " . $type . ": " . Hash::hash($text, $type) . "<br>";
|
||||
|
||||
echo "<hr>";
|
||||
print "<br>CURRENT STANDARD_HASH_SHORT: " . Hash::STANDARD_HASH_SHORT . "<br>";
|
||||
print "<br>CURRENT STANDARD_HASH_LONG: " . Hash::STANDARD_HASH_LONG . "<br>";
|
||||
print "HASH SHORT: " . $to_crc . ": " . Hash::__hash($to_crc) . "<br>";
|
||||
print "HASH LONG: " . $to_crc . ": " . Hash::__hashLong($to_crc) . "<br>";
|
||||
print "CURRENT STANDARD_HASH_SHORT: " . Hash::STANDARD_HASH_SHORT . "<br>";
|
||||
print "CURRENT STANDARD_HASH_LONG: " . Hash::STANDARD_HASH_LONG . "<br>";
|
||||
print "CURRENT STANDARD_HASH: " . Hash::STANDARD_HASH . "<br>";
|
||||
print "HASH SHORT: " . $to_crc . ": " . Hash::hashShort($to_crc) . "<br>";
|
||||
print "HASH LONG: " . $to_crc . ": " . Hash::hashLong($to_crc) . "<br>";
|
||||
print "HASH DEFAULT: " . $to_crc . ": " . Hash::hashStd($to_crc) . "<br>";
|
||||
|
||||
echo "<hr>";
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$key = "FIX KEY";
|
||||
print "Secret Key: " . $key . "<br>";
|
||||
print "HASHMAC DEFAULT (fix): " . $to_crc . ": " . Hash::hashHmac($to_crc, $key) . "<br>";
|
||||
$key = CreateKey::generateRandomKey();
|
||||
print "Secret Key: " . $key . "<br>";
|
||||
print "HASHMAC DEFAULT (random): " . $to_crc . ": " . Hash::hashHmac($to_crc, $key) . "<br>";
|
||||
|
||||
echo "<hr>";
|
||||
$hash_types = ['crc32b', 'sha256', 'invalid'];
|
||||
foreach ($hash_types as $hash_type) {
|
||||
echo "<b>Checking $hash_type:</b><br>";
|
||||
if (Hash::isValidHashType($hash_type)) {
|
||||
echo "hash type: $hash_type is valid<br>";
|
||||
} else {
|
||||
echo "hash type: $hash_type is INVALID<br>";
|
||||
}
|
||||
if (Hash::isValidHashHmacType($hash_type)) {
|
||||
echo "hash hmac type: $hash_type is valid<br>";
|
||||
} else {
|
||||
echo "hash hmac type: $hash_type is INVALID<br>";
|
||||
}
|
||||
}
|
||||
|
||||
// print "UNIQU ID SHORT : " . Hash::__uniqId() . "<br>";
|
||||
// print "UNIQU ID LONG : " . Hash::__uniqIdLong() . "<br>";
|
||||
|
||||
@@ -46,6 +46,7 @@ $json = '["f: {b"""ar}]';
|
||||
$output = Json::jsonConvertToArray($json);
|
||||
print "S::E-JSON: $json: " . DgS::printAr($output) . "<br>";
|
||||
print "S::E-JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
|
||||
print "S::E Validate: " . Json::jsonValidate($json) . ": " . Json::jsonGetLastError(true) . "<br>";
|
||||
|
||||
// direct
|
||||
$json = '{"direct": "static function call"}';
|
||||
@@ -58,6 +59,24 @@ $output = $json_class::jsonConvertToArray($json);
|
||||
print "J/S::E-JSON: $json: " . DgS::printAr($output) . "<br>";
|
||||
print "J/S::E-JSON ERROR: " . $json_class::jsonGetLastError() . ": " . $json_class::jsonGetLastError(true) . "<br>";
|
||||
|
||||
echo "<hr>";
|
||||
$json = '{"valid":"json","invalid":"\xB1\x31"}';
|
||||
$json = '{"valid":"json","invalid":"abc\x80def"}';
|
||||
$output_no_flag = Json::jsonConvertToArray($json);
|
||||
print "No Flag JSON: $json: " . DgS::printAr($output_no_flag) . "<br>";
|
||||
print "No Flag JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
|
||||
$output_flag = Json::jsonConvertToArray($json, flags:JSON_INVALID_UTF8_IGNORE);
|
||||
print "No Flag JSON: $json: " . DgS::printAr($output_flag) . "<br>";
|
||||
print "No Flag JSON ERROR: " . Json::jsonGetLastError() . ": " . Json::jsonGetLastError(true) . "<br>";
|
||||
$output_raw = json_decode($json, true, flags:JSON_INVALID_UTF8_IGNORE);
|
||||
print "No Flag JSON RAW (F-1): $json: " . DgS::printAr($output_raw) . "<br>";
|
||||
$output_raw = json_decode($json, true, flags:JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
print "No Flag JSON RAW (F-2): $json: " . DgS::printAr($output_raw) . "<br>";
|
||||
$output_raw = json_decode($json, true);
|
||||
print "No Flag JSON RAW: $json: " . DgS::printAr($output_raw) . "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
|
||||
// $json = '{"foo": "bar"}';
|
||||
// $output = Jason::jsonConvertToArray($json);
|
||||
// print "S::JSON: $json: " . DgS::printAr($output) . "<br>";
|
||||
@@ -67,6 +86,8 @@ print "J/S::E-JSON ERROR: " . $json_class::jsonGetLastError() . ": " . $json_cla
|
||||
$array = ['foo' => 'bar'];
|
||||
$output = Json::jsonConvertArrayTo($array);
|
||||
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>";
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ $log->error('Cannot process data', ['error' => 'log']);
|
||||
$log->critical('Critical message', ['critical' => 'log']);
|
||||
$log->alert('Alert message', ['Alert' => 'log']);
|
||||
$log->emergency('Emergency message', ['Emergency' => 'log']);
|
||||
error_log('TRIGGER ERROR LOG MANUAL: Emergency');
|
||||
print "Log File: " . $log->getLogFile() . "<br>";
|
||||
|
||||
$log->setLogFlag(Flag::per_run);
|
||||
@@ -120,6 +121,12 @@ Class TestP
|
||||
public function test(): void
|
||||
{
|
||||
$this->log->info('TestL::test call');
|
||||
$this->subCall();
|
||||
}
|
||||
|
||||
public function subCall(): void
|
||||
{
|
||||
$this->log->info('TestL::sub_call call');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ $log = new CoreLibs\Logging\Logging([
|
||||
'log_per_date' => true,
|
||||
]);
|
||||
$db = new CoreLibs\DB\IO(DB_CONFIG, $log);
|
||||
$log->setLogFileId('classTest-login-override');
|
||||
$login = new CoreLibs\ACL\Login(
|
||||
$db,
|
||||
$log,
|
||||
@@ -45,6 +46,7 @@ $login = new CoreLibs\ACL\Login(
|
||||
'locale_path' => BASE . INCLUDES . LOCALE,
|
||||
]
|
||||
);
|
||||
$log->setLogFileId($LOG_FILE_ID);
|
||||
ob_end_flush();
|
||||
$login->loginMainCall();
|
||||
|
||||
@@ -117,7 +119,7 @@ if (isset($login->loginGetAcl()['unit'])) {
|
||||
if ($login->loginCheckEditAccessCuid($edit_access_cuid)) {
|
||||
print "Set new:" . $edit_access_cuid . "<br>";
|
||||
} else {
|
||||
print "Load default unit id: " . $login->loginGetAcl()['unit_id'] . "<br>";
|
||||
print "Load default unit id: " . $login->loginGetAcl()['unit_cuid'] . "<br>";
|
||||
}
|
||||
} else {
|
||||
print "Something went wrong with the login<br>";
|
||||
@@ -127,6 +129,12 @@ if (isset($login->loginGetAcl()['unit'])) {
|
||||
// IP check: 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'CLIENT_IP' in _SERVER
|
||||
// Agent check: 'HTTP_USER_AGENT'
|
||||
|
||||
print "<hr>";
|
||||
print "PAGE lookup:<br>";
|
||||
$file_name = 'test_edit_base.php';
|
||||
print "Access to '$file_name': " . $log->prAr($login->loginPageAccessAllowed($file_name)) . "<br>";
|
||||
$file_name = 'i_do_not_exists.php';
|
||||
print "Access to '$file_name': " . $log->prAr($login->loginPageAccessAllowed($file_name)) . "<br>";
|
||||
|
||||
echo "<hr>";
|
||||
print "SESSION: " . Support::printAr($_SESSION) . "<br>";
|
||||
@@ -140,4 +148,18 @@ $login->writeLog(
|
||||
write_type:'JSON'
|
||||
);
|
||||
|
||||
echo "<hr>";
|
||||
print "<h3>Legacy Lookups</h3>";
|
||||
|
||||
$edit_access_id = 1;
|
||||
$edit_access_cuid = $login->loginGetEditAccessCuidFromId($edit_access_id);
|
||||
$edit_access_id_rev = null;
|
||||
if (is_string($edit_access_cuid)) {
|
||||
$edit_access_id_rev = $login->loginGetEditAccessIdFromCuid($edit_access_cuid);
|
||||
}
|
||||
print "EA ID: " . $edit_access_id . "<br>";
|
||||
print "EA CUID: " . $log->prAr($edit_access_cuid) . "<br>";
|
||||
print "REV EA CUID: " . $log->prAr($edit_access_id_rev) . "<br>";
|
||||
$log->info('This is a test');
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
@@ -95,6 +95,7 @@ $test_files = [
|
||||
'class_test.db.dbReturn.php' => 'Class Test: DB dbReturn',
|
||||
'class_test.db.single.php' => 'Class Test: DB single query tests',
|
||||
'class_test.db.convert-placeholder.php' => 'Class Test: DB convert placeholder',
|
||||
'class_test.db.encryption.php' => 'Class Test: DB pgcrypto',
|
||||
'class_test.convert.colors.php' => 'Class Test: CONVERT COLORS',
|
||||
'class_test.check.colors.php' => 'Class Test: CHECK COLORS',
|
||||
'class_test.mime.php' => 'Class Test: MIME',
|
||||
@@ -141,6 +142,7 @@ $test_files = [
|
||||
'class_test.error_msg.php' => 'Class Test: ERROR MSG',
|
||||
'class_test.url-requests.curl.php' => 'Class Test: URL REQUESTS: CURL',
|
||||
'subfolder/class_test.config.direct.php' => 'Class Test: CONFIG DIRECT SUB',
|
||||
'class_test.deprecated.helper.php' => 'Class Test: DEPRECATED HELPERS',
|
||||
];
|
||||
|
||||
asort($test_files);
|
||||
|
||||
@@ -28,8 +28,6 @@ $log = new CoreLibs\Logging\Logging([
|
||||
$_phpv = new CoreLibs\Check\PhpVersion();
|
||||
$phpv_class = 'CoreLibs\Check\PhpVersion';
|
||||
|
||||
// define a list of from to color sets for conversion test
|
||||
|
||||
$PAGE_NAME = 'TEST CLASS: PHP VERSION';
|
||||
print "<!DOCTYPE html>";
|
||||
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
|
||||
|
||||
@@ -34,17 +34,40 @@ print "<body>";
|
||||
print '<div><a href="class_test.php">Class Test Master</a></div>';
|
||||
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
||||
|
||||
$output = RandomKey::randomKeyGen(
|
||||
12,
|
||||
[
|
||||
0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K',
|
||||
11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
|
||||
21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e',
|
||||
31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o',
|
||||
41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', 49 => 'x', 50 => 'y',
|
||||
51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8',
|
||||
61 => '9'
|
||||
]
|
||||
);
|
||||
print "CUSTOM OUTPUT: " . $output . "<br>";
|
||||
|
||||
$key_length = 10;
|
||||
$key_length_b = 5;
|
||||
$key_lenght_long = 64;
|
||||
print "S::RANDOMKEYGEN(auto): " . RandomKey::randomKeyGen() . "<br>";
|
||||
print "S::SETRANDOMKEYLENGTH($key_length): " . RandomKey::setRandomKeyLength($key_length) . "<br>";
|
||||
print "S::RANDOMKEYGEN($key_length): " . RandomKey::randomKeyGen() . "<br>";
|
||||
// print "S::SETRANDOMKEYLENGTH($key_length): " . RandomKey::setRandomKeyLength($key_length) . "<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): " . 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, 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();
|
||||
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>";
|
||||
|
||||
|
||||
@@ -86,8 +86,10 @@ if (!isset($_SESSION['counter'])) {
|
||||
$_SESSION['counter']++;
|
||||
print "[READ] A " . $var . ": " . ($_SESSION[$var] ?? '{UNSET}') . "<br>";
|
||||
$_SESSION[$var] = $value;
|
||||
/** @phpstan-ignore-next-line nullCoalesce.offset */
|
||||
print "[READ] B " . $var . ": " . ($_SESSION[$var] ?? '{UNSET}') . "<br>";
|
||||
print "[READ] Confirm " . $var . " is " . $value . ": "
|
||||
/** @phpstan-ignore-next-line equal.alwaysTrue, nullCoalesce.offset */
|
||||
. (($_SESSION[$var] ?? '') == $value ? 'Matching' : 'Not matching') . "<br>";
|
||||
|
||||
// test set wrappers methods
|
||||
|
||||
@@ -14,6 +14,9 @@ require 'config.php';
|
||||
$LOG_FILE_ID = 'classTest-string';
|
||||
ob_end_flush();
|
||||
|
||||
use CoreLibs\Convert\Strings;
|
||||
use CoreLibs\Debug\Support as DgS;
|
||||
|
||||
$log = new CoreLibs\Logging\Logging([
|
||||
'log_folder' => BASE . LOG,
|
||||
'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>';
|
||||
|
||||
$split = '4-4-4';
|
||||
$split_length = 4;
|
||||
$test_strings = [
|
||||
'13',
|
||||
'1234',
|
||||
@@ -40,20 +44,59 @@ $test_strings = [
|
||||
];
|
||||
|
||||
foreach ($test_strings as $string) {
|
||||
print "Convert: $string with $split to: "
|
||||
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
|
||||
print "A) Convert: $string with $split to: "
|
||||
. Strings::splitFormatString($string, $split)
|
||||
. "<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_length = 2;
|
||||
$string = '1234';
|
||||
print "Convert: $string with $split to: "
|
||||
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
|
||||
print "A) Convert: $string with $split to: "
|
||||
. Strings::splitFormatString($string, $split)
|
||||
. "<br>";
|
||||
print "B) Convert: $string with $split_length to: "
|
||||
. Strings::splitFormatStringFixed($string, $split_length, "_")
|
||||
. "<br>";
|
||||
$split = '2-2';
|
||||
$string = 'あいうえ';
|
||||
print "Convert: $string with $split to: "
|
||||
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
|
||||
try {
|
||||
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>";
|
||||
|
||||
$test_splits = [
|
||||
@@ -63,7 +106,70 @@ $test_splits = [
|
||||
'2-3-4',
|
||||
];
|
||||
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>";
|
||||
|
||||
@@ -56,6 +56,8 @@ print "UNIQU ID LONG : " . Uids::uniqIdLong() . "<br>";
|
||||
$uuidv4 = Uids::uuidv4();
|
||||
if (!Uids::validateUuuidv4($uuidv4)) {
|
||||
print "Invalid UUIDv4: " . $uuidv4 . "<br>";
|
||||
} else {
|
||||
print "Valid UUIDv4: " . $uuidv4 . "<br>";
|
||||
}
|
||||
if (!Uids::validateUuuidv4("foobar")) {
|
||||
print "Invalid UUIDv4: hard coded<Br>";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* general edit javascript */
|
||||
/* jquery version */
|
||||
/*
|
||||
general edit javascript
|
||||
jquery version
|
||||
*/
|
||||
|
||||
/* jshint esversion: 11 */
|
||||
/** @deprecated Do not use this anymore, use utils.js or utils.min.js */
|
||||
|
||||
/* global i18n */
|
||||
|
||||
@@ -18,11 +20,28 @@ if (!DEBUG) {
|
||||
var GL_OB_S = 100;
|
||||
var GL_OB_BASE = 100;
|
||||
|
||||
/**
|
||||
* Gets html element or throws an error
|
||||
* @param {string} el_id Element ID to get
|
||||
* @returns {HTMLElement}
|
||||
* @throws Error
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function loadEl(el_id)
|
||||
{
|
||||
let el = document.getElementById(el_id);
|
||||
if (el === null) {
|
||||
throw new Error('Cannot find: ' + el_id);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* opens a popup window with winName and given features (string)
|
||||
* @param {String} theURL the url
|
||||
* @param {String} winName window name
|
||||
* @param {Object} features popup features
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function pop(theURL, winName, features) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -33,6 +52,7 @@ function pop(theURL, winName, features) // eslint-disable-line no-unused-vars
|
||||
/**
|
||||
* automatically resize a text area based on the amount of lines in it
|
||||
* @param {string} ta_id element id
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function expandTA(ta_id) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -58,6 +78,7 @@ function expandTA(ta_id) // eslint-disable-line no-unused-vars
|
||||
/**
|
||||
* wrapper to get the real window size for the current browser window
|
||||
* @return {Object} object with width/height
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getWindowSize()
|
||||
{
|
||||
@@ -73,6 +94,7 @@ function getWindowSize()
|
||||
/**
|
||||
* wrapper to get the correct scroll offset
|
||||
* @return {Object} object with x/y px
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getScrollOffset()
|
||||
{
|
||||
@@ -88,6 +110,7 @@ function getScrollOffset()
|
||||
/**
|
||||
* wrapper to get the correct scroll offset for opener page (from popup)
|
||||
* @return {Object} object with x/y px
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getScrollOffsetOpener() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -105,13 +128,14 @@ function getScrollOffsetOpener() // eslint-disable-line no-unused-vars
|
||||
* @param {String} id element to center
|
||||
* @param {Boolean} left if true centers to the middle from the left
|
||||
* @param {Boolean} top if true centers to the middle from the top
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function setCenter(id, left, top)
|
||||
{
|
||||
// get size of id
|
||||
var dimensions = {
|
||||
height: $('#' + id).height(),
|
||||
width: $('#' + id).width()
|
||||
height: $('#' + id).height() ?? 0,
|
||||
width: $('#' + id).width() ?? 0
|
||||
};
|
||||
var type = $('#' + id).css('position');
|
||||
var viewport = getWindowSize();
|
||||
@@ -122,14 +146,14 @@ function setCenter(id, left, top)
|
||||
// console.log('Left: %s, Top: %s (%s)', parseInt((viewport.width / 2) - (dimensions.width / 2) + offset.left), parseInt((viewport.height / 2) - (dimensions.height / 2) + offset.top), parseInt((viewport.height / 2) - (dimensions.height / 2)));
|
||||
if (left) {
|
||||
$('#' + id).css({
|
||||
left: parseInt((viewport.width / 2) - (dimensions.width / 2) + offset.left) + 'px'
|
||||
left: ((viewport.width / 2) - (dimensions.width / 2) + offset.left) + 'px'
|
||||
});
|
||||
}
|
||||
if (top) {
|
||||
// if we have fixed, we do not add the offset, else it moves out of the screen
|
||||
var top_pos = type == 'fixed' ?
|
||||
parseInt((viewport.height / 2) - (dimensions.height / 2)) :
|
||||
parseInt((viewport.height / 2) - (dimensions.height / 2) + offset.top);
|
||||
(viewport.height / 2) - (dimensions.height / 2) :
|
||||
(viewport.height / 2) - (dimensions.height / 2) + offset.top;
|
||||
$('#' + id).css({
|
||||
top: top_pos + 'px'
|
||||
});
|
||||
@@ -142,6 +166,7 @@ function setCenter(id, left, top)
|
||||
* @param {Number} [offset=0] offset from top, default is 0 (px)
|
||||
* @param {Number} [duration=500] animation time, default 500ms
|
||||
* @param {String} [base='body,html'] base element for offset scroll
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function goToPos(element, offset = 0, duration = 500, base = 'body,html') // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -156,11 +181,25 @@ function goToPos(element, offset = 0, duration = 500, base = 'body,html') // esl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* go to element, scroll
|
||||
* non jquery
|
||||
* @param {string} target
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function goTo(target) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
loadEl(target).scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* uses the i18n object created in the translation template
|
||||
* that is filled from gettext in PHP
|
||||
* @param {String} string text to translate
|
||||
* @return {String} translated text (based on PHP selected language)
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function __(string)
|
||||
{
|
||||
@@ -177,37 +216,70 @@ function __(string)
|
||||
* First, checks if it isn't implemented yet.
|
||||
* @param {String} String.prototype.format string with elements to be replaced
|
||||
* @return {String} Formated string
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
if (!String.prototype.format) {
|
||||
String.prototype.format = function()
|
||||
{
|
||||
var args = arguments;
|
||||
return this.replace(/{(\d+)}/g, function(match, number)
|
||||
{
|
||||
return typeof args[number] != 'undefined' ?
|
||||
args[number] :
|
||||
match
|
||||
;
|
||||
});
|
||||
console.error('[DEPRECATED] use formatString');
|
||||
return formatString(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* simple sprintf formater for replace
|
||||
* usage: "{0} is cool, {1} is not".format("Alpha", "Beta");
|
||||
* First, checks if it isn't implemented yet.
|
||||
* @param {String} string String with {..} entries
|
||||
* @param {...any} args List of replacement
|
||||
* @returns {String} Escaped string
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function formatString(string, ...args)
|
||||
{
|
||||
return string.replace(/{(\d+)}/g, function(match, number)
|
||||
{
|
||||
return typeof args[number] != 'undefined' ?
|
||||
args[number] :
|
||||
match
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* round to digits (float)
|
||||
* @param {Number} Number.prototype.round Float type number to round
|
||||
* @param {Number} prec Precision to round to
|
||||
* @return {Float} Rounded number
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
if (Number.prototype.round) {
|
||||
Number.prototype.round = function (prec) {
|
||||
console.error('[DEPRECATED] use roundPrecision');
|
||||
return roundPrecision(this, prec);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* round to digits (float)
|
||||
* @param {Float} Number.prototype.round Float type number to round
|
||||
* @param {Number} prec Precision to round to
|
||||
* @return {Float} Rounded number
|
||||
* @param {Number} number Float type number to round
|
||||
* @param {Number} precision Precision to round to
|
||||
* @return {Number} Rounded number
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
if (Number.prototype.round) {
|
||||
Number.prototype.round = function (prec) {
|
||||
return Math.round(this * Math.pow(10, prec)) / Math.pow(10, prec);
|
||||
};
|
||||
function roundPrecision(number, precision)
|
||||
{
|
||||
if (!isNaN(number) || !isNaN(precision)) {
|
||||
return number;
|
||||
}
|
||||
return Math.round(number * Math.pow(10, precision)) / Math.pow(10, precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* formats flat number 123456 to 123,456
|
||||
* @param {Number} x number to be formated
|
||||
* @return {String} formatted with , in thousands
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function numberWithCommas(x) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -220,6 +292,7 @@ function numberWithCommas(x) // eslint-disable-line no-unused-vars
|
||||
* converts line breaks to br
|
||||
* @param {String} string any string
|
||||
* @return {String} string with <br>
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function convertLBtoBR(string) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -228,51 +301,78 @@ function convertLBtoBR(string) // eslint-disable-line no-unused-vars
|
||||
|
||||
/**
|
||||
* escape HTML string
|
||||
* @param {String} !String.prototype.escapeHTML HTML data string to be escaped
|
||||
* @return {String} escaped string
|
||||
* @param {String} String.prototype.escapeHTML HTML data string to be escaped
|
||||
* @return {String} escaped string
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
if (!String.prototype.escapeHTML) {
|
||||
String.prototype.escapeHTML = function() {
|
||||
return this.replace(/[&<>"'/]/g, function (s) {
|
||||
var entityMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
'\'': ''',
|
||||
'/': '/'
|
||||
};
|
||||
|
||||
return entityMap[s];
|
||||
});
|
||||
console.error('[DEPRECATED] use escapeHtml');
|
||||
return escapeHtml(this);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* unescape a HTML encoded string
|
||||
* @param {String} !String.prototype.unescapeHTML data with escaped entries
|
||||
* @return {String} HTML formated string
|
||||
* @param {String} String.prototype.unescapeHTML data with escaped entries
|
||||
* @return {String} HTML formated string
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
if (!String.prototype.unescapeHTML) {
|
||||
String.prototype.unescapeHTML = function() {
|
||||
return this.replace(/&[#\w]+;/g, function (s) {
|
||||
var entityMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
''': '\'',
|
||||
'/': '/'
|
||||
};
|
||||
|
||||
return entityMap[s];
|
||||
});
|
||||
console.error('[DEPRECATED] use unescapeHtml');
|
||||
return unescapeHtml(this);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes HTML in string
|
||||
* @param {String} string Text to escape HTML in
|
||||
* @returns {String}
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function escapeHtml(string)
|
||||
{
|
||||
return string.replace(/[&<>"'/]/g, function (s) {
|
||||
var entityMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
'\'': ''',
|
||||
'/': '/'
|
||||
};
|
||||
|
||||
return entityMap[s];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescape a HTML encoded string
|
||||
* @param {String} string Text to unescape HTML in
|
||||
* @returns {String}
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function unescapeHtml(string)
|
||||
{
|
||||
return string.replace(/&[#\w]+;/g, function (s) {
|
||||
var entityMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
''': '\'',
|
||||
'/': '/'
|
||||
};
|
||||
|
||||
return entityMap[s];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* returns current timestamp (unix timestamp)
|
||||
* @return {Number} timestamp (in milliseconds)
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getTimestamp() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -285,6 +385,7 @@ function getTimestamp() // eslint-disable-line no-unused-vars
|
||||
* i.e. 0-255 -> '00'-'ff'
|
||||
* @param {Number} dec decimal string
|
||||
* @return {String} hex encdoded number
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function dec2hex(dec)
|
||||
{
|
||||
@@ -296,6 +397,7 @@ function dec2hex(dec)
|
||||
* only works on mondern browsers
|
||||
* @param {Number} len length of unique id string
|
||||
* @return {String} random string in length of len
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function generateId(len) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -309,6 +411,7 @@ function generateId(len) // eslint-disable-line no-unused-vars
|
||||
* works on all browsers
|
||||
* after many runs it will create duplicates
|
||||
* @return {String} not true random string
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function randomIdF() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -322,6 +425,7 @@ function randomIdF() // eslint-disable-line no-unused-vars
|
||||
* @param {Number} min minimum int number inclusive
|
||||
* @param {Number} max maximumg int number inclusive
|
||||
* @return {Number} Random number
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getRandomIntInclusive(min, max) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -335,6 +439,7 @@ function getRandomIntInclusive(min, max) // eslint-disable-line no-unused-vars
|
||||
* check if name is a function
|
||||
* @param {string} name Name of function to check if exists
|
||||
* @return {Boolean} true/false
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function isFunction(name) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -354,6 +459,7 @@ function isFunction(name) // eslint-disable-line no-unused-vars
|
||||
* @param {mixed} context context (window or first namespace)
|
||||
* hidden next are all the arguments
|
||||
* @return {mixed} Return values from functon
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function executeFunctionByName(functionName, context /*, args */) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -370,6 +476,7 @@ function executeFunctionByName(functionName, context /*, args */) // eslint-disa
|
||||
* checks if a variable is an object
|
||||
* @param {Mixed} val possible object
|
||||
* @return {Boolean} true/false if it is an object or not
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function isObject(val)
|
||||
{
|
||||
@@ -383,6 +490,7 @@ function isObject(val)
|
||||
* get the length of an object (entries)
|
||||
* @param {Object} object object to check
|
||||
* @return {Number} number of entry
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getObjectCount(object)
|
||||
{
|
||||
@@ -394,6 +502,7 @@ function getObjectCount(object)
|
||||
* @param {String} key key name
|
||||
* @param {Object} object object to search key in
|
||||
* @return {Boolean} true/false if key exists in object
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function keyInObject(key, object)
|
||||
{
|
||||
@@ -402,9 +511,10 @@ function keyInObject(key, object)
|
||||
|
||||
/**
|
||||
* returns matching key of value
|
||||
* @param {Object} obj object to search value in
|
||||
* @param {Mixed} value any value (String, Number, etc)
|
||||
* @return {String} the key found for the first matching value
|
||||
* @param {Object} object object to search value in
|
||||
* @param {Mixed} value any value (String, Number, etc)
|
||||
* @return {String} the key found for the first matching value
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getKeyByValue(object, value) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -416,9 +526,10 @@ function getKeyByValue(object, value) // eslint-disable-line no-unused-vars
|
||||
|
||||
/**
|
||||
* returns true if value is found in object with a key
|
||||
* @param {Object} obj object to search value in
|
||||
* @param {Mixed} value any value (String, Number, etc)
|
||||
* @return {Boolean} true on value found, false on not found
|
||||
* @param {Object} object object to search value in
|
||||
* @param {Mixed} value any value (String, Number, etc)
|
||||
* @return {Boolean} true on value found, false on not found
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function valueInObject(object, value) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -434,6 +545,7 @@ function valueInObject(object, value) // eslint-disable-line no-unused-vars
|
||||
* or if JSON.parse(JSON.stringify(obj)) is failing
|
||||
* @param {Object} inObject Object to copy
|
||||
* @return {Object} Copied Object
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function deepCopyFunction(inObject)
|
||||
{
|
||||
@@ -457,6 +569,7 @@ function deepCopyFunction(inObject)
|
||||
* checks if a DOM element actually exists
|
||||
* @param {String} id Element id to check for
|
||||
* @return {Boolean} true if element exists, false on failure
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function exists(id)
|
||||
{
|
||||
@@ -468,6 +581,7 @@ function exists(id)
|
||||
* currently precision is fixed, if dynamic needs check for max/min precision
|
||||
* @param {Number} bytes bytes in int
|
||||
* @return {String} string in GB/MB/KB
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function formatBytes(bytes) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -484,6 +598,7 @@ function formatBytes(bytes) // eslint-disable-line no-unused-vars
|
||||
* like formatBytes, but returns bytes for <1KB and not 0.n KB
|
||||
* @param {Number} bytes bytes in int
|
||||
* @return {String} string in GB/MB/KB
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function formatBytesLong(bytes) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -496,6 +611,7 @@ function formatBytesLong(bytes) // eslint-disable-line no-unused-vars
|
||||
* Convert a string with B/K/M/etc into a byte number
|
||||
* @param {String|Number} bytes Any string with B/K/M/etc
|
||||
* @return {String|Number} A byte number, or original string as is
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function stringByteFormat(bytes) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -526,6 +642,7 @@ function stringByteFormat(bytes) // eslint-disable-line no-unused-vars
|
||||
/**
|
||||
* prints out error messages based on data available from the browser
|
||||
* @param {Object} err error from try/catch block
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function errorCatch(err)
|
||||
{
|
||||
@@ -533,22 +650,20 @@ function errorCatch(err)
|
||||
if (err.stack) {
|
||||
// only FF
|
||||
if (err.lineNumber) {
|
||||
console.log('ERROR[%s:%s] %s', err.name, err.lineNumber, err.message);
|
||||
console.error('ERROR[%s:%s] ', err.name, err.lineNumber, err);
|
||||
} else if (err.line) {
|
||||
// only Safari
|
||||
console.log('ERROR[%s:%s] %s', err.name, err.line, err.message);
|
||||
console.error('ERROR[%s:%s] ', err.name, err.line, err);
|
||||
} else {
|
||||
console.log('ERROR[%s] %s', err.name, err.message);
|
||||
console.error('ERROR[%s] ', err.name, err);
|
||||
}
|
||||
// stack trace
|
||||
console.log('ERROR[stack] %s', err.stack);
|
||||
} else if (err.number) {
|
||||
// IE
|
||||
console.log('ERROR[%s:%s] %s', err.name, err.number, err.message);
|
||||
console.log('ERROR[description] %s', err.description);
|
||||
console.error('ERROR[%s:%s] %s', err.name, err.number, err.message);
|
||||
console.error('ERROR[description] %s', err.description);
|
||||
} else {
|
||||
// the rest
|
||||
console.log('ERROR[%s] %s', err.name, err.message);
|
||||
console.error('ERROR[%s] %s', err.name, err.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,6 +686,7 @@ function errorCatch(err)
|
||||
* @param {String} loc location name for action indicator
|
||||
* default empty. for console.log
|
||||
* @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function actionIndicator(loc, overlay = true) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -587,6 +703,7 @@ function actionIndicator(loc, overlay = true) // eslint-disable-line no-unused-v
|
||||
* @param {String} loc location name for action indicator
|
||||
* default empty. for console.log
|
||||
* @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function actionIndicatorShow(loc, overlay = true)
|
||||
{
|
||||
@@ -609,6 +726,7 @@ function actionIndicatorShow(loc, overlay = true)
|
||||
* @param {String} loc location name for action indicator
|
||||
* default empty. for console.log
|
||||
* @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function actionIndicatorHide(loc, overlay = true)
|
||||
{
|
||||
@@ -621,6 +739,7 @@ function actionIndicatorHide(loc, overlay = true)
|
||||
|
||||
/**
|
||||
* shows the overlay box or if already visible, bumps the zIndex to 100
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function overlayBoxShow()
|
||||
{
|
||||
@@ -635,6 +754,7 @@ function overlayBoxShow()
|
||||
|
||||
/**
|
||||
* hides the overlay box or if zIndex is 100 bumps it down to previous level
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function overlayBoxHide()
|
||||
{
|
||||
@@ -648,6 +768,7 @@ function overlayBoxHide()
|
||||
|
||||
/**
|
||||
* position the overlay block box and shows it
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function setOverlayBox() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -658,6 +779,7 @@ function setOverlayBox() // eslint-disable-line no-unused-vars
|
||||
|
||||
/**
|
||||
* opposite of set, always hides overlay box
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function hideOverlayBox() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -668,6 +790,7 @@ function hideOverlayBox() // eslint-disable-line no-unused-vars
|
||||
|
||||
/**
|
||||
* the abort call, clears the action box and hides it and the overlay box
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function ClearCall() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -689,6 +812,7 @@ function ClearCall() // eslint-disable-line no-unused-vars
|
||||
* zIndex of 1000
|
||||
* - indicator is page centered
|
||||
* @param {String} loc ID string, only used for console log
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function showActionIndicator(loc) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -727,6 +851,7 @@ function showActionIndicator(loc) // eslint-disable-line no-unused-vars
|
||||
* the overlayBox is not hidden but the zIndex
|
||||
* is set to this value
|
||||
* @param {String} loc ID string, only used for console log
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function hideActionIndicator(loc) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -750,6 +875,7 @@ function hideActionIndicator(loc) // eslint-disable-line no-unused-vars
|
||||
/**
|
||||
* checks if overlayBox exists, if not it is
|
||||
* added as hidden item at the body end
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function checkOverlayExists()
|
||||
{
|
||||
@@ -767,6 +893,7 @@ function checkOverlayExists()
|
||||
* if not visible show and set zIndex to 10 (GL_OB_BASE)
|
||||
* if visible, add +1 to the GL_OB_S variable and
|
||||
* up zIndex by this value
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function showOverlayBoxLayers(el_id) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -799,8 +926,9 @@ function showOverlayBoxLayers(el_id) // eslint-disable-line no-unused-vars
|
||||
* and set zIndex and GL_OB_S to 0
|
||||
* else just set zIndex to the new GL_OB_S value
|
||||
* @param {String} el_id Target to hide layer
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function hideOverlayBoxLayers(el_id)
|
||||
function hideOverlayBoxLayers(el_id='')
|
||||
{
|
||||
// console.log('HIDE overlaybox: %s', GL_OB_S);
|
||||
// remove on layer
|
||||
@@ -824,6 +952,7 @@ function hideOverlayBoxLayers(el_id)
|
||||
|
||||
/**
|
||||
* only for single action box
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function clearCallActionBox() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -841,6 +970,7 @@ function clearCallActionBox() // eslint-disable-line no-unused-vars
|
||||
* @param {Array} [css=[]] array for css tags
|
||||
* @param {Object} [options={}] anything else (value, placeholder, OnClick, style)
|
||||
* @return {Object} created element as an object
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function cel(tag, id = '', content = '', css = [], options = {})
|
||||
{
|
||||
@@ -861,6 +991,7 @@ function cel(tag, id = '', content = '', css = [], options = {})
|
||||
* @param {Object} attach the object to be attached
|
||||
* @param {String} [id=''] optional id, if given search in base for this id and attach there
|
||||
* @return {Object} "none", technically there is no return needed as it is global attach
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function ael(base, attach, id = '')
|
||||
{
|
||||
@@ -891,6 +1022,7 @@ function ael(base, attach, id = '')
|
||||
* @param {Object} base object to where we attach the elements
|
||||
* @param {...Object} attach attach 1..n: attach directly to the base element those attachments
|
||||
* @return {Object} "none", technically there is no return needed, global attach
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function aelx(base, ...attach)
|
||||
{
|
||||
@@ -907,6 +1039,7 @@ function aelx(base, ...attach)
|
||||
* @param {Object} base object to where we attach the elements
|
||||
* @param {Array} attach array of objects to attach
|
||||
* @return {Object} "none", technically there is no return needed, global attach
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function aelxar(base, attach) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -921,6 +1054,7 @@ function aelxar(base, attach) // eslint-disable-line no-unused-vars
|
||||
* resets the sub elements of the base element given
|
||||
* @param {Object} base cel created element
|
||||
* @return {Object} returns reset base element
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function rel(base) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -933,6 +1067,7 @@ function rel(base) // eslint-disable-line no-unused-vars
|
||||
* @param {Object} _element element to work one
|
||||
* @param {String} css style sheet to remove (name)
|
||||
* @return {Object} returns full element
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function rcssel(_element, css)
|
||||
{
|
||||
@@ -948,6 +1083,7 @@ function rcssel(_element, css)
|
||||
* @param {Object} _element element to work on
|
||||
* @param {String} css style sheet to add (name)
|
||||
* @return {Object} returns full element
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function acssel(_element, css)
|
||||
{
|
||||
@@ -965,6 +1101,7 @@ function acssel(_element, css)
|
||||
* @param {String} rcss style to remove (name)
|
||||
* @param {String} acss style to add (name)
|
||||
* @return {Object} returns full element
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function scssel(_element, rcss, acss) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -977,6 +1114,7 @@ function scssel(_element, rcss, acss) // eslint-disable-line no-unused-vars
|
||||
* that can be inserted into the page
|
||||
* @param {Object} tree object tree with dom element declarations
|
||||
* @return {String} HTML string that can be used as innerHTML
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function phfo(tree)
|
||||
{
|
||||
@@ -1083,6 +1221,7 @@ function phfo(tree)
|
||||
* Is like tree.sub call
|
||||
* @param {Array} list Array of cel created objects
|
||||
* @return {String} HTML String
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function phfa(list) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -1109,11 +1248,14 @@ function phfa(list) // eslint-disable-line no-unused-vars
|
||||
* @param {String} [sort=''] if empty as is, else allowed 'keys',
|
||||
* 'values' all others are ignored
|
||||
* @return {String} html with build options block
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function html_options(name, data, selected = '', options_only = false, return_string = false, sort = '') // eslint-disable-line no-unused-vars
|
||||
{
|
||||
// wrapper to new call
|
||||
return html_options_block(name, data, selected, false, options_only, return_string, sort);
|
||||
return html_options_block(
|
||||
name, data, selected, 0, options_only, return_string, sort
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1134,9 +1276,11 @@ function html_options(name, data, selected = '', options_only = false, return_st
|
||||
* 'values' all others are ignored
|
||||
* @param {String} [onchange=''] onchange trigger call, default unset
|
||||
* @return {String} html with build options block
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function html_options_block(name, data, selected = '', multiple = 0, options_only = false, return_string = false, sort = '', onchange = '')
|
||||
{
|
||||
function html_options_block(
|
||||
name, data, selected = '', multiple = 0, options_only = false, return_string = false, sort = '', onchange = ''
|
||||
) {
|
||||
var content = [];
|
||||
var element_select;
|
||||
var select_options = {};
|
||||
@@ -1173,7 +1317,8 @@ function html_options_block(name, data, selected = '', multiple = 0, options_onl
|
||||
// basic options init
|
||||
options = {
|
||||
'label': value,
|
||||
'value': key
|
||||
'value': key,
|
||||
'selected': ''
|
||||
};
|
||||
// add selected if matching
|
||||
if (multiple == 0 && !Array.isArray(selected) && selected == key) {
|
||||
@@ -1184,7 +1329,7 @@ function html_options_block(name, data, selected = '', multiple = 0, options_onl
|
||||
options.selected = '';
|
||||
}
|
||||
// create the element option
|
||||
element_option = cel('option', '', value, '', options);
|
||||
element_option = cel('option', '', value, [], options);
|
||||
// attach it to the select element
|
||||
ael(element_select, element_option);
|
||||
}
|
||||
@@ -1214,6 +1359,7 @@ function html_options_block(name, data, selected = '', multiple = 0, options_onl
|
||||
* @param {String} name name/id
|
||||
* @param {Object} data array of options
|
||||
* @param {String} [sort=''] if empty as is, else allowed 'keys', 'values'
|
||||
* @deprecated use utils.js
|
||||
* all others are ignored
|
||||
*/
|
||||
function html_options_refill(name, data, sort = '') // eslint-disable-line no-unused-vars
|
||||
@@ -1236,7 +1382,7 @@ function html_options_refill(name, data, sort = '') // eslint-disable-line no-un
|
||||
[].forEach.call(document.querySelectorAll('#' + name + ' :checked'), function(elm) {
|
||||
option_selected = elm.value;
|
||||
});
|
||||
document.getElementById(name).innerHTML = '';
|
||||
loadEl(name).innerHTML = '';
|
||||
for (const key of data_list) {
|
||||
value = data[key];
|
||||
// console.log('add [%s] options: key: %s, value: %s', name, key, value);
|
||||
@@ -1247,7 +1393,7 @@ function html_options_refill(name, data, sort = '') // eslint-disable-line no-un
|
||||
if (key == option_selected) {
|
||||
element_option.selected = true;
|
||||
}
|
||||
document.getElementById(name).appendChild(element_option);
|
||||
loadEl(name).appendChild(element_option);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1262,6 +1408,7 @@ function html_options_refill(name, data, sort = '') // eslint-disable-line no-un
|
||||
* @param {String} [return_key=''] if set only returns this key entry
|
||||
* or empty for none
|
||||
* @return {Object|String} parameter entry list
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function parseQueryString(query = '', return_key = '') // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -1311,11 +1458,12 @@ function parseQueryString(query = '', return_key = '') // eslint-disable-line no
|
||||
* all parameters are returned
|
||||
* @param {String} [query=''] different query string to parse, if not
|
||||
* set (default) the current window href is used
|
||||
* @param {Bool} [single=false] if set to true then only the first found
|
||||
* @param {Boolean} [single=false] if set to true then only the first found
|
||||
* will be returned
|
||||
* @return {Object|Array|String} if search is empty, object, if search is set
|
||||
* and only one entry, then string, else array
|
||||
* unless single is true
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function getQueryStringParam(search = '', query = '', single = false) // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -1323,7 +1471,7 @@ function getQueryStringParam(search = '', query = '', single = false) // eslint-
|
||||
query = window.location.href;
|
||||
}
|
||||
const url = new URL(query);
|
||||
let param = '';
|
||||
let param = null;
|
||||
if (search) {
|
||||
let _params = url.searchParams.getAll(search);
|
||||
if (_params.length == 1 || single === true) {
|
||||
@@ -1353,6 +1501,7 @@ function getQueryStringParam(search = '', query = '', single = false) // eslint-
|
||||
// *** MASTER logout call
|
||||
/**
|
||||
* submits basic data for form logout
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function loginLogout() // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -1373,6 +1522,7 @@ function loginLogout() // eslint-disable-line no-unused-vars
|
||||
* @param {String} [header_id='mainHeader'] the target for the main element block
|
||||
* if not set mainHeader is assumed
|
||||
* this is the target div for the "loginRow"
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function createLoginRow(login_string, header_id = 'mainHeader') // eslint-disable-line no-unused-vars
|
||||
{
|
||||
@@ -1408,6 +1558,7 @@ function createLoginRow(login_string, header_id = 'mainHeader') // eslint-disabl
|
||||
* @param {String} [header_id='mainHeader'] the target for the main element block
|
||||
* if not set mainHeader is assumed
|
||||
* this is the target div for the "menuRow"
|
||||
* @deprecated use utils.js
|
||||
*/
|
||||
function createNavMenu(nav_menu, header_id = 'mainHeader') // eslint-disable-line no-unused-vars
|
||||
{
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
/* general edit javascript */
|
||||
/* prototype version */
|
||||
/*
|
||||
general edit javascript
|
||||
prototype version
|
||||
*/
|
||||
|
||||
/** @deprecated Do not use this anymore, use utils.js */
|
||||
|
||||
throw new Error("Prototype Support is deprected, please switch to jquery and utils.js/utils.min.js");
|
||||
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
@@ -25,7 +31,7 @@ function pop(theURL, winName, features) {
|
||||
|
||||
/**
|
||||
* automatically resize a text area based on the amount of lines in it
|
||||
* @param {[string} ta_id element id
|
||||
* @param {string} ta_id element id
|
||||
*/
|
||||
function expandTA(ta_id) {
|
||||
var ta;
|
||||
|
||||
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
5
www/admin/layout/javascript/translateTest-ja_JP.UTF-8.js
Normal file
5
www/admin/layout/javascript/translateTest-ja_JP.UTF-8.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var i18n = {
|
||||
"Original": "Translated"
|
||||
};
|
||||
|
||||
// __END__
|
||||
1646
www/admin/layout/javascript/utils.js
Normal file
1646
www/admin/layout/javascript/utils.js
Normal file
File diff suppressed because it is too large
Load Diff
3
www/admin/layout/javascript/utils.min.js
vendored
Normal file
3
www/admin/layout/javascript/utils.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
www/admin/layout/javascript/utils.min.js.map
Normal file
7
www/admin/layout/javascript/utils.min.js.map
Normal file
File diff suppressed because one or more lines are too long
37
www/admin/test.javascript.html
Normal file
37
www/admin/test.javascript.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>JavaScript Test</title>
|
||||
<script type="text/javascript" src="layout/javascript/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="layout/javascript/translateTest-ja_JP.UTF-8.js"></script>
|
||||
<script type="text/javascript" src="layout/javascript/utils.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>JavaScript tests</h1>
|
||||
<div id="test-div">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script languagae="JavaScript">
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('MAIN PAGE LOADED');
|
||||
// console.log('Random: %o', mh.randomIdF());
|
||||
console.log('Random: %o', randomIdF());
|
||||
console.log("GW: %o", getWindowSize());
|
||||
let bytes = 1021152;
|
||||
console.log('FB: %o', formatBytes(bytes));
|
||||
console.log('FBL: %o', formatBytesLong(bytes));
|
||||
console.log('TR: %s', l10n.__('Original'));
|
||||
console.log('TR: %s', l10n.__('Not exists'));
|
||||
|
||||
setCenter('test-div', true, true);
|
||||
ClearCall();
|
||||
overlayBoxShow();
|
||||
actionIndicatorShow('testSmarty');
|
||||
setTimeout(function() {
|
||||
console.log('Waiting dummy ...');
|
||||
actionIndicatorHide('testSmarty');
|
||||
ClearCall();
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
@@ -24,6 +24,7 @@
|
||||
"egrajp/smarty-extended": "^5.4",
|
||||
"php": ">=8.1",
|
||||
"gullevek/dotenv": "^2.0",
|
||||
"psr/log": "^2.0 || ^3.0"
|
||||
"psr/log": "^2.0 || ^3.0",
|
||||
"php-privacy/openpgp": "^2.1"
|
||||
}
|
||||
}
|
||||
|
||||
3
www/configs/.target.example
Normal file
3
www/configs/.target.example
Normal file
@@ -0,0 +1,3 @@
|
||||
# target can be live, stage, test, dev
|
||||
# this overrides the SITE set "location" entry
|
||||
TARGET=
|
||||
@@ -78,42 +78,11 @@ define('TEMPLATES_C', 'templates_c' . DIRECTORY_SEPARATOR);
|
||||
// template base
|
||||
define('TEMPLATES', 'templates' . DIRECTORY_SEPARATOR);
|
||||
|
||||
/************* HASH / ACL DEFAULT / ERROR SETTINGS / SMARTY *************/
|
||||
/************* HASH / ACL DEFAULT *************/
|
||||
// default hash type
|
||||
define('DEFAULT_HASH', 'sha256');
|
||||
// default acl level
|
||||
define('DEFAULT_ACL_LEVEL', 80);
|
||||
// SSL host name
|
||||
// define('SSL_HOST', $_ENV['SSL_HOST'] ?? '');
|
||||
// error page strictness, Default is 3
|
||||
// 1: only show error page as the last mesure if really no mid & aid can be loaded and found at all
|
||||
// 2: if template not found, do not search, show error template
|
||||
// 3: if default template is not found, show error template, do not fall back to default tree
|
||||
// 4: very strict, even on normal fixable errors through error
|
||||
// define('ERROR_STRICT', 3);
|
||||
// allow page caching in general, set to 'false' if you do debugging or development!
|
||||
// define('ALLOW_SMARTY_CACHE', false);
|
||||
// cache life time, in second', default here is 2 days (172800s)
|
||||
// -1 is never expire cache
|
||||
// define('SMARTY_CACHE_LIFETIME', -1);
|
||||
|
||||
/************* LOGOUT ********************/
|
||||
// logout target
|
||||
define('LOGOUT_TARGET', '');
|
||||
|
||||
/************* AJAX / ACCESS *************/
|
||||
// ajax request type
|
||||
define('AJAX_REQUEST_TYPE', 'POST');
|
||||
// what AJAX type to use
|
||||
define('USE_PROTOTYPE', false);
|
||||
define('USE_SCRIPTACULOUS', false);
|
||||
define('USE_JQUERY', true);
|
||||
|
||||
/************* LAYOUT WIDTHS *************/
|
||||
define('PAGE_WIDTH', '100%');
|
||||
define('CONTENT_WIDTH', '100%');
|
||||
// the default template name
|
||||
define('MASTER_TEMPLATE_NAME', 'main_body.tpl');
|
||||
define('DEFAULT_ACL_LEVEL', $ENV['DEFAULT_ACL_LEVEL'] ?? 80);
|
||||
|
||||
/************* OVERALL CONTROL NAMES *************/
|
||||
// BELOW has HAS to be changed
|
||||
@@ -136,24 +105,15 @@ define('COMPILE_ID', 'COMPILE_' . BASE_NAME . '_' . SERVER_NAME_HASH);
|
||||
|
||||
/************* LANGUAGE / ENCODING *******/
|
||||
// default lang + encoding
|
||||
define('DEFAULT_LOCALE', 'en_US.UTF-8');
|
||||
define('DEFAULT_LOCALE', $_ENV['LOCALE'] ?? 'en_US.UTF-8');
|
||||
// default web page encoding setting
|
||||
define('DEFAULT_ENCODING', 'UTF-8');
|
||||
define('DEFAULT_ENCODING', (string)array_pad(explode('.', DEFAULT_LOCALE, 2), 2, 'UTF-8')[1]);
|
||||
|
||||
/************* QUEUE TABLE *************/
|
||||
// if we have a dev/live system
|
||||
// set_live is a per page/per item
|
||||
// live_queue is a global queue system
|
||||
// define('QUEUE', 'live_queue');
|
||||
|
||||
/************* DB PATHS (PostgreSQL) *****************/
|
||||
// schema names, can also be defined per <DB INFO>
|
||||
define('PUBLIC_SCHEMA', 'public');
|
||||
define('DEV_SCHEMA', 'public');
|
||||
define('TEST_SCHEMA', 'public');
|
||||
define('LIVE_SCHEMA', 'public');
|
||||
define('GLOBAL_DB_SCHEMA', '');
|
||||
define('LOGIN_DB_SCHEMA', '');
|
||||
/************* HOST NAME *****************/
|
||||
// get the name without the port
|
||||
list($HOST_NAME) = array_pad(explode(':', $_SERVER['HTTP_HOST'], 2), 2, null);
|
||||
// set HOST name
|
||||
define('HOST_NAME', $HOST_NAME);
|
||||
|
||||
/************* CORE HOST SETTINGS *****************/
|
||||
if (file_exists(BASE . CONFIGS . 'config.host.php')) {
|
||||
@@ -162,6 +122,14 @@ if (file_exists(BASE . CONFIGS . 'config.host.php')) {
|
||||
if (!isset($SITE_CONFIG)) {
|
||||
$SITE_CONFIG = [];
|
||||
}
|
||||
// BAIL ON MISSING MASTER SITE CONFIG
|
||||
if (!isset($SITE_CONFIG[HOST_NAME]['location'])) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Missing SITE_CONFIG entry for: "' . HOST_NAME . '". Contact Administrator'
|
||||
);
|
||||
}
|
||||
// set target first
|
||||
define('TARGET', $_ENV['TARGET'] ?? $SITE_CONFIG[HOST_NAME]['location'] ?? 'test');
|
||||
/************* DB ACCESS *****************/
|
||||
if (file_exists(BASE . CONFIGS . 'config.db.php')) {
|
||||
require BASE . CONFIGS . 'config.db.php';
|
||||
@@ -175,17 +143,6 @@ if (file_exists(BASE . CONFIGS . 'config.path.php')) {
|
||||
}
|
||||
|
||||
/************* MASTER INIT *****************/
|
||||
// live frontend pages
|
||||
// ** missing live domains **
|
||||
// get the name without the port
|
||||
[$HOST_NAME] = array_pad(explode(':', $_SERVER['HTTP_HOST'], 2), 2, null);
|
||||
// set HOST name
|
||||
define('HOST_NAME', $HOST_NAME);
|
||||
// BAIL ON MISSING MASTER SITE CONFIG
|
||||
if (!isset($SITE_CONFIG[HOST_NAME]['location'])) {
|
||||
echo 'Missing SITE_CONFIG entry for: "' . HOST_NAME . '". Contact Administrator';
|
||||
exit;
|
||||
}
|
||||
// BAIL ON MISSING DB CONFIG:
|
||||
// we have either no db selction for this host but have db config entries
|
||||
// or we have a db selection but no db config as array or empty
|
||||
@@ -200,8 +157,9 @@ if (
|
||||
empty($DB_CONFIG[$SITE_CONFIG[HOST_NAME]['db_host']]))
|
||||
)
|
||||
) {
|
||||
echo 'No matching DB config found for: "' . HOST_NAME . '". Contact Administrator';
|
||||
exit;
|
||||
throw new \InvalidArgumentException(
|
||||
'No matching DB config found for: "' . HOST_NAME . '". Contact Administrator'
|
||||
);
|
||||
}
|
||||
// set SSL on
|
||||
$is_secure = false;
|
||||
@@ -235,35 +193,31 @@ define('DB_CONFIG', $DB_CONFIG[DB_CONFIG_NAME] ?? [
|
||||
]);
|
||||
// because we can't change constant, but we want to for db debug flag
|
||||
$GLOBALS['DB_CONFIG_SET'] = DB_CONFIG;
|
||||
// define('DB_CONFIG_TARGET', SITE_CONFIG[$HOST_NAME]['db_host_target']);
|
||||
// define('DB_CONFIG_OTHER', SITE_CONFIG[$HOST_NAME]['db_host_other']);
|
||||
// override for login and global schemas
|
||||
// where the edit* tables are
|
||||
// define('LOGIN_DB_SCHEMA', PUBLIC_SCHEMA);
|
||||
// where global tables are that are used by all schemas (eg queue tables for online, etc)
|
||||
// define('GLOBAL_DB_SCHEMA', PUBLIC_SCHEMA);
|
||||
// debug settings, site lang, etc
|
||||
define('TARGET', $SITE_CONFIG[HOST_NAME]['location'] ?? 'test');
|
||||
define('DEBUG_LEVEL', $SITE_CONFIG[HOST_NAME]['debug_level'] ?? 'debug');
|
||||
define('SITE_LOCALE', $SITE_CONFIG[HOST_NAME]['site_locale'] ?? DEFAULT_LOCALE);
|
||||
define('SITE_DOMAIN', str_replace(DIRECTORY_SEPARATOR, '', CONTENT_PATH));
|
||||
define('SITE_ENCODING', $SITE_CONFIG[HOST_NAME]['site_encoding'] ?? DEFAULT_ENCODING);
|
||||
define('LOGIN_ENABLED', $SITE_CONFIG[HOST_NAME]['login_enabled'] ?? false);
|
||||
define('AUTH', $SITE_CONFIG[HOST_NAME]['auth'] ?? false);
|
||||
// paths
|
||||
// define('CSV_PATH', $PATHS[TARGET]['csv_path'] ?? '');
|
||||
// define('EXPORT_SCRIPT', $PATHS[TARGET]['perl_bin'] ?? '');
|
||||
// define('REDIRECT_URL', $PATHS[TARGET]['redirect_url'] ?? '');
|
||||
|
||||
// NOTE: everything below is smarty related and should be removed from here
|
||||
/************* GENERAL PAGE TITLE ********/
|
||||
define('G_TITLE', $_ENV['G_TITLE'] ?? '');
|
||||
|
||||
/************* LAYOUT WIDTHS *************/
|
||||
define('PAGE_WIDTH', $_ENV['SMARTY.PAGE_WIDTH'] ?? '100%');
|
||||
define('CONTENT_WIDTH', $_ENV['SMARTY.CONTENT_WIDTH'] ?? '100%');
|
||||
// the default template name
|
||||
define('MASTER_TEMPLATE_NAME', $_ENV['MASTER_TEMPLATE_NAME'] ?? 'main_body.tpl');
|
||||
/************* JS LIBRARIES *************/
|
||||
define('USE_PROTOTYPE', false);
|
||||
define('USE_SCRIPTACULOUS', false);
|
||||
define('USE_JQUERY', true);
|
||||
/************ STYLE SHEETS / JS **********/
|
||||
define('ADMIN_STYLESHEET', 'edit.css');
|
||||
define('ADMIN_JAVASCRIPT', 'edit.js');
|
||||
define('ADMIN_STYLESHEET', $_ENV['ADMIN.STYLESHEET'] ?? 'edit.css');
|
||||
define('ADMIN_JAVASCRIPT', $_ENV['ADMIN.JAVASCRIPT'] ?? 'edit.js');
|
||||
define('STYLESHEET', $_ENV['STYLESHEET'] ?? 'frontend.css');
|
||||
define('JAVASCRIPT', $_ENV['JAVASCRIPT'] ?? 'frontend.js');
|
||||
|
||||
// anything optional
|
||||
/************* INTERNAL ******************/
|
||||
// any other global definitons in the config.other.php
|
||||
|
||||
@@ -15,6 +15,12 @@ define('EDIT_BASE_STYLESHEET', 'edit.css');
|
||||
|
||||
// define('SOME_ID', <SOME VALUE>);
|
||||
|
||||
/************* QUEUE TABLE *************/
|
||||
// if we have a dev/live system
|
||||
// set_live is a per page/per item
|
||||
// live_queue is a global queue system
|
||||
// define('QUEUE', 'live_queue');
|
||||
|
||||
/************* CONVERT *******************/
|
||||
// this only needed if the external thumbnail create is used
|
||||
$paths = [
|
||||
|
||||
@@ -35,4 +35,9 @@ define('CONTENT_PATH', $folder . DIRECTORY_SEPARATOR);
|
||||
],
|
||||
];*/
|
||||
|
||||
// paths
|
||||
// define('CSV_PATH', $PATHS[TARGET]['csv_path'] ?? '');
|
||||
// define('EXPORT_SCRIPT', $PATHS[TARGET]['perl_bin'] ?? '');
|
||||
// define('REDIRECT_URL', $PATHS[TARGET]['redirect_url'] ?? '');
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -53,6 +53,11 @@ for (
|
||||
\gullevek\dotEnv\DotEnv::readEnvFile(
|
||||
$__DIR__PATH . $CONFIG_PATH_PREFIX . CONFIG_PATH
|
||||
);
|
||||
// load target file if it exists
|
||||
\gullevek\dotEnv\DotEnv::readEnvFile(
|
||||
$__DIR__PATH . $CONFIG_PATH_PREFIX . CONFIG_PATH,
|
||||
'.target'
|
||||
);
|
||||
// load master config file that loads all other config files
|
||||
require $__DIR__PATH . $CONFIG_PATH_PREFIX . CONFIG_PATH . 'config.master.php';
|
||||
break;
|
||||
|
||||
@@ -423,14 +423,9 @@ class Login
|
||||
|
||||
// LOGOUT TARGET
|
||||
if (!isset($options['logout_target'])) {
|
||||
if (defined('LOGOUT_TARGET')) {
|
||||
trigger_error(
|
||||
'loginMainCall: LOGOUT_TARGET should not be used',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$options['logout_target'] = LOGOUT_TARGET;
|
||||
$this->logout_target = $options['logout_target'];
|
||||
}
|
||||
// defaults to ''
|
||||
$options['logout_target'] = '';
|
||||
$this->logout_target = $options['logout_target'];
|
||||
}
|
||||
|
||||
// *** PASSWORD SETTINGS
|
||||
@@ -929,7 +924,9 @@ class Login
|
||||
$mandatory_session_vars = [
|
||||
'LOGIN_USER_NAME', 'LOGIN_GROUP_NAME', 'LOGIN_EUCUID', 'LOGIN_EUCUUID',
|
||||
'LOGIN_USER_ADDITIONAL_ACL', 'LOGIN_GROUP_ADDITIONAL_ACL',
|
||||
'LOGIN_ADMIN', 'LOGIN_GROUP_ACL_LEVEL', 'LOGIN_PAGES_ACL_LEVEL', 'LOGIN_USER_ACL_LEVEL',
|
||||
'LOGIN_ADMIN', 'LOGIN_GROUP_ACL_LEVEL',
|
||||
'LOGIN_PAGES', 'LOGIN_PAGES_LOOKUP', 'LOGIN_PAGES_ACL_LEVEL',
|
||||
'LOGIN_USER_ACL_LEVEL',
|
||||
'LOGIN_UNIT', 'LOGIN_UNIT_DEFAULT_EACUID'
|
||||
];
|
||||
$force_reauth = false;
|
||||
@@ -1157,7 +1154,7 @@ class Login
|
||||
$q
|
||||
);
|
||||
// reset any query data that might exist
|
||||
$this->db->dbCacheReset($q, $params);
|
||||
$this->db->dbCacheReset($q, $params, show_warning:false);
|
||||
// never cache return data
|
||||
$res = $this->db->dbReturnParams($q, $params, $this->db::NO_CACHE);
|
||||
// query was not run successful
|
||||
@@ -1269,6 +1266,7 @@ class Login
|
||||
}
|
||||
$edit_page_ids = [];
|
||||
$pages = [];
|
||||
$pages_lookup = [];
|
||||
$pages_acl = [];
|
||||
// set pages access
|
||||
$q = <<<SQL
|
||||
@@ -1312,6 +1310,7 @@ class Login
|
||||
'query' => [],
|
||||
'visible' => []
|
||||
];
|
||||
$pages_lookup[$res['filename']] = $res['cuid'];
|
||||
// make reference filename -> level
|
||||
$pages_acl[$res['filename']] = $res['level'];
|
||||
} // for each page
|
||||
@@ -1372,6 +1371,7 @@ class Login
|
||||
// write back the pages data to the output array
|
||||
$this->session->setMany([
|
||||
'LOGIN_PAGES' => $pages,
|
||||
'LOGIN_PAGES_LOOKUP' => $pages_lookup,
|
||||
'LOGIN_PAGES_ACL_LEVEL' => $pages_acl,
|
||||
]);
|
||||
// load the edit_access user rights
|
||||
@@ -1418,6 +1418,7 @@ class Login
|
||||
'additional_acl' => Json::jsonConvertToArray($res['additional_acl']),
|
||||
'data' => $ea_data
|
||||
];
|
||||
// LEGACY LOOKUP
|
||||
$unit_access_eaid[$res['edit_access_id']] = [
|
||||
'cuid' => $res['cuid'],
|
||||
];
|
||||
@@ -1477,6 +1478,8 @@ class Login
|
||||
// username (login), group name
|
||||
$this->acl['user_name'] = $_SESSION['LOGIN_USER_NAME'];
|
||||
$this->acl['group_name'] = $_SESSION['LOGIN_GROUP_NAME'];
|
||||
// DEPRECATED
|
||||
$this->acl['euid'] = $_SESSION['LOGIN_EUID'];
|
||||
// edit user cuid
|
||||
$this->acl['eucuid'] = $_SESSION['LOGIN_EUCUID'];
|
||||
$this->acl['eucuuid'] = $_SESSION['LOGIN_EUCUUID'];
|
||||
@@ -1528,8 +1531,10 @@ class Login
|
||||
) {
|
||||
$this->acl['page'] = $_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name];
|
||||
}
|
||||
$this->acl['pages_detail'] = $_SESSION['LOGIN_PAGES'];
|
||||
$this->acl['pages_lookup_cuid'] = $_SESSION['LOGIN_PAGES_LOOKUP'];
|
||||
|
||||
$this->acl['unit_id'] = null;
|
||||
$this->acl['unit_cuid'] = null;
|
||||
$this->acl['unit_name'] = null;
|
||||
$this->acl['unit_uid'] = null;
|
||||
$this->acl['unit'] = [];
|
||||
@@ -1557,6 +1562,7 @@ class Login
|
||||
'uid' => $unit['uid'],
|
||||
'cuuid' => $unit['cuuid'],
|
||||
'level' => $this->default_acl_list[$this->acl['unit'][$ea_cuid]]['name'] ?? -1,
|
||||
'level_number' => $this->acl['unit'][$ea_cuid],
|
||||
'default' => $unit['default'],
|
||||
'data' => $unit['data'],
|
||||
'additional_acl' => $unit['additional_acl']
|
||||
@@ -2535,7 +2541,7 @@ HTML;
|
||||
$this->login_user_id,
|
||||
-1,
|
||||
$login_user_id_changed
|
||||
);
|
||||
) ?? '';
|
||||
// flag unclean input data
|
||||
if ($login_user_id_changed > 0) {
|
||||
$this->login_user_id_unclear = true;
|
||||
@@ -2729,6 +2735,31 @@ HTML;
|
||||
return $this->session->get('LOGIN_PAGES');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current loaded list of pages the user can access
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function loginGetPageLookupList(): array
|
||||
{
|
||||
return $this->session->get('LOGIN_PAGES_LOOKUP');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check access to a file in the pages list
|
||||
*
|
||||
* @param string $filename File name to check
|
||||
* @return bool True if page in list and anything other than None access, False if failed
|
||||
*/
|
||||
public function loginPageAccessAllowed(string $filename): bool
|
||||
{
|
||||
return (
|
||||
$this->session->get('LOGIN_PAGES')[
|
||||
$this->session->get('LOGIN_PAGES_LOOKUP')[$filename] ?? ''
|
||||
] ?? 0
|
||||
) != 0 ? true : false;
|
||||
}
|
||||
|
||||
// MARK: logged in uid(pk)/eucuid/eucuuid
|
||||
|
||||
/**
|
||||
@@ -3214,7 +3245,7 @@ HTML;
|
||||
* @return int|null same edit access id if ok
|
||||
* or the default edit access id
|
||||
* if given one is not valid
|
||||
* @deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessValidCuid()
|
||||
* @#deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessValidCuid()
|
||||
*/
|
||||
public function loginCheckEditAccessId(?int $edit_access_id): ?int
|
||||
{
|
||||
@@ -3279,6 +3310,34 @@ HTML;
|
||||
return (int)$_SESSION['LOGIN_UNIT_CUID'][$uid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy lookup for edit access id to cuid
|
||||
*
|
||||
* @param int $id edit access id PK
|
||||
* @return string|false edit access cuid or false if not found
|
||||
*/
|
||||
public function loginGetEditAccessCuidFromId(int $id): string|false
|
||||
{
|
||||
if (!isset($_SESSION['LOGIN_UNIT_LEGACY'][$id])) {
|
||||
return false;
|
||||
}
|
||||
return (string)$_SESSION['LOGIN_UNIT_LEGACY'][$id]['cuid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a Legacy lookup from the edit access id to cuid for further lookups in the normal list
|
||||
*
|
||||
* @param string $cuid edit access cuid
|
||||
* @return int|false false on not found or edit access id PK
|
||||
*/
|
||||
public function loginGetEditAccessIdFromCuid(string $cuid): int|false
|
||||
{
|
||||
if (!isset($_SESSION['LOGIN_UNIT'][$cuid])) {
|
||||
return false;
|
||||
}
|
||||
return $_SESSION['LOGIN_UNIT'][$cuid]['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if admin flag is set
|
||||
*
|
||||
|
||||
@@ -76,7 +76,7 @@ class EditBase
|
||||
);
|
||||
if ($this->form->mobile_phone) {
|
||||
echo "I am sorry, but this page cannot be viewed by a mobile phone";
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
// $this->log->debug('POST', $this->log->prAr($_POST));
|
||||
}
|
||||
|
||||
@@ -103,11 +103,7 @@ class Basic
|
||||
'VIDEOS', 'DOCUMENTS', 'PDFS', 'BINARIES', 'ICONS', 'UPLOADS', 'CSV', 'JS',
|
||||
'CSS', 'TABLE_ARRAYS', 'SMARTY', 'LANG', 'CACHE', 'TMP', 'LOG', 'TEMPLATES',
|
||||
'TEMPLATES_C', 'DEFAULT_LANG', 'DEFAULT_ENCODING', 'DEFAULT_HASH',
|
||||
'DEFAULT_ACL_LEVEL', 'LOGOUT_TARGET', 'PASSWORD_CHANGE', 'AJAX_REQUEST_TYPE',
|
||||
'USE_PROTOTYPE', 'USE_SCRIPTACULOUS', 'USE_JQUERY', 'PAGE_WIDTH',
|
||||
'MASTER_TEMPLATE_NAME', 'PUBLIC_SCHEMA', 'TEST_SCHEMA', 'DEV_SCHEMA',
|
||||
'LIVE_SCHEMA', 'DB_CONFIG_NAME', 'DB_CONFIG', 'TARGET', 'DEBUG',
|
||||
'SHOW_ALL_ERRORS'
|
||||
'DB_CONFIG_NAME', 'DB_CONFIG', 'TARGET'
|
||||
] as $constant
|
||||
) {
|
||||
if (!defined($constant)) {
|
||||
@@ -387,7 +383,8 @@ class Basic
|
||||
public function initRandomKeyLength(int $key_length): bool
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -992,10 +989,10 @@ class Basic
|
||||
* @param bool $auto_check default true, if source encoding is set
|
||||
* check that the source is actually matching
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
return \CoreLibs\Convert\Encoding::convertEncoding($string, $to_encoding, $source_encoding, $auto_check);
|
||||
@@ -1028,8 +1025,12 @@ class Basic
|
||||
*/
|
||||
public function __sha1Short(string $string, bool $use_sha = false): string
|
||||
{
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::__sha1Short()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Create\Hash::__sha1Short($string, $use_sha);
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::sha1Short() or ::__crc32b()', E_USER_DEPRECATED);
|
||||
if ($use_sha) {
|
||||
return \CoreLibs\Create\Hash::sha1Short($string);
|
||||
} else {
|
||||
return \CoreLibs\Create\Hash::__crc32b($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1044,8 +1045,8 @@ class Basic
|
||||
*/
|
||||
public function __hash(string $string, string $hash_type = 'adler32'): string
|
||||
{
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::__hash()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Create\Hash::__hash($string, $hash_type);
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::hash()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Create\Hash::hash($string, $hash_type);
|
||||
}
|
||||
|
||||
// *** HASH FUNCTIONS END
|
||||
|
||||
@@ -10,12 +10,16 @@ class Email
|
||||
/** @var array<int,string> */
|
||||
private static array $email_regex_check = [
|
||||
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
|
||||
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
|
||||
// . "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
|
||||
// fixed pattern matching for domain
|
||||
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // MASTER
|
||||
1 => "@(.*)@(.*)", // double @
|
||||
2 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@", // wrong part before @
|
||||
3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
|
||||
4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
|
||||
5 => "\.([a-zA-Z]{2,6}){1}$", // wrong top level part
|
||||
// 3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
|
||||
3 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // wrong part after @
|
||||
// 4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
|
||||
4 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.", // wrong domain name part
|
||||
5 => "\.[a-zA-Z]{2,6}$", // wrong top level part
|
||||
6 => "@(.*)\.{2,}", // double .. in domain name part
|
||||
7 => "@.*\.$" // ends with a dot, top level, domain missing
|
||||
];
|
||||
|
||||
@@ -56,7 +56,11 @@ class Encoding
|
||||
{
|
||||
// return mb_substitute_character();
|
||||
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 {
|
||||
return self::$mb_error_char;
|
||||
}
|
||||
@@ -88,7 +92,13 @@ class Encoding
|
||||
): array|false {
|
||||
// convert to target encoding and convert back
|
||||
$temp = mb_convert_encoding($string, $to_encoding, $from_encoding);
|
||||
if ($temp === false) {
|
||||
return false;
|
||||
}
|
||||
$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 == $compare) {
|
||||
return false;
|
||||
@@ -104,7 +114,7 @@ class Encoding
|
||||
(($char != $r_char && (!self::$mb_error_char ||
|
||||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
|
||||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
|
||||
ord($char) != 194
|
||||
ord($char[0]) != 194
|
||||
) {
|
||||
$failed[] = $char;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace CoreLibs\Combined;
|
||||
|
||||
class ArrayHandler
|
||||
{
|
||||
public const string DATA_SEPARATOR = ':';
|
||||
|
||||
/**
|
||||
* searches key = value in an array / array
|
||||
* only returns the first one found
|
||||
@@ -148,28 +150,32 @@ class ArrayHandler
|
||||
* array search simple. looks for key, value combination, if found, returns true
|
||||
* on default does not strict check, so string '4' will match int 4 and vica versa
|
||||
*
|
||||
* @param array<mixed> $array search in as array
|
||||
* @param string|int $key key (key to search in)
|
||||
* @param string|int|bool $value value (what to find)
|
||||
* @param bool $strict [false], if set to true, will strict check key/value
|
||||
* @return bool true on found, false on not found
|
||||
* @param array<mixed> $in_array search in as array
|
||||
* @param string|int $key key (key to search in)
|
||||
* @param string|int|bool|array<string|int|bool> $value values list (what to find)
|
||||
* @param bool $strict [false], if set to true, will strict check key/value
|
||||
* @return bool true on found, false on not found
|
||||
*/
|
||||
public static function arraySearchSimple(
|
||||
array $array,
|
||||
array $in_array,
|
||||
string|int $key,
|
||||
string|int|bool $value,
|
||||
string|int|bool|array $value,
|
||||
bool $strict = false
|
||||
): 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 (is_array($_value)) {
|
||||
// call recursive, and return result if it is true, else continue
|
||||
if (($result = self::arraySearchSimple($_value, $key, $value, $strict)) !== false) {
|
||||
return $result;
|
||||
}
|
||||
} elseif ($strict === false && $_key == $key && $_value == $value) {
|
||||
} elseif ($strict === false && $_key == $key && in_array($_value, $value)) {
|
||||
return true;
|
||||
} elseif ($strict === true && $_key === $key && $_value === $value) {
|
||||
} elseif ($strict === true && $_key === $key && in_array($_value, $value, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -183,19 +189,19 @@ class ArrayHandler
|
||||
* If prefix is turned on each found group will be prefixed with the
|
||||
* search key
|
||||
*
|
||||
* @param array<mixed> $array array to search in
|
||||
* @param array<mixed> $in_array array to search in
|
||||
* @param array<mixed> $needles keys to find in array
|
||||
* @param bool $flat [false] Turn on flat output
|
||||
* @param bool $prefix [false] Prefix found with needle key
|
||||
* @return array<mixed> Found values
|
||||
*/
|
||||
public static function arraySearchKey(
|
||||
array $array,
|
||||
array $in_array,
|
||||
array $needles,
|
||||
bool $flat = false,
|
||||
bool $prefix = false
|
||||
): array {
|
||||
$iterator = new \RecursiveArrayIterator($array);
|
||||
$iterator = new \RecursiveArrayIterator($in_array);
|
||||
$recursive = new \RecursiveIteratorIterator(
|
||||
$iterator,
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
@@ -236,17 +242,171 @@ class ArrayHandler
|
||||
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
|
||||
*
|
||||
* @param array<mixed> $array array to search in
|
||||
* @param array<mixed> $in_array array to search in
|
||||
* @param int|string $key key for next/prev
|
||||
* @param bool $next [=true] if to search next or prev
|
||||
* @return int|string|null Next/prev key or null for end/first
|
||||
*/
|
||||
private static function arrayGetKey(array $array, int|string $key, bool $next = true): int|string|null
|
||||
private static function arrayGetKey(array $in_array, int|string $key, bool $next = true): int|string|null
|
||||
{
|
||||
$keys = array_keys($array);
|
||||
$keys = array_keys($in_array);
|
||||
if (($position = array_search($key, $keys, true)) === false) {
|
||||
return null;
|
||||
}
|
||||
@@ -262,26 +422,26 @@ class ArrayHandler
|
||||
* Get previous array key from an array
|
||||
* null on not found
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
* @param array<mixed> $in_array
|
||||
* @param int|string $key
|
||||
* @return int|string|null Next key, or null for not found
|
||||
*/
|
||||
public static function arrayGetPrevKey(array $array, int|string $key): int|string|null
|
||||
public static function arrayGetPrevKey(array $in_array, int|string $key): int|string|null
|
||||
{
|
||||
return self::arrayGetKey($array, $key, false);
|
||||
return self::arrayGetKey($in_array, $key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next array key from an array
|
||||
* null on not found
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
* @param array<mixed> $in_array
|
||||
* @param int|string $key
|
||||
* @return int|string|null Next key, or null for not found
|
||||
*/
|
||||
public static function arrayGetNextKey(array $array, int|string $key): int|string|null
|
||||
public static function arrayGetNextKey(array $in_array, int|string $key): int|string|null
|
||||
{
|
||||
return self::arrayGetKey($array, $key, true);
|
||||
return self::arrayGetKey($in_array, $key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,27 +463,27 @@ class ArrayHandler
|
||||
}
|
||||
// default key is not string
|
||||
$key_is_string = false;
|
||||
$arrays = func_get_args();
|
||||
$in_arrays = func_get_args();
|
||||
// if last is not array, then assume it is trigger for key is always string
|
||||
if (!is_array(end($arrays))) {
|
||||
if (array_pop($arrays)) {
|
||||
if (!is_array(end($in_arrays))) {
|
||||
if (array_pop($in_arrays)) {
|
||||
$key_is_string = true;
|
||||
}
|
||||
}
|
||||
// check that arrays count is at least two, else we don't have enough to do anything
|
||||
if (count($arrays) < 2) {
|
||||
if (count($in_arrays) < 2) {
|
||||
throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments');
|
||||
}
|
||||
$merged = [];
|
||||
while ($arrays) {
|
||||
$array = array_shift($arrays);
|
||||
if (!is_array($array)) {
|
||||
while ($in_arrays) {
|
||||
$in_array = array_shift($in_arrays);
|
||||
if (!is_array($in_array)) {
|
||||
throw new \TypeError(__FUNCTION__ . ' encountered a non array argument');
|
||||
}
|
||||
if (!$array) {
|
||||
if (!$in_array) {
|
||||
continue;
|
||||
}
|
||||
foreach ($array as $key => $value) {
|
||||
foreach ($in_array as $key => $value) {
|
||||
// if string or if key is assumed to be string do key match
|
||||
// else add new entry
|
||||
if (is_string($key) || $key_is_string === false) {
|
||||
@@ -429,14 +589,14 @@ class ArrayHandler
|
||||
* converts multi dimensional array to a flat array
|
||||
* does NOT preserve keys
|
||||
*
|
||||
* @param array<mixed> $array multi dimensionial array
|
||||
* @param array<mixed> $in_array multi dimensionial array
|
||||
* @return array<mixed> flattened array
|
||||
*/
|
||||
public static function flattenArray(array $array): array
|
||||
public static function flattenArray(array $in_array): array
|
||||
{
|
||||
$return = [];
|
||||
array_walk_recursive(
|
||||
$array,
|
||||
$in_array,
|
||||
function ($value) use (&$return) {
|
||||
$return[] = $value;
|
||||
}
|
||||
@@ -447,13 +607,13 @@ class ArrayHandler
|
||||
/**
|
||||
* will loop through an array recursivly and write the array keys back
|
||||
*
|
||||
* @param array<mixed> $array multidemnsional array to flatten
|
||||
* @param array<mixed> $in_array multidemnsional array to flatten
|
||||
* @param array<mixed> $return recoursive pass on array of keys
|
||||
* @return array<mixed> flattened keys array
|
||||
*/
|
||||
public static function flattenArrayKey(array $array, array $return = []): array
|
||||
public static function flattenArrayKey(array $in_array, array $return = []): array
|
||||
{
|
||||
foreach ($array as $key => $sub) {
|
||||
foreach ($in_array as $key => $sub) {
|
||||
$return[] = $key;
|
||||
if (is_array($sub) && count($sub) > 0) {
|
||||
$return = self::flattenArrayKey($sub, $return);
|
||||
@@ -466,14 +626,14 @@ class ArrayHandler
|
||||
* as above will flatten an array, but in this case only the outmost
|
||||
* leave nodes, all other keyswill be skipped
|
||||
*
|
||||
* @param array<mixed> $array multidemnsional array to flatten
|
||||
* @param array<mixed> $in_array multidemnsional array to flatten
|
||||
* @return array<mixed> flattened keys array
|
||||
*/
|
||||
public static function flattenArrayKeyLeavesOnly(array $array): array
|
||||
public static function flattenArrayKeyLeavesOnly(array $in_array): array
|
||||
{
|
||||
$return = [];
|
||||
array_walk_recursive(
|
||||
$array,
|
||||
$in_array,
|
||||
function ($value, $key) use (&$return) {
|
||||
$return[] = $key;
|
||||
}
|
||||
@@ -485,14 +645,14 @@ class ArrayHandler
|
||||
* searches for key -> value in an array tree and writes the value one level up
|
||||
* this will remove this leaf will all other values
|
||||
*
|
||||
* @param array<mixed> $array nested array
|
||||
* @param array<mixed> $in_array nested array
|
||||
* @param string|int $search key to find that has no sub leaf
|
||||
* and will be pushed up
|
||||
* @return array<mixed> modified, flattened array
|
||||
*/
|
||||
public static function arrayFlatForKey(array $array, string|int $search): array
|
||||
public static function arrayFlatForKey(array $in_array, string|int $search): array
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
foreach ($in_array as $key => $value) {
|
||||
// if it is not an array do just nothing
|
||||
if (!is_array($value)) {
|
||||
continue;
|
||||
@@ -500,14 +660,14 @@ class ArrayHandler
|
||||
// probe it has search key
|
||||
if (isset($value[$search])) {
|
||||
// set as current
|
||||
$array[$key] = $value[$search];
|
||||
$in_array[$key] = $value[$search];
|
||||
} else {
|
||||
// call up next node down
|
||||
// $array[$key] = call_user_func(__METHOD__, $value, $search);
|
||||
$array[$key] = self::arrayFlatForKey($value, $search);
|
||||
// $in_array[$key] = call_user_func(__METHOD__, $value, $search);
|
||||
$in_array[$key] = self::arrayFlatForKey($value, $search);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
return $in_array;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,13 +677,13 @@ class ArrayHandler
|
||||
*
|
||||
* https://stackoverflow.com/a/369608
|
||||
*
|
||||
* @param array<mixed> $array Array where elements are located
|
||||
* @param array<mixed> $in_array Array where elements are located
|
||||
* @param array<mixed> $remove Elements to remove
|
||||
* @return array<mixed> Array with $remove elements removed
|
||||
*/
|
||||
public static function arrayRemoveEntry(array $array, array $remove): array
|
||||
public static function arrayRemoveEntry(array $in_array, array $remove): array
|
||||
{
|
||||
return array_diff($array, $remove);
|
||||
return array_diff($in_array, $remove);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -533,24 +693,124 @@ class ArrayHandler
|
||||
* key list is a list[string]
|
||||
* if key list is empty, return array as is
|
||||
*
|
||||
* @param array<string,mixed> $array
|
||||
* @param array<string,mixed> $in_array
|
||||
* @param array<string> $key_list
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function arrayReturnMatchingKeyOnly(
|
||||
array $array,
|
||||
array $in_array,
|
||||
array $key_list
|
||||
): array {
|
||||
// on empty return as is
|
||||
if (empty($key_list)) {
|
||||
return $array;
|
||||
return $in_array;
|
||||
}
|
||||
return array_filter(
|
||||
$array,
|
||||
$in_array,
|
||||
fn($key) => in_array($key, $key_list),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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__
|
||||
|
||||
@@ -395,39 +395,68 @@ class DateTime
|
||||
* does a reverse of the timeStringFormat and converts the string from
|
||||
* xd xh xm xs xms to a timestamp.microtime format
|
||||
*
|
||||
* @param string|int|float $timestring formatted interval
|
||||
* @return string|int|float converted float interval, or string as is
|
||||
* @param string|int|float $timestring formatted interval
|
||||
* @param bool $throw_exception [default=false] if set to true will throw exception
|
||||
* instead of returning input value as is
|
||||
* @return string|int|float converted float interval, or string as is
|
||||
*/
|
||||
public static function stringToTime(string|int|float $timestring): string|int|float
|
||||
{
|
||||
public static function stringToTime(
|
||||
string|int|float $timestring,
|
||||
bool $throw_exception = false
|
||||
): string|int|float {
|
||||
$timestamp = 0;
|
||||
if (!preg_match("/(d|h|m|s|ms)/", (string)$timestring)) {
|
||||
return $timestring;
|
||||
}
|
||||
$timestring = (string)$timestring;
|
||||
// pos for preg match read + multiply factor
|
||||
$timegroups = [2 => 86400, 4 => 3600, 6 => 60, 8 => 1];
|
||||
$matches = [];
|
||||
// if start with -, strip and set negative
|
||||
$negative = false;
|
||||
if (preg_match("/^-/", $timestring)) {
|
||||
$negative = true;
|
||||
$timestring = substr($timestring, 1);
|
||||
}
|
||||
// preg match: 0: full string
|
||||
// 2, 4, 6, 8 are the to need values
|
||||
preg_match("/^((\d+)d ?)?((\d+)h ?)?((\d+)m ?)?((\d+)s ?)?((\d+)ms)?$/", $timestring, $matches);
|
||||
if (
|
||||
!preg_match(
|
||||
"/^\s*(-)?\s*"
|
||||
. "((\d+)\s*d(?:ay(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*h(?:our(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*m(?:in(?:ute)?(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*s(?:ec(?:ond)?(?:s)?)?)?\s*"
|
||||
. "((\d+)\s*m(?:illi)?s(?:ec(?:ond)?(?:s)?)?)?\s*"
|
||||
. "$/",
|
||||
(string)$timestring,
|
||||
$matches
|
||||
)
|
||||
) {
|
||||
if ($throw_exception) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid time string format, cannot parse: "' . (string)$timestring . '"',
|
||||
1
|
||||
);
|
||||
}
|
||||
return $timestring;
|
||||
}
|
||||
if (count($matches) < 2) {
|
||||
if ($throw_exception) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid time string format, no interval value found: "' . (string)$timestring . '"',
|
||||
2
|
||||
);
|
||||
}
|
||||
return $timestring;
|
||||
}
|
||||
// pos for preg match read + multiply factor
|
||||
$timegroups = [3 => 86400, 5 => 3600, 7 => 60, 9 => 1];
|
||||
// if start with -, strip and set negative
|
||||
$negative = false;
|
||||
if (!empty($matches[1])) {
|
||||
$negative = true;
|
||||
}
|
||||
// multiply the returned matches and sum them up. the last one (ms) is added with .
|
||||
foreach ($timegroups as $i => $time_multiply) {
|
||||
if (isset($matches[$i]) && is_numeric($matches[$i])) {
|
||||
$timestamp += (float)$matches[$i] * $time_multiply;
|
||||
}
|
||||
}
|
||||
if (isset($matches[10]) && is_numeric($matches[10])) {
|
||||
$timestamp .= '.' . $matches[10];
|
||||
if (isset($matches[11]) && is_numeric($matches[11])) {
|
||||
// for milliseconds, we need to divide by 1000 and add them
|
||||
$timestamp += (float)($matches[11] / 1000);
|
||||
}
|
||||
if ($negative) {
|
||||
// cast to flaot so we can do a negative multiplication
|
||||
// cast to float so we can do a negative multiplication
|
||||
$timestamp = (float)$timestamp * -1;
|
||||
}
|
||||
return $timestamp;
|
||||
@@ -639,16 +668,26 @@ class DateTime
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $return_named return array type, false (default), true for named
|
||||
* @return array<mixed> 0/overall, 1/weekday, 2/weekend
|
||||
* @param bool $return_named [default=false] return array type, false (default), true for named
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{0:int,1:int,2:int,3:bool}|array{overall:int,weekday:int,weekend:int,reverse:bool}
|
||||
* 0/overall, 1/weekday, 2/weekend, 3/reverse
|
||||
*/
|
||||
public static function calcDaysInterval(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $return_named = false
|
||||
bool $return_named = false,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
// pos 0 all, pos 1 weekday, pos 2 weekend
|
||||
$days = [];
|
||||
$days = [
|
||||
0 => 0,
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => false,
|
||||
];
|
||||
// if anything invalid, return 0,0,0
|
||||
try {
|
||||
$start = new \DateTime($start_date);
|
||||
@@ -659,19 +698,30 @@ class DateTime
|
||||
'overall' => 0,
|
||||
'weekday' => 0,
|
||||
'weekend' => 0,
|
||||
'reverse' => false
|
||||
];
|
||||
} else {
|
||||
return [0, 0, 0];
|
||||
return $days;
|
||||
}
|
||||
}
|
||||
// so we include the last day too, we need to add +1 second in the time
|
||||
$end->setTime(0, 0, 1);
|
||||
// if end date before start date, only this will be filled
|
||||
$days[0] = $end->diff($start)->days;
|
||||
$days[1] = 0;
|
||||
$days[2] = 0;
|
||||
// if start is before end, switch dates and flag
|
||||
$days[3] = false;
|
||||
if ($start > $end) {
|
||||
$new_start = $end;
|
||||
$end = $start;
|
||||
$start = $new_start;
|
||||
$days[3] = true;
|
||||
}
|
||||
// get period for weekends/weekdays
|
||||
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end);
|
||||
$options = 0;
|
||||
if ($include_end_date) {
|
||||
$options |= \DatePeriod::INCLUDE_END_DATE;
|
||||
}
|
||||
if ($exclude_start_date) {
|
||||
$options |= \DatePeriod::EXCLUDE_START_DATE;
|
||||
}
|
||||
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end, $options);
|
||||
foreach ($period as $dt) {
|
||||
$curr = $dt->format('D');
|
||||
if ($curr == 'Sat' || $curr == 'Sun') {
|
||||
@@ -679,18 +729,80 @@ class DateTime
|
||||
} else {
|
||||
$days[1]++;
|
||||
}
|
||||
$days[0]++;
|
||||
}
|
||||
if ($return_named === true) {
|
||||
return [
|
||||
'overall' => $days[0],
|
||||
'weekday' => $days[1],
|
||||
'weekend' => $days[2],
|
||||
'reverse' => $days[3],
|
||||
];
|
||||
} else {
|
||||
return $days;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for calcDaysInterval with numeric return only
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{0:int,1:int,2:int,3:bool}
|
||||
*/
|
||||
public static function calcDaysIntervalNumIndex(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
$values = self::calcDaysInterval(
|
||||
$start_date,
|
||||
$end_date,
|
||||
false,
|
||||
$include_end_date,
|
||||
$exclude_start_date
|
||||
);
|
||||
return [
|
||||
$values[0] ?? 0,
|
||||
$values[1] ?? 0,
|
||||
$values[2] ?? 0,
|
||||
$values[3] ?? false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for calcDaysInterval with named return only
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{overall:int,weekday:int,weekend:int,reverse:bool}
|
||||
*/
|
||||
public static function calcDaysIntervalNamedIndex(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
$values = self::calcDaysInterval(
|
||||
$start_date,
|
||||
$end_date,
|
||||
true,
|
||||
$include_end_date,
|
||||
$exclude_start_date
|
||||
);
|
||||
return [
|
||||
'overall' => $values['overall'] ?? 0,
|
||||
'weekday' => $values['weekday'] ?? 0,
|
||||
'weekend' => $values['weekend'] ?? 0,
|
||||
'reverse' => $values['reverse'] ?? false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a weekend day (sat/sun) is in the given date range
|
||||
* Can have time too, but is not needed
|
||||
@@ -705,6 +817,13 @@ class DateTime
|
||||
): bool {
|
||||
$dd_start = new \DateTime($start_date);
|
||||
$dd_end = new \DateTime($end_date);
|
||||
// flip if start is after end
|
||||
if ($dd_start > $dd_end) {
|
||||
$new_start = $dd_end;
|
||||
$dd_end = $dd_start;
|
||||
$dd_start = $new_start;
|
||||
}
|
||||
// if start > end, flip
|
||||
if (
|
||||
// starts with a weekend
|
||||
$dd_start->format('N') >= 6 ||
|
||||
|
||||
@@ -14,6 +14,7 @@ class Byte
|
||||
public const BYTE_FORMAT_NOSPACE = 1;
|
||||
public const BYTE_FORMAT_ADJUST = 2;
|
||||
public const BYTE_FORMAT_SI = 4;
|
||||
public const RETURN_AS_STRING = 8;
|
||||
|
||||
/**
|
||||
* This function replaces the old byteStringFormat
|
||||
@@ -77,7 +78,7 @@ class Byte
|
||||
// labels in order of size [Y, Z]
|
||||
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
|
||||
// exp position calculation
|
||||
$exp = floor(log($abs_bytes, $unit));
|
||||
$exp = (int)floor(log($abs_bytes, $unit));
|
||||
// avoid printing out anything larger than max labels
|
||||
if ($exp >= count($labels)) {
|
||||
$exp = count($labels) - 1;
|
||||
@@ -119,7 +120,9 @@ class Byte
|
||||
* @param int $flags bitwise flag with use space turned on
|
||||
* BYTE_FORMAT_SI: use 1000 instead of 1024
|
||||
* @return string|int|float converted value or original value
|
||||
* @throws \InvalidArgumentException 1: no valid flag set
|
||||
* @throws \InvalidArgumentException no valid flag set
|
||||
* @throws \LengthException number too large to convert to int
|
||||
* @throws \RuntimeException BCMath extension not loaded if flag is set to string
|
||||
*/
|
||||
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
|
||||
{
|
||||
@@ -129,7 +132,12 @@ class Byte
|
||||
} else {
|
||||
$si = false;
|
||||
}
|
||||
if ($flags != 0 && $flags != 4) {
|
||||
if ($flags & self::RETURN_AS_STRING) {
|
||||
$return_as_string = true;
|
||||
} else {
|
||||
$return_as_string = false;
|
||||
}
|
||||
if ($flags != 0 && $flags != 4 && $flags != 8 && $flags != 12) {
|
||||
throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
|
||||
}
|
||||
// matches in regex
|
||||
@@ -142,6 +150,10 @@ class Byte
|
||||
strtolower((string)$number),
|
||||
$matches
|
||||
);
|
||||
$number_negative = false;
|
||||
if (!empty($matches[1])) {
|
||||
$number_negative = true;
|
||||
}
|
||||
if (isset($matches[2]) && isset($matches[3])) {
|
||||
// remove all non valid characters from the number
|
||||
$number = preg_replace('/[^0-9\.]/', '', $matches[2]);
|
||||
@@ -152,12 +164,48 @@ class Byte
|
||||
if ($unit) {
|
||||
$number = $number * pow($si ? 1000 : 1024, stripos($valid_units_, $unit[0]) ?: 0);
|
||||
}
|
||||
// if the number is too large, we cannot convert to int directly
|
||||
if ($number <= PHP_INT_MIN || $number >= PHP_INT_MAX) {
|
||||
// if we do not want to convert to string
|
||||
if (!$return_as_string) {
|
||||
throw new \LengthException(
|
||||
'Number too large be converted to int: ' . (string)$number
|
||||
);
|
||||
}
|
||||
// for string, check if bcmath is loaded, if not this will not work
|
||||
if (!extension_loaded('bcmath')) {
|
||||
throw new \RuntimeException(
|
||||
'Number too large be converted to int and BCMath extension not loaded: ' . (string)$number
|
||||
);
|
||||
}
|
||||
}
|
||||
// string return
|
||||
if ($return_as_string) {
|
||||
// return as string to avoid overflow
|
||||
// $number = (string)round($number);
|
||||
$number = bcmul(number_format(
|
||||
$number,
|
||||
12,
|
||||
'.',
|
||||
''
|
||||
), "1");
|
||||
if ($number_negative) {
|
||||
$number = '-' . $number;
|
||||
}
|
||||
return $number;
|
||||
}
|
||||
// convert to INT to avoid +E output
|
||||
$number = (int)round($number);
|
||||
// if negative input, keep nnegative
|
||||
if (!empty($matches[1])) {
|
||||
if ($number_negative) {
|
||||
$number *= -1;
|
||||
}
|
||||
// check if number is negative but should be, this is Lenght overflow
|
||||
if (!$number_negative && $number < 0) {
|
||||
throw new \LengthException(
|
||||
'Number too large be converted to int: ' . (string)$number
|
||||
);
|
||||
}
|
||||
}
|
||||
// if not matching return as is
|
||||
return $number;
|
||||
|
||||
@@ -23,14 +23,14 @@ class Encoding
|
||||
* @param bool $auto_check default true, if source encoding is set
|
||||
* check that the source is actually matching
|
||||
* 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(
|
||||
string $string,
|
||||
string $to_encoding,
|
||||
string $source_encoding = '',
|
||||
bool $auto_check = true
|
||||
): string {
|
||||
): string|false {
|
||||
// set if not given
|
||||
if (!$source_encoding) {
|
||||
$source_encoding = mb_detect_encoding($string);
|
||||
|
||||
@@ -10,9 +10,16 @@ namespace CoreLibs\Convert;
|
||||
|
||||
class Html
|
||||
{
|
||||
/** @var int */
|
||||
public const SELECTED = 0;
|
||||
/** @var int */
|
||||
public const CHECKED = 1;
|
||||
|
||||
// TODO: check for not valid htmlentites encoding
|
||||
// as of PHP 8.4: https://www.php.net/manual/en/function.htmlentities.php
|
||||
/** @#var array<string> */
|
||||
// public const VALID_HTMLENT_ENCODINGS = [];
|
||||
|
||||
/**
|
||||
* full wrapper for html entities
|
||||
*
|
||||
@@ -22,14 +29,19 @@ class Html
|
||||
* encodes in UTF-8
|
||||
* does not double encode
|
||||
*
|
||||
* @param mixed $string string to html encode
|
||||
* @param int $flags [default: ENT_QUOTES | ENT_HTML5]
|
||||
* @param mixed $string string to html encode
|
||||
* @param int $flags [default=ENT_QUOTES | ENT_HTML5]
|
||||
* @param string $encoding [default=UTF-8]
|
||||
* @return mixed if string, encoded, else as is (eg null)
|
||||
*/
|
||||
public static function htmlent(mixed $string, int $flags = ENT_QUOTES | ENT_HTML5): mixed
|
||||
{
|
||||
public static function htmlent(
|
||||
mixed $string,
|
||||
int $flags = ENT_QUOTES | ENT_HTML5,
|
||||
string $encoding = 'UTF-8'
|
||||
): mixed {
|
||||
if (is_string($string)) {
|
||||
return htmlentities($string, $flags, 'UTF-8', false);
|
||||
// if not a valid encoding this will throw a warning and use UTF-8
|
||||
return htmlentities($string, $flags, $encoding, false);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
@@ -37,7 +49,7 @@ class Html
|
||||
/**
|
||||
* strips out all line breaks or replaced with given string
|
||||
* @param string $string string
|
||||
* @param string $replace replace character, default ' '
|
||||
* @param string $replace [default=' '] replace character
|
||||
* @return string cleaned string without any line breaks
|
||||
*/
|
||||
public static function removeLB(string $string, string $replace = ' '): string
|
||||
|
||||
@@ -27,10 +27,14 @@ class Json
|
||||
* set original value as array
|
||||
* @return array<mixed> returns an array from the json values
|
||||
*/
|
||||
public static function jsonConvertToArray(?string $json, bool $override = false): array
|
||||
public static function jsonConvertToArray(?string $json, bool $override = false, int $flags = 0): array
|
||||
{
|
||||
if ($json !== null) {
|
||||
$_json = json_decode($json, true);
|
||||
// if flags has JSON_THROW_ON_ERROR remove it
|
||||
if ($flags & JSON_THROW_ON_ERROR) {
|
||||
$flags = $flags & ~JSON_THROW_ON_ERROR;
|
||||
}
|
||||
$_json = json_decode($json, true, flags:$flags);
|
||||
if (self::$json_last_error = json_last_error()) {
|
||||
if ($override == true) {
|
||||
// init return as array with original as element
|
||||
@@ -55,16 +59,31 @@ class Json
|
||||
* Deos not throw errors
|
||||
*
|
||||
* @param array<mixed> $data
|
||||
* @param int $flags json_encode flags as is
|
||||
* @param int $flags [JSON_UNESCAPED_UNICODE] json_encode flags as is
|
||||
* @return string JSON string or '{}' if false
|
||||
*/
|
||||
public static function jsonConvertArrayTo(array $data, int $flags = 0): string
|
||||
public static function jsonConvertArrayTo(array $data, int $flags = JSON_UNESCAPED_UNICODE): string
|
||||
{
|
||||
$json_string = json_encode($data, $flags) ?: '{}';
|
||||
self::$json_last_error = json_last_error();
|
||||
return (string)$json_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if a json string could be decoded.
|
||||
* Weill set the internval last error state and info can be read with jsonGetLastError
|
||||
*
|
||||
* @param string $json
|
||||
* @param int $flags only JSON_INVALID_UTF8_IGNORE is currently allowed
|
||||
* @return bool
|
||||
*/
|
||||
public static function jsonValidate(string $json, int $flags = 0): bool
|
||||
{
|
||||
$json_valid = json_validate($json, flags:$flags);
|
||||
self::$json_last_error = json_last_error();
|
||||
return $json_valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns human readable string for json errors thrown in jsonConvertToArray
|
||||
* Source: https://www.php.net/manual/en/function.json-last-error.php
|
||||
@@ -119,6 +138,23 @@ class Json
|
||||
}
|
||||
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__
|
||||
|
||||
@@ -62,10 +62,15 @@ class Math
|
||||
*
|
||||
* @param float $number Number to cubic root
|
||||
* @return float Calculated value
|
||||
* @throws \InvalidArgumentException if $number is negative
|
||||
*/
|
||||
public static function cbrt(float|int $number): float
|
||||
{
|
||||
return pow((float)$number, 1.0 / 3);
|
||||
$value = pow((float)$number, 1.0 / 3);
|
||||
if (is_nan($value)) {
|
||||
throw new \InvalidArgumentException('cube root from this number is not supported: ' . $number);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,15 +204,17 @@ class Math
|
||||
callback: fn ($col) => is_array($row) ?
|
||||
array_reduce(
|
||||
array: $row,
|
||||
callback: fn ($a, $v, $i = null) => $a + $v * (
|
||||
// TODO check that v is not an array
|
||||
callback: fn ($a, $v, $i = null) => $a + $v * ( /** @phpstan-ignore-line Possible array + int */
|
||||
// if last entry missing for full copy add a 0 to it
|
||||
$col[$i ?? array_search($v, $row, true)] ?? 0 /** @phpstan-ignore-line */
|
||||
$col[$i ?? array_search($v, $row, true)] ?? 0
|
||||
),
|
||||
initial: 0,
|
||||
) :
|
||||
array_reduce(
|
||||
array: $col,
|
||||
callback: fn ($a, $v) => $a + $v * $row,
|
||||
// TODO check that v is not an array
|
||||
callback: fn ($a, $v) => $a + $v * $row, /** @phpstan-ignore-line Possible array + int */
|
||||
initial: 0,
|
||||
),
|
||||
array: $bCols,
|
||||
|
||||
@@ -8,8 +8,20 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Convert;
|
||||
|
||||
use CoreLibs\Combined\ArrayHandler;
|
||||
|
||||
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
|
||||
* 0 if nothing / invalid split
|
||||
@@ -52,29 +64,42 @@ class Strings
|
||||
* Note a string LONGER then the maxium will be attached with the LAST
|
||||
* split character. In above exmaple
|
||||
* 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_characters list of charcters with which we split
|
||||
* if not set uses dash ('-')
|
||||
* @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(
|
||||
string $value,
|
||||
string $string,
|
||||
string $split_format,
|
||||
string $split_characters = '-'
|
||||
): string {
|
||||
if (
|
||||
// abort if split format is empty
|
||||
empty($split_format) ||
|
||||
// if not in the valid ASCII character range for any of the strings
|
||||
preg_match('/[^\x20-\x7e]/', $value) ||
|
||||
// preg_match('/[^\x20-\x7e]/', $split_format) ||
|
||||
preg_match('/[^\x20-\x7e]/', $split_characters) ||
|
||||
// only numbers and split characters in split_format
|
||||
!preg_match("/[0-9" . $split_characters . "]/", $split_format)
|
||||
) {
|
||||
return $value;
|
||||
// skip if string or split format is empty is empty
|
||||
if (empty($string) || empty($split_format)) {
|
||||
return $string;
|
||||
}
|
||||
if (preg_match('/[^\x20-\x7e]/', $string)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"The string to split can only be ascii characters: " . $string
|
||||
);
|
||||
}
|
||||
// get the split characters that are not numerical and check they are ascii
|
||||
$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_list = preg_split(
|
||||
@@ -86,14 +111,14 @@ class Strings
|
||||
);
|
||||
// if this is false, or only one array, abort split
|
||||
if (!is_array($split_list) || count($split_list) == 1) {
|
||||
return $value;
|
||||
return $string;
|
||||
}
|
||||
$out = '';
|
||||
$pos = 0;
|
||||
$last_split = '';
|
||||
foreach ($split_list as $offset) {
|
||||
if (is_numeric($offset)) {
|
||||
$_part = substr($value, $pos, (int)$offset);
|
||||
$_part = substr($string, $pos, (int)$offset);
|
||||
if (empty($_part)) {
|
||||
break;
|
||||
}
|
||||
@@ -104,8 +129,8 @@ class Strings
|
||||
$last_split = $offset;
|
||||
}
|
||||
}
|
||||
if (!empty($out) && $pos < strlen($value)) {
|
||||
$out .= $last_split . substr($value, $pos);
|
||||
if (!empty($out) && $pos < strlen($string)) {
|
||||
$out .= $last_split . substr($string, $pos);
|
||||
}
|
||||
// if last is not alphanumeric remove, remove
|
||||
if (!strcspn(substr($out, -1, 1), $split_characters)) {
|
||||
@@ -115,10 +140,49 @@ class Strings
|
||||
if (!empty($out)) {
|
||||
return $out;
|
||||
} 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
|
||||
* eg: //foo///bar/foo.inc -> /foo/bar/foo.inc
|
||||
@@ -146,6 +210,165 @@ class Strings
|
||||
{
|
||||
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__
|
||||
|
||||
@@ -38,6 +38,7 @@ class Email
|
||||
* @param string $encoding Encoding, if not set UTF-8
|
||||
* @param bool $kv_folding If set to true and a valid encoding, do KV folding
|
||||
* @return string Correctly encoded and build email string
|
||||
* @throws \IntlException if email name cannot be converted to UTF-8
|
||||
*/
|
||||
public static function encodeEmailName(
|
||||
string $email,
|
||||
@@ -52,6 +53,10 @@ class Email
|
||||
if ($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 =
|
||||
mb_encode_mimeheader(
|
||||
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,
|
||||
* do KV folding
|
||||
* @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(
|
||||
string $subject,
|
||||
@@ -102,6 +109,12 @@ class Email
|
||||
$subject = mb_convert_encoding($subject, $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
|
||||
$subject = mb_encode_mimeheader(
|
||||
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
|
||||
|
||||
@@ -10,9 +10,14 @@ namespace CoreLibs\Create;
|
||||
|
||||
class Hash
|
||||
{
|
||||
/** @var string default short hash -> deprecated use STANDARD_HASH_SHORT */
|
||||
public const DEFAULT_HASH = 'adler32';
|
||||
/** @var string default long hash (40 chars) */
|
||||
public const STANDARD_HASH_LONG = 'ripemd160';
|
||||
/** @var string default short hash (8 chars) */
|
||||
public const STANDARD_HASH_SHORT = 'adler32';
|
||||
/** @var string this is the standard hash to use hashStd and hash (64 chars) */
|
||||
public const STANDARD_HASH = 'sha256';
|
||||
|
||||
/**
|
||||
* checks php version and if >=5.2.7 it will flip the string
|
||||
@@ -20,6 +25,7 @@ class Hash
|
||||
* hash returns false
|
||||
* preg_replace fails for older php version
|
||||
* Use __hash with crc32b or hash('crc32b', ...) for correct output
|
||||
* For future short hashes use hashShort() instead
|
||||
*
|
||||
* @param string $string string to crc
|
||||
* @return string crc32b hash (old type)
|
||||
@@ -43,19 +49,31 @@ class Hash
|
||||
* replacement for __crc32b call
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @param bool $use_sha use sha instead of crc32b (default false)
|
||||
* @param bool $use_sha [default=false] use sha1 instead of crc32b
|
||||
* @return string hash of the string
|
||||
* @deprecated use __crc32b() for drop in replacement with default, or sha1Short() for use sha true
|
||||
*/
|
||||
public static function __sha1Short(string $string, bool $use_sha = false): string
|
||||
{
|
||||
if ($use_sha) {
|
||||
// return only the first 9 characters
|
||||
return substr(hash('sha1', $string), 0, 9);
|
||||
return self::sha1Short($string);
|
||||
} else {
|
||||
return self::__crc32b($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a short sha1
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function sha1Short(string $string): string
|
||||
{
|
||||
// return only the first 9 characters
|
||||
return substr(hash('sha1', $string), 0, 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* replacemend for __crc32b call (alternate)
|
||||
* defaults to adler 32
|
||||
@@ -63,34 +81,135 @@ class Hash
|
||||
* all that create 8 char long hashes
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @param string $hash_type hash type (default adler32)
|
||||
* @param string $hash_type [default=STANDARD_HASH_SHORT] hash type (default adler32)
|
||||
* @return string hash of the string
|
||||
* @deprecated use hashShort() of short hashes with adler 32 or hash() for other hash types
|
||||
*/
|
||||
public static function __hash(
|
||||
string $string,
|
||||
string $hash_type = self::DEFAULT_HASH
|
||||
string $hash_type = self::STANDARD_HASH_SHORT
|
||||
): string {
|
||||
return self::hash($string, $hash_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if hash type is valid, returns false if not
|
||||
*
|
||||
* @param string $hash_type
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidHashType(string $hash_type): bool
|
||||
{
|
||||
if (!in_array($hash_type, hash_algos())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if hash hmac type is valid, returns false if not
|
||||
*
|
||||
* @param string $hash_hmac_type
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidHashHmacType(string $hash_hmac_type): bool
|
||||
{
|
||||
if (!in_array($hash_hmac_type, hash_hmac_algos())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a hash over string if any valid hash given.
|
||||
* if no hash type set use sha256
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @param string $hash_type [default=STANDARD_HASH] hash type (default sha256)
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function hash(
|
||||
string $string,
|
||||
string $hash_type = self::STANDARD_HASH
|
||||
): string {
|
||||
// if not empty, check if in valid list
|
||||
if (
|
||||
empty($hash_type) ||
|
||||
!in_array($hash_type, hash_algos())
|
||||
) {
|
||||
// fallback to default hash type if none set or invalid
|
||||
$hash_type = self::DEFAULT_HASH;
|
||||
// fallback to default hash type if empty or invalid
|
||||
$hash_type = self::STANDARD_HASH;
|
||||
}
|
||||
return hash($hash_type, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for standard long hashd
|
||||
* creates a hash mac key
|
||||
*
|
||||
* @param string $string string to hash mac
|
||||
* @param string $key key to use
|
||||
* @param string $hash_type [default=STANDARD_HASH]
|
||||
* @return string hash mac string
|
||||
*/
|
||||
public static function hashHmac(
|
||||
string $string,
|
||||
#[\SensitiveParameter]
|
||||
string $key,
|
||||
string $hash_type = self::STANDARD_HASH
|
||||
): string {
|
||||
if (
|
||||
empty($hash_type) ||
|
||||
!in_array($hash_type, hash_hmac_algos())
|
||||
) {
|
||||
// fallback to default hash type if e or invalid
|
||||
$hash_type = self::STANDARD_HASH;
|
||||
}
|
||||
return hash_hmac($hash_type, $string, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* short hash with max length of 8, uses adler32
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function hashShort(string $string): string
|
||||
{
|
||||
return hash(self::STANDARD_HASH_SHORT, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for standard long hash
|
||||
*
|
||||
* @param string $string String to be hashed
|
||||
* @return string Hashed string
|
||||
* @deprecated use hashLong()
|
||||
*/
|
||||
public static function __hashLong(string $string): string
|
||||
{
|
||||
return self::hashLong($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for standard long hash, uses ripmd160
|
||||
*
|
||||
* @param string $string String to be hashed
|
||||
* @return string Hashed string
|
||||
*/
|
||||
public static function __hashLong(string $string): string
|
||||
public static function hashLong(string $string): string
|
||||
{
|
||||
return hash(self::STANDARD_HASH_LONG, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* create standard hash basd on STANDAR_HASH, currently sha256
|
||||
*
|
||||
* @param string $string string in
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function hashStd(string $string): string
|
||||
{
|
||||
return self::hash($string, self::STANDARD_HASH);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -8,39 +8,97 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Create;
|
||||
|
||||
use CoreLibs\Convert\Strings;
|
||||
|
||||
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
|
||||
/** @var string */
|
||||
private static string $key_range = '';
|
||||
/** @var int */
|
||||
private static int $one_key_length;
|
||||
/** @var int */
|
||||
private static int $key_length = 4; // default key length
|
||||
/** @var int */
|
||||
private static int $max_key_length = 256; // max allowed length
|
||||
/** @var string all the characters that are int he current radnom key range */
|
||||
private static string $key_character_range = '';
|
||||
/** @var int character count in they key character range */
|
||||
private static int $key_character_range_length = 0;
|
||||
/** @var int default key lenghth */
|
||||
/** @deprecated Will be removed, as setting has moved to randomKeyGen */
|
||||
private static int $key_length = 4;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param array<string> $key_range a list of key ranges as array
|
||||
* @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
|
||||
self::$key_range = join('', array_merge(
|
||||
range('A', 'Z'),
|
||||
range('a', 'z'),
|
||||
range('0', '9')
|
||||
));
|
||||
self::$one_key_length = strlen(self::$key_range);
|
||||
// if key range is not set
|
||||
if (!count($key_range)) {
|
||||
self::$key_character_range = self::KEY_CHARACTER_RANGE_DEFAULT;
|
||||
} else {
|
||||
self::$key_character_range = self::validateRandomKeyData(...$key_range);
|
||||
// random key generation base string
|
||||
}
|
||||
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
|
||||
* @return bool true for valid, false for invalid length
|
||||
*/
|
||||
private static function validateRandomKeyLenght(int $key_length): bool
|
||||
private static function validateRandomKeyLength(int $key_length): bool
|
||||
{
|
||||
if (
|
||||
$key_length > 0 &&
|
||||
$key_length <= self::$max_key_length
|
||||
$key_length <= self::KEY_LENGTH_MAX
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
@@ -67,11 +125,12 @@ class RandomKey
|
||||
*
|
||||
* @param int $key_length key length
|
||||
* @return bool true/false for set status
|
||||
* @deprecated This function does no longer set the key length, the randomKeyGen parameter has to be used
|
||||
*/
|
||||
public static function setRandomKeyLength(int $key_length): bool
|
||||
{
|
||||
// only if valid int key with valid length
|
||||
if (self::validateRandomKeyLenght($key_length) === true) {
|
||||
if (self::validateRandomKeyLength($key_length) === true) {
|
||||
self::$key_length = $key_length;
|
||||
return true;
|
||||
} else {
|
||||
@@ -83,6 +142,7 @@ class RandomKey
|
||||
* get the current set random 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
|
||||
{
|
||||
@@ -94,28 +154,37 @@ class RandomKey
|
||||
* if override key length is set, it will check on valid key and use this
|
||||
* this will not set the class key length variable
|
||||
*
|
||||
* @param int $key_length key length override, -1 for use default
|
||||
* @return string random key
|
||||
* @param int $key_length [default=-1] key length override,
|
||||
* 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
|
||||
{
|
||||
// init random key strings if not set
|
||||
if (
|
||||
!isset(self::$one_key_length)
|
||||
) {
|
||||
self::initRandomKeyData();
|
||||
}
|
||||
$use_key_length = 0;
|
||||
// only if valid int key with valid length
|
||||
if (self::validateRandomKeyLenght($key_length) === true) {
|
||||
$use_key_length = $key_length;
|
||||
public static function randomKeyGen(
|
||||
int $key_length = self::KEY_LENGTH_DEFAULT,
|
||||
array ...$key_range
|
||||
): string {
|
||||
$key_character_range = '';
|
||||
if (count($key_range)) {
|
||||
$key_character_range = self::validateRandomKeyData(...$key_range);
|
||||
$key_character_range_length = strlen($key_character_range);
|
||||
} 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
|
||||
$random_string = '';
|
||||
for ($i = 1; $i <= $use_key_length; $i++) {
|
||||
$random_string .= self::$key_range[random_int(0, self::$one_key_length - 1)];
|
||||
for ($i = 1; $i <= $key_length; $i++) {
|
||||
$random_string .= $key_character_range[
|
||||
random_int(0, $key_character_range_length - 1)
|
||||
];
|
||||
}
|
||||
return $random_string;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class Uids
|
||||
*/
|
||||
public static function validateUuuidv4(string $uuidv4): bool
|
||||
{
|
||||
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/", $uuidv4)) {
|
||||
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i", $uuidv4)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -39,9 +39,9 @@ class ArrayIO extends \CoreLibs\DB\IO
|
||||
{
|
||||
// main calss variables
|
||||
/** @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 */
|
||||
private string $table_name; // the table_name
|
||||
private string $table_name = ''; // the table_name
|
||||
/** @var string */
|
||||
private string $pk_name = ''; // the primary key from this table
|
||||
/** @var int|string|null */
|
||||
@@ -127,9 +127,9 @@ class ArrayIO extends \CoreLibs\DB\IO
|
||||
public function getTableArray(bool $reset = false): array
|
||||
{
|
||||
if (!$reset) {
|
||||
return $this->table_array ?? [];
|
||||
return $this->table_array;
|
||||
}
|
||||
$table_array = $this->table_array ?? [];
|
||||
$table_array = $this->table_array;
|
||||
reset($table_array);
|
||||
return $table_array;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ class ArrayIO extends \CoreLibs\DB\IO
|
||||
*/
|
||||
public function getTableName(): string
|
||||
{
|
||||
return $this->table_name ?? '';
|
||||
return $this->table_name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -303,6 +303,8 @@ class IO
|
||||
private string $query = '';
|
||||
/** @var array<mixed> current params for query */
|
||||
private array $params = [];
|
||||
/** @var string current hash build from query and params */
|
||||
private string $query_hash = '';
|
||||
// if we do have a convert call, store the convert data in here, else it will be empty
|
||||
/** @var array{}|array{original:array{query:string,params:array<mixed>},type:''|'named'|'numbered'|'question_mark',found:int,matches:array<string>,params_lookup:array<mixed>,query:string,params:array<mixed>} */
|
||||
private array $placeholder_converted = [];
|
||||
@@ -500,7 +502,7 @@ class IO
|
||||
die('<!-- Cannot load db functions class for: ' . $this->db_type . ' -->');
|
||||
}
|
||||
// write to internal one, once OK
|
||||
$this->db_functions = $db_functions;
|
||||
$this->db_functions = $db_functions; /** @phan-suppress-current-line PhanPossiblyNullTypeMismatchProperty */
|
||||
|
||||
// connect to DB
|
||||
if (!$this->__connectToDB()) {
|
||||
@@ -1319,7 +1321,7 @@ class IO
|
||||
*/
|
||||
private function __dbCountQueryParams(string $query): int
|
||||
{
|
||||
return $this->db_functions->__dbCountQueryParams($query);
|
||||
return count($this->db_functions->__dbGetQueryParams($query));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1382,6 +1384,8 @@ class IO
|
||||
$this->query = $query;
|
||||
// current params
|
||||
$this->params = $params;
|
||||
// empty on new
|
||||
$this->query_hash = '';
|
||||
// no query set
|
||||
if (empty($this->query)) {
|
||||
$this->__dbError(11);
|
||||
@@ -1413,10 +1417,7 @@ class IO
|
||||
$this->pk_name_table[$table] ?
|
||||
$this->pk_name_table[$table] : 'NULL';
|
||||
}
|
||||
if (
|
||||
!preg_match(self::REGEX_RETURNING, $this->query) &&
|
||||
$this->pk_name && $this->pk_name != 'NULL'
|
||||
) {
|
||||
if (!preg_match(self::REGEX_RETURNING, $this->query) && $this->pk_name != 'NULL') {
|
||||
// check if this query has a ; at the end and remove it
|
||||
$__query = preg_replace("/(;\s*)$/", '', $this->query);
|
||||
// must be query, if preg replace failed, use query as before
|
||||
@@ -1426,7 +1427,7 @@ class IO
|
||||
} elseif (
|
||||
preg_match(self::REGEX_RETURNING, $this->query, $matches)
|
||||
) {
|
||||
if ($this->pk_name && $this->pk_name != 'NULL') {
|
||||
if ($this->pk_name != 'NULL') {
|
||||
// add the primary key if it is not in the returning set
|
||||
if (!preg_match("/$this->pk_name/", $matches[1])) {
|
||||
$this->query .= " , " . $this->pk_name;
|
||||
@@ -1444,7 +1445,7 @@ class IO
|
||||
$this->returning_id = true;
|
||||
}
|
||||
// import protection, hash needed
|
||||
$query_hash = $this->dbGetQueryHash($this->query, $this->params);
|
||||
$query_hash = $this->dbBuildQueryHash($this->query, $this->params);
|
||||
// QUERY PARAMS: run query params check and rewrite
|
||||
if ($this->dbGetConvertPlaceholder() === true) {
|
||||
try {
|
||||
@@ -1478,7 +1479,8 @@ class IO
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// set query hash
|
||||
$this->query_hash = $query_hash;
|
||||
// $this->debug('DB IO', 'Q: ' . $this->query . ', RETURN: ' . $this->returning_id);
|
||||
// for DEBUG, only on first time ;)
|
||||
$this->__dbDebug(
|
||||
@@ -1962,7 +1964,7 @@ class IO
|
||||
{
|
||||
// set start array
|
||||
if ($query) {
|
||||
$array = $this->cursor_ext[$this->dbGetQueryHash($query)] ?? [];
|
||||
$array = $this->cursor_ext[$this->dbBuildQueryHash($query)] ?? [];
|
||||
} else {
|
||||
$array = $this->cursor_ext;
|
||||
}
|
||||
@@ -2364,7 +2366,7 @@ class IO
|
||||
return false;
|
||||
}
|
||||
// create hash from query ...
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
$query_hash = $this->dbBuildQueryHash($query, $params);
|
||||
// pre declare array
|
||||
if (!isset($this->cursor_ext[$query_hash])) {
|
||||
$this->cursor_ext[$query_hash] = [
|
||||
@@ -2542,7 +2544,10 @@ class IO
|
||||
} // only go if NO 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) {
|
||||
$this->cursor_ext[$query_hash]['log'][] = 'First call';
|
||||
// count the rows returned (if select)
|
||||
@@ -2940,13 +2945,15 @@ class IO
|
||||
* data to create a unique call one, optional
|
||||
* @return bool False if query not found, true if success
|
||||
*/
|
||||
public function dbCacheReset(string $query, array $params = []): bool
|
||||
public function dbCacheReset(string $query, array $params = [], bool $show_warning = true): bool
|
||||
{
|
||||
$this->__dbErrorReset();
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
$query_hash = $this->dbBuildQueryHash($query, $params);
|
||||
// clears cache for this query
|
||||
if (empty($this->cursor_ext[$query_hash]['query'])) {
|
||||
$this->__dbError(18, context: [
|
||||
if (
|
||||
$show_warning &&
|
||||
empty($this->cursor_ext[$query_hash]['query'])
|
||||
) {
|
||||
$this->__dbWarning(18, context: [
|
||||
'query' => $query,
|
||||
'params' => $params,
|
||||
'hash' => $query_hash,
|
||||
@@ -2985,7 +2992,7 @@ class IO
|
||||
if ($query === null) {
|
||||
return $this->cursor_ext;
|
||||
}
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
$query_hash = $this->dbBuildQueryHash($query, $params);
|
||||
if (
|
||||
!empty($this->cursor_ext) &&
|
||||
isset($this->cursor_ext[$query_hash])
|
||||
@@ -3015,7 +3022,7 @@ class IO
|
||||
$this->__dbError(11);
|
||||
return false;
|
||||
}
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
$query_hash = $this->dbBuildQueryHash($query, $params);
|
||||
if (
|
||||
!empty($this->cursor_ext) &&
|
||||
isset($this->cursor_ext[$query_hash])
|
||||
@@ -3041,7 +3048,7 @@ class IO
|
||||
$this->__dbError(11);
|
||||
return false;
|
||||
}
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
$query_hash = $this->dbBuildQueryHash($query, $params);
|
||||
if (
|
||||
!empty($this->cursor_ext) &&
|
||||
isset($this->cursor_ext[$query_hash])
|
||||
@@ -3067,7 +3074,7 @@ class IO
|
||||
*/
|
||||
public function dbResetQueryCalled(string $query, array $params = []): void
|
||||
{
|
||||
$this->query_called[$this->dbGetQueryHash($query, $params)] = 0;
|
||||
$this->query_called[$this->dbBuildQueryHash($query, $params)] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3080,7 +3087,7 @@ class IO
|
||||
*/
|
||||
public function dbGetQueryCalled(string $query, array $params = []): int
|
||||
{
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
$query_hash = $this->dbBuildQueryHash($query, $params);
|
||||
if (!empty($this->query_called[$query_hash])) {
|
||||
return $this->query_called[$query_hash];
|
||||
} else {
|
||||
@@ -3141,6 +3148,7 @@ class IO
|
||||
'pk_name' => '',
|
||||
'count' => 0,
|
||||
'query' => '',
|
||||
'query_raw' => $query,
|
||||
'result' => null,
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
@@ -3237,11 +3245,12 @@ class IO
|
||||
}
|
||||
} else {
|
||||
// if we try to use the same statement name for a differnt query, error abort
|
||||
if ($this->prepare_cursor[$stm_name]['query'] != $query) {
|
||||
if ($this->prepare_cursor[$stm_name]['query_raw'] != $query) {
|
||||
// thrown error
|
||||
$this->__dbError(26, false, context: [
|
||||
'statement_name' => $stm_name,
|
||||
'prepared_query' => $this->prepare_cursor[$stm_name]['query'],
|
||||
'prepared_query_raw' => $this->prepare_cursor[$stm_name]['query_raw'],
|
||||
'query' => $query,
|
||||
'pk_name' => $pk_name,
|
||||
]);
|
||||
@@ -4047,7 +4056,7 @@ class IO
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hash for query
|
||||
* Creates hash for query and parameters
|
||||
* Hash is used in all internal storage systems for return data
|
||||
*
|
||||
* @param string $query The query to create the hash from
|
||||
@@ -4055,9 +4064,9 @@ class IO
|
||||
* data to create a unique call one, optional
|
||||
* @return string Hash, as set by hash long
|
||||
*/
|
||||
public function dbGetQueryHash(string $query, array $params = []): string
|
||||
public function dbBuildQueryHash(string $query, array $params = []): string
|
||||
{
|
||||
return Hash::__hashLong(
|
||||
return Hash::hashLong(
|
||||
$query . (
|
||||
$params !== [] ?
|
||||
'#' . json_encode($params) : ''
|
||||
@@ -4105,6 +4114,26 @@ class IO
|
||||
$this->params = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* get the current set query hash
|
||||
*
|
||||
* @return string Current Query hash
|
||||
*/
|
||||
public function dbGetQueryHash(): string
|
||||
{
|
||||
return $this->query_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset query hash
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dbResetQueryHash(): void
|
||||
{
|
||||
$this->query_hash = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the placeholder convert set or empty
|
||||
*
|
||||
@@ -4284,6 +4313,17 @@ class IO
|
||||
return $this->field_names[$pos] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all the $ placeholders
|
||||
*
|
||||
* @param string $query
|
||||
* @return array<string>
|
||||
*/
|
||||
public function dbGetQueryParamPlaceholders(string $query): array
|
||||
{
|
||||
return $this->db_functions->__dbGetQueryParams($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a field type for a field name or pos,
|
||||
* will return false if field is not found in list
|
||||
@@ -4364,6 +4404,37 @@ class IO
|
||||
return $this->prepare_cursor[$stm_name][$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a prepared query eixsts
|
||||
*
|
||||
* @param string $stm_name Statement to check
|
||||
* @param string $query [default=''] If set then query must also match
|
||||
* @return false|int<0,2> False on missing stm_name
|
||||
* 0: ok, 1: stm_name matchin, 2: stm_name and query matching
|
||||
*/
|
||||
public function dbPreparedCursorStatus(string $stm_name, string $query = ''): false|int
|
||||
{
|
||||
if (empty($stm_name)) {
|
||||
$this->__dbError(
|
||||
101,
|
||||
false,
|
||||
'No statement name given'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// does not exist
|
||||
$return_value = 0;
|
||||
if (!empty($this->prepare_cursor[$stm_name]['query_raw'])) {
|
||||
// statement name eixts
|
||||
$return_value = 1;
|
||||
if ($this->prepare_cursor[$stm_name]['query_raw'] == $query) {
|
||||
// query also matches
|
||||
$return_value = 2;
|
||||
}
|
||||
}
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
// ***************************
|
||||
// ERROR AND WARNING DATA
|
||||
// ***************************
|
||||
|
||||
@@ -379,9 +379,9 @@ interface SqlFunctions
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $query
|
||||
* @return int
|
||||
* @return array<string>
|
||||
*/
|
||||
public function __dbCountQueryParams(string $query): int;
|
||||
public function __dbGetQueryParams(string $query): array;
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -978,12 +978,12 @@ class PgSQL implements Interface\SqlFunctions
|
||||
}
|
||||
|
||||
/**
|
||||
* Count placeholder queries. $ only
|
||||
* Get the all the $ params, as a unique list
|
||||
*
|
||||
* @param string $query
|
||||
* @return int
|
||||
* @return array<string>
|
||||
*/
|
||||
public function __dbCountQueryParams(string $query): int
|
||||
public function __dbGetQueryParams(string $query): array
|
||||
{
|
||||
$matches = [];
|
||||
// regex for params: only stand alone $number allowed
|
||||
@@ -998,11 +998,11 @@ class PgSQL implements Interface\SqlFunctions
|
||||
// Matches in 1:, must be array_filtered to remove empty, count with array_unique
|
||||
// Regex located in the ConvertPlaceholder class
|
||||
preg_match_all(
|
||||
ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS,
|
||||
ConvertPlaceholder::REGEX_LOOKUP_NUMBERED,
|
||||
$query,
|
||||
$matches
|
||||
);
|
||||
return count(array_unique(array_filter($matches[3])));
|
||||
return array_unique(array_filter($matches[ConvertPlaceholder::MATCHING_POS]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,76 +14,57 @@ namespace CoreLibs\DB\Support;
|
||||
|
||||
class ConvertPlaceholder
|
||||
{
|
||||
// NOTE for missing: range */+ are not iplemented in the regex below, but - is for now
|
||||
// NOTE some combinations are allowed, but the query will fail before this
|
||||
/** @var string split regex, entries before $ group */
|
||||
private const PATTERN_QUERY_SPLIT =
|
||||
'\?\?|' // UNKNOWN: double ??, is this to avoid something?
|
||||
. '[\(,]|' // for ',' and '(' mostly in INSERT or ANY()
|
||||
. '[<>=]|' // general set for <, >, = in any query with any combination
|
||||
. '\^@|' // text search for start from text with ^@
|
||||
. '\|\||' // concats two elements
|
||||
. '&&|' // array overlap
|
||||
. '\-\|\-|' // range overlap for array
|
||||
. '[^-]-{1}|' // single -, used in JSON too
|
||||
. '->|->>|#>|#>>|@>|<@|@@|@\?|\?{1}|\?\||\?&|#-|' // JSON searches, Array searchs, etc
|
||||
. 'THEN|ELSE' // command parts (CASE)
|
||||
;
|
||||
/** @var string the main regex including the pattern query split */
|
||||
private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:' . self::PATTERN_QUERY_SPLIT . ')\s*';
|
||||
/** @var string text block in SQL, single quited
|
||||
* Note that does not include $$..$$ strings or anything with token name or nested ones
|
||||
*/
|
||||
private const PATTERN_TEXT_BLOCK_SINGLE_QUOTE = '(?:\'(?:[^\'\\\\]|\\\\.)*\')';
|
||||
/** @var string text block in SQL, dollar quoted
|
||||
* NOTE: if this is added everything shifts by one lookup number
|
||||
*/
|
||||
private const PATTERN_TEXT_BLOCK_DOLLAR = '(?:\$(\w*)\$.*?\$\1\$)';
|
||||
/** @var string comment regex
|
||||
* anything that starts with -- and ends with a line break but any character that is not line break inbetween */
|
||||
private const PATTERN_COMMENT = '(?:\-\-[^\r\n]*?\r?\n)*\s*';
|
||||
/** @var string parts to ignore in the SQL */
|
||||
private const PATTERN_IGNORE =
|
||||
// digit -> ignore
|
||||
'\d+|'
|
||||
// other string -> ignore
|
||||
. '(?:\'.*?\')|';
|
||||
/** @var string named parameters */
|
||||
private const PATTERN_NAMED = '(:\w+)';
|
||||
/** @var string question mark parameters */
|
||||
private const PATTERN_QUESTION_MARK = '(?:(?:\?\?)?\s*(\?{1}))';
|
||||
/** @var string numbered parameters */
|
||||
* anything that starts with -- and ends with a line break but any character that is not line break inbetween
|
||||
* this is the FIRST thing in the line and will skip any further lookups */
|
||||
private const PATTERN_COMMENT = '(?:\-\-[^\r\n]*?\r?\n)';
|
||||
// below are the params lookups
|
||||
/** @var string named parameters, must start with single : */
|
||||
private const PATTERN_NAMED = '((?<!:):(?:\w+))';
|
||||
/** @var string question mark parameters, will catch any */
|
||||
private const PATTERN_QUESTION_MARK = '(\?{1})';
|
||||
/** @var string numbered parameters, can only start 1 to 9, second and further digits can be 0-9
|
||||
* This ignores the $$ ... $$ escape syntax. If we find something like this will fail
|
||||
* It is recommended to use proper string escape quiting for writing data to the DB
|
||||
*/
|
||||
private const PATTERN_NUMBERED = '(\$[1-9]{1}(?:[0-9]{1,})?)';
|
||||
// below here are full regex that will be used
|
||||
/** @var string replace regex for named (:...) entries */
|
||||
public const REGEX_REPLACE_NAMED = '/'
|
||||
. '(' . self::PATTERN_ELEMENT . ')'
|
||||
. self::PATTERN_COMMENT
|
||||
. '('
|
||||
. self::PATTERN_IGNORE
|
||||
. self::PATTERN_COMMENT . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||
. self::PATTERN_NAMED
|
||||
. ')'
|
||||
. '/s';
|
||||
/** @var string replace regex for question mark (?) entries */
|
||||
public const REGEX_REPLACE_QUESTION_MARK = '/'
|
||||
. '(' . self::PATTERN_ELEMENT . ')'
|
||||
. self::PATTERN_COMMENT
|
||||
. '('
|
||||
. self::PATTERN_IGNORE
|
||||
. self::PATTERN_COMMENT . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||
. self::PATTERN_QUESTION_MARK
|
||||
. ')'
|
||||
. '/s';
|
||||
/** @var string replace regex for numbered ($n) entries */
|
||||
public const REGEX_REPLACE_NUMBERED = '/'
|
||||
. '(' . self::PATTERN_ELEMENT . ')'
|
||||
. self::PATTERN_COMMENT
|
||||
. '('
|
||||
. self::PATTERN_IGNORE
|
||||
. self::PATTERN_COMMENT . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||
. self::PATTERN_NUMBERED
|
||||
. ')'
|
||||
. '/s';
|
||||
/** @var string the main lookup query for all placeholders */
|
||||
public const REGEX_LOOKUP_PLACEHOLDERS = '/'
|
||||
// prefix string part, must match towards
|
||||
// seperator for ( = , ? - [and json/jsonb in pg doc section 9.15]
|
||||
. self::PATTERN_ELEMENT
|
||||
. self::PATTERN_COMMENT
|
||||
. self::PATTERN_COMMENT . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||
// match for replace part
|
||||
. '(?:'
|
||||
// ignore parts
|
||||
. self::PATTERN_IGNORE
|
||||
// :name named part (PDO) [1]
|
||||
. self::PATTERN_NAMED . '|'
|
||||
// ? question mark part (PDO) [2]
|
||||
@@ -94,6 +75,26 @@ class ConvertPlaceholder
|
||||
. ')'
|
||||
// single line -> add line break to matches in "."
|
||||
. '/s';
|
||||
/** @var string lookup for only numbered placeholders */
|
||||
public const REGEX_LOOKUP_NUMBERED = '/'
|
||||
. self::PATTERN_COMMENT . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_SINGLE_QUOTE . '|'
|
||||
. self::PATTERN_TEXT_BLOCK_DOLLAR . '|'
|
||||
// match for replace part
|
||||
. '(?:'
|
||||
// $n numbered part (\PG php) [1]
|
||||
. self::PATTERN_NUMBERED
|
||||
// end match
|
||||
. ')'
|
||||
. '/s';
|
||||
/** @var int position for regex in full placeholder lookup: named */
|
||||
public const LOOOKUP_NAMED_POS = 2;
|
||||
/** @var int position for regex in full placeholder lookup: question mark */
|
||||
public const LOOOKUP_QUESTION_MARK_POS = 3;
|
||||
/** @var int position for regex in full placeholder lookup: numbered */
|
||||
public const LOOOKUP_NUMBERED_POS = 4;
|
||||
/** @var int matches position for replacement and single lookup */
|
||||
public const MATCHING_POS = 2;
|
||||
|
||||
/**
|
||||
* Convert PDO type query with placeholders to \PG style and vica versa
|
||||
@@ -132,11 +133,12 @@ class ConvertPlaceholder
|
||||
$found = -1;
|
||||
}
|
||||
/** @var array<string> 1: named */
|
||||
$named_matches = array_filter($matches[1]);
|
||||
$named_matches = array_filter($matches[self::LOOOKUP_NAMED_POS]);
|
||||
/** @var array<string> 2: open ? */
|
||||
$qmark_matches = array_filter($matches[2]);
|
||||
$qmark_matches = array_filter($matches[self::LOOOKUP_QUESTION_MARK_POS]);
|
||||
/** @var array<string> 3: $n matches */
|
||||
$numbered_matches = array_filter($matches[3]);
|
||||
$numbered_matches = array_filter($matches[self::LOOOKUP_NUMBERED_POS]);
|
||||
// print "**MATCHES**: <pre>" . print_r($matches, true) . "</pre>";
|
||||
// count matches
|
||||
$count_named = count(array_unique($named_matches));
|
||||
$count_qmark = count($qmark_matches);
|
||||
@@ -235,38 +237,37 @@ class ConvertPlaceholder
|
||||
$empty_params = $converted_placeholders['original']['empty_params'];
|
||||
switch ($converted_placeholders['type']) {
|
||||
case 'named':
|
||||
// 0: full
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: replace part :named
|
||||
// 1: replace part :named
|
||||
$pos = 0;
|
||||
$query_new = preg_replace_callback(
|
||||
self::REGEX_REPLACE_NAMED,
|
||||
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params, $empty_params) {
|
||||
// only count up if $match[3] is not yet in lookup table
|
||||
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
|
||||
if (!isset($matches[self::MATCHING_POS])) {
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . self::MATCHING_POS . ' in matches list',
|
||||
209
|
||||
);
|
||||
}
|
||||
$match = $matches[self::MATCHING_POS];
|
||||
// only count up if $match[1] is not yet in lookup table
|
||||
if (empty($params_lookup[$match])) {
|
||||
$pos++;
|
||||
$params_lookup[$matches[3]] = '$' . $pos;
|
||||
$params_lookup[$match] = '$' . $pos;
|
||||
// skip params setup if param list is empty
|
||||
if (!$empty_params) {
|
||||
$params_new[] = $params[$matches[3]] ??
|
||||
$params_new[] = $params[$match] ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $matches[3] . ' in params list',
|
||||
'Cannot lookup ' . $match . ' in params list',
|
||||
210
|
||||
);
|
||||
}
|
||||
}
|
||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||
return $matches[1] . (
|
||||
empty($matches[3]) ?
|
||||
$matches[2] :
|
||||
$params_lookup[$matches[3]] ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $matches[3] . ' in params lookup list',
|
||||
211
|
||||
)
|
||||
);
|
||||
return $params_lookup[$match]/* ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $match . ' in params lookup list',
|
||||
211
|
||||
)*/;
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
@@ -276,61 +277,61 @@ class ConvertPlaceholder
|
||||
// order and data stays the same
|
||||
$params_new = $params ?? [];
|
||||
}
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: replace part ?
|
||||
// 1: replace part ?
|
||||
$pos = 0;
|
||||
$query_new = preg_replace_callback(
|
||||
self::REGEX_REPLACE_QUESTION_MARK,
|
||||
function ($matches) use (&$pos, &$params_lookup) {
|
||||
if (!isset($matches[self::MATCHING_POS])) {
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . self::MATCHING_POS . ' in matches list',
|
||||
229
|
||||
);
|
||||
}
|
||||
$match = $matches[self::MATCHING_POS];
|
||||
// only count pos up for actual replacements we will do
|
||||
if (!empty($matches[3])) {
|
||||
if (!empty($match)) {
|
||||
$pos++;
|
||||
$params_lookup[] = '$' . $pos;
|
||||
}
|
||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||
return $matches[1] . (
|
||||
empty($matches[3]) ?
|
||||
$matches[2] :
|
||||
'$' . $pos
|
||||
);
|
||||
return '$' . $pos;
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
break;
|
||||
case 'numbered':
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: replace part $numbered
|
||||
// 1: replace part $numbered
|
||||
$pos = 0;
|
||||
$query_new = preg_replace_callback(
|
||||
self::REGEX_REPLACE_NUMBERED,
|
||||
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params, $empty_params) {
|
||||
// only count up if $match[3] is not yet in lookup table
|
||||
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
|
||||
if (!isset($matches[self::MATCHING_POS])) {
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . self::MATCHING_POS . ' in matches list',
|
||||
239
|
||||
);
|
||||
}
|
||||
$match = $matches[self::MATCHING_POS];
|
||||
// only count up if $match[1] is not yet in lookup table
|
||||
if (empty($params_lookup[$match])) {
|
||||
$pos++;
|
||||
$params_lookup[$matches[3]] = ':' . $pos . '_named';
|
||||
$params_lookup[$match] = ':' . $pos . '_named';
|
||||
// skip params setup if param list is empty
|
||||
if (!$empty_params) {
|
||||
$params_new[] = $params[($pos - 1)] ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . ($pos - 1) . ' in params list',
|
||||
220
|
||||
230
|
||||
);
|
||||
}
|
||||
}
|
||||
// add the connectors back (1), and the data sets only if no replacement will be done
|
||||
return $matches[1] . (
|
||||
empty($matches[3]) ?
|
||||
$matches[2] :
|
||||
$params_lookup[$matches[3]] ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $matches[3] . ' in params lookup list',
|
||||
221
|
||||
)
|
||||
);
|
||||
return $params_lookup[$match]/* ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $match . ' in params lookup list',
|
||||
231
|
||||
)*/;
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
|
||||
@@ -33,6 +33,36 @@ class Support
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* print ISO type datetime with microseconds and timezone
|
||||
* Y-m-dTH:i:s.uP
|
||||
* if no micro time the ".u" part is omitted
|
||||
*
|
||||
* @param bool $set_micro_time
|
||||
* @return string
|
||||
*/
|
||||
public static function printIsoTime(bool $set_micro_time = true): string
|
||||
{
|
||||
$datetime = new \DateTime();
|
||||
|
||||
// Format the DateTime object to ISO 8601 with microseconds
|
||||
// 'Y-m-d\TH:i:s.uP' is the format string:
|
||||
// Y: Full year (e.g., 2025)
|
||||
// m: Month (01-12)
|
||||
// d: Day of the month (01-31)
|
||||
// T: Literal 'T' to separate date and time (escaped with a backslash)
|
||||
// H: Hour (00-23)
|
||||
// i: Minute (00-59)
|
||||
// s: Second (00-59)
|
||||
// u: Microseconds (e.g., 654321)
|
||||
// P: Difference to Greenwich time (GMT) with colon (e.g., +09:00)
|
||||
if ($set_micro_time) {
|
||||
return $datetime->format('Y-m-d\TH:i:s.uP');
|
||||
} else {
|
||||
return $datetime->format('Y-m-d\TH:i:sP');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prints a html formatted (pre) data
|
||||
*
|
||||
|
||||
95
www/lib/CoreLibs/DeprecatedHelper/Deprecated84.php
Normal file
95
www/lib/CoreLibs/DeprecatedHelper/Deprecated84.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2025/1/17
|
||||
* DESCRIPTION:
|
||||
* Deprecated helper for fputcsv
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DeprecatedHelper;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Deprecated84
|
||||
{
|
||||
/**
|
||||
* This is a wrapper for fputcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.fputcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param mixed $stream
|
||||
* @param array<mixed> $fields
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @param string $eol
|
||||
* @return int|false
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fputcsv(
|
||||
mixed $stream,
|
||||
array $fields,
|
||||
string $separator = ",",
|
||||
string $enclosure = '"',
|
||||
string $escape = '', // set to empty for future compatible
|
||||
string $eol = PHP_EOL
|
||||
): int | false {
|
||||
if (!is_resource($stream)) {
|
||||
throw new \InvalidArgumentException("fputcsv stream parameter must be a resrouce");
|
||||
}
|
||||
return fputcsv($stream, $fields, $separator, $enclosure, $escape, $eol);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper for fgetcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.fgetcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param mixed $stream
|
||||
* @param null|int<0,max> $length
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @return array<mixed>|false
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fgetcsv(
|
||||
mixed $stream,
|
||||
?int $length = null,
|
||||
string $separator = ',',
|
||||
string $enclosure = '"',
|
||||
string $escape = '' // set to empty for future compatible
|
||||
): array | false {
|
||||
if (!is_resource($stream)) {
|
||||
throw new \InvalidArgumentException("fgetcsv stream parameter must be a resrouce");
|
||||
}
|
||||
return fgetcsv($stream, $length, $separator, $enclosure, $escape);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper for str_getcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.str-getcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @return array<mixed>
|
||||
*/
|
||||
// phpcs:disable PSR1.Methods.CamelCapsMethodName
|
||||
public static function str_getcsv(
|
||||
string $string,
|
||||
string $separator = ",",
|
||||
string $enclosure = '"',
|
||||
string $escape = '' // set to empty for future compatible
|
||||
): array {
|
||||
return str_getcsv($string, $separator, $enclosure, $escape);
|
||||
}
|
||||
// phpcs:enable PSR1.Methods.CamelCapsMethodName
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -50,7 +50,6 @@ class GetLocale
|
||||
$locale = defined('SITE_LOCALE') && !empty(SITE_LOCALE) ?
|
||||
SITE_LOCALE :
|
||||
// else parse from default, if not 'en'
|
||||
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
|
||||
(defined('DEFAULT_LOCALE') && !empty(DEFAULT_LOCALE) ?
|
||||
DEFAULT_LOCALE : 'en');
|
||||
}
|
||||
@@ -97,8 +96,7 @@ class GetLocale
|
||||
$encoding = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
|
||||
SITE_ENCODING :
|
||||
// or default encoding, if not 'UTF-8'
|
||||
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
|
||||
(defined('DEFAULT_ENCODING') && !empty(DEFAULT_ENCODING) ?
|
||||
(defined('DEFAULT_ENCODING') ?
|
||||
DEFAULT_ENCODING : 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ class ErrorMessage
|
||||
// set a jump target
|
||||
$this->setJumpTarget($jump_target['target'] ?? null, $jump_target['info'] ?? null, $level);
|
||||
// write to log for abort/crash
|
||||
$this->log->setErrorMessageCallSetErrorMsg();
|
||||
switch ($level) {
|
||||
case 'notice':
|
||||
$this->log->notice($message ?? $str, array_merge([
|
||||
@@ -210,6 +211,7 @@ class ErrorMessage
|
||||
?bool $log_error = null,
|
||||
?bool $log_warning = null,
|
||||
): void {
|
||||
$this->log->setErrorMessageCallSetMessage();
|
||||
$this->setErrorMsg(
|
||||
$error_id ?? '',
|
||||
$level,
|
||||
@@ -289,7 +291,7 @@ class ErrorMessage
|
||||
*/
|
||||
public function getLastErrorMsg(): array
|
||||
{
|
||||
return $this->error_str[array_key_last($this->error_str)] ?? [
|
||||
return $this->error_str[array_key_last($this->error_str) ?? -1] ?? [
|
||||
'level' => '',
|
||||
'str' => '',
|
||||
'id' => '',
|
||||
|
||||
@@ -112,7 +112,7 @@ enum Level: int
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed $level is higher or equal to $this
|
||||
* Returns true if the passed $level is included in set level
|
||||
*
|
||||
* @param Level $level
|
||||
* @return bool
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace CoreLibs\Logging\Logger;
|
||||
|
||||
enum MessageLevel: int
|
||||
{
|
||||
case noset = 0;
|
||||
case ok = 100;
|
||||
case success = 150; // special for file uploads
|
||||
case info = 200;
|
||||
|
||||
@@ -29,8 +29,21 @@ use Stringable;
|
||||
class Logging
|
||||
{
|
||||
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
|
||||
public const MIN_LOG_MAX_FILESIZE = 10 * 1024;
|
||||
public const int MIN_LOG_MAX_FILESIZE = 10 * 1024;
|
||||
/** @var string log file extension, not changeable */
|
||||
private const string LOG_FILE_NAME_EXT = "log";
|
||||
/** @var string log file block separator, not changeable */
|
||||
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
|
||||
/** @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 = [
|
||||
@@ -46,6 +59,7 @@ class Logging
|
||||
'type' => 'string', 'mandatory' => false,
|
||||
'default' => '', 'deprecated' => true, 'use' => 'log_file_id'
|
||||
],
|
||||
// log level
|
||||
'log_level' => [
|
||||
'type' => 'instance',
|
||||
'type_info' => '\CoreLibs\Logging\Logger\Level',
|
||||
@@ -53,6 +67,14 @@ class Logging
|
||||
'default' => Level::Debug,
|
||||
'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
|
||||
'log_per_run' => [
|
||||
'type' => 'bool', 'mandatory' => false,
|
||||
@@ -82,14 +104,21 @@ class Logging
|
||||
'type' => 'bool', 'mandatory' => false,
|
||||
'default' => false, 'deprecated' => true, 'use' => 'log_per_date'
|
||||
],
|
||||
// if turned off uses old time format without time zone
|
||||
'log_time_format_iso' => [
|
||||
'type' => 'bool', 'mandatory' => false,
|
||||
'default' => true, 'deprecated' => false
|
||||
]
|
||||
];
|
||||
|
||||
// options
|
||||
/** @var array<mixed> */
|
||||
private array $options = [];
|
||||
|
||||
/** @var Level set level */
|
||||
/** @var Level set logging 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
|
||||
/** @var string */
|
||||
@@ -104,8 +133,6 @@ class Logging
|
||||
private string $log_folder = '';
|
||||
/** @var string a alphanumeric name that has to be set as global definition */
|
||||
private string $log_file_id = '';
|
||||
/** @var string log file name extension */
|
||||
private string $log_file_name_ext = 'log';
|
||||
/** @var string log file name with folder, for actual writing */
|
||||
private string $log_file_name = '';
|
||||
/** @var int set in bytes */
|
||||
@@ -119,6 +146,12 @@ class Logging
|
||||
/** @var string Y-m-d file in file name */
|
||||
private string $log_file_date = '';
|
||||
|
||||
// speical flags for ErrorMessage calls
|
||||
/** @var bool Flag to set if called from ErrorMessage::setErrorMsg */
|
||||
private bool $error_message_call_set_error_msg = false;
|
||||
/** @var bool Flag to set if called from ErrorMessage::setMessage */
|
||||
private bool $error_message_call_set_message = false;
|
||||
|
||||
/**
|
||||
* 1: create a new log file per run (time stamp + unique ID)
|
||||
* 2: add Y-m-d and do automatic daily rotation
|
||||
@@ -143,12 +176,13 @@ class Logging
|
||||
];
|
||||
|
||||
/**
|
||||
* Init logger
|
||||
* MARK: Init logger
|
||||
*
|
||||
* options array layout
|
||||
* - log_folder:
|
||||
* - log_file_id / file_id (will be deprecated):
|
||||
* - log_level:
|
||||
* - error_log_write_level: at what level we write to error_log
|
||||
*
|
||||
* - log_per_run:
|
||||
* - log_per_date: (was print_file_date)
|
||||
@@ -170,6 +204,8 @@ class Logging
|
||||
|
||||
// set log level
|
||||
$this->initLogLevel();
|
||||
// set error log write level
|
||||
$this->initErrorLogWriteLevel();
|
||||
// set log folder from options
|
||||
$this->initLogFolder();
|
||||
// set per run UID for logging
|
||||
@@ -188,8 +224,10 @@ class Logging
|
||||
// PRIVATE METHODS
|
||||
// *********************************************************************
|
||||
|
||||
// MARK: options check
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
* validate options
|
||||
*
|
||||
* @param array<mixed> $options
|
||||
* @return bool
|
||||
@@ -261,6 +299,8 @@ class Logging
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: init log elvels
|
||||
|
||||
/**
|
||||
* init log level, just a wrapper to auto set from options
|
||||
*
|
||||
@@ -278,6 +318,24 @@ class Logging
|
||||
$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
|
||||
* If folder is not writeable the script will throw an E_USER_ERROR
|
||||
@@ -319,6 +377,8 @@ class Logging
|
||||
return $status;
|
||||
}
|
||||
|
||||
// MARK: set host name
|
||||
|
||||
/**
|
||||
* Set the hostname and port
|
||||
* If port is not defaul 80 it will be added to the host name
|
||||
@@ -335,6 +395,8 @@ class Logging
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: set log file id (file)
|
||||
|
||||
/**
|
||||
* set log file prefix id
|
||||
*
|
||||
@@ -393,6 +455,8 @@ class Logging
|
||||
return $status;
|
||||
}
|
||||
|
||||
// MARK init log flags and levels
|
||||
|
||||
/**
|
||||
* set flags from options and option flags connection internal settings
|
||||
*
|
||||
@@ -421,6 +485,19 @@ class Logging
|
||||
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
|
||||
*
|
||||
@@ -431,7 +508,7 @@ class Logging
|
||||
private function buildLogFileName(Level $level, string $group_id = ''): string
|
||||
{
|
||||
// init base file path
|
||||
$fn = $this->log_print_file . '.' . $this->log_file_name_ext;
|
||||
$fn = $this->log_print_file . '.' . self::LOG_FILE_NAME_EXT;
|
||||
// log ID prefix settings, if not valid, replace with empty
|
||||
if (!empty($this->log_file_id)) {
|
||||
$rpl_string = $this->log_file_id;
|
||||
@@ -440,14 +517,15 @@ class Logging
|
||||
}
|
||||
$fn = str_replace('{LOGID}', $rpl_string, $fn); // log id (like a log file prefix)
|
||||
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_level) ? '' :
|
||||
'_' . $level->getName();
|
||||
$rpl_string = $this->getLogFlag(Flag::per_level) ?
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . $level->getName() :
|
||||
'';
|
||||
$fn = str_replace('{LEVEL}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// write per level
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_group) ? '' :
|
||||
$rpl_string = $this->getLogFlag(Flag::per_group) ?
|
||||
// normalize level, replace all non alphanumeric characters with -
|
||||
'_' . (
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . (
|
||||
// if return is only - then set error string
|
||||
preg_match(
|
||||
"/^-+$/",
|
||||
@@ -455,25 +533,29 @@ class Logging
|
||||
) ?
|
||||
'INVALID-LEVEL-STRING' :
|
||||
$level_string
|
||||
);
|
||||
) :
|
||||
'';
|
||||
$fn = str_replace('{GROUP}', $rpl_string, $fn); // create output filename
|
||||
// set per class, but don't use get_class as we will only get self
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_class) ? '' : '_'
|
||||
// set sub class settings
|
||||
. str_replace('\\', '-', Support::getCallerTopLevelClass());
|
||||
$rpl_string = $this->getLogFlag(Flag::per_class) ?
|
||||
// set sub class settings
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . str_replace('\\', '-', Support::getCallerTopLevelClass()) :
|
||||
'';
|
||||
$fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// if request to write to one file
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_page) ?
|
||||
'' :
|
||||
'_' . System::getPageName(System::NO_EXTENSION);
|
||||
$rpl_string = $this->getLogFlag(Flag::per_page) ?
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . System::getPageName(System::NO_EXTENSION) :
|
||||
'';
|
||||
$fn = str_replace('{PAGENAME}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// if run id, we auto add ymd, so we ignore the log file date
|
||||
if ($this->getLogFlag(Flag::per_run)) {
|
||||
$rpl_string = '_' . $this->getLogUniqueId(); // add 8 char unique string
|
||||
// add 8 char unique string and date block with time
|
||||
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogUniqueId();
|
||||
} elseif ($this->getLogFlag(Flag::per_date)) {
|
||||
$rpl_string = '_' . $this->getLogDate(); // add date to file
|
||||
// add date to file
|
||||
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogDate();
|
||||
} else {
|
||||
$rpl_string = '';
|
||||
}
|
||||
@@ -483,6 +565,8 @@ class Logging
|
||||
return $fn;
|
||||
}
|
||||
|
||||
// MARK: master write log to file
|
||||
|
||||
/**
|
||||
* writes error msg data to file for current level
|
||||
*
|
||||
@@ -500,6 +584,10 @@ class Logging
|
||||
if (!$this->checkLogLevel($level)) {
|
||||
return false;
|
||||
}
|
||||
// if we match level then write to error_log
|
||||
if ($this->checkErrorLogWriteLevel($level)) {
|
||||
error_log((string)$message);
|
||||
}
|
||||
|
||||
// build logging file name
|
||||
// fn is log folder + file name
|
||||
@@ -524,6 +612,8 @@ class Logging
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: master prepare log
|
||||
|
||||
/**
|
||||
* Prepare the log message with all needed info blocks:
|
||||
* [timestamp] [host name] [file path + file::row number] [running uid] {class::/->method}
|
||||
@@ -551,31 +641,65 @@ class Logging
|
||||
$file_line = '';
|
||||
$caller_class_method = '-';
|
||||
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
// print "[" . $level->getName() . "] [$message] prepareLog:<br>" . Support::printAr($traces);
|
||||
// file + line: call not this but one before (the one that calls this)
|
||||
// start from this level, if unset fall down until we are at null
|
||||
$start_trace_level = 2;
|
||||
for ($trace_level = $start_trace_level; $trace_level >= 0; $trace_level--) {
|
||||
if (isset($traces[$trace_level])) {
|
||||
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
|
||||
. ':' . ($traces[$trace_level]['line'] ?? '-');
|
||||
// as namespace\class->method
|
||||
$caller_class_method =
|
||||
// get the last call before we are in the Logging class
|
||||
($traces[$trace_level]['class'] ?? '')
|
||||
// connector, if unkown use ==
|
||||
. ($traces[$trace_level]['type'] ?? '')
|
||||
// method/function: prepareLog->(debug|info|...)->[THIS]
|
||||
. $traces[$trace_level]['function'];
|
||||
break;
|
||||
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
|
||||
// set stack trace level +1 if called from ErrorMessage::setMessage
|
||||
if ($this->error_message_call_set_message) {
|
||||
$stack_trace_start_level_line = 3;
|
||||
} elseif ($this->error_message_call_set_error_msg) {
|
||||
$stack_trace_start_level_line = 2;
|
||||
}
|
||||
// if we have line > default, then check if valid, else reset to default
|
||||
if ($stack_trace_start_level_line > self::DEFAULT_STACK_TRACE_LEVEL_LINE) {
|
||||
// check if function at level is one of the override checks
|
||||
$fn_check = $traces[$stack_trace_start_level_line]['function'] ?? '';
|
||||
if (
|
||||
!isset(self::STACK_OVERRIDE_CHECK[$fn_check]) ||
|
||||
self::STACK_OVERRIDE_CHECK[$fn_check] != $stack_trace_start_level_line
|
||||
) {
|
||||
$stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
|
||||
}
|
||||
}
|
||||
$this->error_message_call_set_message = false;
|
||||
$this->error_message_call_set_error_msg = false;
|
||||
// set stack trace level +1 if called from ErrorMessage::setMessage
|
||||
// print "[" . $level->getName() . "] [$message] [" . $stack_trace_start_level_line . "] "
|
||||
// . "prepareLog:<br>" . Support::printAr($traces);
|
||||
// file + line: call not this but one before (the one that calls this)
|
||||
// start from this level, if unset fall down until we are at null
|
||||
// NOTE this has to be pushed to 3 for setMessage wrap calls
|
||||
for ($trace_level = $stack_trace_start_level_line; $trace_level >= 0; $trace_level--) {
|
||||
if (!isset($traces[$trace_level])) {
|
||||
continue;
|
||||
}
|
||||
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
|
||||
. ':' . ($traces[$trace_level]['line'] ?? '-');
|
||||
// call function is one stack level above
|
||||
$trace_level++;
|
||||
// skip setting if we are in the top level already
|
||||
if (!isset($traces[$trace_level])) {
|
||||
break;
|
||||
}
|
||||
// as namespace\class->method
|
||||
$caller_class_method =
|
||||
// get the last call before we are in the Logging class
|
||||
($traces[$trace_level]['class'] ?? '')
|
||||
// connector, if unkown use ==
|
||||
. ($traces[$trace_level]['type'] ?? '')
|
||||
// method/function: prepareLog->(debug|info|...)->[THIS]
|
||||
. $traces[$trace_level]['function'];
|
||||
break;
|
||||
}
|
||||
// if not line is set
|
||||
if (empty($file_line)) {
|
||||
$file_line = System::getPageName(System::FULL_PATH);
|
||||
}
|
||||
// print "CLASS: " . $class . "<br>";
|
||||
// get timestamp
|
||||
$timestamp = Support::printTime();
|
||||
if (!empty($this->options['log_time_format_iso'])) {
|
||||
$timestamp = Support::printIsoTime();
|
||||
} else {
|
||||
$timestamp = Support::printTime();
|
||||
}
|
||||
|
||||
// if group id is empty replace it with current level
|
||||
$group_str = $level->getName();
|
||||
@@ -603,6 +727,7 @@ class Logging
|
||||
// PUBLIC STATIC METHJODS
|
||||
// *********************************************************************
|
||||
|
||||
// MARK: set log level
|
||||
/**
|
||||
* set the log level
|
||||
*
|
||||
@@ -663,7 +788,7 @@ class Logging
|
||||
|
||||
// **** GET/SETTER
|
||||
|
||||
// log level set and get
|
||||
// MARK: log level
|
||||
|
||||
/**
|
||||
* set new log level
|
||||
@@ -698,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
|
||||
@@ -726,7 +874,7 @@ class Logging
|
||||
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)
|
||||
@@ -739,7 +887,10 @@ class Logging
|
||||
{
|
||||
if (empty($this->log_file_unique_id) || $override == true) {
|
||||
$this->log_file_unique_id =
|
||||
date('Y-m-d_His') . '_U_'
|
||||
date('Y-m-d_His')
|
||||
. self::LOG_FILE_BLOCK_SEPARATOR
|
||||
. 'U_'
|
||||
// this doesn't have to be unique for everything, just for this logging purpose
|
||||
. substr(hash(
|
||||
'sha1',
|
||||
random_bytes(63)
|
||||
@@ -758,7 +909,7 @@ class Logging
|
||||
return $this->log_file_unique_id;
|
||||
}
|
||||
|
||||
// general log date
|
||||
// MARK: general log date
|
||||
|
||||
/**
|
||||
* set the log file date to Y-m-d
|
||||
@@ -781,7 +932,7 @@ class Logging
|
||||
return $this->log_file_date;
|
||||
}
|
||||
|
||||
// general flag set
|
||||
// MARK: general flag set
|
||||
|
||||
/**
|
||||
* set one of the basic flags
|
||||
@@ -836,7 +987,7 @@ class Logging
|
||||
return $this->log_flags;
|
||||
}
|
||||
|
||||
// log folder/file
|
||||
// MARK: log folder/file
|
||||
|
||||
/**
|
||||
* set new log folder, check that folder is writeable
|
||||
@@ -880,7 +1031,7 @@ class Logging
|
||||
return $this->log_file_name;
|
||||
}
|
||||
|
||||
// max log file size
|
||||
// MARK: max log file size
|
||||
|
||||
/**
|
||||
* set mag log file size
|
||||
@@ -911,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
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
@@ -929,6 +1104,8 @@ class Logging
|
||||
// MAIN CALLS
|
||||
// *********************************************************************
|
||||
|
||||
// MARK: main log call
|
||||
|
||||
/**
|
||||
* Commong log interface
|
||||
*
|
||||
@@ -966,7 +1143,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* DEBUG: 100
|
||||
* MARK: DEBUG: 100
|
||||
*
|
||||
* write debug data to error_msg array
|
||||
*
|
||||
@@ -998,7 +1175,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* INFO: 200
|
||||
* MARK: INFO: 200
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1017,7 +1194,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTICE: 250
|
||||
* MARK: NOTICE: 250
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1036,7 +1213,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: 300
|
||||
* MARK: WARNING: 300
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1055,7 +1232,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* ERROR: 400
|
||||
* MARK: ERROR: 400
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1074,7 +1251,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* CTRITICAL: 500
|
||||
* MARK: CTRITICAL: 500
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1093,7 +1270,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* ALERT: 550
|
||||
* MARK: ALERT: 550
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1112,7 +1289,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* EMERGENCY: 600
|
||||
* MARK: EMERGENCY: 600
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1131,7 +1308,7 @@ class Logging
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// DEPRECATED SUPPORT CALLS
|
||||
// MARK: DEPRECATED SUPPORT CALLS
|
||||
// *********************************************************************
|
||||
|
||||
// legacy, but there are too many implemented
|
||||
@@ -1189,7 +1366,7 @@ class Logging
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// DEPRECATED METHODS
|
||||
// MARK: DEPRECATED METHODS
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
@@ -1355,7 +1532,7 @@ class Logging
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// DEBUG METHODS
|
||||
// MARK: DEBUG METHODS
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
@@ -1375,19 +1552,21 @@ class Logging
|
||||
Level::Error, Level::Critical, Level::Alert, Level::Emergency
|
||||
] as $l
|
||||
) {
|
||||
print "Check: " . $this->log_level->getName() . " | " . $l->getName() . "<br>";
|
||||
if ($this->log_level->isHigherThan($l)) {
|
||||
print "L: " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
|
||||
print "L(gt): " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
|
||||
}
|
||||
if ($this->log_level->includes($l)) {
|
||||
print "L: " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
|
||||
print "L(le): " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
|
||||
}
|
||||
if ($this->log_level->isLowerThan($l)) {
|
||||
print "L: " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
|
||||
print "L(lt): " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
|
||||
}
|
||||
echo "<br>";
|
||||
}
|
||||
// back to options level
|
||||
$this->initLogLevel();
|
||||
$this->initErrorLogWriteLevel();
|
||||
print "OPT set level: " . $this->getLoggingLevel()->getName() . "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1371,7 +1371,7 @@ class Generate
|
||||
) {
|
||||
$this->msg .= sprintf(
|
||||
$this->l->__('Please enter a valid (%s) input for the <b>%s</b> Field!<br>'),
|
||||
$this->dba->getTableArray()[$key]['error_example'],
|
||||
$this->dba->getTableArray()[$key]['error_example'] ?? '[MISSING]',
|
||||
$this->dba->getTableArray()[$key]['output_name']
|
||||
);
|
||||
}
|
||||
@@ -2602,7 +2602,7 @@ class Generate
|
||||
}
|
||||
}
|
||||
// add lost error ones
|
||||
$this->log->error('P: ' . $data['prefix'] . ', '
|
||||
$this->log->error('Prefix: ' . $data['prefix'] . ', '
|
||||
. Support::prAr($_POST['ERROR'][$data['prefix']] ?? []));
|
||||
if ($this->error && !empty($_POST['ERROR'][$data['prefix']])) {
|
||||
$prfx = $data['prefix']; // short
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user