Compare commits

..

12 Commits

Author SHA1 Message Date
Clemens Schwaighofer
cd65604073 Add parseCharacterRanges function to Strings.php and tests
Parses character ranges like A-Z into individual characters and returns as an array
2026-01-14 10:36:09 +09:00
Clemens Schwaighofer
a3cf5f45f9 Fix function name in Create/RandomKey 2026-01-07 17:21:26 +09:00
Clemens Schwaighofer
6f3dacdec0 Remove double color settings entry in Phan config 2026-01-07 13:32:31 +09:00
Clemens Schwaighofer
2ab1ee90ef Add color output in phan 2026-01-07 13:17:54 +09:00
Clemens Schwaighofer
b8c0aff975 Update phive phars with correct version and update scripts to use both
new "-c" switch for all checking scripts to swtich to the composer version from the phive installed version

NOTE: phpstan plugins only work in the composer version.

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

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

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

Output shows file and line where the message/log call was done and the function/class method where the log call was done
2025-11-27 17:54:28 +09:00
Clemens Schwaighofer
8af71b70a3 general SQL update to use uuid for uid, update edit.jq.js for some testing 2025-11-06 11:51:29 +09:00
Clemens Schwaighofer
7d10b4c5af Change UUIdv4 validation to properly check for version 4 UUIDs 2025-11-04 11:51:08 +09:00
Clemens Schwaighofer
c81b602657 phpunit: redirect error_log message to temp file so they are not printed to console
This is for any logging part that logs to emergency where emergency logging is
set to log to the error_log too
2025-10-09 15:05:00 +09:00
32 changed files with 860 additions and 1775 deletions

1
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -698,6 +698,84 @@ final class CoreLibsConvertStringsTest extends TestCase
'Cannot match last preg error string' '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,
],
'invalid ranges' => [
['a-あ', 'A-あ', '0-あ'],
'',
\InvalidArgumentException::class,
],
];
}
/**
* Undocumented function
*
* @covers ::parseCharacterRanges
* @dataProvider parseCharacterRangesProvider
* @testdox parseCharacterRanges $input to $expected [$_dataName]
*
* @param array $input
* @param string $expected
* @param string|null $expected_exception
* @return void
*/
public function testParseCharacterRanges(
array $input,
string $expected,
?string $expected_exception
): void {
if ($expected_exception !== null) {
$this->expectException($expected_exception);
}
$this->assertEquals(
$expected,
implode('', \CoreLibs\Convert\Strings::parseCharacterRanges(implode('', $input)))
);
}
} }
// __END__ // __END__

View File

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

View File

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

1567
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -58,6 +58,14 @@ $em->setErrorMsg('100-2', 'error', 'Input wring', jump_target:['target' => 'foo-
$em->setMessage('error', 'I have no id set', jump_target:['target' => 'bar-123', 'info' => 'Jump Bar']); $em->setMessage('error', 'I have no id set', jump_target:['target' => 'bar-123', 'info' => 'Jump Bar']);
$em->setMessage('error', 'Jump empty', jump_target:['target' => 'bar-empty']); $em->setMessage('error', 'Jump empty', jump_target:['target' => 'bar-empty']);
function inLine(\CoreLibs\Logging\ErrorMessage $em): void
{
$em->log->error('Direct log before from ', context:['function' => __FUNCTION__]);
$em->setMessage('error', 'Inline call', context:['test' => 'inLine Function']);
$em->log->error('Direct log from ', context:['function' => __FUNCTION__]);
}
inLine($em);
print "ErrorsLast: <pre>" . $log->prAr($em->getLastErrorMsg()) . "</pre>"; print "ErrorsLast: <pre>" . $log->prAr($em->getLastErrorMsg()) . "</pre>";
print "ErrorsIds: <pre>" . $log->prAr($em->getErrorIds()) . "</pre>"; print "ErrorsIds: <pre>" . $log->prAr($em->getErrorIds()) . "</pre>";
print "Errors: <pre>" . $log->prAr($em->getErrorMsg()) . "</pre>"; print "Errors: <pre>" . $log->prAr($em->getErrorMsg()) . "</pre>";

View File

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

View File

@@ -140,6 +140,27 @@ $preg_error = Strings::isValidRegex($regex_string);
print "[B] LAST PREGE ERROR: " . preg_last_error() . " -> " print "[B] LAST PREGE ERROR: " . preg_last_error() . " -> "
. Strings::getLastRegexErrorString() . " -> " . preg_last_error_msg() . "<br>"; . Strings::getLastRegexErrorString() . " -> " . preg_last_error_msg() . "<br>";
$base_strings = [
'A-Z',
'a-z',
'A-Za-z',
'A-Df-g',
'A-D0-9',
'D-A7-0',
'A-FB-G',
'0-9',
'あ-お',
'ア-オ',
];
foreach ($base_strings as $string) {
try {
$parsed = Strings::parseCharacterRanges($string);
print "Parsed ranges for '$string': " . DgS::printAr($parsed) . "<br>";
} catch (\InvalidArgumentException $e) {
print "Error parsing ranges for '$string': " . $e->getMessage() . "<br>";
}
}
print "</body></html>"; print "</body></html>";
// __END__ // __END__

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@@ -62,10 +62,15 @@ class Math
* *
* @param float $number Number to cubic root * @param float $number Number to cubic root
* @return float Calculated value * @return float Calculated value
* @throws \InvalidArgumentException if $number is negative
*/ */
public static function cbrt(float|int $number): float public static function cbrt(float|int $number): float
{ {
return pow((float)$number, 1.0 / 3); $value = pow((float)$number, 1.0 / 3);
if (is_nan($value)) {
throw new \InvalidArgumentException('cube root from this number is not supported: ' . $number);
}
return $value;
} }
/** /**

View File

@@ -268,6 +268,51 @@ class Strings
)); ));
} }
/**
* Split up character ranges in format A-Z, a-z, 0-9
*
* @param string $input
* @return string[]
*/
public static function parseCharacterRanges(string $input): array
{
// if not alphanumeric, throw value error
if (!preg_match("/^[A-Za-z0-9\-\s]+$/u", $input)) {
throw new \InvalidArgumentException(
"The input string contains invalid characters, "
. "only alphanumeric, dash (-), space and 'or' are allowed: "
. $input
);
}
// Remove all spaces
$input = str_replace(' ', '', $input);
$result = [];
// 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 * Check if a regex is valid. Does not return the detail regex parser error
* *

View File

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

View File

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

View File

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

View File

@@ -29,11 +29,19 @@ use Stringable;
class Logging class Logging
{ {
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */ /** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
public const MIN_LOG_MAX_FILESIZE = 10 * 1024; public const int MIN_LOG_MAX_FILESIZE = 10 * 1024;
/** @var string log file extension, not changeable */ /** @var string log file extension, not changeable */
private const LOG_FILE_NAME_EXT = "log"; private const string LOG_FILE_NAME_EXT = "log";
/** @var string log file block separator, not changeable */ /** @var string log file block separator, not changeable */
private const LOG_FILE_BLOCK_SEPARATOR = '.'; private const string LOG_FILE_BLOCK_SEPARATOR = '.';
/** @var int the base stack trace level for the line number */
private const int DEFAULT_STACK_TRACE_LEVEL_LINE = 1;
/** @var array<string,int> */
private const array STACK_OVERRIDE_CHECK = [
'setErrorMsg' => 2,
'setMessage' => 3,
];
// MARK: OPTION array // MARK: OPTION array
// NOTE: the second party array{} hs some errors // NOTE: the second party array{} hs some errors
@@ -138,6 +146,12 @@ class Logging
/** @var string Y-m-d file in file name */ /** @var string Y-m-d file in file name */
private string $log_file_date = ''; private string $log_file_date = '';
// speical flags for ErrorMessage calls
/** @var bool Flag to set if called from ErrorMessage::setErrorMsg */
private bool $error_message_call_set_error_msg = false;
/** @var bool Flag to set if called from ErrorMessage::setMessage */
private bool $error_message_call_set_message = false;
/** /**
* 1: create a new log file per run (time stamp + unique ID) * 1: create a new log file per run (time stamp + unique ID)
* 2: add Y-m-d and do automatic daily rotation * 2: add Y-m-d and do automatic daily rotation
@@ -627,25 +641,55 @@ class Logging
$file_line = ''; $file_line = '';
$caller_class_method = '-'; $caller_class_method = '-';
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "[" . $level->getName() . "] [$message] prepareLog:<br>" . Support::printAr($traces); $stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
// file + line: call not this but one before (the one that calls this) // set stack trace level +1 if called from ErrorMessage::setMessage
// start from this level, if unset fall down until we are at null if ($this->error_message_call_set_message) {
$start_trace_level = 2; $stack_trace_start_level_line = 3;
for ($trace_level = $start_trace_level; $trace_level >= 0; $trace_level--) { } elseif ($this->error_message_call_set_error_msg) {
if (isset($traces[$trace_level])) { $stack_trace_start_level_line = 2;
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function']) }
. ':' . ($traces[$trace_level]['line'] ?? '-'); // if we have line > default, then check if valid, else reset to default
// as namespace\class->method if ($stack_trace_start_level_line > self::DEFAULT_STACK_TRACE_LEVEL_LINE) {
$caller_class_method = // check if function at level is one of the override checks
// get the last call before we are in the Logging class $fn_check = $traces[$stack_trace_start_level_line]['function'] ?? '';
($traces[$trace_level]['class'] ?? '') if (
// connector, if unkown use == !isset(self::STACK_OVERRIDE_CHECK[$fn_check]) ||
. ($traces[$trace_level]['type'] ?? '') self::STACK_OVERRIDE_CHECK[$fn_check] != $stack_trace_start_level_line
// method/function: prepareLog->(debug|info|...)->[THIS] ) {
. $traces[$trace_level]['function']; $stack_trace_start_level_line = self::DEFAULT_STACK_TRACE_LEVEL_LINE;
break;
} }
} }
$this->error_message_call_set_message = false;
$this->error_message_call_set_error_msg = false;
// set stack trace level +1 if called from ErrorMessage::setMessage
// print "[" . $level->getName() . "] [$message] [" . $stack_trace_start_level_line . "] "
// . "prepareLog:<br>" . Support::printAr($traces);
// file + line: call not this but one before (the one that calls this)
// start from this level, if unset fall down until we are at null
// NOTE this has to be pushed to 3 for setMessage wrap calls
for ($trace_level = $stack_trace_start_level_line; $trace_level >= 0; $trace_level--) {
if (!isset($traces[$trace_level])) {
continue;
}
$file_line = ($traces[$trace_level]['file'] ?? $traces[$trace_level]['function'])
. ':' . ($traces[$trace_level]['line'] ?? '-');
// call function is one stack level above
$trace_level++;
// skip setting if we are in the top level already
if (!isset($traces[$trace_level])) {
break;
}
// as namespace\class->method
$caller_class_method =
// get the last call before we are in the Logging class
($traces[$trace_level]['class'] ?? '')
// connector, if unkown use ==
. ($traces[$trace_level]['type'] ?? '')
// method/function: prepareLog->(debug|info|...)->[THIS]
. $traces[$trace_level]['function'];
break;
}
// if not line is set
if (empty($file_line)) { if (empty($file_line)) {
$file_line = System::getPageName(System::FULL_PATH); $file_line = System::getPageName(System::FULL_PATH);
} }
@@ -1017,6 +1061,30 @@ class Logging
return $this->log_max_filesize; return $this->log_max_filesize;
} }
// *********************************************************************
// MARK: ErrorMessage class overrides
// *********************************************************************
/**
* call if called from Error Message setMessage wrapper
*
* @return void
*/
public function setErrorMessageCallSetMessage(): void
{
$this->error_message_call_set_message = true;
}
/**
* call if called from Error Message setMessage wrapper
*
* @return void
*/
public function setErrorMessageCallSetErrorMsg(): void
{
$this->error_message_call_set_error_msg = true;
}
// ********************************************************************* // *********************************************************************
// MARK: OPTIONS CALLS // MARK: OPTIONS CALLS
// ********************************************************************* // *********************************************************************

View File

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

View File

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