Compare commits
47 Commits
v9.29.0
...
feature/TT
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59948c0f3d | ||
|
|
0022ea6b9a | ||
|
|
1b387baea9 | ||
|
|
8574175e78 | ||
|
|
ec8c4d6e60 | ||
|
|
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 |
@@ -1,9 +1,9 @@
|
||||
<?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="phpunit" version="^10.3.5" installed="10.5.46" location="./tools/phpunit" copy="false"/>
|
||||
<phar name="phpcbf" version="^3.7.2" installed="3.13.0" location="./tools/phpcbf" copy="false"/>
|
||||
<phar name="phpcs" version="^3.10.3" installed="3.13.0" location="./tools/phpcs" copy="false"/>
|
||||
<phar name="phpstan" version="^2.0" installed="2.1.17" location="./tools/phpstan" copy="false"/>
|
||||
<phar name="phan" version="^5.4.3" installed="5.4.3" location="./tools/phan" copy="false"/>
|
||||
<phar name="psalm" version="^5.15.0" installed="5.24.0" location="./tools/psalm" copy="false"/>
|
||||
<phar name="phpdox" version="^0.12.0" installed="0.12.0" location="./tools/phpdox" copy="false"/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
||||
base=$(pwd)"/";
|
||||
# must be run in ${base}
|
||||
cd $base || exit;
|
||||
${base}tools/phan --progress-bar -C --analyze-twice;
|
||||
#PHAN_DISABLE_XDEBUG_WARN=1;${base}tools/phan --progress-bar -C --analyze-twice
|
||||
PHAN_DISABLE_XDEBUG_WARN=1;${base}vendor/bin/phan --progress-bar -C --analyze-twice
|
||||
cd ~ || exit;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
|
||||
base=$(pwd)"/";
|
||||
# must be run in ${base}
|
||||
cd $base || exit;
|
||||
${base}tools/phpstan;
|
||||
|
||||
@@ -23,7 +23,7 @@ EOF
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
@@ -12,6 +12,8 @@ Not yet covered tests:
|
||||
- loginGetLocale
|
||||
- loginGetHeaderColor
|
||||
- loginGetPages
|
||||
- loginGetPageLookupList
|
||||
- loginPageAccessAllowed
|
||||
- loginGetEuid
|
||||
*/
|
||||
|
||||
|
||||
@@ -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
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,222 @@ 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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __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;
|
||||
@@ -288,7 +292,7 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::hash
|
||||
* @testdox hash with invalid type [$_dataName]
|
||||
* @testdox hash with invalid type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -301,6 +305,122 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
\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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -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");
|
||||
@@ -4744,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);
|
||||
@@ -4757,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;
|
||||
}
|
||||
}
|
||||
@@ -4774,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;
|
||||
}
|
||||
}
|
||||
@@ -4794,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;
|
||||
}
|
||||
}
|
||||
@@ -4818,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;
|
||||
}
|
||||
}
|
||||
@@ -5008,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 '
|
||||
);
|
||||
@@ -5235,6 +5236,9 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
$3
|
||||
-- comment 3
|
||||
, $4
|
||||
-- ignore $5, $6
|
||||
-- $7, $8
|
||||
-- digest($9, 10)
|
||||
)
|
||||
SQL,
|
||||
'count' => 4,
|
||||
@@ -5305,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
'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__
|
||||
|
||||
@@ -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__
|
||||
@@ -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>";
|
||||
|
||||
@@ -83,6 +83,9 @@ function mtParseCSV(
|
||||
'UTF-8',
|
||||
$encoding
|
||||
);
|
||||
if ($string === false) {
|
||||
return $lines;
|
||||
}
|
||||
}
|
||||
if ($flag == 'INTERN') {
|
||||
// split with PHP function
|
||||
|
||||
@@ -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,
|
||||
@@ -43,16 +44,24 @@ print "S::__CRC32B: $to_crc: " . Hash::__crc32b($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>";
|
||||
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::__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 (old): " . $type . ": " . Hash::__hash($text, $type) . "<br>";
|
||||
@@ -66,6 +75,31 @@ 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>";
|
||||
|
||||
|
||||
@@ -67,6 +67,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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>";
|
||||
@@ -152,5 +160,6 @@ if (is_string($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',
|
||||
|
||||
@@ -38,13 +38,21 @@ $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>";
|
||||
|
||||
|
||||
@@ -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,9 +106,40 @@ $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>";
|
||||
|
||||
$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>";
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -34,6 +34,17 @@ function executeFunctionByName(functionName, context) {
|
||||
}
|
||||
return context[func].apply(context, args);
|
||||
}
|
||||
function runFunction(name) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
runFunctionArgsArray(name, args);
|
||||
}
|
||||
function runFunctionArgsArray(name, args) {
|
||||
var fn = window[name];
|
||||
if (typeof fn !== "function") {
|
||||
return;
|
||||
}
|
||||
fn.apply(window, args);
|
||||
}
|
||||
function isObject(val) {
|
||||
if (val === null) {
|
||||
return false;
|
||||
@@ -659,6 +670,31 @@ function getQueryStringParam(search = "", query = "", single = false) {
|
||||
}
|
||||
return param;
|
||||
}
|
||||
function hasUrlParameter(key) {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.has(key);
|
||||
}
|
||||
function getUrlParameter(key) {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.get(key);
|
||||
}
|
||||
function updateUrlParameter(key, value, reload = false) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set(key, value);
|
||||
const newUrl = url.toString();
|
||||
window.history.pushState({ path: newUrl }, "", newUrl);
|
||||
if (reload) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
function removeUrlParameter(key, reload = false) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete(key);
|
||||
window.history.pushState({}, "", url.toString());
|
||||
if (reload) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// src/utils/LoginLogout.mjs
|
||||
function loginLogout() {
|
||||
@@ -979,7 +1015,7 @@ var ActionBox = class {
|
||||
$("#overlayBox").css("zIndex", this.zIndex.base);
|
||||
}
|
||||
$("#overlayBox").show();
|
||||
if (!keyInObject(target_id, this.zIndex.boxes)) {
|
||||
if (!objectKeyExists(this.zIndex.boxes, target_id)) {
|
||||
this.zIndex.boxes[target_id] = this.zIndex.max;
|
||||
this.zIndex.max += 10;
|
||||
} else if (this.zIndex.boxes[target_id] + 10 < this.zIndex.max) {
|
||||
@@ -1005,7 +1041,7 @@ var ActionBox = class {
|
||||
if (!exists(target_id)) {
|
||||
return;
|
||||
}
|
||||
if (keyInObject(target_id, this.action_box_storage) && clean === true) {
|
||||
if (objectKeyExists(this.action_box_storage, target_id) && clean === true) {
|
||||
this.action_box_storage[target_id] = {};
|
||||
}
|
||||
if (clean === true) {
|
||||
@@ -1043,15 +1079,15 @@ var ActionBox = class {
|
||||
* @param {Object} [settings={}] Optional settings, eg style sheets
|
||||
*/
|
||||
createActionBox(target_id = "actionBox", title = "", contents = {}, headers = {}, settings = {}, show_close = true) {
|
||||
if (!keyInObject(target_id, this.action_box_storage)) {
|
||||
if (!objectKeyExists(this.action_box_storage, target_id)) {
|
||||
this.action_box_storage[target_id] = {};
|
||||
}
|
||||
let header_css = [];
|
||||
if (keyInObject("header_css", settings)) {
|
||||
if (objectKeyExists(settings, "header_css")) {
|
||||
header_css = settings.header_css;
|
||||
}
|
||||
let action_box_css = [];
|
||||
if (keyInObject("action_box_css", settings)) {
|
||||
if (objectKeyExists(settings, "action_box_css")) {
|
||||
action_box_css = settings.action_box_css;
|
||||
}
|
||||
let elements = [];
|
||||
@@ -1082,14 +1118,14 @@ var ActionBox = class {
|
||||
)
|
||||
));
|
||||
if (getObjectCount(headers) > 0) {
|
||||
if (keyInObject("raw_string", headers)) {
|
||||
if (objectKeyExists(headers, "raw_string")) {
|
||||
elements.push(headers.raw_string);
|
||||
} else {
|
||||
elements.push(this.hec.phfo(headers));
|
||||
}
|
||||
}
|
||||
if (getObjectCount(contents) > 0) {
|
||||
if (keyInObject("raw_string", contents)) {
|
||||
if (objectKeyExists(contents, "raw_string")) {
|
||||
elements.push(contents.raw_string);
|
||||
} else {
|
||||
elements.push(this.hec.phfo(contents));
|
||||
@@ -1517,12 +1553,24 @@ function html_options_block2(name, data, selected = "", multiple = 0, options_on
|
||||
function html_options_refill2(name, data, sort = "") {
|
||||
html_options_refill(name, data, sort);
|
||||
}
|
||||
function parseQueryString2(query = "", return_key = "") {
|
||||
return parseQueryString(query, return_key);
|
||||
function parseQueryString2(query = "", return_key = "", single = false) {
|
||||
return parseQueryString(query, return_key, single);
|
||||
}
|
||||
function getQueryStringParam2(search = "", query = "", single = false) {
|
||||
return getQueryStringParam(search, query, single);
|
||||
}
|
||||
function updateUrlParameter2(key, value, reload = false) {
|
||||
return updateUrlParameter(key, value, reload);
|
||||
}
|
||||
function removeUrlParameter2(key, reload = false) {
|
||||
return removeUrlParameter(key, reload);
|
||||
}
|
||||
function hasUrlParameter2(key) {
|
||||
return hasUrlParameter(key);
|
||||
}
|
||||
function getUrlParameter2(key) {
|
||||
return getUrlParameter(key);
|
||||
}
|
||||
function loginLogout2() {
|
||||
loginLogout();
|
||||
}
|
||||
|
||||
4
www/admin/layout/javascript/utils.min.js
vendored
4
www/admin/layout/javascript/utils.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,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;
|
||||
@@ -1152,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
|
||||
@@ -1264,6 +1266,7 @@ class Login
|
||||
}
|
||||
$edit_page_ids = [];
|
||||
$pages = [];
|
||||
$pages_lookup = [];
|
||||
$pages_acl = [];
|
||||
// set pages access
|
||||
$q = <<<SQL
|
||||
@@ -1307,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
|
||||
@@ -1367,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
|
||||
@@ -1526,6 +1531,8 @@ 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_cuid'] = null;
|
||||
$this->acl['unit_name'] = null;
|
||||
@@ -2728,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
|
||||
|
||||
/**
|
||||
|
||||
@@ -383,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -988,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,18 +150,22 @@ 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> $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,
|
||||
string|int $key,
|
||||
string|int|bool $value,
|
||||
string|int|bool|array $value,
|
||||
bool $strict = false
|
||||
): bool {
|
||||
// convert to array
|
||||
if (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
foreach ($array as $_key => $_value) {
|
||||
// if value is an array, we search
|
||||
if (is_array($_value)) {
|
||||
@@ -167,9 +173,9 @@ class ArrayHandler
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -236,6 +242,160 @@ 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> $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 $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 ($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> $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 $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 ($array == []) {
|
||||
return [];
|
||||
}
|
||||
// init return result
|
||||
$result = [];
|
||||
// case sensitive convert if string
|
||||
if ($case_insensitive && is_string($search)) {
|
||||
$search = strtolower($search);
|
||||
}
|
||||
|
||||
foreach ($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
|
||||
*
|
||||
@@ -551,6 +711,106 @@ class ArrayHandler
|
||||
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> $array Array to sort by values
|
||||
* @param bool $case_insensitive [false] Sort case insensitive
|
||||
* @param bool $reverse [false] Reverse sort
|
||||
* @param bool $maintain_keys [false] Maintain keys
|
||||
* @param int $flag [SORT_REGULAR] Sort flags
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function sortArray(
|
||||
array $array,
|
||||
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($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
|
||||
(usort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
|
||||
) :
|
||||
(
|
||||
$maintain_keys ?
|
||||
($reverse ? arsort($array, $flag) : asort($array, $flag)) :
|
||||
($reverse ? rsort($array, $flag) : sort($array, $flag))
|
||||
);
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* sort by key ascending or descending and return
|
||||
*
|
||||
* @param array<mixed> $array Array to srt
|
||||
* @param bool $case_insensitive [false] Sort keys case insenstive
|
||||
* @param bool $reverse [false] Reverse key sort
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function ksortArray(array $array, bool $case_insensitive = false, bool $reverse = false): array
|
||||
{
|
||||
$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(
|
||||
$array,
|
||||
$case_insensitive ?
|
||||
($reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case) :
|
||||
($reverse ? $fk_sort_reverse : $fk_sort)
|
||||
);
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -55,10 +55,10 @@ 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();
|
||||
@@ -119,6 +119,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__
|
||||
|
||||
@@ -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,116 @@ 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)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ?
|
||||
|
||||
@@ -49,7 +49,7 @@ class Hash
|
||||
* replacement for __crc32b call
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @param bool $use_sha use sha1 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
|
||||
*/
|
||||
@@ -81,7 +81,7 @@ 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
|
||||
*/
|
||||
@@ -92,12 +92,40 @@ class Hash
|
||||
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 ash
|
||||
* @param string $hash_type hash type (default 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(
|
||||
@@ -108,12 +136,36 @@ class Hash
|
||||
empty($hash_type) ||
|
||||
!in_array($hash_type, hash_algos())
|
||||
) {
|
||||
// fallback to default hash type if none set or invalid
|
||||
// fallback to default hash type if empty or invalid
|
||||
$hash_type = self::STANDARD_HASH;
|
||||
}
|
||||
return hash($hash_type, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
@@ -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 */
|
||||
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(self::$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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,7 +111,7 @@ class RandomKey
|
||||
{
|
||||
if (
|
||||
$key_length > 0 &&
|
||||
$key_length <= self::$max_key_length
|
||||
$key_length <= self::KEY_LENGTH_MAX
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
@@ -67,6 +125,7 @@ class RandomKey
|
||||
*
|
||||
* @param int $key_length key length
|
||||
* @return bool true/false for set status
|
||||
* @deprecated This function does no longer set the key length, the randomKeyGen parameter has to b used
|
||||
*/
|
||||
public static function setRandomKeyLength(int $key_length): bool
|
||||
{
|
||||
@@ -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::validateRandomKeyLenght($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;
|
||||
}
|
||||
|
||||
@@ -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 = [];
|
||||
@@ -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);
|
||||
@@ -1441,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 {
|
||||
@@ -1475,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(
|
||||
@@ -1959,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;
|
||||
}
|
||||
@@ -2361,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] = [
|
||||
@@ -2539,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)
|
||||
@@ -2937,12 +2945,14 @@ 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'])) {
|
||||
if (
|
||||
$show_warning &&
|
||||
empty($this->cursor_ext[$query_hash]['query'])
|
||||
) {
|
||||
$this->__dbWarning(18, context: [
|
||||
'query' => $query,
|
||||
'params' => $params,
|
||||
@@ -2982,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])
|
||||
@@ -3012,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])
|
||||
@@ -3038,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])
|
||||
@@ -3064,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3077,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 {
|
||||
@@ -4046,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
|
||||
@@ -4054,7 +4064,7 @@ 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(
|
||||
$query . (
|
||||
@@ -4104,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
|
||||
*
|
||||
@@ -4283,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
|
||||
|
||||
90
www/lib/CoreLibs/DB/SQL/Interface/DatabaseInterface.php
Normal file
90
www/lib/CoreLibs/DB/SQL/Interface/DatabaseInterface.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2025/7/17
|
||||
* DESCRIPTION:
|
||||
* SQLite IO basic interface
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DB\SQL\Interface;
|
||||
|
||||
interface DatabaseInterface
|
||||
{
|
||||
/**
|
||||
* Table meta data
|
||||
* Note that if columns have multi
|
||||
*
|
||||
* @param string $table
|
||||
* @return array<array<string,mixed>>|false
|
||||
*/
|
||||
public function dbShowTableMetaData(string $table): array|false;
|
||||
|
||||
/**
|
||||
* for reading or simple execution, no return data
|
||||
*
|
||||
* @param string $query
|
||||
* @return int|false
|
||||
*/
|
||||
public function dbExec(string $query): int|false;
|
||||
|
||||
/**
|
||||
* Run a simple query and return its statement
|
||||
*
|
||||
* @param string $query
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function dbQuery(string $query): \PDOStatement|false;
|
||||
|
||||
/**
|
||||
* Execute one query with params
|
||||
*
|
||||
* @param string $query
|
||||
* @param array<mixed> $params
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function dbExecParams(string $query, array $params): \PDOStatement|false;
|
||||
|
||||
/**
|
||||
* Prepare query
|
||||
*
|
||||
* @param string $query
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function dbPrepare(string $query): \PDOStatement|false;
|
||||
|
||||
/**
|
||||
* execute a cursor
|
||||
*
|
||||
* @param \PDOStatement $cursor
|
||||
* @param array<mixed> $params
|
||||
* @return bool
|
||||
*/
|
||||
public function dbCursorExecute(\PDOStatement $cursor, array $params): bool;
|
||||
|
||||
/**
|
||||
* return array with data, when finshed return false
|
||||
* also returns false on error
|
||||
*
|
||||
* TODO: This is currently a one time run
|
||||
* if the same query needs to be run again, the cursor_ext must be reest
|
||||
* with dbCacheReset
|
||||
*
|
||||
* @param string $query
|
||||
* @param array<mixed> $params
|
||||
* @return array<mixed>|false
|
||||
*/
|
||||
public function dbReturnArray(string $query, array $params = []): array|false;
|
||||
|
||||
/**
|
||||
* get current db handler
|
||||
* this is for raw access
|
||||
*
|
||||
* @return \PDO
|
||||
*/
|
||||
public function getDbh(): \PDO;
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
433
www/lib/CoreLibs/DB/SQLiteIO.php
Normal file
433
www/lib/CoreLibs/DB/SQLiteIO.php
Normal file
@@ -0,0 +1,433 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2024/8/21
|
||||
* DESCRIPTION:
|
||||
* SQL Lite interface
|
||||
* Note: This is a very simple library and in future should perhaps merge with the master
|
||||
* CoreLibs SQL interface
|
||||
*
|
||||
* TODO: This should move to the CoreLibs\DB\IO class as a sub type for "sqlite" next to "pgsql"
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DB;
|
||||
|
||||
use CoreLibs\Create\Hash;
|
||||
|
||||
class SqLite implements SQL\Interface\DatabaseInterface
|
||||
{
|
||||
/** @var \CoreLibs\Logging\Logging logging */
|
||||
public \CoreLibs\Logging\Logging $log;
|
||||
|
||||
/** @var string database connection string */
|
||||
private string $dsn;
|
||||
/** @var \PDO database handler */
|
||||
private \PDO $dbh;
|
||||
/** @var PDOStatement|false one cursor, for internal handling */
|
||||
// private \PDOStatement|false $cursor;
|
||||
/** @var array<string,mixed> extended cursoers string index with content */
|
||||
private array $cursor_ext = [];
|
||||
|
||||
/**
|
||||
* init database system
|
||||
*
|
||||
* @param \CoreLibs\Logging\Logging $log
|
||||
* @param string $dsn
|
||||
*/
|
||||
public function __construct(
|
||||
\CoreLibs\Logging\Logging $log,
|
||||
string $dsn
|
||||
) {
|
||||
$this->log = $log;
|
||||
// open new connection
|
||||
if ($this->__connectToDB($dsn) === false) {
|
||||
throw new \ErrorException("Cannot load database: " . $dsn, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// MARK: PRIVATE METHODS
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
* Get a cursor dump with all info
|
||||
*
|
||||
* @param \PDOStatement $cursor
|
||||
* @return string|false
|
||||
*/
|
||||
private function __dbGetCursorDump(\PDOStatement $cursor): string|false
|
||||
{
|
||||
// get the cursor info
|
||||
ob_start();
|
||||
$cursor->debugDumpParams();
|
||||
$cursor_dump = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $cursor_dump;
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch rows from a cursor (post execute)
|
||||
*
|
||||
* @param \PDOStatement $cursor
|
||||
* @return array<mixed>|false
|
||||
*/
|
||||
private function __dbFetchArray(\PDOStatement $cursor): array|false
|
||||
{
|
||||
try {
|
||||
// on empty array return false
|
||||
// TODO make that more elegant?
|
||||
return empty($row = $cursor->fetch(mode:\PDO::FETCH_NAMED)) ? false : $row;
|
||||
} catch (\PDOException $e) {
|
||||
$this->log->error(
|
||||
"Cannot fetch from cursor",
|
||||
[
|
||||
"dsn" => $this->dsn,
|
||||
"DumpParams" => $this->__dbGetCursorDump($cursor),
|
||||
"PDOException" => $e
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: open database
|
||||
|
||||
/**
|
||||
* Open database
|
||||
* reports errors for wrong DSN or failed connection
|
||||
*
|
||||
* @param string $dsn
|
||||
* @return bool
|
||||
*/
|
||||
private function __connectToDB(string $dsn): bool
|
||||
{
|
||||
// check if dsn starts with ":"
|
||||
if (!str_starts_with($dsn, "sqlite:")) {
|
||||
$this->log->error(
|
||||
"Invalid dsn string",
|
||||
[
|
||||
"dsn" => $dsn
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// TODO: if not ":memory:" check if path to file is writeable by system
|
||||
// avoid double open
|
||||
if (!empty($this->dsn) && $dsn == $this->dsn) {
|
||||
$this->log->info(
|
||||
"Connection already establisehd with this dsn",
|
||||
[
|
||||
"dsn" => $dsn,
|
||||
]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
// TODO: check that folder is writeable
|
||||
// set DSN and open connection
|
||||
$this->dsn = $dsn;
|
||||
try {
|
||||
$this->dbh = new \PDO($this->dsn);
|
||||
} catch (\PDOException $e) {
|
||||
$this->log->error(
|
||||
"Cannot open database",
|
||||
[
|
||||
"dsn" => $this->dsn,
|
||||
"PDOException" => $e
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// MARK: PUBLIC METHODS
|
||||
// *********************************************************************
|
||||
|
||||
// MARK: db meta data (table info)
|
||||
|
||||
/**
|
||||
* Table meta data
|
||||
* Note that if columns have multi entries multiple lines are returned
|
||||
* ?1 is equal to $1 in this query
|
||||
*
|
||||
* @param string $table
|
||||
* @return array<array<string,mixed>>|false
|
||||
*/
|
||||
public function dbShowTableMetaData(string $table): array|false
|
||||
{
|
||||
$table_info = [];
|
||||
$query = <<<SQL
|
||||
SELECT
|
||||
ti.cid, ti.name, ti.type, ti.'notnull', ti.dflt_value, ti.pk,
|
||||
il_ii.idx_name, il_ii.idx_unique, il_ii.idx_origin, il_ii.idx_partial
|
||||
FROM
|
||||
sqlite_schema AS m,
|
||||
pragma_table_info(m.name) AS ti
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
il.name AS idx_name, il.'unique' AS idx_unique, il.origin AS idx_origin, il.partial AS idx_partial,
|
||||
ii.cid AS tbl_cid
|
||||
FROM
|
||||
sqlite_schema AS m,
|
||||
pragma_index_list(m.name) AS il,
|
||||
pragma_index_info(il.name) AS ii
|
||||
WHERE m.name = ?1
|
||||
) AS il_ii ON (ti.cid = il_ii.tbl_cid)
|
||||
WHERE
|
||||
m.name = ?1
|
||||
SQL;
|
||||
while (is_array($row = $this->dbReturnArray($query, [$table]))) {
|
||||
$table_info[] = [
|
||||
'cid' => $row['cid'],
|
||||
'name' => $row['name'],
|
||||
'type' => $row['type'],
|
||||
'notnull' => $row['notnull'],
|
||||
'dflt_value' => $row['dflt_value'],
|
||||
'pk' => $row['pk'],
|
||||
'idx_name' => $row['idx_name'],
|
||||
'idx_unique' => $row['idx_unique'],
|
||||
'idx_origin' => $row['idx_origin'],
|
||||
'idx_partial' => $row['idx_partial'],
|
||||
];
|
||||
}
|
||||
|
||||
if (!$table_info) {
|
||||
return false;
|
||||
}
|
||||
return $table_info;
|
||||
}
|
||||
|
||||
// MARK: db exec
|
||||
|
||||
/**
|
||||
* for reading or simple execution, no return data
|
||||
*
|
||||
* @param string $query
|
||||
* @return int|false
|
||||
*/
|
||||
public function dbExec(string $query): int|false
|
||||
{
|
||||
try {
|
||||
return $this->dbh->exec($query);
|
||||
} catch (\PDOException $e) {
|
||||
$this->log->error(
|
||||
"Cannot execute query",
|
||||
[
|
||||
"dsn" => $this->dsn,
|
||||
"query" => $query,
|
||||
"PDOException" => $e
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: db query
|
||||
|
||||
/**
|
||||
* Run a simple query and return its statement
|
||||
*
|
||||
* @param string $query
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function dbQuery(string $query): \PDOStatement|false
|
||||
{
|
||||
try {
|
||||
return $this->dbh->query($query, \PDO::FETCH_NAMED);
|
||||
} catch (\PDOException $e) {
|
||||
$this->log->error(
|
||||
"Cannot run query",
|
||||
[
|
||||
"dsn" => $this->dsn,
|
||||
"query" => $query,
|
||||
"PDOException" => $e
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: db prepare & execute calls
|
||||
|
||||
/**
|
||||
* Execute one query with params
|
||||
*
|
||||
* @param string $query
|
||||
* @param array<mixed> $params
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function dbExecParams(string $query, array $params): \PDOStatement|false
|
||||
{
|
||||
// prepare query
|
||||
if (($cursor = $this->dbPrepare($query)) === false) {
|
||||
return false;
|
||||
}
|
||||
// execute the query, on failure return false
|
||||
if ($this->dbCursorExecute($cursor, $params) === false) {
|
||||
return false;
|
||||
}
|
||||
return $cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare query
|
||||
*
|
||||
* @param string $query
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function dbPrepare(string $query): \PDOStatement|false
|
||||
{
|
||||
try {
|
||||
// store query with cursor so we can reference?
|
||||
return $this->dbh->prepare($query);
|
||||
} catch (\PDOException $e) {
|
||||
$this->log->error(
|
||||
"Cannot open cursor",
|
||||
[
|
||||
"dsn" => $this->dsn,
|
||||
"query" => $query,
|
||||
"PDOException" => $e
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* execute a cursor
|
||||
*
|
||||
* @param \PDOStatement $cursor
|
||||
* @param array<mixed> $params
|
||||
* @return bool
|
||||
*/
|
||||
public function dbCursorExecute(\PDOStatement $cursor, array $params): bool
|
||||
{
|
||||
try {
|
||||
return $cursor->execute($params);
|
||||
} catch (\PDOException $e) {
|
||||
// write error log
|
||||
$this->log->error(
|
||||
"Cannot execute prepared query",
|
||||
[
|
||||
"dsn" => $this->dsn,
|
||||
"params" => $params,
|
||||
"DumpParams" => $this->__dbGetCursorDump($cursor),
|
||||
"PDOException" => $e
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: db return array
|
||||
|
||||
/**
|
||||
* Returns hash for query
|
||||
* Hash is used in all internal storage systems for return data
|
||||
*
|
||||
* @param string $query The query to create the hash from
|
||||
* @param array<mixed> $params If the query is params type we need params
|
||||
* data to create a unique call one, optional
|
||||
* @return string Hash, as set by hash long
|
||||
*/
|
||||
public function dbGetQueryHash(string $query, array $params = []): string
|
||||
{
|
||||
return Hash::hashLong(
|
||||
$query . (
|
||||
$params !== [] ?
|
||||
'#' . json_encode($params) : ''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* resets all data stored to this query
|
||||
* @param string $query The Query whose cache should be cleaned
|
||||
* @param array<mixed> $params If the query is params type we need params
|
||||
* 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
|
||||
{
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
// clears cache for this query
|
||||
if (empty($this->cursor_ext[$query_hash]['query'])) {
|
||||
$this->log->error('Cannot reset cursor_ext with given query and params', [
|
||||
"query" => $query,
|
||||
"params" => $params,
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
unset($this->cursor_ext[$query_hash]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* return array with data, when finshed return false
|
||||
* also returns false on error
|
||||
*
|
||||
* TODO: This is currently a one time run
|
||||
* if the same query needs to be run again, the cursor_ext must be reest
|
||||
* with dbCacheReset
|
||||
*
|
||||
* @param string $query
|
||||
* @param array<mixed> $params
|
||||
* @return array<mixed>|false
|
||||
*/
|
||||
public function dbReturnArray(string $query, array $params = []): array|false
|
||||
{
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
if (!isset($this->cursor_ext[$query_hash])) {
|
||||
$this->cursor_ext[$query_hash] = [
|
||||
// cursor null: unset, if set \PDOStatement
|
||||
'cursor' => null,
|
||||
// the query used in this call
|
||||
'query' => $query,
|
||||
// parameter
|
||||
'params' => $params,
|
||||
// how many rows have been read from db
|
||||
'read_rows' => 0,
|
||||
// when fetch array or cache read returns false
|
||||
// in loop read that means dbReturn retuns false without error
|
||||
'finished' => false,
|
||||
];
|
||||
if (!empty($params)) {
|
||||
if (($cursor = $this->dbExecParams($query, $params)) === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (($cursor = $this->dbQuery($query)) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->cursor_ext[$query_hash]['cursor'] = $cursor;
|
||||
}
|
||||
// flag finished if row is false
|
||||
$row = $this->__dbFetchArray($this->cursor_ext[$query_hash]['cursor']);
|
||||
if ($row === false) {
|
||||
$this->cursor_ext[$query_hash]['finished'] = true;
|
||||
} else {
|
||||
$this->cursor_ext[$query_hash]['read_rows']++;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
// MARK: other interface
|
||||
|
||||
/**
|
||||
* get current db handler
|
||||
* this is for raw access
|
||||
*
|
||||
* @return \PDO
|
||||
*/
|
||||
public function getDbh(): \PDO
|
||||
{
|
||||
return $this->dbh;
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -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']
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -35,6 +35,7 @@ class Logging
|
||||
/** @var string log file block separator, not changeable */
|
||||
private const LOG_FILE_BLOCK_SEPARATOR = '.';
|
||||
|
||||
// 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 = [
|
||||
@@ -50,6 +51,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',
|
||||
@@ -57,6 +59,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,
|
||||
@@ -92,8 +102,10 @@ class Logging
|
||||
/** @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 */
|
||||
@@ -145,12 +157,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)
|
||||
@@ -172,6 +185,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
|
||||
@@ -190,8 +205,10 @@ class Logging
|
||||
// PRIVATE METHODS
|
||||
// *********************************************************************
|
||||
|
||||
// MARK: options check
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
* validate options
|
||||
*
|
||||
* @param array<mixed> $options
|
||||
* @return bool
|
||||
@@ -263,6 +280,8 @@ class Logging
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: init log elvels
|
||||
|
||||
/**
|
||||
* init log level, just a wrapper to auto set from options
|
||||
*
|
||||
@@ -280,6 +299,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
|
||||
@@ -321,6 +358,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
|
||||
@@ -337,6 +376,8 @@ class Logging
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: set log file id (file)
|
||||
|
||||
/**
|
||||
* set log file prefix id
|
||||
*
|
||||
@@ -395,6 +436,8 @@ class Logging
|
||||
return $status;
|
||||
}
|
||||
|
||||
// MARK init log flags and levels
|
||||
|
||||
/**
|
||||
* set flags from options and option flags connection internal settings
|
||||
*
|
||||
@@ -423,6 +466,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
|
||||
*
|
||||
@@ -490,6 +546,8 @@ class Logging
|
||||
return $fn;
|
||||
}
|
||||
|
||||
// MARK: master write log to file
|
||||
|
||||
/**
|
||||
* writes error msg data to file for current level
|
||||
*
|
||||
@@ -507,6 +565,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
|
||||
@@ -531,6 +593,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}
|
||||
@@ -610,6 +674,7 @@ class Logging
|
||||
// PUBLIC STATIC METHJODS
|
||||
// *********************************************************************
|
||||
|
||||
// MARK: set log level
|
||||
/**
|
||||
* set the log level
|
||||
*
|
||||
@@ -670,7 +735,7 @@ class Logging
|
||||
|
||||
// **** GET/SETTER
|
||||
|
||||
// log level set and get
|
||||
// MARK: log level
|
||||
|
||||
/**
|
||||
* set new log level
|
||||
@@ -705,7 +770,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
|
||||
@@ -733,7 +821,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)
|
||||
@@ -768,7 +856,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
|
||||
@@ -791,7 +879,7 @@ class Logging
|
||||
return $this->log_file_date;
|
||||
}
|
||||
|
||||
// general flag set
|
||||
// MARK: general flag set
|
||||
|
||||
/**
|
||||
* set one of the basic flags
|
||||
@@ -846,7 +934,7 @@ class Logging
|
||||
return $this->log_flags;
|
||||
}
|
||||
|
||||
// log folder/file
|
||||
// MARK: log folder/file
|
||||
|
||||
/**
|
||||
* set new log folder, check that folder is writeable
|
||||
@@ -890,7 +978,7 @@ class Logging
|
||||
return $this->log_file_name;
|
||||
}
|
||||
|
||||
// max log file size
|
||||
// MARK: max log file size
|
||||
|
||||
/**
|
||||
* set mag log file size
|
||||
@@ -921,7 +1009,7 @@ class Logging
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// OPTIONS CALLS
|
||||
// MARK: OPTIONS CALLS
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
@@ -939,6 +1027,8 @@ class Logging
|
||||
// MAIN CALLS
|
||||
// *********************************************************************
|
||||
|
||||
// MARK: main log call
|
||||
|
||||
/**
|
||||
* Commong log interface
|
||||
*
|
||||
@@ -976,7 +1066,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* DEBUG: 100
|
||||
* MARK: DEBUG: 100
|
||||
*
|
||||
* write debug data to error_msg array
|
||||
*
|
||||
@@ -1008,7 +1098,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* INFO: 200
|
||||
* MARK: INFO: 200
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1027,7 +1117,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTICE: 250
|
||||
* MARK: NOTICE: 250
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1046,7 +1136,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: 300
|
||||
* MARK: WARNING: 300
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1065,7 +1155,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* ERROR: 400
|
||||
* MARK: ERROR: 400
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1084,7 +1174,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* CTRITICAL: 500
|
||||
* MARK: CTRITICAL: 500
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1103,7 +1193,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* ALERT: 550
|
||||
* MARK: ALERT: 550
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1122,7 +1212,7 @@ class Logging
|
||||
}
|
||||
|
||||
/**
|
||||
* EMERGENCY: 600
|
||||
* MARK: EMERGENCY: 600
|
||||
*
|
||||
* @param string|Stringable $message
|
||||
* @param mixed[] $context
|
||||
@@ -1141,7 +1231,7 @@ class Logging
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// DEPRECATED SUPPORT CALLS
|
||||
// MARK: DEPRECATED SUPPORT CALLS
|
||||
// *********************************************************************
|
||||
|
||||
// legacy, but there are too many implemented
|
||||
@@ -1199,7 +1289,7 @@ class Logging
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// DEPRECATED METHODS
|
||||
// MARK: DEPRECATED METHODS
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
@@ -1365,7 +1455,7 @@ class Logging
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// DEBUG METHODS
|
||||
// MARK: DEBUG METHODS
|
||||
// *********************************************************************
|
||||
|
||||
/**
|
||||
@@ -1385,19 +1475,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
|
||||
|
||||
@@ -50,7 +50,8 @@ class EditUsers implements Interface\TableArraysInterface
|
||||
'HIDDEN_value' => $_POST['HIDDEN_password'] ?? '',
|
||||
'CONFIRM_value' => $_POST['CONFIRM_password'] ?? '',
|
||||
'output_name' => 'Password',
|
||||
'mandatory' => 1,
|
||||
// make it not mandatory to create dummy accounts that can only login via login url id
|
||||
'mandatory' => 0,
|
||||
'type' => 'password', // later has to be password for encryption in database
|
||||
'update' => [ // connected field updates, and update data
|
||||
'password_change_date' => [ // db row to update
|
||||
@@ -182,6 +183,7 @@ class EditUsers implements Interface\TableArraysInterface
|
||||
'type' => 'text',
|
||||
'error_check' => 'unique|custom',
|
||||
'error_regex' => "/^[A-Za-z0-9]+$/",
|
||||
'error_example' => "ABCdef123",
|
||||
'emptynull' => 1,'min_edit_acl' => '100',
|
||||
'min_show_acl' => '100',
|
||||
],
|
||||
|
||||
@@ -599,7 +599,7 @@ class Curl implements Interface\RequestsInterface
|
||||
// for post we set POST option
|
||||
if ($type == "post") {
|
||||
curl_setopt($handle, CURLOPT_POST, true);
|
||||
} elseif (!empty($type) && in_array($type, self::CUSTOM_REQUESTS)) {
|
||||
} elseif (in_array($type, self::CUSTOM_REQUESTS)) {
|
||||
curl_setopt($handle, CURLOPT_CUSTOMREQUEST, strtoupper($type));
|
||||
}
|
||||
// set body data if not null, will send empty [] for empty data
|
||||
|
||||
Reference in New Issue
Block a user