Compare commits
10 Commits
b033a718ad
...
v9.23.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c13934de99 | ||
|
|
ba11a936db | ||
|
|
5343034768 | ||
|
|
880f15ac6f | ||
|
|
a46601fe03 | ||
|
|
022c39e791 | ||
|
|
a7742bd5c8 | ||
|
|
78591d6ba4 | ||
|
|
98bf3a40cd | ||
|
|
cbd47fb015 |
@@ -13,7 +13,7 @@ if [ "${GO}" != "go" ]; then
|
||||
fi;
|
||||
|
||||
BASE="/storage/var/www/html/developers/clemens/core_data/";
|
||||
SOURCE="${BASE}php_libraries/trunk/"
|
||||
SOURCE="${BASE}php_libraries/master/"
|
||||
TARGET="${BASE}composer-packages/CoreLibs-Composer-All/"
|
||||
|
||||
rsync ${DRY_RUN}-Plzvrupt --stats --delete ${SOURCE}4dev/tests/ ${TARGET}test/phpunit/
|
||||
|
||||
@@ -10,10 +10,10 @@ CREATE TABLE edit_log (
|
||||
edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
euid INT, -- this is a foreign key, but I don't nedd to reference to it
|
||||
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
username VARCHAR,
|
||||
password VARCHAR,
|
||||
ecuid VARCHAR,
|
||||
ecuuid UUID,
|
||||
username VARCHAR,
|
||||
password VARCHAR,
|
||||
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
ip VARCHAR,
|
||||
error TEXT,
|
||||
|
||||
@@ -321,7 +321,7 @@ CREATE TABLE edit_generic (
|
||||
|
||||
-- DROP TABLE edit_visible_group;
|
||||
CREATE TABLE edit_visible_group (
|
||||
edit_visible_group_id SERIAL PRIMARY KEY,
|
||||
edit_visible_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
name VARCHAR,
|
||||
flag VARCHAR
|
||||
) INHERITS (edit_generic) WITHOUT OIDS;
|
||||
@@ -336,7 +336,7 @@ CREATE TABLE edit_visible_group (
|
||||
|
||||
-- DROP TABLE edit_menu_group;
|
||||
CREATE TABLE edit_menu_group (
|
||||
edit_menu_group_id SERIAL PRIMARY KEY,
|
||||
edit_menu_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
name VARCHAR,
|
||||
flag VARCHAR,
|
||||
order_number INT NOT NULL
|
||||
@@ -354,7 +354,7 @@ CREATE TABLE edit_menu_group (
|
||||
|
||||
-- DROP TABLE edit_page;
|
||||
CREATE TABLE edit_page (
|
||||
edit_page_id SERIAL PRIMARY KEY,
|
||||
edit_page_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages
|
||||
FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
filename VARCHAR,
|
||||
@@ -378,7 +378,7 @@ CREATE TABLE edit_page (
|
||||
|
||||
-- DROP TABLE edit_query_string;
|
||||
CREATE TABLE edit_query_string (
|
||||
edit_query_string_id SERIAL PRIMARY KEY,
|
||||
edit_query_string_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
edit_page_id INT NOT NULL,
|
||||
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
@@ -430,7 +430,7 @@ CREATE TABLE edit_page_menu_group (
|
||||
|
||||
-- DROP TABLE edit_access_right;
|
||||
CREATE TABLE edit_access_right (
|
||||
edit_access_right_id SERIAL PRIMARY KEY,
|
||||
edit_access_right_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
name VARCHAR,
|
||||
level SMALLINT,
|
||||
type VARCHAR,
|
||||
@@ -447,7 +447,7 @@ CREATE TABLE edit_access_right (
|
||||
|
||||
-- DROP TABLE edit_scheme;
|
||||
CREATE TABLE edit_scheme (
|
||||
edit_scheme_id SERIAL PRIMARY KEY,
|
||||
edit_scheme_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
name VARCHAR,
|
||||
header_color VARCHAR,
|
||||
@@ -466,7 +466,7 @@ CREATE TABLE edit_scheme (
|
||||
|
||||
-- DROP TABLE edit_language;
|
||||
CREATE TABLE edit_language (
|
||||
edit_language_id SERIAL PRIMARY KEY,
|
||||
edit_language_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
lang_default SMALLINT NOT NULL DEFAULT 0,
|
||||
long_name VARCHAR,
|
||||
@@ -485,7 +485,7 @@ CREATE TABLE edit_language (
|
||||
|
||||
-- DROP TABLE edit_group;
|
||||
CREATE TABLE edit_group (
|
||||
edit_group_id SERIAL PRIMARY KEY,
|
||||
edit_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
edit_scheme_id INT,
|
||||
FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
edit_access_right_id INT NOT NULL,
|
||||
@@ -507,7 +507,7 @@ CREATE TABLE edit_group (
|
||||
|
||||
-- DROP TABLE edit_page_access;
|
||||
CREATE TABLE edit_page_access (
|
||||
edit_page_access_id SERIAL PRIMARY KEY,
|
||||
edit_page_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
edit_group_id INT NOT NULL,
|
||||
FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
edit_page_id INT NOT NULL,
|
||||
@@ -530,7 +530,7 @@ CREATE TABLE edit_page_access (
|
||||
|
||||
-- DROP TABLE edit_page_content;
|
||||
CREATE TABLE edit_page_content (
|
||||
edit_page_content_id SERIAL PRIMARY KEY,
|
||||
edit_page_content_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
edit_page_id INT NOT NULL,
|
||||
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
edit_access_right_id INT NOT NULL,
|
||||
@@ -551,7 +551,7 @@ CREATE TABLE edit_page_content (
|
||||
|
||||
-- DROP TABLE edit_user;
|
||||
CREATE TABLE edit_user (
|
||||
edit_user_id SERIAL PRIMARY KEY,
|
||||
edit_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
connect_edit_user_id INT, -- possible reference to other user
|
||||
FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
edit_language_id INT NOT NULL,
|
||||
@@ -652,11 +652,11 @@ COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List st
|
||||
|
||||
-- DROP TABLE edit_log;
|
||||
CREATE TABLE edit_log (
|
||||
edit_log_id SERIAL PRIMARY KEY,
|
||||
edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
euid INT, -- this is a foreign key, but I don't nedd to reference to it
|
||||
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
ecuid VARCHAR,
|
||||
ecuuid UUID,
|
||||
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
username VARCHAR,
|
||||
password VARCHAR,
|
||||
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
@@ -712,7 +712,7 @@ ALTER TABLE edit_log_overflow ADD CONSTRAINT edit_log_overflow_euid_fkey FOREIGN
|
||||
|
||||
-- DROP TABLE edit_access;
|
||||
CREATE TABLE edit_access (
|
||||
edit_access_id SERIAL PRIMARY KEY,
|
||||
edit_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
protected SMALLINT DEFAULT 0,
|
||||
deleted SMALLINT DEFAULT 0,
|
||||
@@ -733,7 +733,7 @@ CREATE TABLE edit_access (
|
||||
|
||||
-- DROP TABLE edit_access_user;
|
||||
CREATE TABLE edit_access_user (
|
||||
edit_access_user_id SERIAL PRIMARY KEY,
|
||||
edit_access_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
edit_access_id INT NOT NULL,
|
||||
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
edit_user_id INT NOT NULL,
|
||||
@@ -754,7 +754,7 @@ CREATE TABLE edit_access_user (
|
||||
|
||||
-- DROP TABLE edit_access_data;
|
||||
CREATE TABLE edit_access_data (
|
||||
edit_access_data_id SERIAL PRIMARY KEY,
|
||||
edit_access_data_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
edit_access_id INT NOT NULL,
|
||||
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
|
||||
@@ -17,7 +17,7 @@ Table with Primary Key: table_with_primary_key
|
||||
Table without Primary Key: table_without_primary_key
|
||||
|
||||
Table with primary key has additional row:
|
||||
row_primary_key SERIAL PRIMARY KEY,
|
||||
row_primary_key INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
Each table has the following rows
|
||||
row_int INT,
|
||||
row_numeric NUMERIC,
|
||||
@@ -160,7 +160,6 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
// create the tables
|
||||
$db->dbExec(
|
||||
// primary key name is table + '_id'
|
||||
// table_with_primary_key_id SERIAL PRIMARY KEY,
|
||||
<<<SQL
|
||||
CREATE TABLE table_with_primary_key (
|
||||
table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
@@ -5136,6 +5135,67 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
SQL,
|
||||
'count' => 6,
|
||||
'convert' => false,
|
||||
],
|
||||
'comments in insert' => [
|
||||
'query' => <<<SQL
|
||||
INSERT INTO table_with_primary_key (
|
||||
row_int, row_numeric, row_varchar, row_varchar_literal
|
||||
) VALUES (
|
||||
-- comment 1 かな
|
||||
$1, $2,
|
||||
-- comment 2 -
|
||||
$3
|
||||
-- comment 3
|
||||
, $4
|
||||
)
|
||||
SQL,
|
||||
'count' => 4,
|
||||
'convert' => false
|
||||
],
|
||||
'comment in update' => [
|
||||
'query' => <<<SQL
|
||||
UPDATE table_with_primary_key SET
|
||||
row_int =
|
||||
-- COMMENT 1
|
||||
$1,
|
||||
row_numeric =
|
||||
$2 -- COMMENT 2
|
||||
,
|
||||
row_varchar -- COMMENT 3
|
||||
= $3
|
||||
WHERE
|
||||
row_varchar = $4
|
||||
SQL,
|
||||
'count' => 4,
|
||||
'convert' => false,
|
||||
],
|
||||
// Note some are not set
|
||||
'a complete set of possible' => [
|
||||
'query' => <<<SQL
|
||||
UPDATE table_with_primary_key SET
|
||||
-- ROW
|
||||
row_varchar = $1
|
||||
WHERE
|
||||
row_varchar = ANY($2) AND row_varchar <> $3
|
||||
AND row_varchar > $4 AND row_varchar < $5
|
||||
AND row_varchar >= $6 AND row_varchar <=$7
|
||||
AND row_jsonb->'a' = $8 AND row_jsonb->>$9 = 'a'
|
||||
AND row_jsonb<@$10 AND row_jsonb@>$11
|
||||
AND row_varchar ^@ $12
|
||||
SQL,
|
||||
'count' => 12,
|
||||
'convert' => false,
|
||||
],
|
||||
// all the same
|
||||
'all the same numbered' => [
|
||||
'query' => <<<SQL
|
||||
UPDATE table_with_primary_key SET
|
||||
row_int = $1::INT, row_numeric = $1::NUMERIC, row_varchar = $1
|
||||
WHERE
|
||||
row_varchar = $1
|
||||
SQL,
|
||||
'count' => 1,
|
||||
'convert' => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test class for DB\SqLite
|
||||
* This will only test the SqLite parts
|
||||
* @coversDefaultClass \CoreLibs\DB\SqLite
|
||||
* @testdox \CoreLibs\SqLite method tests for extended DB interface
|
||||
*/
|
||||
final class CoreLibsDBESqLiteTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
if (!extension_loaded('sqlite')) {
|
||||
$this->markTestSkipped(
|
||||
'The SqLite extension is not available.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @testdox DB\SqLite Class tests
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSqLite()
|
||||
{
|
||||
$this->markTestIncomplete(
|
||||
'DB\SqLite Tests have not yet been implemented'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
2
4dev/tests/Language/locale_other/.gitignore
vendored
Normal file
2
4dev/tests/Language/locale_other/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -56,7 +56,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$decrypted,
|
||||
'Class call',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt indirect $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessIndirect(string $input, string $expected): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
// test indirect
|
||||
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
|
||||
$decrypted = SymmetricEncryption::getInstance($key)->decrypt($encrypted);
|
||||
@@ -65,7 +82,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$decrypted,
|
||||
'Class Instance call',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt static $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessStatic(string $input, string $expected): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
// test static
|
||||
$encrypted = SymmetricEncryption::encryptKey($input, $key);
|
||||
$decrypted = SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
@@ -114,13 +148,51 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$crypt = new SymmetricEncryption($key);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKey($key);
|
||||
$crypt->setKey($wrong_key);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt indirect with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailedIndirect(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$wrong_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class instance
|
||||
$encrypted = SymmetricEncryption::getInstance($key)->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($wrong_key)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt static with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailedStatic(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$wrong_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class static
|
||||
$encrypted = SymmetricEncryption::encryptKey($input, $key);
|
||||
@@ -190,6 +262,56 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKey
|
||||
* @testdox wrong key indirect $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyIndirect(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->encrypt('test');
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = SymmetricEncryption::getInstance($enc_key)->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKey
|
||||
* @testdox wrong key static $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyStatic(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::encryptKey('test', $key);
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = SymmetricEncryption::encryptKey('test', $enc_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -232,6 +354,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($input, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext indirect $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertextIndirect(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::getInstance($key)->decrypt($input);
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($input, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext static $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertextStatic(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($input, $key);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -28,7 +28,6 @@ $log = new CoreLibs\Logging\Logging([
|
||||
'log_per_date' => true,
|
||||
]);
|
||||
|
||||
|
||||
$PAGE_NAME = 'TEST CLASS: DB CONVERT PLACEHOLDER';
|
||||
print "<!DOCTYPE html>";
|
||||
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
|
||||
|
||||
@@ -53,6 +53,9 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
|
||||
} else {
|
||||
print "NO DB HANDLER<br>";
|
||||
}
|
||||
// REGEX for placeholder count
|
||||
print "Placeholder regex: <pre>" . CoreLibs\DB\Support\ConvertPlaceholder::REGEX_LOOKUP_PLACEHOLDERS . "</pre>";
|
||||
|
||||
// turn on debug replace for placeholders
|
||||
$db->dbSetDebugReplacePlaceholder(true);
|
||||
|
||||
@@ -62,59 +65,115 @@ $db->dbExec("TRUNCATE test_foo");
|
||||
$uniqid = \CoreLibs\Create\Uids::uniqIdShort();
|
||||
$binary_data = $db->dbEscapeBytea(file_get_contents('class_test.db.php') ?: '');
|
||||
$query_params = [
|
||||
$uniqid,
|
||||
true,
|
||||
'STRING A',
|
||||
2,
|
||||
2.5,
|
||||
1,
|
||||
date('H:m:s'),
|
||||
date('Y-m-d H:i:s'),
|
||||
json_encode(['a' => 'string', 'b' => 1, 'c' => 1.5, 'f' => true, 'g' => ['a', 1, 1.5]]),
|
||||
null,
|
||||
'{"a", "b"}',
|
||||
'{1,2}',
|
||||
'{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}',
|
||||
'("Text", 4, 6.3)',
|
||||
$binary_data
|
||||
$uniqid, // test
|
||||
true, // some_bool
|
||||
'STRING A', // string_a
|
||||
2, // number_a
|
||||
2.5, // numeric_a
|
||||
1, // smallint
|
||||
date('H:m:s'), // some_internval
|
||||
date('Y-m-d H:i:s'), // some_timestamp
|
||||
json_encode(['a' => 'string', 'b' => 1, 'c' => 1.5, 'f' => true, 'g' => ['a', 1, 1.5]]), // json_string
|
||||
null, // null_var
|
||||
'{"a", "b"}', // array_char_1
|
||||
'{1,2}', // array_int_1
|
||||
'{"(array Text A, 5, 8.8)","(array Text B, 10, 15.2)"}', // array_composite
|
||||
'("Text", 4, 6.3)', // composite_item
|
||||
$binary_data, // some_binary
|
||||
date('Y-m-d'), // some_date
|
||||
date('H:i:s'), // some_time
|
||||
'{"c", "d", "e"}', // array_char_2
|
||||
'{3,4,5}', // array_int_2
|
||||
12345667778818, // bigint
|
||||
1.56, // numbrer_real
|
||||
3.75, // number_double
|
||||
124.5, // numeric_3
|
||||
\CoreLibs\Create\Uids::uuidv4() // uuid_var
|
||||
];
|
||||
|
||||
$query_insert = <<<SQL
|
||||
INSERT INTO test_foo (
|
||||
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
|
||||
some_time, some_timestamp, json_string, null_var,
|
||||
-- row 1
|
||||
test, some_bool, string_a, number_a, numeric_a, smallint_a,
|
||||
-- row 2
|
||||
some_internval, some_timestamp, json_string, null_var,
|
||||
-- row 3
|
||||
array_char_1, array_int_1,
|
||||
-- row 4
|
||||
array_composite,
|
||||
-- row 5
|
||||
composite_item,
|
||||
some_binary
|
||||
-- row 6
|
||||
some_binary,
|
||||
-- row 7
|
||||
some_date, some_time,
|
||||
-- row 8
|
||||
array_char_2, array_int_2,
|
||||
-- row 9
|
||||
bigint_a, number_real, number_double, numeric_3,
|
||||
-- row 10
|
||||
uuid_var
|
||||
) VALUES (
|
||||
-- row 1
|
||||
$1, $2, $3, $4, $5, $6,
|
||||
-- row 2
|
||||
$7, $8, $9, $10,
|
||||
-- row 3
|
||||
$11, $12,
|
||||
-- row 4
|
||||
$13,
|
||||
-- row 5
|
||||
$14,
|
||||
$15
|
||||
-- row 6
|
||||
$15,
|
||||
-- row 7
|
||||
$16, $17,
|
||||
-- row 8
|
||||
$18, $19,
|
||||
-- row 9
|
||||
$20, $21, $22, $23,
|
||||
-- row 10
|
||||
$24
|
||||
)
|
||||
RETURNING
|
||||
test_foo_id,
|
||||
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
|
||||
some_time, some_timestamp, json_string, null_var,
|
||||
test_foo_id, number_serial, identity_always, identitiy_default, default_uuid,
|
||||
test, some_bool, string_a, number_a, numeric_a, smallint_a,
|
||||
some_internval, some_timestamp, json_string, null_var,
|
||||
array_char_1, array_int_1,
|
||||
array_composite,
|
||||
composite_item,
|
||||
some_binary
|
||||
some_binary,
|
||||
some_date,
|
||||
array_char_2, array_int_2,
|
||||
bigint_a, number_real, number_double, numeric_3,
|
||||
uuid_var
|
||||
SQL;
|
||||
$status = $db->dbExecParams($query_insert, $query_params);
|
||||
echo "<b>*</b><br>";
|
||||
echo "INSERT ALL COLUMN TYPES: "
|
||||
. Support::printToString($query_params) . " |<br>"
|
||||
. "QUERY: " . $db->dbGetQuery() . " |<br>"
|
||||
. "QUERY: <pre>" . $db->dbGetQuery() . "</pre> |<br>"
|
||||
. "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " |<br>"
|
||||
. "RETURNING EXT: <pre>" . print_r($db->dbGetReturningExt(), true) . "</pre> |<br>"
|
||||
. "RETURNING RETURN: <pre>" . print_r($db->dbGetReturningArray(), true) . "<pre> |<br>"
|
||||
. "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
print "<b>ANY call</b><br>";
|
||||
$query = <<<SQL
|
||||
SELECT test
|
||||
FROM test_foo
|
||||
WHERE string_a = ANY($1)
|
||||
SQL;
|
||||
$query_value = '{'
|
||||
. join(',', ['STRING A'])
|
||||
. '}';
|
||||
while (is_array($res = $db->dbReturnParams($query, [$query_value]))) {
|
||||
print "Result: " . Support::prAr($res) . "<br>";
|
||||
}
|
||||
|
||||
echo "<hr>";
|
||||
|
||||
// test connectors: = , <> () for query detection
|
||||
|
||||
// convert placeholder tests
|
||||
@@ -131,6 +190,16 @@ SQL,
|
||||
'params' => [],
|
||||
'direction' => 'pg',
|
||||
],
|
||||
'numbers' => [
|
||||
'query' => <<<SQL
|
||||
SELECT test, string_a, number_a
|
||||
FROM test_foo
|
||||
WHERE
|
||||
foo = $1 AND bar = $1 AND foobar = $2
|
||||
SQL,
|
||||
'params' => [\CoreLibs\Create\Uids::uniqIdShort(), 'string A-1', 1234],
|
||||
'direction' => 'pdo',
|
||||
],
|
||||
'a?' => [
|
||||
'query' => <<<SQL
|
||||
INSERT INTO test_foo (
|
||||
@@ -157,6 +226,18 @@ SQL,
|
||||
],
|
||||
'direction' => 'pg',
|
||||
],
|
||||
'select, compare $' => [
|
||||
'query' => <<<SQL
|
||||
SELECT string_a
|
||||
FROM test_foo
|
||||
WHERE
|
||||
number_a >= $1 OR number_a <= $2 OR
|
||||
number_a > $3 OR number_a < $4
|
||||
OR number_a = $5 OR number_a <> $6
|
||||
SQL,
|
||||
'params' => [1, 2, 3, 4, 5, 6],
|
||||
'direction' => 'pg'
|
||||
]
|
||||
];
|
||||
|
||||
$db->dbSetConvertPlaceholder(true);
|
||||
@@ -169,11 +250,12 @@ foreach ($test_queries as $info => $data) {
|
||||
// . "<br>";
|
||||
if ($db->dbCheckQueryForSelect($query)) {
|
||||
$row = $db->dbReturnRowParams($query, $params);
|
||||
print "[$info] SELECT: " . Support::prAr($row) . "<br>";
|
||||
print "<b>[$info]</b> SELECT: " . Support::prAr($row) . "<br>";
|
||||
} else {
|
||||
$db->dbExecParams($query, $params);
|
||||
}
|
||||
print "[$info] " . Support::printAr($db->dbGetPlaceholderConverted()) . "<br>";
|
||||
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||
print "<b>[$info]</b> " . Support::printAr($db->dbGetPlaceholderConverted()) . "<br>";
|
||||
echo "<hr>";
|
||||
}
|
||||
|
||||
@@ -188,22 +270,29 @@ SQL,
|
||||
['string A-1']
|
||||
))
|
||||
) {
|
||||
print "RES: " . Support::prAr($res) . "<br>";
|
||||
print "<b>RES</b>: " . Support::prAr($res) . "<br>";
|
||||
}
|
||||
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
print "CursorExt: " . Support::prAr($db->dbGetCursorExt(<<<SQL
|
||||
SELECT test, string_a, number_a
|
||||
FROM test_foo
|
||||
WHERE string_a = ?
|
||||
SQL, ['string A-1']));
|
||||
echo "<hr>";
|
||||
|
||||
// ERROR BELOW: missing params
|
||||
$res = $db->dbReturnRowParams(<<<SQL
|
||||
SELECT test, string_a, number_a
|
||||
FROM test_foo
|
||||
WHERE string_a = $1
|
||||
SQL, []);
|
||||
print "PL: " . Support::PrAr($db->dbGetPlaceholderConverted()) . "<br>";
|
||||
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||
echo "<hr>";
|
||||
|
||||
// ERROR BELOW: LIKE cannot have placeholder
|
||||
echo "dbReturn read LIKE: <br>";
|
||||
while (
|
||||
is_array($res = $db->dbReturnParams(
|
||||
@@ -217,6 +306,7 @@ SQL,
|
||||
) {
|
||||
print "RES: " . Support::prAr($res) . "<br>";
|
||||
}
|
||||
print "ERROR: " . $db->dbGetLastError(true) . "<br>";
|
||||
|
||||
print "</body></html>";
|
||||
$db->log->debug('DEBUGEND', '==================================== [END]');
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php // phpcs:ignore warning
|
||||
|
||||
/**
|
||||
* @phan-file-suppress PhanTypeSuspiciousStringExpression
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// turn on all error reporting
|
||||
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
|
||||
|
||||
ob_start();
|
||||
|
||||
// basic class test file
|
||||
define('USE_DATABASE', true);
|
||||
define('DATABASE', 'sqlite' . DIRECTORY_SEPARATOR);
|
||||
// sample config
|
||||
require 'config.php';
|
||||
// define log file id
|
||||
$LOG_FILE_ID = 'classTest-db';
|
||||
ob_end_flush();
|
||||
|
||||
$sql_file = BASE . MEDIA . DATABASE . "class_test.db.sqlite.sq3";
|
||||
|
||||
use CoreLibs\DB\SqLite;
|
||||
use CoreLibs\Debug\Support;
|
||||
use CoreLibs\Convert\SetVarType;
|
||||
|
||||
$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\SqLite($log, "sqlite:" . $sql_file);
|
||||
$db->log->debug('START', '=============================>');
|
||||
|
||||
$PAGE_NAME = 'TEST CLASS: DB: SqLite';
|
||||
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 "<hr>";
|
||||
|
||||
echo "Create Tables on demand<br>";
|
||||
|
||||
$query = <<<SQL
|
||||
CREATE TABLE IF NOT EXISTS test (
|
||||
test_id INTEGER PRIMARY KEY,
|
||||
c_text TEXT,
|
||||
c_integer INTEGER,
|
||||
c_integer_default INTEGER DEFAULT -1,
|
||||
c_bool BOOLEAN,
|
||||
c_datetime TEXT,
|
||||
c_datetime_microseconds TEXT,
|
||||
c_datetime_default TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
c_date TEXT,
|
||||
c_julian REAL,
|
||||
c_unixtime DATETIME,
|
||||
c_unixtime_alt DATETIME,
|
||||
c_numeric NUMERIC,
|
||||
c_real REAL,
|
||||
c_blob
|
||||
)
|
||||
SQL;
|
||||
$db->dbExec($query);
|
||||
// **********************
|
||||
$query = <<<SQL
|
||||
CREATE TABLE IF NOT EXISTS test_no_pk (
|
||||
c_text TEXT,
|
||||
c_integer INTEGER
|
||||
)
|
||||
SQL;
|
||||
$db->dbExec($query);
|
||||
|
||||
print "<hr>";
|
||||
|
||||
$table = 'test';
|
||||
echo "Table info for: " . $table . "<br>";
|
||||
|
||||
if (($table_info = $db->dbShowTableMetaData($table)) === false) {
|
||||
print "Read problem for: $table<br>";
|
||||
} else {
|
||||
print "TABLE INFO: <pre>" . print_r($table_info, true) . "</pre><br>";
|
||||
}
|
||||
|
||||
print "<hr>";
|
||||
|
||||
echo "Insert into 'test'<br>";
|
||||
|
||||
$query = <<<SQL
|
||||
INSERT INTO test (
|
||||
c_text, c_integer, c_bool,
|
||||
c_datetime, c_datetime_microseconds, c_date,
|
||||
c_julian, c_unixtime, c_unixtime_alt,
|
||||
c_numeric, c_real, c_blob
|
||||
) VALUES (
|
||||
?, ?, ?,
|
||||
?, ?, ?,
|
||||
julianday(?), ?, unixepoch(?),
|
||||
?, ?, ?
|
||||
)
|
||||
SQL;
|
||||
$db->dbExecParams($query, [
|
||||
'test', rand(1, 100), true,
|
||||
date('Y-m-d H:i:s'), date_format(date_create("now"), 'Y-m-d H:i:s.u'), date('Y-m-d'),
|
||||
// julianday pass through
|
||||
date('Y-m-d H:i:s'),
|
||||
// use "U" if no unixepoch in query
|
||||
date('U'), date('Y-m-d H:i:s'),
|
||||
1.5, 10.5, 'Anything'
|
||||
]);
|
||||
|
||||
print "<hr>";
|
||||
|
||||
echo "Insert into 'test_no_pk'<br>";
|
||||
|
||||
$query = <<<SQL
|
||||
INSERT INTO test_no_pk (
|
||||
c_text, c_integer
|
||||
) VALUES (
|
||||
?, ?
|
||||
)
|
||||
SQL;
|
||||
$db->dbExecParams($query, ['test no pk', rand(100, 200)]);
|
||||
|
||||
print "<hr>";
|
||||
|
||||
$query = <<<SQL
|
||||
SELECT test_id, c_text, c_integer, c_integer_default, c_datetime_default
|
||||
FROM test
|
||||
SQL;
|
||||
while (is_array($row = $db->dbReturnArray($query))) {
|
||||
print "ROW: PK(test_id): " . $row["test_id"]
|
||||
. ", Text: " . $row["c_text"] . ", Int: " . $row["c_integer"]
|
||||
. ", Int Default: " . $row["c_integer_default"]
|
||||
. ", Date Default: " . $row["c_datetime_default"]
|
||||
. "<br>";
|
||||
}
|
||||
|
||||
echo "<hr>";
|
||||
|
||||
$query = <<<SQL
|
||||
SELECT rowid, c_text, c_integer
|
||||
FROM test_no_pk
|
||||
SQL;
|
||||
|
||||
while (is_array($row = $db->dbReturnArray($query))) {
|
||||
print "ROW[CURSOR]: PK(rowid): " . $row["rowid"]
|
||||
. ", Text: " . $row["c_text"] . ", Int: " . $row["c_integer"]
|
||||
. "<br>";
|
||||
}
|
||||
|
||||
print "</body></html>";
|
||||
|
||||
// __END__
|
||||
@@ -62,19 +62,39 @@ $backend = new CoreLibs\Admin\Backend(
|
||||
$backend->db->dbInfo(true);
|
||||
ob_end_flush();
|
||||
|
||||
print "<!DOCTYPE html>";
|
||||
print "<html><head><title>TEST CLASS</title></head>";
|
||||
print "<body>";
|
||||
print <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title>TEST CLASS</title>
|
||||
<script language="JavaScript">
|
||||
function loginLogout()
|
||||
{
|
||||
const form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
const hiddenField = document.createElement('input');
|
||||
hiddenField.type = 'hidden';
|
||||
hiddenField.name = 'login_logout';
|
||||
hiddenField.value = 'Logout';
|
||||
form.appendChild(hiddenField);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="margin: 20px 0;">
|
||||
<button onclick="loginLogout();" type="button">Logout</button>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
// key: file name, value; name
|
||||
$test_files = [
|
||||
'class_test.db.php' => 'Class Test: DB',
|
||||
'class_test.db.types.php' => 'Class Test: DB column type convert',
|
||||
'class_test.db.query-placeholder.php' => 'Class Test: DB query placeholder convert',
|
||||
'class_test.db.query-placeholder.php' => 'Class Test: DB placeholder queries',
|
||||
'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.sqlite.php' => 'Class Test: DB: SqLite',
|
||||
'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',
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: Ymd
|
||||
* DESCRIPTION:
|
||||
* DescriptionHere
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DB\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__
|
||||
@@ -1,432 +0,0 @@
|
||||
<?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 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->dbh instanceof \PDO) {
|
||||
$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
|
||||
*
|
||||
* @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,10 +14,24 @@ namespace CoreLibs\DB\Support;
|
||||
|
||||
class ConvertPlaceholder
|
||||
{
|
||||
/** @var string split regex */
|
||||
private const PATTERN_QUERY_SPLIT = '[(<>=,?-]|->|->>|#>|#>>|@>|<@|\?\|\?\&|\|\||#-';
|
||||
// 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
|
||||
/** @var string the main regex including the pattern query split */
|
||||
private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:\?\?|' . self::PATTERN_QUERY_SPLIT . ')\s*';
|
||||
private const PATTERN_ELEMENT = '(?:\'.*?\')?\s*(?:' . self::PATTERN_QUERY_SPLIT . ')\s*';
|
||||
/** @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
|
||||
@@ -34,6 +48,7 @@ class ConvertPlaceholder
|
||||
/** @var string replace regex for named (:...) entries */
|
||||
public const REGEX_REPLACE_NAMED = '/'
|
||||
. '(' . self::PATTERN_ELEMENT . ')'
|
||||
. self::PATTERN_COMMENT
|
||||
. '('
|
||||
. self::PATTERN_IGNORE
|
||||
. self::PATTERN_NAMED
|
||||
@@ -42,6 +57,7 @@ class ConvertPlaceholder
|
||||
/** @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_QUESTION_MARK
|
||||
@@ -50,6 +66,7 @@ class ConvertPlaceholder
|
||||
/** @var string replace regex for numbered ($n) entries */
|
||||
public const REGEX_REPLACE_NUMBERED = '/'
|
||||
. '(' . self::PATTERN_ELEMENT . ')'
|
||||
. self::PATTERN_COMMENT
|
||||
. '('
|
||||
. self::PATTERN_IGNORE
|
||||
. self::PATTERN_NUMBERED
|
||||
@@ -60,6 +77,7 @@ class ConvertPlaceholder
|
||||
// prefix string part, must match towards
|
||||
// seperator for ( = , ? - [and json/jsonb in pg doc section 9.15]
|
||||
. self::PATTERN_ELEMENT
|
||||
. self::PATTERN_COMMENT
|
||||
// match for replace part
|
||||
. '(?:'
|
||||
// ignore parts
|
||||
|
||||
@@ -49,7 +49,11 @@ class SymmetricEncryption
|
||||
*/
|
||||
public static function getInstance(string|null $key = null): self
|
||||
{
|
||||
if (empty(self::$instance)) {
|
||||
// new if no instsance or key is different
|
||||
if (
|
||||
empty(self::$instance) ||
|
||||
self::$instance->key != $key
|
||||
) {
|
||||
self::$instance = new self($key);
|
||||
}
|
||||
return self::$instance;
|
||||
@@ -130,7 +134,7 @@ class SymmetricEncryption
|
||||
*/
|
||||
private function encryptData(string $message, ?string $key): string
|
||||
{
|
||||
if (empty($this->key) || $key === null) {
|
||||
if ($key === null) {
|
||||
throw new \UnexpectedValueException('Key not set');
|
||||
}
|
||||
$key = $this->createKey($key);
|
||||
|
||||
Reference in New Issue
Block a user