Compare commits

..
8 Commits
Author SHA1 Message Date
cliphouse f9c070802f Fix ACLlogin tests 2026-10-15 11:53:50 +09:00
cliphouse 4809d98737 Update test page for crc64 2026-10-15 11:42:01 +09:00
cliphouse 6d0c83ab69 Add base crc64 for small files 2026-10-15 11:38:24 +09:00
cliphouse b759ba70f3 Add try / catch block for DB tests 2026-10-07 10:42:29 +09:00
cliphouse 68ea44964f findArraysMissingKey: php param documentation update 2026-09-24 11:51:05 +09:00
cliphouse 369f2e2586 Immutable hash for arrays, add not sort keys flag 2026-09-18 21:53:36 +09:00
cliphouse 3a33f07ae7 Add DB:IO strip non assoc entries from result
if we have a mixed result, strip all int key (non assoc) entries from the result
2026-09-18 18:56:43 +09:00
cliphouse 0b057864ae Hash: immutable hash over array
with sorted keys, but arrays stay unsorted
2026-09-18 18:36:40 +09:00
19 changed files with 579 additions and 106 deletions

No files matched your search

+3 -1
View File
@@ -46,7 +46,8 @@ final class CoreLibsACLLoginTest extends TestCase
// database/CoreLibsACLLogin_database_create_data.sql // database/CoreLibsACLLogin_database_create_data.sql
$load_sql_file = __DIR__ $load_sql_file = __DIR__
. DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR
. 'database/CoreLibsACLLogin_database_create_data.sql'; . 'database' . DIRECTORY_SEPARATOR
. 'CoreLibsACLLogin_database_create_data.sql';
if (!is_file($load_sql_file)) { if (!is_file($load_sql_file)) {
self::markTestIncomplete( self::markTestIncomplete(
'Missing ACL\Login database load SQL file' 'Missing ACL\Login database load SQL file'
@@ -63,6 +64,7 @@ final class CoreLibsACLLoginTest extends TestCase
. "CoreLibsACLLogin_database_prepare.sh " . "CoreLibsACLLogin_database_prepare.sh "
. "$load_sql_file " . "$load_sql_file "
. "$db_user " . "$db_user "
. "$db_password "
. "$db_name " . "$db_name "
. "$db_host "; . "$db_host ";
exec($command, $ouput, $result); exec($command, $ouput, $result);
@@ -22,31 +22,32 @@ if ! command -v dropdb &> /dev/null || ! command -v createdb &> /dev/null || ! c
fi; fi;
db_user="${2}"; db_user="${2}";
db_name="${3}"; db_password="${3}";
db_host="${4}"; db_name="${4}";
db_host="${5}";
# empty db name or db user -> exit with 2 # empty db name or db user -> exit with 2
if [ -z "${db_user}" ] || [ -z "${db_name}" ] || [ -z "${db_host}" ]; then if [ -z "${db_user}" ] || [ -z "${db_name}" ] || [ -z "${db_password}" ] || [ -z "${db_host}" ]; then
echo 2; echo 2;
exit 2; exit 2;
fi; fi;
# drop database, on error exit with 3 # drop database, on error exit with 3
dropdb -U "${db_user}" -h "${db_host}" "${db_name}" 2>&1; PGPASSWORD="${db_password}" dropdb -U "${db_user}" -h "${db_host}" "${db_name}" 2>&1;
if [ ! $? ]; then if [ ! $? ]; then
echo 3; echo 3;
exit 3; exit 3;
fi; fi;
# create database, on error exit with 4 # create database, on error exit with 4
createdb -U "${db_user}" -O "${db_user}" -h "${db_host}" -E utf8 "${db_name}" 2>&1; PGPASSWORD="${db_password}" createdb -U "${db_user}" -O "${db_user}" -h "${db_host}" -E utf8 "${db_name}" 2>&1;
if [ ! $? ]; then if [ ! $? ]; then
echo 4; echo 4;
exit 4; exit 4;
fi; fi;
# if error 5 thrown, test with enabled below # if error 5 thrown, test with enabled below
if [ -n "${5}" ]; then if [ -n "${5}" ]; then
psql -U "${db_user}" -h "${db_host}" -f "${load_sql}" "${db_name}"; PGPASSWORD="${db_password}" psql -U "${db_user}" -h "${db_host}" -f "${load_sql}" "${db_name}";
else else
# load data (redirect ALL error to null), on error exit with 5 # load data (redirect ALL error to null), on error exit with 5
psql -U "${db_user}" -h "${db_host}" -f "${load_sql}" "${db_name}" 2>&1 1>/dev/null 2>/dev/null; PGPASSWORD="${db_password}" psql -U "${db_user}" -h "${db_host}" -f "${load_sql}" "${db_name}" 2>&1 1>/dev/null 2>/dev/null;
fi; fi;
if [ ! $? ]; then if [ ! $? ]; then
echo 5; echo 5;
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for Create\Crc64
* @coversDefaultClass \CoreLibs\Create\Crc64
* @testdox \CoreLibs\Create\Crc64 method tests
*/
final class CoreLibsCreateCrc64Test extends TestCase
{
public function providerFiles(): array
{
return [
'10kb' => [
'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_10KB.bin',
'crc64_base64' => 'oMgkUkGrDME=',
'crc64_hex' => 'a0c8245241ab0cc1',
],
'100kb' => [
'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_100KB.bin',
'crc64_base64' => 'i9Lpf3Y3D3M=',
'crc64_hex' => '8bd2e97f76370f73',
],
'1mb' => [
'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_1MB.bin',
'crc64_base64' => 'WqYAirRJ0q8=',
'crc64_hex' => '5aa6008ab449d2af',
],
'5mb' => [
'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_5MB.bin',
'crc64_base64' => '9hS5kUxbgR0=',
'crc64_hex' => 'f614b9914c5b811d',
],
'10mb' => [
'file' => __DIR__ . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . 'random_10MB.bin',
'crc64_base64' => 'xDmtkk1JhjM=',
'crc64_hex' => 'c439ad924d498633',
],
// everything above 10MB is too slow, sow do not check this
];
}
/**
* Undocumented function
*
* @covers ::crc64
* @covers ::crc64hex
* @dataProvider providerFiles
* @testdox generate crc64 for $file and expecting $crc64_base64 and $crc64_hex [$_dataName]
*
* @return void
*/
public function testCrc64(string $file, string $crc64_base64, string $crc64_hex): void
{
$this->assertSame(
$crc64_base64,
\CoreLibs\Create\Crc64::crc64($file),
"Failed asserting base64 CRC64 for $file"
);
$this->assertSame(
$crc64_hex,
\CoreLibs\Create\Crc64::crc64hex($file),
"Failed asserting hex CRC64 for $file"
);
}
}
// __END__
@@ -421,6 +421,100 @@ final class CoreLibsCreateHashTest extends TestCase
'hash hmac valid' 'hash hmac valid'
); );
} }
// MARK: generateImmutableHashForArray
/**
* Data provider for testGenerateImmutableHashForArray.
*
* @return array
*/
public function generateImmutableHashForArrayProvider(): array
{
return [
'Empty array' => [
[],
null,
'064f01fe',
],
'Simple array' => [
['key' => 'value'],
null,
'78f40899',
],
'flat array a' => [
[0, 1, 'b', 'c', 'a'],
null,
'ccec0f5b',
],
'flat array b' => [
['b', 'c', 'a', 1, 0],
null,
'c6040f5b',
],
'flat array b, not sorted' => [
['b', 'c', 'a', 1, 0],
false,
'c6040f5b',
],
'key array a' => [
['A' => 0, 'F' => 1, 'C' => 'b', 'K' => 'c', 'B' => 'a'],
null,
'01f11355',
],
'key array a, not sorted' => [
['A' => 0, 'F' => 1, 'C' => 'b', 'K' => 'c', 'B' => 'a'],
false,
'04911355',
],
'key array b' => [
['A' => 0, 'B' => 'a', 'C' => 'b', 'F' => 1, 'K' => 'c'],
null,
'01f11355',
],
'key array b, not sorted' => [
['A' => 0, 'B' => 'a', 'C' => 'b', 'F' => 1, 'K' => 'c'],
false,
'01f11355',
],
'complex nested array' => [
[
'level1' => [
'level2' => [
'level3' => 'value'
]
]
],
null,
'd8de1565',
]
];
}
/**
* Test for generating an immutable hash from an array.
*
* @covers ::generateImmutableHashForArray
* @dataProvider generateImmutableHashForArrayProvider
* @testdox generate immutable hash for array with result $expected [$_dataName]
*
* @param array<mixed> $input
* @param string $expected
*/
public function testGenerateImmutableHashForArray(array $input, ?bool $sort_keys, string $expected): void
{
if ($sort_keys === null) {
$this->assertEquals(
$expected,
\CoreLibs\Create\Hash::generateImmutableHashForArray($input)
);
} else {
$this->assertEquals(
$expected,
\CoreLibs\Create\Hash::generateImmutableHashForArray($input, $sort_keys)
);
}
}
} }
// __END__ // __END__
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+53
View File
@@ -1007,6 +1007,59 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbClose(); $db->dbClose();
} }
// MARK: stripAssocOnly
/**
* Data provider for stripAssocOnly tests
*
* @return array<mixed>
*/
public function stripAssocOnlyProvider(): array
{
return [
'empty' => [
[],
[]
],
'int only' => [
[0 => 'foo', 1 => 'bar'],
[],
],
'mixed' => [
[0 => 'foo', 'col_a' => 'foo', 1 => 'bar', 'col_b' => 'bar'],
['col_a' => 'foo', 'col_b' => 'bar'],
],
'key only' => [
['col_a' => 'foo', 'col_b' => 'bar'],
['col_a' => 'foo', 'col_b' => 'bar'],
]
];
}
/**
* Test for stripAssocOnly function
*
* @covers ::stripAssocOnly
* @dataProvider stripAssocOnlyProvider
* @testdox strip assoc non string entries from return [$_dataName]
*
* @param array<mixed> $input
* @param array<string,mixed> $expected
* @return void
*/
public function testStripAssocOnly(array $input, array $expected): void
{
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$this->assertEquals(
$expected,
$db->stripAssocOnly($input)
);
$db->dbClose();
}
// - string escape tests // - string escape tests
// dbEscapeString, dbEscapeLiteral, dbEscapeIdentifier, // dbEscapeString, dbEscapeLiteral, dbEscapeIdentifier,
+39 -32
View File
@@ -342,38 +342,6 @@ print "(ksort): " . DgS::printAr(ArrayHandler::ksortArray($nested)) . "<br>";
print "<hr>"; 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 = [ $search_array = [
'a' => [ 'a' => [
'lookup' => 1, 'lookup' => 1,
@@ -528,6 +496,45 @@ $result = ArrayHandler::selectArrayFromOption(
); );
print "*2*Result: " . DgS::printAr($result) . "<br>"; print "*2*Result: " . DgS::printAr($result) . "<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, search single): " . DgS::printAr(ArrayHandler::findArraysMissingKey(
$search_array,
'other_block',
'time_validation'
)) . "<br>";
print "Result (key limit): " . DgS::printAr(ArrayHandler::findArraysMissingKey(
$search_array,
'other_block',
'time_validation',
'pflaume'
)) . "<br>";
print "Result (all, search array): " . DgS::printAr(ArrayHandler::findArraysMissingKey(
$search_array,
'other_block',
['data', 'time_validation'],
)) . "<br>";
print "Result (key limit, search array): " . DgS::printAr(ArrayHandler::findArraysMissingKey(
$search_array,
'other_block',
['data', 'time_validation'],
'pflaume'
)) . "<br>";
print "<hr>";
print "</body></html>"; print "</body></html>";
// __END__ // __END__
+76
View File
@@ -0,0 +1,76 @@
<?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-crc64';
ob_end_flush();
use CoreLibs\Create\Crc64;
use CoreLibs\Debug\RunningTime;
$log = new CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
$PAGE_NAME = 'TEST CLASS: CRC64';
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>';
$file = getcwd() . DIRECTORY_SEPARATOR . 'class_test.file.php';
print "CRC64: $file: " . Crc64::crc64($file) . "<br>";
print "CRC64HEX: $file: " . Crc64::crc64hex($file) . "<br>";
print "<hr>";
// big file check including speed
$base_path = __DIR__ . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR
. '4dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR .
'Create' . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR;
$max_size = 1024 * 1024 * 10;
# open the base path and loop over each file in there
$files = glob($base_path . '*');
if (!empty($files)) {
foreach ($files as $file) {
if (!is_file($file)) {
continue;
}
if (filesize($file) > $max_size) {
print "Skipping large file: $file<br>";
continue;
}
RunningTime::hrRunningTime();
print "CRC64: $file: " . Crc64::crc64($file) . "<br>";
print "Running Time: $file: " . RunningTime::hrRunningTime() . "<br>";
print "<hr>";
print "CRC64HEX: $file: " . Crc64::crc64hex($file) . "<br>";
print "Running Time: $file: " . RunningTime::hrRunningTime() . "<br>";
RunningTime::hrRunningTimeReset();
print "<hr>";
}
} else {
print "No files found in base path: $base_path<br>";
}
print "</body></html>";
// __END__
+41 -35
View File
@@ -683,42 +683,48 @@ echo "<hr>";
// NOTE: try to replacate connection still exists if script is run a second time // NOTE: try to replacate connection still exists if script is run a second time
// open pg bouncer connection // open pg bouncer connection
$db_pgb = new CoreLibs\DB\IO($DB_CONFIG['test_pgbouncer'] ?? [], $log); try {
print "[PGB] DBINFO: " . $db_pgb->dbInfo() . "<br>"; $db_pgb = new CoreLibs\DB\IO($DB_CONFIG['test_pgbouncer'] ?? [], $log);
if ($db->dbPrepare('pgb_sel_test_foo', $q_prep) === false) { print "[PGB] DBINFO: " . $db_pgb->dbInfo() . "<br>";
print "[PGB] [1] Warning in pgb_sel_test_foo prepare<br>"; if ($db->dbPrepare('pgb_sel_test_foo', $q_prep) === false) {
} else { print "[PGB] [1] Warning in pgb_sel_test_foo prepare<br>";
print "[PGB] [1] pgb_sel_test_foo prepare OK<br>"; } else {
} print "[PGB] [1] pgb_sel_test_foo prepare OK<br>";
// second prepare }
if ($db->dbPrepare('pgb_sel_test_foo', $q_prep) === false) { // second prepare
print "[PGB] [2] Warning in pgb_sel_test_foo prepare<br>"; if ($db->dbPrepare('pgb_sel_test_foo', $q_prep) === false) {
} else { print "[PGB] [2] Warning in pgb_sel_test_foo prepare<br>";
print "[PGB] [2] pgb_sel_test_foo prepare OK<br>"; } else {
} print "[PGB] [2] pgb_sel_test_foo prepare OK<br>";
// same statment name, different query }
if ( // same statment name, different query
$db->dbPrepare('pgb_sel_test_foo', <<<SQL if (
SELECT * FROM test_foo WHERE test = $1 $db->dbPrepare('pgb_sel_test_foo', <<<SQL
ORDER BY test_foo_id DESC LIMIT 5 SELECT * FROM test_foo WHERE test = $1
SQL) === false ORDER BY test_foo_id DESC LIMIT 5
) { SQL) === false
print "[PGB] [3] Error in pgb_sel_test_foo prepare<br>"; ) {
} else { print "[PGB] [3] Error in pgb_sel_test_foo prepare<br>";
print "[PGB] [3] pgb_sel_test_foo prepare OK<br>"; } 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('');
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foobar'); print "[PGB] Empty statement name: " . $log->prAr($stm_status) . "<br>";
print "[PGB] Prepared name not match status: $stm_status<br>"; $stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foobar');
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo'); print "[PGB] Prepared name not match status: $stm_status<br>";
print "[PGB] Prepared name match status: $stm_status<br>"; $stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo');
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo', $q_prep); print "[PGB] Prepared name match status: $stm_status<br>";
print "[PGB] prepared exists and query match status: $stm_status<br>"; $stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo', $q_prep);
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo', "SELECT * FROM test_foo"); print "[PGB] prepared exists and query match status: $stm_status<br>";
print "[PGB] prepared exists and query not 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_pgb->dbClose();
} catch (\Exception $e) {
print "<font color='red'>";
print "[PGB] Exception caught: " . $e->getMessage() . "<br>";
print "</font>";
}
# db write class test # db write class test
$table = 'test_foo'; $table = 'test_foo';
+14
View File
@@ -103,6 +103,20 @@ foreach ($hash_types as $hash_type) {
// print "UNIQU ID SHORT : " . Hash::__uniqId() . "<br>"; // print "UNIQU ID SHORT : " . Hash::__uniqId() . "<br>";
// print "UNIQU ID LONG : " . Hash::__uniqIdLong() . "<br>"; // print "UNIQU ID LONG : " . Hash::__uniqIdLong() . "<br>";
$tests = [
// array diff
[0, 1, 'b', 'c', 'a'],
['b', 'c', 'a', 1, 0],
// key set arrays DO NOT diff
['A' => 0, 'F' => 1, 'C' => 'b', 'K' => 'c', 'B' => 'a'],
['A' => 0, 'B' => 'a', 'C' => 'b', 'F' => 1, 'K' => 'c'],
];
foreach ($tests as $test) {
print "IMMUTABLE HASH FOR ARRAY: " . Hash::generateImmutableHashForArray($test) . "<br>";
print "IMMUTABLE HASH FOR ARRAY: " . Hash::generateImmutableHashForArray($test, sort_keys:false) . "<br>";
print "SERIALIZED HASH FOR ARRAY: " . hash(Hash::DEFAULT_HASH, serialize($test)) . "<br>";
}
print "</body></html>"; print "</body></html>";
// __END__ // __END__
+1
View File
@@ -120,6 +120,7 @@ $test_files = [
'class_test.datetime.php' => 'Class Test: DATE/TIME', 'class_test.datetime.php' => 'Class Test: DATE/TIME',
'class_test.array.php' => 'Class Test: ARRAY HANDLER', 'class_test.array.php' => 'Class Test: ARRAY HANDLER',
'class_test.file.php' => 'Class Test: FILE', 'class_test.file.php' => 'Class Test: FILE',
'class_test.crc64.php' => 'Class Test: CRC64',
'class_test.randomkey.php' => 'Class Test: RANDOM KEY', 'class_test.randomkey.php' => 'Class Test: RANDOM KEY',
'class_test.system.php' => 'Class Test: SYSTEM', 'class_test.system.php' => 'Class Test: SYSTEM',
'class_test.readenvfile.php' => 'Class Test: READ ENV FILE', 'class_test.readenvfile.php' => 'Class Test: READ ENV FILE',
+12 -9
View File
@@ -248,12 +248,15 @@ class ArrayHandler
* If not found return an array with the array block there the required key is missing, * 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 * the path as string with seperator block set and the missing key entry
* *
* @param array<mixed> $in_array * alternate to findArraysWithMissingKey (single missing key)
* @param string|int|float|bool $search_value *
* @param string|array<string> $required_key * @param array<mixed> $in_array Array to search in
* @param ?string $search_key [null] * @param string|int|float|bool $search_value Value that must be set in any
* @param string $path_separator [DATA_SEPARATOR] * (or with search_key in one entry of the array)
* @param string $current_path * @param string|array<string> $required_key Key or Keys that must be set
* @param ?string $search_key [null] limit search_value check to this key
* @param string $path_separator [DATA_SEPARATOR] For the path string separator
* @param string $current_path [''] Current path in the array (used for recursion)
* @return array<array{content?:array<mixed>,path?:string,missing_key?:array<string>}> * @return array<array{content?:array<mixed>,path?:string,missing_key?:array<string>}>
*/ */
public static function findArraysMissingKey( public static function findArraysMissingKey(
@@ -272,14 +275,14 @@ class ArrayHandler
// Check if this array contains the search value // Check if this array contains the search value
// either any value match or with key // either any value match or with key
if ($search_key === null) { if ($search_key === null) {
$containsValue = in_array($search_value, $value, true); $contains_value = in_array($search_value, $value, true);
} else { } else {
$containsValue = array_key_exists($search_key, $value) && $value[$search_key] === $search_value; $contains_value = array_key_exists($search_key, $value) && $value[$search_key] === $search_value;
} }
// If it contains the value but doesn't have the required key // If it contains the value but doesn't have the required key
if ( if (
$containsValue && $contains_value &&
( (
( (
is_string($required_key) && is_string($required_key) &&
+106
View File
@@ -0,0 +1,106 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2026/10/15
* DESCRIPTION:
* AWS CRC64 Implementation
* This can be replaced by:
* https://github.com/awesomized/crc-fast-php-ext
* This this needs: https://github.com/awesomized/crc-fast-rust
* installed, and there is no easy binary package available.
*
* NOTE: for anything larger than 10MB this is absolutely NOT recommended
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class Crc64
{
/**
* Internal CRC64 calculation for a given data string.
*
* @param string $data
* @param int $crc
* @return int
*/
private function crc64Calc(string $data, int $crc = 0): int
{
static $table = null;
if ($table === null) {
// reflected poly 0x9A6C9329AC4BC9B5 (built from halves, too big for a PHP int literal)
$poly = (0x9A6C9329 << 32) | 0xAC4BC9B5;
$table = [];
for ($i = 0; $i < 256; $i++) {
$c = $i;
for ($k = 0; $k < 8; $k++) {
// logical right shift: PHP >> is arithmetic, so mask off the sign bit
$c = ($c & 1)
? ((($c >> 1) & PHP_INT_MAX) ^ $poly)
: (($c >> 1) & PHP_INT_MAX);
}
$table[$i] = $c;
}
}
$crc = ~$crc; // xor 0xFFFFFFFFFFFFFFFF
foreach (unpack('C*', $data) ?: [] as $b) {
$crc = $table[($crc ^ $b) & 0xFF] ^ (($crc >> 8) & 0x00FFFFFFFFFFFFFF);
}
return ~$crc;
}
/**
* Returns int value of the CRC64 checksum for the given file.
*
* @param string $path
* @param int<1,16777216> $chunkSize
* @return int
*/
private function crc64CalcFile(string $path, int $chunkSize = 8 * 1024 * 1024): int
{
$fh = fopen($path, 'rb');
if ($fh === false) {
throw new \RuntimeException("Cannot open $path");
}
$crc = 0;
try {
while (!feof($fh)) {
$chunk = fread($fh, $chunkSize);
if ($chunk === false) {
throw new \RuntimeException("Read error on $path");
}
$crc = $this->crc64Calc($chunk, $crc);
}
} finally {
fclose($fh);
}
return $crc;
}
/**
* Returns the Base64-encoded CRC64 checksum for the given file.
*
* @param string $path
* @return string
*/
public static function crc64(string $path): string
{
return base64_encode(pack('J', (new self())->crc64CalcFile($path))); // J = uint64 big-endian
}
/**
* Returns the 16-character lowercase hexadecimal CRC64 checksum for the given file.
* Same as base64.b64decode(...).hex()
*
* @param string $path
* @return string
*/
public static function crc64hex(string $path): string
{
return sprintf('%016x', (new self())->crc64CalcFile($path));
}
}
// __END__
+23
View File
@@ -8,6 +8,8 @@ declare(strict_types=1);
namespace CoreLibs\Create; namespace CoreLibs\Create;
use CoreLibs\Combined\ArrayHandler;
class Hash class Hash
{ {
/** @var string default short hash -> deprecated use STANDARD_HASH_SHORT */ /** @var string default short hash -> deprecated use STANDARD_HASH_SHORT */
@@ -210,6 +212,27 @@ class Hash
{ {
return self::hash($string, self::STANDARD_HASH); return self::hash($string, self::STANDARD_HASH);
} }
/**
* generate a hash over any array data
* optional will not sort keys and so create hashes based on the original data.
* for correct array comapre it is recommended to keep sort keys on
*
* @param array<mixed> $data
* @param bool $sort_keys [true] Whether to sort the array keys before hashing
* @return string
*/
public static function generateImmutableHashForArray(array $data, bool $sort_keys = true): string
{
// Create a sorted copy to ensure consistent hashing
// Generate hash using serialize for better accuracy
return hash(
self::DEFAULT_HASH,
serialize(
$sort_keys ? ArrayHandler::createSortedArrayByKey($data) : $data
)
);
}
} }
// __END__ // __END__
+36 -22
View File
@@ -520,7 +520,7 @@ class IO
} }
// ************************************************************* // *************************************************************
// PRIVATE METHODS // MARK: PRIVATE METHODS
// ************************************************************* // *************************************************************
/** /**
@@ -1703,11 +1703,11 @@ class IO
} }
// ************************************************************* // *************************************************************
// PUBLIC METHODS // MARK: PUBLIC METHODS
// ************************************************************* // *************************************************************
// *************************** // ***************************
// CLOSE, STATUS, SETTINGS VARIABLE READ // MARK: CLOSE, STATUS, SETTINGS VARIABLE READ
// *************************** // ***************************
/** /**
@@ -1950,7 +1950,7 @@ class IO
} }
// *************************** // ***************************
// DEBUG DATA DUMP // MARK: DEBUG DATA DUMP
// *************************** // ***************************
/** /**
@@ -1978,7 +1978,7 @@ class IO
} }
// *************************** // ***************************
// CHECK QUERY TYPE // MARK: CHECK QUERY TYPE
// *************************** // ***************************
/** /**
@@ -2035,7 +2035,7 @@ class IO
} }
// *************************** // ***************************
// DATA WRITE CONVERSION // MARK: DATA WRITE CONVERSION
// *************************** // ***************************
/** /**
@@ -2156,7 +2156,7 @@ class IO
} }
// *************************** // ***************************
// DATA READ/WRITE CONVERSION // MARK: DATA READ/WRITE CONVERSION
// *************************** // ***************************
/** /**
@@ -2194,7 +2194,7 @@ class IO
} }
// *************************** // ***************************
// DATA READ CONVERSION // MARK: DATA READ CONVERSION
// *************************** // ***************************
/** /**
@@ -2263,8 +2263,22 @@ class IO
return is_array($__db_array_parse) ? $__db_array_parse : []; return is_array($__db_array_parse) ? $__db_array_parse : [];
} }
/**
* Will strip all single numberic entries from the array.
* This is for database return
*
* @param array<mixed> $res
* @return array<string,mixed>
*/
public function stripAssocOnly(array $res): array
{
return array_filter($res, function ($key) {
return !is_numeric($key);
}, ARRAY_FILTER_USE_KEY);
}
// *************************** // ***************************
// TABLE META DATA READ // MARK: TABLE META DATA READ
// *************************** // ***************************
/** /**
@@ -2290,7 +2304,7 @@ class IO
} }
// *************************** // ***************************
// QUERY EXECUSION AND DATA READ // MARK: QUERY EXECUSION AND DATA READ
// *************************** // ***************************
/** /**
@@ -2927,7 +2941,7 @@ class IO
} }
// *************************** // ***************************
// CURSOR RETURN // MARK: CURSOR RETURN
// *************************** // ***************************
/** /**
@@ -2941,7 +2955,7 @@ class IO
} }
// *************************** // ***************************
// CURSOR EXT CACHE RESET // MARK: CURSOR EXT CACHE RESET
// *************************** // ***************************
/** /**
@@ -2971,7 +2985,7 @@ class IO
} }
// *************************** // ***************************
// CURSOR EXT DATA CHECK // MARK: CURSOR EXT DATA CHECK
// *************************** // ***************************
/** /**
@@ -3066,7 +3080,7 @@ class IO
} }
// *************************** // ***************************
// MAXIMUM QUERY EXECUTION CHECK HELPERS // MARK: MAXIMUM QUERY EXECUTION CHECK HELPERS
// *************************** // ***************************
/** /**
@@ -3102,7 +3116,7 @@ class IO
} }
// *************************** // ***************************
// PREPARED QUERY WORK // MARK: PREPARED QUERY WORK
// *************************** // ***************************
/** /**
@@ -3379,7 +3393,7 @@ class IO
} }
// *************************** // ***************************
// ASYNCHRONUS EXECUTION/CHECK // MARK: ASYNCHRONUS EXECUTION/CHECK
// *************************** // ***************************
/** /**
@@ -3539,7 +3553,7 @@ class IO
} }
// *************************** // ***************************
// COMPLEX WRITE WITH CONFIG ARRAYS // MARK: COMPLEX WRITE WITH CONFIG ARRAYS
// *************************** // ***************************
// ** REMARK ** // ** REMARK **
@@ -3720,7 +3734,7 @@ class IO
} }
// *************************** // ***************************
// INTERNAL SETTINGS READ/CHANGE // MARK: INTERNAL SETTINGS READ/CHANGE
// *************************** // ***************************
/** /**
@@ -4048,7 +4062,7 @@ class IO
} }
// *************************** // ***************************
// QUERY DATA AND DB HANDLER // MARK: QUERY DATA AND DB HANDLER
// *************************** // ***************************
/** /**
@@ -4151,7 +4165,7 @@ class IO
} }
// *************************** // ***************************
// INTERNAL VARIABLES READ POST QUERY RUN // MARK: INTERNAL VARIABLES READ POST QUERY RUN
// *************************** // ***************************
/** /**
@@ -4442,7 +4456,7 @@ class IO
} }
// *************************** // ***************************
// ERROR AND WARNING DATA // MARK: ERROR AND WARNING DATA
// *************************** // ***************************
/** /**
@@ -4499,7 +4513,7 @@ class IO
} }
// *************************** // ***************************
// DEPEREACTED CALLS // MARK: DEPEREACTED CALLS
// all call below are no longer in use and throw deprecated errors // all call below are no longer in use and throw deprecated errors
// *************************** // ***************************