Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82f35535ae | ||
|
|
c41796a478 | ||
|
|
a310fab3ee | ||
|
|
9914815285 | ||
|
|
969467fa15 | ||
|
|
f4dd78fff2 | ||
|
|
ba5e78e839 | ||
|
|
1a5ee2e16d | ||
|
|
e1d9985ec8 | ||
|
|
2316c151ac | ||
|
|
8ff8aa195b | ||
|
|
f176d12a1e | ||
|
|
f974b15f78 | ||
|
|
91fad09367 | ||
|
|
e8fe1feda5 | ||
|
|
23fd78e5c8 | ||
|
|
6cdede2997 | ||
|
|
ace02b14d8 | ||
|
|
58e916d314 | ||
|
|
4f6d85f4da | ||
|
|
cd45590a72 | ||
|
|
4d42da201c | ||
|
|
e310cb626a | ||
|
|
c04c71d755 | ||
|
|
9fc40a6629 | ||
|
|
6362e7f2f0 | ||
|
|
50dfc10d31 | ||
|
|
24077e483f | ||
|
|
6585c6bfef | ||
|
|
f180046283 | ||
|
|
b64d0ce5f0 | ||
|
|
bab8460f80 | ||
|
|
a092217201 | ||
|
|
e286d7f913 | ||
|
|
e148a39902 | ||
|
|
b7d5a79c3a | ||
|
|
9f8a86b4b0 | ||
|
|
50e593789e | ||
|
|
4ee141f8df | ||
|
|
9ee8f43478 | ||
|
|
2c75dbdf6c | ||
|
|
5fe61388fc | ||
|
|
a03c7e7319 | ||
|
|
7e01152bb4 | ||
|
|
fbea8f4aca | ||
|
|
346cdaad72 | ||
|
|
6887f17e15 | ||
|
|
5b1ca4241c | ||
|
|
c8d6263c0f | ||
|
|
bd1972d894 | ||
|
|
fa29477c80 | ||
|
|
20ee958db9 | ||
|
|
157616582f | ||
|
|
0f7bf0ab44 | ||
|
|
10dc56c7cb | ||
|
|
d19842007c | ||
|
|
c5bd16de74 | ||
|
|
e580e5430c | ||
|
|
da9717265c | ||
|
|
6c6b33cacc | ||
|
|
5509d1c92e | ||
|
|
16d789f061 | ||
|
|
3450b6263b | ||
|
|
bcc1e833c1 | ||
|
|
fea4c315f3 | ||
|
|
3ac57e05b0 | ||
|
|
b0230e7050 |
@@ -54,7 +54,8 @@ return [
|
||||
// Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist.
|
||||
// (See `backward_compatibility_checks` for additional options)
|
||||
// Automatically inferred from composer.json requirement for "php" of ">=8.2"
|
||||
'target_php_version' => '8.1',
|
||||
'target_php_version' => '8.2',
|
||||
"minimum_target_php_version" => "8.2",
|
||||
|
||||
// If enabled, missing properties will be created when
|
||||
// they are first seen. If false, we'll report an
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"description": "CoreLibs in a composer package",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": ["corelib", "logging", "database", "templating", "tools"],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"CoreLibs\\": "src/"
|
||||
@@ -24,7 +25,7 @@
|
||||
"phpstan/phpdoc-parser": "^2.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||
"phan/phan": "^5.4",
|
||||
"egrajp/smarty-extended": "^4.3",
|
||||
"egrajp/smarty-extended": "^5.4",
|
||||
"gullevek/dotenv": "dev-master",
|
||||
"phpunit/phpunit": "^9"
|
||||
},
|
||||
|
||||
@@ -22,6 +22,9 @@ parameters:
|
||||
# - vendor
|
||||
# ignore errores with
|
||||
ignoreErrors:
|
||||
-
|
||||
message: '#Expression in empty\(\) is not falsy.#'
|
||||
path: %currentWorkingDirectory%/src/Language/GetLocale.php
|
||||
#- # this error is ignore because of the PHP 8.0 to 8.1 change for pg_*, only for 8.0 or lower
|
||||
# message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects resource(\\|null)?, object\\|resource(\\|bool)? given\\.$#"
|
||||
# path: %currentWorkingDirectory%/www/lib/CoreLibs/DB/SQL/PgSQL.php
|
||||
|
||||
@@ -4,4 +4,9 @@
|
||||
verbose="true"
|
||||
bootstrap="test/phpunit/bootstrap.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="deploy">
|
||||
<directory>test/phpunit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
|
||||
@@ -1 +1 @@
|
||||
9.19.0
|
||||
9.28.1
|
||||
|
||||
2367
src/ACL/Login.php
2367
src/ACL/Login.php
File diff suppressed because it is too large
Load Diff
68
src/ACL/LoginUserStatus.php
Normal file
68
src/ACL/LoginUserStatus.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2024/12/12
|
||||
* DESCRIPTION:
|
||||
* ACL Login user status bitmap list
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\ACL;
|
||||
|
||||
final class LoginUserStatus
|
||||
{
|
||||
// lock status bitmap (smallint, 256)
|
||||
/** @var int enabled flag */
|
||||
public const ENABLED = 1;
|
||||
/** @var int deleted flag */
|
||||
public const DELETED = 2;
|
||||
/** @var int locked flag */
|
||||
public const LOCKED = 4;
|
||||
/** @var int banned/suspened flag [not implemented] */
|
||||
public const BANNED = 8;
|
||||
/** @var int password reset in progress [not implemented] */
|
||||
public const RESET = 16;
|
||||
/** @var int confirm/paending, eg waiting for confirm of email [not implemented] */
|
||||
public const CONFIRM = 32;
|
||||
/** @var int strict, on error lock */
|
||||
public const STRICT = 64;
|
||||
/** @var int proected, cannot delete */
|
||||
public const PROTECTED = 128;
|
||||
/** @var int master admin flag */
|
||||
public const ADMIN = 256;
|
||||
|
||||
/**
|
||||
* Returns an array mapping the numerical role values to their descriptive names
|
||||
*
|
||||
* @return array<int|string,string>
|
||||
*/
|
||||
public static function getMap()
|
||||
{
|
||||
return array_flip((new \ReflectionClass(static::class))->getConstants());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the descriptive role names
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getNames()
|
||||
{
|
||||
|
||||
return array_keys((new \ReflectionClass(static::class))->getConstants());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numerical role values
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getValues()
|
||||
{
|
||||
return array_values((new \ReflectionClass(static::class))->getConstants());
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -31,6 +31,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Admin;
|
||||
|
||||
use CoreLibs\Create\Uids;
|
||||
use CoreLibs\Convert\Json;
|
||||
|
||||
class Backend
|
||||
@@ -258,6 +259,27 @@ class Backend
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return all the action data, if not set, sets entry to null
|
||||
*
|
||||
* @return array{action:?string,action_id:null|string|int,action_sub_id:null|string|int,action_yes:null|string|int|bool,action_flag:?string,action_menu:?string,action_loaded:?string,action_value:?string,action_type:?string,action_error:?string}
|
||||
*/
|
||||
public function adbGetActionSet(): array
|
||||
{
|
||||
return [
|
||||
'action' => $this->action ?? null,
|
||||
'action_id' => $this->action_id ?? null,
|
||||
'action_sub_id' => $this->action_sub_id ?? null,
|
||||
'action_yes' => $this->action_yes ?? null,
|
||||
'action_flag' => $this->action_flag ?? null,
|
||||
'action_menu' => $this->action_menu ?? null,
|
||||
'action_loaded' => $this->action_loaded ?? null,
|
||||
'action_value' => $this->action_value ?? null,
|
||||
'action_type' => $this->action_type ?? null,
|
||||
'action_error' => $this->action_error ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* writes all action vars plus other info into edit_log table
|
||||
*
|
||||
@@ -267,6 +289,7 @@ class Backend
|
||||
* JSON, STRING/SERIEAL, BINARY/BZIP or ZLIB
|
||||
* @param string|null $db_schema [default=null] override target schema
|
||||
* @return void
|
||||
* @deprecated Use $login->writeLog($event, $data, action_set:$cms->adbGetActionSet(), write_type:$write_type)
|
||||
*/
|
||||
public function adbEditLog(
|
||||
string $event = '',
|
||||
@@ -335,17 +358,17 @@ class Backend
|
||||
}
|
||||
$q = <<<SQL
|
||||
INSERT INTO {DB_SCHEMA}.edit_log (
|
||||
euid, event_date, event, data, data_binary, page,
|
||||
username, euid, eucuid, eucuuid, event_date, event, error, data, data_binary, page,
|
||||
ip, user_agent, referer, script_name, query_string, server_name, http_host,
|
||||
http_accept, http_accept_charset, http_accept_encoding, session_id,
|
||||
action, action_id, action_yes, action_flag, action_menu, action_loaded,
|
||||
action, action_id, action_sub_id, action_yes, action_flag, action_menu, action_loaded,
|
||||
action_value, action_type, action_error
|
||||
) VALUES (
|
||||
$1, NOW(), $2, $3, $4, $5,
|
||||
$6, $7, $8, $9, $10, $11, $12,
|
||||
$13, $14, $15, $16,
|
||||
$17, $18, $19, $20, $21, $22,
|
||||
$23, $24, $25
|
||||
$1, $2, $3, $4, NOW(), $5, $6, $7, $8, $9,
|
||||
$10, $11, $12, $13, $14, $15, $16,
|
||||
$17, $18, $19, $20,
|
||||
$21, $22, $23, $24, $25, $26, $27,
|
||||
$28, $29, $30
|
||||
)
|
||||
SQL;
|
||||
$this->db->dbExecParams(
|
||||
@@ -356,9 +379,15 @@ class Backend
|
||||
),
|
||||
[
|
||||
// row 1
|
||||
isset($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
|
||||
$_SESSION['EUID'] : null,
|
||||
'',
|
||||
is_numeric($this->session->get('EUID')) ?
|
||||
$this->session->get('EUID') : null,
|
||||
is_string($this->session->get('ECUID')) ?
|
||||
$this->session->get('ECUID') : null,
|
||||
!empty($this->session->get('ECUUID')) && Uids::validateUuuidv4($this->session->get('ECUID')) ?
|
||||
$this->session->get('ECUID') : null,
|
||||
(string)$event,
|
||||
'',
|
||||
$data_write,
|
||||
$data_binary,
|
||||
(string)$this->page_name,
|
||||
@@ -374,11 +403,12 @@ class Backend
|
||||
$_SERVER['HTTP_ACCEPT'] ?? '',
|
||||
$_SERVER['HTTP_ACCEPT_CHARSET'] ?? '',
|
||||
$_SERVER['HTTP_ACCEPT_ENCODING'] ?? '',
|
||||
$this->session->getSessionId() !== false ?
|
||||
$this->session->getSessionId() !== '' ?
|
||||
$this->session->getSessionId() : null,
|
||||
// row 4
|
||||
$this->action ?? '',
|
||||
$this->action_id ?? '',
|
||||
$this->action_sub_id ?? '',
|
||||
$this->action_yes ?? '',
|
||||
$this->action_flag ?? '',
|
||||
$this->action_menu ?? '',
|
||||
@@ -438,7 +468,7 @@ class Backend
|
||||
}
|
||||
|
||||
// get the session pages array
|
||||
$PAGES = $_SESSION['PAGES'] ?? null;
|
||||
$PAGES = $this->session->get('PAGES');
|
||||
if (!isset($PAGES) || !is_array($PAGES)) {
|
||||
$PAGES = [];
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Admin;
|
||||
|
||||
use Exception;
|
||||
use SmartyException;
|
||||
|
||||
class EditBase
|
||||
{
|
||||
/** @var array<mixed> */
|
||||
@@ -63,6 +60,7 @@ class EditBase
|
||||
// smarty template engine (extended Translation version)
|
||||
$this->smarty = new \CoreLibs\Template\SmartyExtend(
|
||||
$l10n,
|
||||
$log,
|
||||
$options['cache_id'] ?? '',
|
||||
$options['compile_id'] ?? '',
|
||||
);
|
||||
@@ -78,7 +76,7 @@ class EditBase
|
||||
);
|
||||
if ($this->form->mobile_phone) {
|
||||
echo "I am sorry, but this page cannot be viewed by a mobile phone";
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
// $this->log->debug('POST', $this->log->prAr($_POST));
|
||||
}
|
||||
@@ -415,8 +413,6 @@ class EditBase
|
||||
$elements[] = $this->form->formCreateElement('lock_until');
|
||||
$elements[] = $this->form->formCreateElement('lock_after');
|
||||
$elements[] = $this->form->formCreateElement('admin');
|
||||
$elements[] = $this->form->formCreateElement('debug');
|
||||
$elements[] = $this->form->formCreateElement('db_debug');
|
||||
$elements[] = $this->form->formCreateElement('edit_language_id');
|
||||
$elements[] = $this->form->formCreateElement('edit_scheme_id');
|
||||
$elements[] = $this->form->formCreateElementListTable('edit_access_user');
|
||||
@@ -540,8 +536,7 @@ class EditBase
|
||||
* builds the smarty content and runs smarty display output
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @throws SmartyException
|
||||
* @throws \Smarty\Exception
|
||||
*/
|
||||
public function editBaseRun(
|
||||
?string $template_dir = null,
|
||||
|
||||
@@ -103,11 +103,7 @@ class Basic
|
||||
'VIDEOS', 'DOCUMENTS', 'PDFS', 'BINARIES', 'ICONS', 'UPLOADS', 'CSV', 'JS',
|
||||
'CSS', 'TABLE_ARRAYS', 'SMARTY', 'LANG', 'CACHE', 'TMP', 'LOG', 'TEMPLATES',
|
||||
'TEMPLATES_C', 'DEFAULT_LANG', 'DEFAULT_ENCODING', 'DEFAULT_HASH',
|
||||
'DEFAULT_ACL_LEVEL', 'LOGOUT_TARGET', 'PASSWORD_CHANGE', 'AJAX_REQUEST_TYPE',
|
||||
'USE_PROTOTYPE', 'USE_SCRIPTACULOUS', 'USE_JQUERY', 'PAGE_WIDTH',
|
||||
'MASTER_TEMPLATE_NAME', 'PUBLIC_SCHEMA', 'TEST_SCHEMA', 'DEV_SCHEMA',
|
||||
'LIVE_SCHEMA', 'DB_CONFIG_NAME', 'DB_CONFIG', 'TARGET', 'DEBUG',
|
||||
'SHOW_ALL_ERRORS'
|
||||
'DB_CONFIG_NAME', 'DB_CONFIG', 'TARGET'
|
||||
] as $constant
|
||||
) {
|
||||
if (!defined($constant)) {
|
||||
@@ -1028,8 +1024,12 @@ class Basic
|
||||
*/
|
||||
public function __sha1Short(string $string, bool $use_sha = false): string
|
||||
{
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::__sha1Short()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Create\Hash::__sha1Short($string, $use_sha);
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::sha1Short() or ::__crc32b()', E_USER_DEPRECATED);
|
||||
if ($use_sha) {
|
||||
return \CoreLibs\Create\Hash::sha1Short($string);
|
||||
} else {
|
||||
return \CoreLibs\Create\Hash::__crc32b($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1044,8 +1044,8 @@ class Basic
|
||||
*/
|
||||
public function __hash(string $string, string $hash_type = 'adler32'): string
|
||||
{
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::__hash()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Create\Hash::__hash($string, $hash_type);
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Create\Hash::hash()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Create\Hash::hash($string, $hash_type);
|
||||
}
|
||||
|
||||
// *** HASH FUNCTIONS END
|
||||
|
||||
@@ -525,6 +525,32 @@ class ArrayHandler
|
||||
{
|
||||
return array_diff($array, $remove);
|
||||
}
|
||||
|
||||
/**
|
||||
* From the array with key -> mixed values,
|
||||
* return only the entries where the key matches the key given in the key list parameter
|
||||
*
|
||||
* key list is a list[string]
|
||||
* if key list is empty, return array as is
|
||||
*
|
||||
* @param array<string,mixed> $array
|
||||
* @param array<string> $key_list
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function arrayReturnMatchingKeyOnly(
|
||||
array $array,
|
||||
array $key_list
|
||||
): array {
|
||||
// on empty return as is
|
||||
if (empty($key_list)) {
|
||||
return $array;
|
||||
}
|
||||
return array_filter(
|
||||
$array,
|
||||
fn($key) => in_array($key, $key_list),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -639,16 +639,26 @@ class DateTime
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $return_named return array type, false (default), true for named
|
||||
* @return array<mixed> 0/overall, 1/weekday, 2/weekend
|
||||
* @param bool $return_named [default=false] return array type, false (default), true for named
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{0:int,1:int,2:int,3:bool}|array{overall:int,weekday:int,weekend:int,reverse:bool}
|
||||
* 0/overall, 1/weekday, 2/weekend, 3/reverse
|
||||
*/
|
||||
public static function calcDaysInterval(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $return_named = false
|
||||
bool $return_named = false,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
// pos 0 all, pos 1 weekday, pos 2 weekend
|
||||
$days = [];
|
||||
$days = [
|
||||
0 => 0,
|
||||
1 => 0,
|
||||
2 => 0,
|
||||
3 => false,
|
||||
];
|
||||
// if anything invalid, return 0,0,0
|
||||
try {
|
||||
$start = new \DateTime($start_date);
|
||||
@@ -659,19 +669,30 @@ class DateTime
|
||||
'overall' => 0,
|
||||
'weekday' => 0,
|
||||
'weekend' => 0,
|
||||
'reverse' => false
|
||||
];
|
||||
} else {
|
||||
return [0, 0, 0];
|
||||
return $days;
|
||||
}
|
||||
}
|
||||
// so we include the last day too, we need to add +1 second in the time
|
||||
$end->setTime(0, 0, 1);
|
||||
// if end date before start date, only this will be filled
|
||||
$days[0] = $end->diff($start)->days;
|
||||
$days[1] = 0;
|
||||
$days[2] = 0;
|
||||
// if start is before end, switch dates and flag
|
||||
$days[3] = false;
|
||||
if ($start > $end) {
|
||||
$new_start = $end;
|
||||
$end = $start;
|
||||
$start = $new_start;
|
||||
$days[3] = true;
|
||||
}
|
||||
// get period for weekends/weekdays
|
||||
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end);
|
||||
$options = 0;
|
||||
if ($include_end_date) {
|
||||
$options |= \DatePeriod::INCLUDE_END_DATE;
|
||||
}
|
||||
if ($exclude_start_date) {
|
||||
$options |= \DatePeriod::EXCLUDE_START_DATE;
|
||||
}
|
||||
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end, $options);
|
||||
foreach ($period as $dt) {
|
||||
$curr = $dt->format('D');
|
||||
if ($curr == 'Sat' || $curr == 'Sun') {
|
||||
@@ -679,18 +700,80 @@ class DateTime
|
||||
} else {
|
||||
$days[1]++;
|
||||
}
|
||||
$days[0]++;
|
||||
}
|
||||
if ($return_named === true) {
|
||||
return [
|
||||
'overall' => $days[0],
|
||||
'weekday' => $days[1],
|
||||
'weekend' => $days[2],
|
||||
'reverse' => $days[3],
|
||||
];
|
||||
} else {
|
||||
return $days;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for calcDaysInterval with numeric return only
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{0:int,1:int,2:int,3:bool}
|
||||
*/
|
||||
public static function calcDaysIntervalNumIndex(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
$values = self::calcDaysInterval(
|
||||
$start_date,
|
||||
$end_date,
|
||||
false,
|
||||
$include_end_date,
|
||||
$exclude_start_date
|
||||
);
|
||||
return [
|
||||
$values[0] ?? 0,
|
||||
$values[1] ?? 0,
|
||||
$values[2] ?? 0,
|
||||
$values[3] ?? false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for calcDaysInterval with named return only
|
||||
*
|
||||
* @param string $start_date valid start date (y/m/d)
|
||||
* @param string $end_date valid end date (y/m/d)
|
||||
* @param bool $include_end_date [default=true] include end date in calc
|
||||
* @param bool $exclude_start_date [default=false] include end date in calc
|
||||
* @return array{overall:int,weekday:int,weekend:int,reverse:bool}
|
||||
*/
|
||||
public static function calcDaysIntervalNamedIndex(
|
||||
string $start_date,
|
||||
string $end_date,
|
||||
bool $include_end_date = true,
|
||||
bool $exclude_start_date = false
|
||||
): array {
|
||||
$values = self::calcDaysInterval(
|
||||
$start_date,
|
||||
$end_date,
|
||||
true,
|
||||
$include_end_date,
|
||||
$exclude_start_date
|
||||
);
|
||||
return [
|
||||
'overall' => $values['overall'] ?? 0,
|
||||
'weekday' => $values['weekday'] ?? 0,
|
||||
'weekend' => $values['weekend'] ?? 0,
|
||||
'reverse' => $values['reverse'] ?? false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a weekend day (sat/sun) is in the given date range
|
||||
* Can have time too, but is not needed
|
||||
@@ -705,6 +788,13 @@ class DateTime
|
||||
): bool {
|
||||
$dd_start = new \DateTime($start_date);
|
||||
$dd_end = new \DateTime($end_date);
|
||||
// flip if start is after end
|
||||
if ($dd_start > $dd_end) {
|
||||
$new_start = $dd_end;
|
||||
$dd_end = $dd_start;
|
||||
$dd_start = $new_start;
|
||||
}
|
||||
// if start > end, flip
|
||||
if (
|
||||
// starts with a weekend
|
||||
$dd_start->format('N') >= 6 ||
|
||||
|
||||
@@ -26,7 +26,7 @@ class HSB implements Interface\CoordinatesInterface
|
||||
private float $B = 0.0;
|
||||
|
||||
/** @var string color space: either ok or cie */
|
||||
private string $colorspace = '';
|
||||
private string $colorspace = ''; /** @phpstan-ignore-line */
|
||||
|
||||
/**
|
||||
* HSB (HSV) color coordinates
|
||||
|
||||
@@ -25,7 +25,7 @@ class HSL implements Interface\CoordinatesInterface
|
||||
/** @var float lightness (luminance) */
|
||||
private float $L = 0.0;
|
||||
|
||||
/** @var string color space: either ok or cie */
|
||||
/** @var string color space: either sRGB */
|
||||
private string $colorspace = '';
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,9 +10,16 @@ namespace CoreLibs\Convert;
|
||||
|
||||
class Html
|
||||
{
|
||||
/** @var int */
|
||||
public const SELECTED = 0;
|
||||
/** @var int */
|
||||
public const CHECKED = 1;
|
||||
|
||||
// TODO: check for not valid htmlentites encoding
|
||||
// as of PHP 8.4: https://www.php.net/manual/en/function.htmlentities.php
|
||||
/** @#var array<string> */
|
||||
// public const VALID_HTMLENT_ENCODINGS = [];
|
||||
|
||||
/**
|
||||
* full wrapper for html entities
|
||||
*
|
||||
@@ -22,14 +29,19 @@ class Html
|
||||
* encodes in UTF-8
|
||||
* does not double encode
|
||||
*
|
||||
* @param mixed $string string to html encode
|
||||
* @param int $flags [default: ENT_QUOTES | ENT_HTML5]
|
||||
* @param mixed $string string to html encode
|
||||
* @param int $flags [default=ENT_QUOTES | ENT_HTML5]
|
||||
* @param string $encoding [default=UTF-8]
|
||||
* @return mixed if string, encoded, else as is (eg null)
|
||||
*/
|
||||
public static function htmlent(mixed $string, int $flags = ENT_QUOTES | ENT_HTML5): mixed
|
||||
{
|
||||
public static function htmlent(
|
||||
mixed $string,
|
||||
int $flags = ENT_QUOTES | ENT_HTML5,
|
||||
string $encoding = 'UTF-8'
|
||||
): mixed {
|
||||
if (is_string($string)) {
|
||||
return htmlentities($string, $flags, 'UTF-8', false);
|
||||
// if not a valid encoding this will throw a warning and use UTF-8
|
||||
return htmlentities($string, $flags, $encoding, false);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
@@ -37,7 +49,7 @@ class Html
|
||||
/**
|
||||
* strips out all line breaks or replaced with given string
|
||||
* @param string $string string
|
||||
* @param string $replace replace character, default ' '
|
||||
* @param string $replace [default=' '] replace character
|
||||
* @return string cleaned string without any line breaks
|
||||
*/
|
||||
public static function removeLB(string $string, string $replace = ' '): string
|
||||
|
||||
@@ -10,9 +10,14 @@ namespace CoreLibs\Create;
|
||||
|
||||
class Hash
|
||||
{
|
||||
/** @var string default short hash -> deprecated use STANDARD_HASH_SHORT */
|
||||
public const DEFAULT_HASH = 'adler32';
|
||||
/** @var string default long hash (40 chars) */
|
||||
public const STANDARD_HASH_LONG = 'ripemd160';
|
||||
/** @var string default short hash (8 chars) */
|
||||
public const STANDARD_HASH_SHORT = 'adler32';
|
||||
/** @var string this is the standard hash to use hashStd and hash (64 chars) */
|
||||
public const STANDARD_HASH = 'sha256';
|
||||
|
||||
/**
|
||||
* checks php version and if >=5.2.7 it will flip the string
|
||||
@@ -20,6 +25,7 @@ class Hash
|
||||
* hash returns false
|
||||
* preg_replace fails for older php version
|
||||
* Use __hash with crc32b or hash('crc32b', ...) for correct output
|
||||
* For future short hashes use hashShort() instead
|
||||
*
|
||||
* @param string $string string to crc
|
||||
* @return string crc32b hash (old type)
|
||||
@@ -43,19 +49,31 @@ class Hash
|
||||
* replacement for __crc32b call
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @param bool $use_sha use sha instead of crc32b (default false)
|
||||
* @param bool $use_sha use sha1 instead of crc32b (default false)
|
||||
* @return string hash of the string
|
||||
* @deprecated use __crc32b() for drop in replacement with default, or sha1Short() for use sha true
|
||||
*/
|
||||
public static function __sha1Short(string $string, bool $use_sha = false): string
|
||||
{
|
||||
if ($use_sha) {
|
||||
// return only the first 9 characters
|
||||
return substr(hash('sha1', $string), 0, 9);
|
||||
return self::sha1Short($string);
|
||||
} else {
|
||||
return self::__crc32b($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a short sha1
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function sha1Short(string $string): string
|
||||
{
|
||||
// return only the first 9 characters
|
||||
return substr(hash('sha1', $string), 0, 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* replacemend for __crc32b call (alternate)
|
||||
* defaults to adler 32
|
||||
@@ -65,32 +83,81 @@ class Hash
|
||||
* @param string $string string to hash
|
||||
* @param string $hash_type hash type (default adler32)
|
||||
* @return string hash of the string
|
||||
* @deprecated use hashShort() of short hashes with adler 32 or hash() for other hash types
|
||||
*/
|
||||
public static function __hash(
|
||||
string $string,
|
||||
string $hash_type = self::DEFAULT_HASH
|
||||
string $hash_type = self::STANDARD_HASH_SHORT
|
||||
): string {
|
||||
return self::hash($string, $hash_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function hash(
|
||||
string $string,
|
||||
string $hash_type = self::STANDARD_HASH
|
||||
): string {
|
||||
// if not empty, check if in valid list
|
||||
if (
|
||||
empty($hash_type) ||
|
||||
!in_array($hash_type, hash_algos())
|
||||
) {
|
||||
// fallback to default hash type if none set or invalid
|
||||
$hash_type = self::DEFAULT_HASH;
|
||||
$hash_type = self::STANDARD_HASH;
|
||||
}
|
||||
return hash($hash_type, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for standard long hashd
|
||||
* short hash with max length of 8, uses adler32
|
||||
*
|
||||
* @param string $string string to hash
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function hashShort(string $string): string
|
||||
{
|
||||
return hash(self::STANDARD_HASH_SHORT, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for standard long hash
|
||||
*
|
||||
* @param string $string String to be hashed
|
||||
* @return string Hashed string
|
||||
* @deprecated use hashLong()
|
||||
*/
|
||||
public static function __hashLong(string $string): string
|
||||
{
|
||||
return self::hashLong($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for standard long hash, uses ripmd160
|
||||
*
|
||||
* @param string $string String to be hashed
|
||||
* @return string Hashed string
|
||||
*/
|
||||
public static function __hashLong(string $string): string
|
||||
public static function hashLong(string $string): string
|
||||
{
|
||||
return hash(self::STANDARD_HASH_LONG, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* create standard hash basd on STANDAR_HASH, currently sha256
|
||||
*
|
||||
* @param string $string string in
|
||||
* @return string hash of the string
|
||||
*/
|
||||
public static function hashStd(string $string): string
|
||||
{
|
||||
return self::hash($string, self::STANDARD_HASH);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -15,17 +15,111 @@ namespace CoreLibs\Create;
|
||||
|
||||
class Session
|
||||
{
|
||||
/** @var string current session name */
|
||||
private string $session_name = '';
|
||||
/** @var string current session id */
|
||||
private string $session_id = '';
|
||||
/** @var bool flag auto write close */
|
||||
private bool $auto_write_close = false;
|
||||
/** @var string regenerate option, default never */
|
||||
private string $regenerate = 'never';
|
||||
/** @var int regenerate interval either 1 to 100 for random or 0 to 3600 for interval */
|
||||
private int $regenerate_interval = 0;
|
||||
|
||||
/** @var array<string> allowed session id regenerate (rotate) options */
|
||||
private const ALLOWED_REGENERATE_OPTIONS = ['none', 'random', 'interval'];
|
||||
/** @var int default random interval */
|
||||
public const DEFAULT_REGENERATE_RANDOM = 100;
|
||||
/** @var int default rotate internval in minutes */
|
||||
public const DEFAULT_REGENERATE_INTERVAL = 5 * 60;
|
||||
/** @var int maximum time for regenerate interval is one hour */
|
||||
public const MAX_REGENERATE_INTERAL = 60 * 60;
|
||||
|
||||
/**
|
||||
* init a session, if array is empty or array does not have session_name set
|
||||
* then no auto init is run
|
||||
*
|
||||
* @param string $session_name if set and not empty, will start session
|
||||
* @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options
|
||||
*/
|
||||
public function __construct(string $session_name = '')
|
||||
public function __construct(
|
||||
string $session_name,
|
||||
array $options = []
|
||||
) {
|
||||
$this->setOptions($options);
|
||||
$this->initSession($session_name);
|
||||
}
|
||||
|
||||
// MARK: private methods
|
||||
|
||||
/**
|
||||
* set session class options
|
||||
*
|
||||
* @param array{auto_write_close?:bool,session_strict?:bool,regenerate?:string,regenerate_interval?:int} $options
|
||||
* @return void
|
||||
*/
|
||||
private function setOptions(array $options): void
|
||||
{
|
||||
if (!empty($session_name)) {
|
||||
$this->startSession($session_name);
|
||||
if (
|
||||
!isset($options['auto_write_close']) ||
|
||||
!is_bool($options['auto_write_close'])
|
||||
) {
|
||||
$options['auto_write_close'] = false;
|
||||
}
|
||||
$this->auto_write_close = $options['auto_write_close'];
|
||||
if (
|
||||
!isset($options['session_strict']) ||
|
||||
!is_bool($options['session_strict'])
|
||||
) {
|
||||
$options['session_strict'] = true;
|
||||
}
|
||||
// set strict options, on not started sessiononly
|
||||
if (
|
||||
$options['session_strict'] &&
|
||||
$this->getSessionStatus() === PHP_SESSION_NONE
|
||||
) {
|
||||
// use cookies to store session IDs
|
||||
ini_set('session.use_cookies', 1);
|
||||
// use cookies only (do not send session IDs in URLs)
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
// do not send session IDs in URLs
|
||||
ini_set('session.use_trans_sid', 0);
|
||||
}
|
||||
// session regenerate id options
|
||||
if (
|
||||
empty($options['regenerate']) ||
|
||||
!in_array($options['regenerate'], self::ALLOWED_REGENERATE_OPTIONS)
|
||||
) {
|
||||
$options['regenerate'] = 'never';
|
||||
}
|
||||
$this->regenerate = (string)$options['regenerate'];
|
||||
// for regenerate: 'random' (default 100)
|
||||
// regenerate_interval must be between (1 = always) and 100 (1 in 100)
|
||||
// for regenerate: 'interval' (default 5min)
|
||||
// regenerate_interval must be 0 = always, to 3600 (every hour)
|
||||
if (
|
||||
$options['regenerate'] == 'random' &&
|
||||
(
|
||||
!isset($options['regenerate_interval']) ||
|
||||
!is_numeric($options['regenerate_interval']) ||
|
||||
$options['regenerate_interval'] < 0 ||
|
||||
$options['regenerate_interval'] > 100
|
||||
)
|
||||
) {
|
||||
$options['regenerate_interval'] = self::DEFAULT_REGENERATE_RANDOM;
|
||||
}
|
||||
if (
|
||||
$options['regenerate'] == 'interval' &&
|
||||
(
|
||||
!isset($options['regenerate_interval']) ||
|
||||
!is_numeric($options['regenerate_interval']) ||
|
||||
$options['regenerate_interval'] < 1 ||
|
||||
$options['regenerate_interval'] > self::MAX_REGENERATE_INTERAL
|
||||
)
|
||||
) {
|
||||
$options['regenerate_interval'] = self::DEFAULT_REGENERATE_INTERVAL;
|
||||
}
|
||||
$this->regenerate_interval = (int)($options['regenerate_interval'] ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,38 +130,100 @@ class Session
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function startSessionCall(): void
|
||||
private function startSessionCall(): void
|
||||
{
|
||||
session_start();
|
||||
}
|
||||
|
||||
/**
|
||||
* check if we are in CLI, we set this, so we can mock this
|
||||
* Not this is just a wrapper for the static System::checkCLI call
|
||||
* get current set session id or false if none started
|
||||
*
|
||||
* @return bool True if we are in a CLI enviroment, or false for everything else
|
||||
* @return string|false
|
||||
*/
|
||||
public function checkCliStatus(): bool
|
||||
public function getSessionIdCall(): string|false
|
||||
{
|
||||
return \CoreLibs\Get\System::checkCLI();
|
||||
return session_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session name call. If not valid session name, will return false
|
||||
* automatically closes a session if the auto write close flag is set
|
||||
*
|
||||
* @param string $session_name A valid string for session name
|
||||
* @return bool True if session name is valid,
|
||||
* False if not
|
||||
* @return bool
|
||||
*/
|
||||
public function setSessionName(string $session_name): bool
|
||||
private function closeSessionCall(): bool
|
||||
{
|
||||
if (!$this->checkValidSessionName($session_name)) {
|
||||
return false;
|
||||
if ($this->auto_write_close) {
|
||||
return $this->writeClose();
|
||||
}
|
||||
session_name($session_name);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// MARK: regenerate session
|
||||
|
||||
/**
|
||||
* auto rotate session id
|
||||
*
|
||||
* @return void
|
||||
* @throws \RuntimeException failure to regenerate session id
|
||||
* @throws \UnexpectedValueException failed to get new session id
|
||||
* @throws \RuntimeException failed to set new sesson id
|
||||
* @throws \UnexpectedValueException new session id generated does not match the new set one
|
||||
*/
|
||||
private function sessionRegenerateSessionId()
|
||||
{
|
||||
// never
|
||||
if ($this->regenerate == 'never') {
|
||||
return;
|
||||
}
|
||||
// regenerate
|
||||
if (
|
||||
!(
|
||||
// is not session obsolete
|
||||
empty($_SESSION['SESSION_REGENERATE_OBSOLETE']) &&
|
||||
(
|
||||
(
|
||||
// random
|
||||
$this->regenerate == 'random' &&
|
||||
mt_rand(1, $this->regenerate_interval) == 1
|
||||
) || (
|
||||
// interval type
|
||||
$this->regenerate == 'interval' &&
|
||||
($_SESSION['SESSION_REGENERATE_TIMESTAMP'] ?? 0) + $this->regenerate_interval < time()
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Set current session to expire in 1 minute
|
||||
$_SESSION['SESSION_REGENERATE_OBSOLETE'] = true;
|
||||
$_SESSION['SESSION_REGENERATE_EXPIRES'] = time() + 60;
|
||||
$_SESSION['SESSION_REGENERATE_TIMESTAMP'] = time();
|
||||
// Create new session without destroying the old one
|
||||
if (session_regenerate_id(false) === false) {
|
||||
throw new \RuntimeException('[SESSION] Session id regeneration failed', 1);
|
||||
}
|
||||
// Grab current session ID and close both sessions to allow other scripts to use them
|
||||
if (false === ($new_session_id = $this->getSessionIdCall())) {
|
||||
throw new \UnexpectedValueException('[SESSION] getSessionIdCall did not return a session id', 2);
|
||||
}
|
||||
$this->writeClose();
|
||||
// Set session ID to the new one, and start it back up again
|
||||
if (($get_new_session_id = session_id($new_session_id)) === false) {
|
||||
throw new \RuntimeException('[SESSION] set session_id failed', 3);
|
||||
}
|
||||
if ($get_new_session_id != $new_session_id) {
|
||||
throw new \UnexpectedValueException('[SESSION] new session id does not match the new set one', 4);
|
||||
}
|
||||
$this->session_id = $new_session_id;
|
||||
$this->startSessionCall();
|
||||
// Don't want this one to expire
|
||||
unset($_SESSION['SESSION_REGENERATE_OBSOLETE']);
|
||||
unset($_SESSION['SESSION_REGENERATE_EXPIRES']);
|
||||
}
|
||||
|
||||
// MARK: session validation
|
||||
|
||||
/**
|
||||
* check if session name is valid
|
||||
*
|
||||
@@ -94,15 +250,34 @@ class Session
|
||||
}
|
||||
|
||||
/**
|
||||
* start session with given session name if set
|
||||
* validate _SESSION key, must be valid variable
|
||||
*
|
||||
* @param int|float|string $key
|
||||
* @return true
|
||||
*/
|
||||
private function checkValidSessionEntryKey(int|float|string $key): true
|
||||
{
|
||||
if (!is_string($key) || is_numeric($key)) {
|
||||
throw new \UnexpectedValueException(
|
||||
'[SESSION] Given key for _SESSION is not a valid value for a varaible: ' . $key,
|
||||
1
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: init session (on class start)
|
||||
|
||||
/**
|
||||
* stinitart session with given session name if set
|
||||
* aborts on command line or if sessions are not enabled
|
||||
* also aborts if session cannot be started
|
||||
* On sucess returns the session id
|
||||
*
|
||||
* @param string|null $session_name
|
||||
* @return string|bool
|
||||
* @param string $session_name
|
||||
* @return void
|
||||
*/
|
||||
public function startSession(?string $session_name = null): string|bool
|
||||
private function initSession(string $session_name): void
|
||||
{
|
||||
// we can't start sessions on command line
|
||||
if ($this->checkCliStatus()) {
|
||||
@@ -115,39 +290,95 @@ class Session
|
||||
// session_status
|
||||
// initial the session if there is no session running already
|
||||
if (!$this->checkActiveSession()) {
|
||||
// if session name is emtpy, check if there is a global set
|
||||
// this is a deprecated fallback
|
||||
$session_name = $session_name ?? $GLOBALS['SET_SESSION_NAME'] ?? '';
|
||||
// DEPRECTED: constant SET_SESSION_NAME is no longer used
|
||||
// if set, set special session name
|
||||
if (!empty($session_name)) {
|
||||
// invalid session name, abort
|
||||
if (!$this->checkValidSessionName($session_name)) {
|
||||
throw new \UnexpectedValueException('[SESSION] Invalid session name: ' . $session_name, 3);
|
||||
}
|
||||
$this->setSessionName($session_name);
|
||||
// invalid session name, abort
|
||||
if (!$this->checkValidSessionName($session_name)) {
|
||||
throw new \UnexpectedValueException('[SESSION] Invalid session name: ' . $this->session_name, 3);
|
||||
}
|
||||
// set session name
|
||||
$this->session_name = $session_name;
|
||||
session_name($this->session_name);
|
||||
// start session
|
||||
$this->startSessionCall();
|
||||
// if we faild to start the session
|
||||
if (!$this->checkActiveSession()) {
|
||||
throw new \RuntimeException('[SESSION] Failed to activate session', 5);
|
||||
}
|
||||
if (
|
||||
!empty($_SESSION['SESSION_REGENERATE_OBSOLETE']) &&
|
||||
!empty($_SESSION['SESSION_REGENERATE_EXPIRES']) && $_SESSION['SESSION_REGENERATE_EXPIRES'] < time()
|
||||
) {
|
||||
$this->sessionDestroy();
|
||||
throw new \RuntimeException('[SESSION] Expired session found', 6);
|
||||
}
|
||||
} elseif ($session_name != $this->getSessionName()) {
|
||||
throw new \UnexpectedValueException(
|
||||
'[SESSION] Another session exists with a different name: ' . $this->getSessionName(),
|
||||
4
|
||||
);
|
||||
}
|
||||
// if we still have no active session
|
||||
// check session id
|
||||
if (false === ($session_id = $this->getSessionIdCall())) {
|
||||
throw new \UnexpectedValueException('[SESSION] getSessionIdCall did not return a session id', 7);
|
||||
}
|
||||
// set session id
|
||||
$this->session_id = $session_id;
|
||||
// run session id re-create from time to time
|
||||
$this->sessionRegenerateSessionId();
|
||||
// if flagged auto close, write close session
|
||||
if ($this->auto_write_close) {
|
||||
$this->writeClose();
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: public set/get status
|
||||
|
||||
/**
|
||||
* start session, will only run after initSession
|
||||
*
|
||||
* @return bool True if started, False if alrady running
|
||||
*/
|
||||
public function restartSession(): bool
|
||||
{
|
||||
if (!$this->checkActiveSession()) {
|
||||
throw new \RuntimeException('[SESSION] Failed to activate session', 4);
|
||||
if (empty($this->session_name)) {
|
||||
throw new \RuntimeException('[SESSION] Cannot restart session without a session name', 1);
|
||||
}
|
||||
$this->startSessionCall();
|
||||
return true;
|
||||
}
|
||||
if (false === ($session_id = $this->getSessionId())) {
|
||||
throw new \UnexpectedValueException('[SESSION] getSessionId did not return a session id', 5);
|
||||
}
|
||||
return $session_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current set session id or false if none started
|
||||
* current set session id
|
||||
*
|
||||
* @return string|bool
|
||||
* @return string
|
||||
*/
|
||||
public function getSessionId(): string|bool
|
||||
public function getSessionId(): string
|
||||
{
|
||||
return session_id();
|
||||
return $this->session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the auto write close flag
|
||||
*
|
||||
* @param bool $flag
|
||||
* @return Session
|
||||
*/
|
||||
public function setAutoWriteClose(bool $flag): Session
|
||||
{
|
||||
$this->auto_write_close = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the auto write close flag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkAutoWriteClose(): bool
|
||||
{
|
||||
return $this->auto_write_close;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,6 +406,34 @@ class Session
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if we are in CLI, we set this, so we can mock this
|
||||
* Not this is just a wrapper for the static System::checkCLI call
|
||||
*
|
||||
* @return bool True if we are in a CLI enviroment, or false for everything else
|
||||
*/
|
||||
public function checkCliStatus(): bool
|
||||
{
|
||||
return \CoreLibs\Get\System::checkCLI();
|
||||
}
|
||||
|
||||
/**
|
||||
* get session status
|
||||
* PHP_SESSION_DISABLED if sessions are disabled.
|
||||
* PHP_SESSION_NONE if sessions are enabled, but none exists.
|
||||
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
|
||||
*
|
||||
* https://www.php.net/manual/en/function.session-status.php
|
||||
*
|
||||
* @return int See possible return int values above
|
||||
*/
|
||||
public function getSessionStatus(): int
|
||||
{
|
||||
return session_status();
|
||||
}
|
||||
|
||||
// MARK: write close session
|
||||
|
||||
/**
|
||||
* unlock the session file, so concurrent AJAX requests can be done
|
||||
* NOTE: after this has been called, no changes in _SESSION will be stored
|
||||
@@ -188,17 +447,24 @@ class Session
|
||||
return session_write_close();
|
||||
}
|
||||
|
||||
// MARK: session close and clean up
|
||||
|
||||
/**
|
||||
* Proper destroy a session
|
||||
* - unset the _SESSION array
|
||||
* - unset cookie if cookie on and we have not strict mode
|
||||
* - unset session_name and session_id internal vars
|
||||
* - destroy session
|
||||
*
|
||||
* @return bool
|
||||
* @return bool True on successful session destroy
|
||||
*/
|
||||
public function sessionDestroy(): bool
|
||||
{
|
||||
$_SESSION = [];
|
||||
// abort to false if not unsetable
|
||||
if (!session_unset()) {
|
||||
return false;
|
||||
}
|
||||
$this->clear();
|
||||
if (
|
||||
ini_get('session.use_cookies') &&
|
||||
!ini_get('session.use_strict_mode')
|
||||
@@ -218,68 +484,93 @@ class Session
|
||||
$params['httponly']
|
||||
);
|
||||
}
|
||||
// unset internal vars
|
||||
$this->session_name = '';
|
||||
$this->session_id = '';
|
||||
return session_destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* get session status
|
||||
* PHP_SESSION_DISABLED if sessions are disabled.
|
||||
* PHP_SESSION_NONE if sessions are enabled, but none exists.
|
||||
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
|
||||
*
|
||||
* https://www.php.net/manual/en/function.session-status.php
|
||||
*
|
||||
* @return int See possible return int values above
|
||||
*/
|
||||
public function getSessionStatus(): int
|
||||
{
|
||||
return session_status();
|
||||
}
|
||||
|
||||
// _SESSION set/unset methods
|
||||
// MARK: _SESSION set/unset methods
|
||||
|
||||
/**
|
||||
* unset all _SESSION entries
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unsetAllS(): void
|
||||
public function clear(): void
|
||||
{
|
||||
foreach (array_keys($_SESSION ?? []) as $name) {
|
||||
unset($_SESSION[$name]);
|
||||
$this->restartSession();
|
||||
if (!session_unset()) {
|
||||
throw new \RuntimeException('[SESSION] Cannot unset session vars', 1);
|
||||
}
|
||||
if (!empty($_SESSION)) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
$this->closeSessionCall();
|
||||
}
|
||||
|
||||
/**
|
||||
* set _SESSION entry 'name' with any value
|
||||
*
|
||||
* @param string|int $name array name in _SESSION
|
||||
* @param mixed $value value to set (can be anything)
|
||||
* @param string $name array name in _SESSION
|
||||
* @param mixed $value value to set (can be anything)
|
||||
* @return Session
|
||||
*/
|
||||
public function set(string $name, mixed $value): Session
|
||||
{
|
||||
$this->checkValidSessionEntryKey($name);
|
||||
$this->restartSession();
|
||||
$_SESSION[$name] = $value;
|
||||
$this->closeSessionCall();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* set many session entries in one set
|
||||
*
|
||||
* @param array<string,mixed> $set key is the key in the _SESSION, value is any data to set
|
||||
* @return void
|
||||
*/
|
||||
public function setS(string|int $name, mixed $value): void
|
||||
public function setMany(array $set): void
|
||||
{
|
||||
$_SESSION[$name] = $value;
|
||||
$this->restartSession();
|
||||
// skip any that are not valid
|
||||
foreach ($set as $key => $value) {
|
||||
$this->checkValidSessionEntryKey($key);
|
||||
$_SESSION[$key] = $value;
|
||||
}
|
||||
$this->closeSessionCall();
|
||||
}
|
||||
|
||||
/**
|
||||
* get _SESSION 'name' entry or empty string if not set
|
||||
*
|
||||
* @param string|int $name value key to get from _SESSION
|
||||
* @return mixed value stored in _SESSION
|
||||
* @param string $name value key to get from _SESSION
|
||||
* @return mixed value stored in _SESSION, if not found set to null
|
||||
*/
|
||||
public function getS(string|int $name): mixed
|
||||
public function get(string $name): mixed
|
||||
{
|
||||
return $_SESSION[$name] ?? '';
|
||||
return $_SESSION[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* get multiple session entries
|
||||
*
|
||||
* @param array<string> $set
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function getMany(array $set): array
|
||||
{
|
||||
return array_intersect_key($_SESSION, array_flip($set));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a name is set in the _SESSION array
|
||||
*
|
||||
* @param string|int $name Name to check for
|
||||
* @return bool True for set, False fornot set
|
||||
* @param string $name Name to check for
|
||||
* @return bool True for set, False fornot set
|
||||
*/
|
||||
public function issetS(string|int $name): bool
|
||||
public function isset(string $name): bool
|
||||
{
|
||||
return isset($_SESSION[$name]);
|
||||
}
|
||||
@@ -287,67 +578,36 @@ class Session
|
||||
/**
|
||||
* unset one _SESSION entry 'name' if exists
|
||||
*
|
||||
* @param string|int $name _SESSION key name to remove
|
||||
* @param string $name _SESSION key name to remove
|
||||
* @return Session
|
||||
*/
|
||||
public function unset(string $name): Session
|
||||
{
|
||||
if (!isset($_SESSION[$name])) {
|
||||
return $this;
|
||||
}
|
||||
$this->restartSession();
|
||||
unset($_SESSION[$name]);
|
||||
$this->closeSessionCall();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset many session entry
|
||||
*
|
||||
* @param array<string> $set list of session keys to reset
|
||||
* @return void
|
||||
*/
|
||||
public function unsetS(string|int $name): void
|
||||
public function unsetMany(array $set): void
|
||||
{
|
||||
if (isset($_SESSION[$name])) {
|
||||
unset($_SESSION[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// set/get below
|
||||
// ->var = value;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string|int $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function __set(string|int $name, mixed $value): void
|
||||
{
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string|int $name
|
||||
* @return mixed If name is not found, it will return null
|
||||
*/
|
||||
public function __get(string|int $name): mixed
|
||||
{
|
||||
if (isset($_SESSION[$name])) {
|
||||
return $_SESSION[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string|int $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset(string|int $name): bool
|
||||
{
|
||||
return isset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string|int $name
|
||||
* @return void
|
||||
*/
|
||||
public function __unset(string|int $name): void
|
||||
{
|
||||
if (isset($_SESSION[$name])) {
|
||||
unset($_SESSION[$name]);
|
||||
$this->restartSession();
|
||||
foreach ($set as $key) {
|
||||
if (!isset($_SESSION[$key])) {
|
||||
continue;
|
||||
}
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
$this->closeSessionCall();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,26 +56,6 @@ class Uids
|
||||
*/
|
||||
public static function uuidv4(): string
|
||||
{
|
||||
/* return sprintf(
|
||||
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||
// 32 bits for "time_low"
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
// 16 bits for "time_mid"
|
||||
mt_rand(0, 0xffff),
|
||||
// 16 bits for "time_hi_and_version",
|
||||
// four most significant bits holds version number 4
|
||||
mt_rand(0, 0x0fff) | 0x4000,
|
||||
// 16 bits, 8 bits for "clk_seq_hi_res",
|
||||
// 8 bits for "clk_seq_low",
|
||||
// two most significant bits holds zero and one for variant DCE1.1
|
||||
mt_rand(0, 0x3fff) | 0x8000,
|
||||
// 48 bits for "node"
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff)
|
||||
); */
|
||||
|
||||
$data = random_bytes(16);
|
||||
assert(strlen($data) == 16);
|
||||
|
||||
@@ -93,6 +73,20 @@ class Uids
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* regex validate uuid v4
|
||||
*
|
||||
* @param string $uuidv4
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateUuuidv4(string $uuidv4): bool
|
||||
{
|
||||
if (!preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/", $uuidv4)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a uniq id based on lengths
|
||||
*
|
||||
|
||||
126
src/DB/IO.php
126
src/DB/IO.php
@@ -284,7 +284,8 @@ class IO
|
||||
public const ERROR_HASH_TYPE = 'adler32';
|
||||
/** @var string regex to get returning with matches at position 1 */
|
||||
public const REGEX_RETURNING = '/\s+returning\s+(.+\s*(?:.+\s*)+);?$/i';
|
||||
/** @var array<string> allowed convert target for placeholder: pg or pdo (currently not available) */
|
||||
/** @var array<string> allowed convert target for placeholder:
|
||||
* pg or pdo (currently not available) */
|
||||
public const DB_CONVERT_PLACEHOLDER_TARGET = ['pg'];
|
||||
// REGEX_SELECT
|
||||
// REGEX_UPDATE
|
||||
@@ -499,7 +500,7 @@ class IO
|
||||
die('<!-- Cannot load db functions class for: ' . $this->db_type . ' -->');
|
||||
}
|
||||
// write to internal one, once OK
|
||||
$this->db_functions = $db_functions;
|
||||
$this->db_functions = $db_functions; /** @phan-suppress-current-line PhanPossiblyNullTypeMismatchProperty */
|
||||
|
||||
// connect to DB
|
||||
if (!$this->__connectToDB()) {
|
||||
@@ -1311,33 +1312,14 @@ class IO
|
||||
}
|
||||
|
||||
/**
|
||||
* count $ leading parameters only
|
||||
* count placeholder entries in the query
|
||||
*
|
||||
* @param string $query Query to check
|
||||
* @return int Number of parameters found
|
||||
*/
|
||||
private function __dbCountQueryParams(string $query): int
|
||||
{
|
||||
$match = [];
|
||||
// regex for params: only stand alone $number allowed
|
||||
// exclude all '' enclosed strings, ignore all numbers [note must start with digit]
|
||||
// can have space/tab/new line
|
||||
// must have <> = , ( [not equal, equal, comma, opening round bracket]
|
||||
// can have space/tab/new line
|
||||
// $ number with 1-9 for first and 0-9 for further digits
|
||||
// /s for matching new line in . list
|
||||
// [disabled, we don't used ^ or $] /m for multi line match
|
||||
// Matches in 1:, must be array_filtered to remove empty, count with array_unique
|
||||
$query_split = '[(=,?-]|->|->>|#>|#>>|@>|<@|\?\|\?\&|\|\||#-';
|
||||
preg_match_all(
|
||||
'/'
|
||||
. '(?:\'.*?\')?\s*(?:\?\?|<>|' . $query_split . ')\s*'
|
||||
. '(?:\d+|(?:\'.*?\')|(\$[1-9]{1}(?:[0-9]{1,})?))'
|
||||
. '/s',
|
||||
$query,
|
||||
$match
|
||||
);
|
||||
return count(array_unique(array_filter($match[1])));
|
||||
return $this->db_functions->__dbCountQueryParams($query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1431,10 +1413,7 @@ class IO
|
||||
$this->pk_name_table[$table] ?
|
||||
$this->pk_name_table[$table] : 'NULL';
|
||||
}
|
||||
if (
|
||||
!preg_match(self::REGEX_RETURNING, $this->query) &&
|
||||
$this->pk_name && $this->pk_name != 'NULL'
|
||||
) {
|
||||
if (!preg_match(self::REGEX_RETURNING, $this->query) && $this->pk_name != 'NULL') {
|
||||
// check if this query has a ; at the end and remove it
|
||||
$__query = preg_replace("/(;\s*)$/", '', $this->query);
|
||||
// must be query, if preg replace failed, use query as before
|
||||
@@ -1444,7 +1423,7 @@ class IO
|
||||
} elseif (
|
||||
preg_match(self::REGEX_RETURNING, $this->query, $matches)
|
||||
) {
|
||||
if ($this->pk_name && $this->pk_name != 'NULL') {
|
||||
if ($this->pk_name != 'NULL') {
|
||||
// add the primary key if it is not in the returning set
|
||||
if (!preg_match("/$this->pk_name/", $matches[1])) {
|
||||
$this->query .= " , " . $this->pk_name;
|
||||
@@ -2964,7 +2943,7 @@ class IO
|
||||
$query_hash = $this->dbGetQueryHash($query, $params);
|
||||
// clears cache for this query
|
||||
if (empty($this->cursor_ext[$query_hash]['query'])) {
|
||||
$this->__dbError(18, context: [
|
||||
$this->__dbWarning(18, context: [
|
||||
'query' => $query,
|
||||
'params' => $params,
|
||||
'hash' => $query_hash,
|
||||
@@ -3159,8 +3138,10 @@ class IO
|
||||
'pk_name' => '',
|
||||
'count' => 0,
|
||||
'query' => '',
|
||||
'query_raw' => $query,
|
||||
'result' => null,
|
||||
'returning_id' => false
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
];
|
||||
// if this is an insert query, check if we can add a return
|
||||
if ($this->dbCheckQueryForInsert($query, true)) {
|
||||
@@ -3200,6 +3181,39 @@ class IO
|
||||
$this->prepare_cursor[$stm_name]['pk_name'] = $pk_name;
|
||||
}
|
||||
}
|
||||
// QUERY PARAMS: run query params check and rewrite
|
||||
if ($this->dbGetConvertPlaceholder() === true) {
|
||||
try {
|
||||
$this->placeholder_converted = ConvertPlaceholder::convertPlaceholderInQuery(
|
||||
$query,
|
||||
null,
|
||||
$this->dbGetConvertPlaceholderTarget()
|
||||
);
|
||||
// write the new queries over the old
|
||||
if (!empty($this->placeholder_converted['query'])) {
|
||||
$query = $this->placeholder_converted['query'];
|
||||
}
|
||||
$this->prepare_cursor[$stm_name]['placeholder_converted'] = $this->placeholder_converted;
|
||||
} catch (\OutOfRangeException $e) {
|
||||
$this->__dbError($e->getCode(), context:[
|
||||
'statement_name' => $stm_name,
|
||||
'query' => $query,
|
||||
'location' => 'dbPrepare',
|
||||
'error' => 'OutOfRangeException',
|
||||
'exception' => $e
|
||||
]);
|
||||
return false;
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->__dbError($e->getCode(), context:[
|
||||
'statement_name' => $stm_name,
|
||||
'query' => $query,
|
||||
'location' => 'dbPrepare',
|
||||
'error' => 'RuntimeException',
|
||||
'exception' => $e
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// check prepared curser parameter count
|
||||
$this->prepare_cursor[$stm_name]['count'] = $this->__dbCountQueryParams($query);
|
||||
$this->prepare_cursor[$stm_name]['query'] = $query;
|
||||
@@ -3221,11 +3235,12 @@ class IO
|
||||
}
|
||||
} else {
|
||||
// if we try to use the same statement name for a differnt query, error abort
|
||||
if ($this->prepare_cursor[$stm_name]['query'] != $query) {
|
||||
if ($this->prepare_cursor[$stm_name]['query_raw'] != $query) {
|
||||
// thrown error
|
||||
$this->__dbError(26, false, context: [
|
||||
'statement_name' => $stm_name,
|
||||
'prepared_query' => $this->prepare_cursor[$stm_name]['query'],
|
||||
'prepared_query_raw' => $this->prepare_cursor[$stm_name]['query_raw'],
|
||||
'query' => $query,
|
||||
'pk_name' => $pk_name,
|
||||
]);
|
||||
@@ -3735,7 +3750,7 @@ class IO
|
||||
}
|
||||
|
||||
/**
|
||||
* convert db values (set)
|
||||
* convert db values (set) to php matching types
|
||||
*
|
||||
* @param Convert $convert
|
||||
* @return void
|
||||
@@ -3746,7 +3761,7 @@ class IO
|
||||
}
|
||||
|
||||
/**
|
||||
* unsert convert db values flag
|
||||
* unsert convert db values flag for converting db to php matching types
|
||||
*
|
||||
* @param Convert $convert
|
||||
* @return void
|
||||
@@ -3757,7 +3772,7 @@ class IO
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to origincal config file set
|
||||
* Reset to original config file set for converting db to php matching type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -3769,7 +3784,7 @@ class IO
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a conert flag is set
|
||||
* check if a convert flag is set for converting db to php matching type
|
||||
*
|
||||
* @param Convert $convert
|
||||
* @return bool
|
||||
@@ -3783,7 +3798,7 @@ class IO
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if we want to auto convert PDO/\Pg placeholders
|
||||
* Set if we want to auto convert to PDO/\Pg placeholders
|
||||
*
|
||||
* @param bool $flag
|
||||
* @return void
|
||||
@@ -4041,7 +4056,7 @@ class IO
|
||||
*/
|
||||
public function dbGetQueryHash(string $query, array $params = []): string
|
||||
{
|
||||
return Hash::__hashLong(
|
||||
return Hash::hashLong(
|
||||
$query . (
|
||||
$params !== [] ?
|
||||
'#' . json_encode($params) : ''
|
||||
@@ -4294,7 +4309,7 @@ class IO
|
||||
* @param string $stm_name The name of the stored statement
|
||||
* @param string $key Key field name in prepared cursor array
|
||||
* Allowed are: pk_name, count, query, returning_id
|
||||
* @return null|string|int|bool Entry from each of the valid keys
|
||||
* @return null|string|int|bool|array<string,mixed> Entry from each of the valid keys
|
||||
* Will return false on error
|
||||
* Not ethat returnin_id also can return false
|
||||
* but will not set an error entry
|
||||
@@ -4302,7 +4317,7 @@ class IO
|
||||
public function dbGetPrepareCursorValue(
|
||||
string $stm_name,
|
||||
string $key
|
||||
): null|string|int|bool {
|
||||
): null|string|int|bool|array {
|
||||
// if no statement name
|
||||
if (empty($stm_name)) {
|
||||
$this->__dbError(
|
||||
@@ -4313,7 +4328,7 @@ class IO
|
||||
return false;
|
||||
}
|
||||
// if not a valid key
|
||||
if (!in_array($key, ['pk_name', 'count', 'query', 'returning_id'])) {
|
||||
if (!in_array($key, ['pk_name', 'count', 'query', 'returning_id', 'placeholder_converted'])) {
|
||||
$this->__dbError(
|
||||
102,
|
||||
false,
|
||||
@@ -4348,6 +4363,37 @@ class IO
|
||||
return $this->prepare_cursor[$stm_name][$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a prepared query eixsts
|
||||
*
|
||||
* @param string $stm_name Statement to check
|
||||
* @param string $query [default=''] If set then query must also match
|
||||
* @return false|int<0,2> False on missing stm_name
|
||||
* 0: ok, 1: stm_name matchin, 2: stm_name and query matching
|
||||
*/
|
||||
public function dbPreparedCursorStatus(string $stm_name, string $query = ''): false|int
|
||||
{
|
||||
if (empty($stm_name)) {
|
||||
$this->__dbError(
|
||||
101,
|
||||
false,
|
||||
'No statement name given'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// does not exist
|
||||
$return_value = 0;
|
||||
if (!empty($this->prepare_cursor[$stm_name]['query_raw'])) {
|
||||
// statement name eixts
|
||||
$return_value = 1;
|
||||
if ($this->prepare_cursor[$stm_name]['query_raw'] == $query) {
|
||||
// query also matches
|
||||
$return_value = 2;
|
||||
}
|
||||
}
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
// ***************************
|
||||
// ERROR AND WARNING DATA
|
||||
// ***************************
|
||||
|
||||
@@ -285,6 +285,22 @@ interface SqlFunctions
|
||||
*/
|
||||
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param bool $strip
|
||||
* @return string
|
||||
*/
|
||||
public function __dbVersionInfo(string $parameter, bool $strip = true): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function __dbVersionInfoParameterList(): array;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -292,6 +308,13 @@ interface SqlFunctions
|
||||
*/
|
||||
public function __dbVersion(): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function __dbVersionNumeric(): int;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -306,6 +329,14 @@ interface SqlFunctions
|
||||
?int &$end = null
|
||||
): ?array;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $parameter
|
||||
* @return string|bool
|
||||
*/
|
||||
public function __dbParameter(string $parameter): string|bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -343,6 +374,14 @@ interface SqlFunctions
|
||||
* @return string
|
||||
*/
|
||||
public function __dbGetEncoding(): string;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $query
|
||||
* @return int
|
||||
*/
|
||||
public function __dbCountQueryParams(string $query): int;
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -51,6 +51,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DB\SQL;
|
||||
|
||||
use CoreLibs\DB\Support\ConvertPlaceholder;
|
||||
|
||||
// below no ignore is needed if we want to use PgSql interface checks with PHP 8.0
|
||||
// as main system. Currently all @var sets are written as object
|
||||
/** @#phan-file-suppress PhanUndeclaredTypeProperty,PhanUndeclaredTypeParameter,PhanUndeclaredTypeReturnType */
|
||||
@@ -102,7 +104,7 @@ class PgSQL implements Interface\SqlFunctions
|
||||
* SELECT foo FROM bar WHERE foobar = $1
|
||||
*
|
||||
* @param string $query Query string with placeholders $1, ..
|
||||
* @param array<mixed> $params Matching parameters for each placerhold
|
||||
* @param array<mixed> $params Matching parameters for each placeholder
|
||||
* @return \PgSql\Result|false Query result
|
||||
*/
|
||||
public function __dbQueryParams(string $query, array $params): \PgSql\Result|false
|
||||
@@ -140,7 +142,7 @@ class PgSQL implements Interface\SqlFunctions
|
||||
* sends an async query to the server with params
|
||||
*
|
||||
* @param string $query Query string with placeholders $1, ..
|
||||
* @param array<mixed> $params Matching parameters for each placerhold
|
||||
* @param array<mixed> $params Matching parameters for each placeholder
|
||||
* @return bool true/false Query sent successful status
|
||||
*/
|
||||
public function __dbSendQueryParams(string $query, array $params): bool
|
||||
@@ -405,17 +407,13 @@ class PgSQL implements Interface\SqlFunctions
|
||||
}
|
||||
// no PK name given at all
|
||||
if (empty($pk_name)) {
|
||||
// if name is plurar, make it singular
|
||||
// if (preg_match("/.*s$/i", $table))
|
||||
// $table = substr($table, 0, -1);
|
||||
// set pk_name to "id"
|
||||
$pk_name = $table . "_id";
|
||||
}
|
||||
$seq = ($schema ? $schema . '.' : '') . $table . "_" . $pk_name . "_seq";
|
||||
$q = "SELECT CURRVAL('$seq') AS insert_id";
|
||||
$q = "SELECT CURRVAL(pg_get_serial_sequence($1, $2)) AS insert_id";
|
||||
// I have to do manually or I overwrite the original insert internal vars ...
|
||||
if ($q = $this->__dbQuery($q)) {
|
||||
if (is_array($res = $this->__dbFetchArray($q))) {
|
||||
if ($cursor = $this->__dbQueryParams($q, [$table, $pk_name])) {
|
||||
if (is_array($res = $this->__dbFetchArray($cursor))) {
|
||||
list($id) = $res;
|
||||
} else {
|
||||
return false;
|
||||
@@ -449,26 +447,36 @@ class PgSQL implements Interface\SqlFunctions
|
||||
$table_prefix = $schema . '.';
|
||||
}
|
||||
}
|
||||
$params = [$table_prefix . $table];
|
||||
$replace = ['', ''];
|
||||
// read from table the PK name
|
||||
// faster primary key get
|
||||
$q = "SELECT pg_attribute.attname AS column_name, "
|
||||
. "format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS type "
|
||||
. "FROM pg_index, pg_class, pg_attribute ";
|
||||
$q = <<<SQL
|
||||
SELECT
|
||||
pg_attribute.attname AS column_name,
|
||||
format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS type
|
||||
FROM pg_index, pg_class, pg_attribute{PG_NAMESPACE}
|
||||
WHERE
|
||||
-- regclass translates the OID to the name
|
||||
pg_class.oid = $1::regclass AND
|
||||
indrelid = pg_class.oid AND
|
||||
pg_attribute.attrelid = pg_class.oid AND
|
||||
pg_attribute.attnum = any(pg_index.indkey) AND
|
||||
indisprimary
|
||||
{NSPNAME}
|
||||
SQL;
|
||||
if ($schema) {
|
||||
$q .= ", pg_namespace ";
|
||||
$params[] = $schema;
|
||||
$replace = [
|
||||
", pg_namespace",
|
||||
"AND pg_class.relnamespace = pg_namespace.oid AND nspname = $2"
|
||||
];
|
||||
}
|
||||
$q .= "WHERE "
|
||||
// regclass translates the OID to the name
|
||||
. "pg_class.oid = '" . $table_prefix . $table . "'::regclass AND "
|
||||
. "indrelid = pg_class.oid AND ";
|
||||
if ($schema) {
|
||||
$q .= "nspname = '" . $schema . "' AND "
|
||||
. "pg_class.relnamespace = pg_namespace.oid AND ";
|
||||
}
|
||||
$q .= "pg_attribute.attrelid = pg_class.oid AND "
|
||||
. "pg_attribute.attnum = any(pg_index.indkey) "
|
||||
. "AND indisprimary";
|
||||
$cursor = $this->__dbQuery($q);
|
||||
$cursor = $this->__dbQueryParams(str_replace(
|
||||
['{PG_NAMESPACE}', '{NSPNAME}'],
|
||||
$replace,
|
||||
$q
|
||||
), $params);
|
||||
if ($cursor !== false) {
|
||||
$__db_fetch_array = $this->__dbFetchArray($cursor);
|
||||
if (!is_array($__db_fetch_array)) {
|
||||
@@ -893,11 +901,13 @@ class PgSQL implements Interface\SqlFunctions
|
||||
public function __dbSetSchema(string $db_schema): int
|
||||
{
|
||||
// check if schema actually exists
|
||||
$query = "SELECT EXISTS("
|
||||
. "SELECT 1 FROM information_schema.schemata "
|
||||
. "WHERE schema_name = " . $this->__dbEscapeLiteral($db_schema)
|
||||
. ")";
|
||||
$cursor = $this->__dbQuery($query);
|
||||
$query = <<<SQL
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM information_schema.schemata
|
||||
WHERE schema_name = $1
|
||||
)
|
||||
SQL;
|
||||
$cursor = $this->__dbQueryParams($query, [$db_schema]);
|
||||
// abort if execution fails
|
||||
if ($cursor === false) {
|
||||
return 1;
|
||||
@@ -966,6 +976,34 @@ class PgSQL implements Interface\SqlFunctions
|
||||
{
|
||||
return $this->__dbShow('client_encoding');
|
||||
}
|
||||
|
||||
/**
|
||||
* Count placeholder queries. $ only
|
||||
*
|
||||
* @param string $query
|
||||
* @return int
|
||||
*/
|
||||
public function __dbCountQueryParams(string $query): int
|
||||
{
|
||||
$matches = [];
|
||||
// regex for params: only stand alone $number allowed
|
||||
// exclude all '' enclosed strings, ignore all numbers [note must start with digit]
|
||||
// can have space/tab/new line
|
||||
// must have <> = , ( [not equal, equal, comma, opening round bracket]
|
||||
// can have space/tab/new line
|
||||
// $ number with 1-9 for first and 0-9 for further digits
|
||||
// Collects also PDO ? and :named, but they are ignored
|
||||
// /s for matching new line in . list
|
||||
// [disabled, we don't used ^ or $] /m for multi line match
|
||||
// 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,
|
||||
$query,
|
||||
$matches
|
||||
);
|
||||
return count(array_unique(array_filter($matches[3])));
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -14,6 +14,87 @@ 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 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 */
|
||||
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_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_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_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
|
||||
// match for replace part
|
||||
. '(?:'
|
||||
// ignore parts
|
||||
. self::PATTERN_IGNORE
|
||||
// :name named part (PDO) [1]
|
||||
. self::PATTERN_NAMED . '|'
|
||||
// ? question mark part (PDO) [2]
|
||||
. self::PATTERN_QUESTION_MARK . '|'
|
||||
// $n numbered part (\PG php) [3]
|
||||
. self::PATTERN_NUMBERED
|
||||
// end match
|
||||
. ')'
|
||||
// single line -> add line break to matches in "."
|
||||
. '/s';
|
||||
|
||||
/**
|
||||
* Convert PDO type query with placeholders to \PG style and vica versa
|
||||
* For PDO to: ? and :named
|
||||
@@ -27,44 +108,24 @@ class ConvertPlaceholder
|
||||
* found has -1 if an error occoured in the preg_match_all call
|
||||
*
|
||||
* @param string $query Query with placeholders to convert
|
||||
* @param array<mixed> $params The parameters that are used for the query, and will be updated
|
||||
* @param ?array<mixed> $params The parameters that are used for the query, and will be updated
|
||||
* @param string $convert_to Either pdo or pg, will be converted to lower case for check
|
||||
* @return 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>}
|
||||
* @throws \OutOfRangeException 200
|
||||
* @return array{original:array{query:string,params:array<mixed>,empty_params:bool},type:''|'named'|'numbered'|'question_mark',found:int,matches:array<string>,params_lookup:array<mixed>,query:string,params:array<mixed>}
|
||||
* @throws \OutOfRangeException 200 If mixed placeholder types
|
||||
* @throws \InvalidArgumentException 300 or 301 if wrong convert to with found placeholders
|
||||
*/
|
||||
public static function convertPlaceholderInQuery(
|
||||
string $query,
|
||||
array $params,
|
||||
?array $params,
|
||||
string $convert_to = 'pg'
|
||||
): array {
|
||||
$convert_to = strtolower($convert_to);
|
||||
$matches = [];
|
||||
$query_split = '[(=,?-]|->|->>|#>|#>>|@>|<@|\?\|\?\&|\|\||#-';
|
||||
$pattern = '/'
|
||||
// prefix string part, must match towards
|
||||
// seperator for ( = , ? - [and json/jsonb in pg doc section 9.15]
|
||||
. '(?:\'.*?\')?\s*(?:\?\?|' . $query_split . ')\s*'
|
||||
// match for replace part
|
||||
. '(?:'
|
||||
// digit -> ignore
|
||||
. '\d+|'
|
||||
// other string -> ignore
|
||||
. '(?:\'.*?\')|'
|
||||
// :name named part (PDO)
|
||||
. '(:\w+)|'
|
||||
// ? question mark part (PDO)
|
||||
. '(?:(?:\?\?)?\s*(\?{1}))|'
|
||||
// $n numbered part (\PG php)
|
||||
. '(\$[1-9]{1}(?:[0-9]{1,})?)'
|
||||
// end match
|
||||
. ')'
|
||||
// single line -> add line break to matches in "."
|
||||
. '/s';
|
||||
// matches:
|
||||
// 1: :named
|
||||
// 2: ? question mark
|
||||
// 3: $n numbered
|
||||
$found = preg_match_all($pattern, $query, $matches, PREG_UNMATCHED_AS_NULL);
|
||||
$found = preg_match_all(self::REGEX_LOOKUP_PLACEHOLDERS, $query, $matches, PREG_UNMATCHED_AS_NULL);
|
||||
// if false or null set to -1
|
||||
// || $found === null
|
||||
if ($found === false) {
|
||||
@@ -77,10 +138,10 @@ class ConvertPlaceholder
|
||||
/** @var array<string> 3: $n matches */
|
||||
$numbered_matches = array_filter($matches[3]);
|
||||
// count matches
|
||||
$count_named = count($named_matches);
|
||||
$count_named = count(array_unique($named_matches));
|
||||
$count_qmark = count($qmark_matches);
|
||||
$count_numbered = count($numbered_matches);
|
||||
// throw if mixed
|
||||
$count_numbered = count(array_unique($numbered_matches));
|
||||
// throw exception if mixed found
|
||||
if (
|
||||
($count_named && $count_qmark) ||
|
||||
($count_named && $count_numbered) ||
|
||||
@@ -88,140 +149,195 @@ class ConvertPlaceholder
|
||||
) {
|
||||
throw new \OutOfRangeException('Cannot have named, question mark and numbered in the same query', 200);
|
||||
}
|
||||
// rebuild
|
||||
$matches_return = [];
|
||||
$type = '';
|
||||
// // throw if invalid conversion
|
||||
// if (($count_named || $count_qmark) && $convert_to != 'pg') {
|
||||
// throw new \InvalidArgumentException('Cannot convert from named or question mark placeholders to PDO', 300);
|
||||
// }
|
||||
// if ($count_numbered && $convert_to != 'pdo') {
|
||||
// throw new \InvalidArgumentException('Cannot convert from numbered placeholders to Pg', 301);
|
||||
// }
|
||||
// return array
|
||||
$return_placeholders = [
|
||||
// original
|
||||
'original' => [
|
||||
'query' => $query,
|
||||
'params' => $params ?? [],
|
||||
'empty_params' => $params === null ? true : false,
|
||||
],
|
||||
// type found, empty if nothing was done
|
||||
'type' => '',
|
||||
// int: found, not found; -1: problem (set from false)
|
||||
'found' => (int)$found,
|
||||
'matches' => [],
|
||||
// old to new lookup check
|
||||
'params_lookup' => [],
|
||||
// this must match the count in params in new
|
||||
'needed' => 0,
|
||||
// new
|
||||
'query' => '',
|
||||
'params' => [],
|
||||
];
|
||||
// replace basic regex and name settings
|
||||
if ($count_named) {
|
||||
$return_placeholders['type'] = 'named';
|
||||
$return_placeholders['matches'] = $named_matches;
|
||||
$return_placeholders['needed'] = $count_named;
|
||||
} elseif ($count_qmark) {
|
||||
$return_placeholders['type'] = 'question_mark';
|
||||
$return_placeholders['matches'] = $qmark_matches;
|
||||
$return_placeholders['needed'] = $count_qmark;
|
||||
// for each ?:DTN: -> replace with $1 ... $n, any remaining :DTN: remove
|
||||
} elseif ($count_numbered) {
|
||||
$return_placeholders['type'] = 'numbered';
|
||||
$return_placeholders['matches'] = $numbered_matches;
|
||||
$return_placeholders['needed'] = $count_numbered;
|
||||
}
|
||||
// run convert only if matching type and direction
|
||||
if (
|
||||
(($count_named || $count_qmark) && $convert_to == 'pg') ||
|
||||
($count_numbered && $convert_to == 'pdo')
|
||||
) {
|
||||
$param_list = self::updateParamList($return_placeholders);
|
||||
$return_placeholders['params_lookup'] = $param_list['params_lookup'];
|
||||
$return_placeholders['query'] = $param_list['query'];
|
||||
$return_placeholders['params'] = $param_list['params'];
|
||||
}
|
||||
// return data
|
||||
return $return_placeholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the params list from one style to the other to match the query output
|
||||
* if original.empty_params is set to true, no params replacement is done
|
||||
* if param replacement has been done in a dbPrepare then this has to be run
|
||||
* with the return palceholders array with params in original filled and empty_params turned off
|
||||
*
|
||||
* phpcs:disable Generic.Files.LineLength
|
||||
* @param array{original:array{query:string,params:array<mixed>,empty_params:bool},type:''|'named'|'numbered'|'question_mark',found:int,matches?:array<string>,params_lookup?:array<mixed>,query?:string,params?:array<mixed>} $converted_placeholders
|
||||
* phpcs:enable Generic.Files.LineLength
|
||||
* @return array{params_lookup:array<mixed>,query:string,params:array<mixed>}
|
||||
*/
|
||||
public static function updateParamList(array $converted_placeholders): array
|
||||
{
|
||||
// skip if nothing set
|
||||
if (!$converted_placeholders['found']) {
|
||||
return [
|
||||
'params_lookup' => [],
|
||||
'query' => '',
|
||||
'params' => []
|
||||
];
|
||||
}
|
||||
$query_new = '';
|
||||
$params_new = [];
|
||||
$params_lookup = [];
|
||||
if ($count_named && $convert_to == 'pg') {
|
||||
$type = 'named';
|
||||
$matches_return = $named_matches;
|
||||
// only check for :named
|
||||
$pattern_replace = '/'
|
||||
. '((?:\'.*?\')?\s*(?:\?\?|' . $query_split . ')\s*)'
|
||||
. '(\d+|(?:\'.*?\')|(:\w+))'
|
||||
. '/s';
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: replace part :named
|
||||
$pos = 0;
|
||||
$query_new = preg_replace_callback(
|
||||
$pattern_replace,
|
||||
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params) {
|
||||
// only count up if $match[3] is not yet in lookup table
|
||||
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
|
||||
$pos++;
|
||||
$params_lookup[$matches[3]] = '$' . $pos;
|
||||
$params_new[] = $params[$matches[3]] ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $matches[3] . ' 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]] ??
|
||||
// set to null if params is empty
|
||||
$params = $converted_placeholders['original']['params'];
|
||||
$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
|
||||
$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]])) {
|
||||
$pos++;
|
||||
$params_lookup[$matches[3]] = '$' . $pos;
|
||||
// skip params setup if param list is empty
|
||||
if (!$empty_params) {
|
||||
$params_new[] = $params[$matches[3]] ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . $matches[3] . ' 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
|
||||
)
|
||||
);
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
break;
|
||||
case 'question_mark':
|
||||
if (!$empty_params) {
|
||||
// order and data stays the same
|
||||
$params_new = $params ?? [];
|
||||
}
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: replace part ?
|
||||
$pos = 0;
|
||||
$query_new = preg_replace_callback(
|
||||
self::REGEX_REPLACE_QUESTION_MARK,
|
||||
function ($matches) use (&$pos, &$params_lookup) {
|
||||
// only count pos up for actual replacements we will do
|
||||
if (!empty($matches[3])) {
|
||||
$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
|
||||
);
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
break;
|
||||
case 'numbered':
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: 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]])) {
|
||||
$pos++;
|
||||
$params_lookup[$matches[3]] = ':' . $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
|
||||
);
|
||||
}
|
||||
}
|
||||
// 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
|
||||
221
|
||||
)
|
||||
);
|
||||
},
|
||||
$query
|
||||
);
|
||||
} elseif ($count_qmark && $convert_to == 'pg') {
|
||||
$type = 'question_mark';
|
||||
$matches_return = $qmark_matches;
|
||||
// order and data stays the same
|
||||
$params_new = $params;
|
||||
// only check for ?
|
||||
$pattern_replace = '/'
|
||||
. '((?:\'.*?\')?\s*(?:\?\?|' . $query_split . ')\s*)'
|
||||
. '(\d+|(?:\'.*?\')|(?:(?:\?\?)?\s*(\?{1})))'
|
||||
. '/s';
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: replace part ?
|
||||
$pos = 0;
|
||||
$query_new = preg_replace_callback(
|
||||
$pattern_replace,
|
||||
function ($matches) use (&$pos, &$params_lookup) {
|
||||
// only count pos up for actual replacements we will do
|
||||
if (!empty($matches[3])) {
|
||||
$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
|
||||
);
|
||||
},
|
||||
$query
|
||||
);
|
||||
// for each ?:DTN: -> replace with $1 ... $n, any remaining :DTN: remove
|
||||
} elseif ($count_numbered && $convert_to == 'pdo') {
|
||||
// convert numbered to named
|
||||
$type = 'numbered';
|
||||
$matches_return = $numbered_matches;
|
||||
// only check for $n
|
||||
$pattern_replace = '/'
|
||||
. '((?:\'.*?\')?\s*(?:\?\?|' . $query_split . ')\s*)'
|
||||
. '(\d+|(?:\'.*?\')|(\$[1-9]{1}(?:[0-9]{1,})?))'
|
||||
. '/s';
|
||||
// 0: full
|
||||
// 1: pre part
|
||||
// 2: keep part UNLESS '3' is set
|
||||
// 3: replace part $numbered
|
||||
$pos = 0;
|
||||
$query_new = preg_replace_callback(
|
||||
$pattern_replace,
|
||||
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params) {
|
||||
// only count up if $match[3] is not yet in lookup table
|
||||
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
|
||||
$pos++;
|
||||
$params_lookup[$matches[3]] = ':' . $pos . '_named';
|
||||
$params_new[] = $params[($pos - 1)] ??
|
||||
throw new \RuntimeException(
|
||||
'Cannot lookup ' . ($pos - 1) . ' in params list',
|
||||
220
|
||||
);
|
||||
}
|
||||
// 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
|
||||
)
|
||||
);
|
||||
},
|
||||
$query
|
||||
);
|
||||
);
|
||||
},
|
||||
$converted_placeholders['original']['query']
|
||||
);
|
||||
break;
|
||||
}
|
||||
// return, old query is always set
|
||||
return [
|
||||
// original
|
||||
'original' => [
|
||||
'query' => $query,
|
||||
'params' => $params,
|
||||
],
|
||||
// type found, empty if nothing was done
|
||||
'type' => $type,
|
||||
// int: found, not found; -1: problem (set from false)
|
||||
'found' => (int)$found,
|
||||
'matches' => $matches_return,
|
||||
// old to new lookup check
|
||||
'params_lookup' => $params_lookup,
|
||||
// new
|
||||
'query' => $query_new ?? '',
|
||||
'params' => $params_new,
|
||||
];
|
||||
|
||||
95
src/DeprecatedHelper/Deprecated84.php
Normal file
95
src/DeprecatedHelper/Deprecated84.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AUTHOR: Clemens Schwaighofer
|
||||
* CREATED: 2025/1/17
|
||||
* DESCRIPTION:
|
||||
* Deprecated helper for fputcsv
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\DeprecatedHelper;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Deprecated84
|
||||
{
|
||||
/**
|
||||
* This is a wrapper for fputcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.fputcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param mixed $stream
|
||||
* @param array<mixed> $fields
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @param string $eol
|
||||
* @return int|false
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fputcsv(
|
||||
mixed $stream,
|
||||
array $fields,
|
||||
string $separator = ",",
|
||||
string $enclosure = '"',
|
||||
string $escape = '', // set to empty for future compatible
|
||||
string $eol = PHP_EOL
|
||||
): int | false {
|
||||
if (!is_resource($stream)) {
|
||||
throw new \InvalidArgumentException("fputcsv stream parameter must be a resrouce");
|
||||
}
|
||||
return fputcsv($stream, $fields, $separator, $enclosure, $escape, $eol);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper for fgetcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.fgetcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param mixed $stream
|
||||
* @param null|int<0,max> $length
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @return array<mixed>|false
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fgetcsv(
|
||||
mixed $stream,
|
||||
?int $length = null,
|
||||
string $separator = ',',
|
||||
string $enclosure = '"',
|
||||
string $escape = '' // set to empty for future compatible
|
||||
): array | false {
|
||||
if (!is_resource($stream)) {
|
||||
throw new \InvalidArgumentException("fgetcsv stream parameter must be a resrouce");
|
||||
}
|
||||
return fgetcsv($stream, $length, $separator, $enclosure, $escape);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper for str_getcsv to fix deprecated warning for $escape parameter
|
||||
* See: https://www.php.net/manual/en/function.str-getcsv.php
|
||||
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $separator
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @return array<mixed>
|
||||
*/
|
||||
// phpcs:disable PSR1.Methods.CamelCapsMethodName
|
||||
public static function str_getcsv(
|
||||
string $string,
|
||||
string $separator = ",",
|
||||
string $enclosure = '"',
|
||||
string $escape = '' // set to empty for future compatible
|
||||
): array {
|
||||
return str_getcsv($string, $separator, $enclosure, $escape);
|
||||
}
|
||||
// phpcs:enable PSR1.Methods.CamelCapsMethodName
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -50,7 +50,6 @@ class GetLocale
|
||||
$locale = defined('SITE_LOCALE') && !empty(SITE_LOCALE) ?
|
||||
SITE_LOCALE :
|
||||
// else parse from default, if not 'en'
|
||||
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
|
||||
(defined('DEFAULT_LOCALE') && !empty(DEFAULT_LOCALE) ?
|
||||
DEFAULT_LOCALE : 'en');
|
||||
}
|
||||
@@ -97,8 +96,7 @@ class GetLocale
|
||||
$encoding = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
|
||||
SITE_ENCODING :
|
||||
// or default encoding, if not 'UTF-8'
|
||||
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
|
||||
(defined('DEFAULT_ENCODING') && !empty(DEFAULT_ENCODING) ?
|
||||
(defined('DEFAULT_ENCODING') ?
|
||||
DEFAULT_ENCODING : 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ class Logging
|
||||
{
|
||||
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
|
||||
public const MIN_LOG_MAX_FILESIZE = 10 * 1024;
|
||||
/** @var string log file extension, not changeable */
|
||||
private const LOG_FILE_NAME_EXT = "log";
|
||||
/** @var string log file block separator, not changeable */
|
||||
private const LOG_FILE_BLOCK_SEPARATOR = '.';
|
||||
|
||||
// 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}} */
|
||||
@@ -104,8 +108,6 @@ class Logging
|
||||
private string $log_folder = '';
|
||||
/** @var string a alphanumeric name that has to be set as global definition */
|
||||
private string $log_file_id = '';
|
||||
/** @var string log file name extension */
|
||||
private string $log_file_name_ext = 'log';
|
||||
/** @var string log file name with folder, for actual writing */
|
||||
private string $log_file_name = '';
|
||||
/** @var int set in bytes */
|
||||
@@ -431,7 +433,7 @@ class Logging
|
||||
private function buildLogFileName(Level $level, string $group_id = ''): string
|
||||
{
|
||||
// init base file path
|
||||
$fn = $this->log_print_file . '.' . $this->log_file_name_ext;
|
||||
$fn = $this->log_print_file . '.' . self::LOG_FILE_NAME_EXT;
|
||||
// log ID prefix settings, if not valid, replace with empty
|
||||
if (!empty($this->log_file_id)) {
|
||||
$rpl_string = $this->log_file_id;
|
||||
@@ -440,14 +442,15 @@ class Logging
|
||||
}
|
||||
$fn = str_replace('{LOGID}', $rpl_string, $fn); // log id (like a log file prefix)
|
||||
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_level) ? '' :
|
||||
'_' . $level->getName();
|
||||
$rpl_string = $this->getLogFlag(Flag::per_level) ?
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . $level->getName() :
|
||||
'';
|
||||
$fn = str_replace('{LEVEL}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// write per level
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_group) ? '' :
|
||||
$rpl_string = $this->getLogFlag(Flag::per_group) ?
|
||||
// normalize level, replace all non alphanumeric characters with -
|
||||
'_' . (
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . (
|
||||
// if return is only - then set error string
|
||||
preg_match(
|
||||
"/^-+$/",
|
||||
@@ -455,25 +458,29 @@ class Logging
|
||||
) ?
|
||||
'INVALID-LEVEL-STRING' :
|
||||
$level_string
|
||||
);
|
||||
) :
|
||||
'';
|
||||
$fn = str_replace('{GROUP}', $rpl_string, $fn); // create output filename
|
||||
// set per class, but don't use get_class as we will only get self
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_class) ? '' : '_'
|
||||
// set sub class settings
|
||||
. str_replace('\\', '-', Support::getCallerTopLevelClass());
|
||||
$rpl_string = $this->getLogFlag(Flag::per_class) ?
|
||||
// set sub class settings
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . str_replace('\\', '-', Support::getCallerTopLevelClass()) :
|
||||
'';
|
||||
$fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// if request to write to one file
|
||||
$rpl_string = !$this->getLogFlag(Flag::per_page) ?
|
||||
'' :
|
||||
'_' . System::getPageName(System::NO_EXTENSION);
|
||||
$rpl_string = $this->getLogFlag(Flag::per_page) ?
|
||||
self::LOG_FILE_BLOCK_SEPARATOR . System::getPageName(System::NO_EXTENSION) :
|
||||
'';
|
||||
$fn = str_replace('{PAGENAME}', $rpl_string, $fn); // create output filename
|
||||
|
||||
// if run id, we auto add ymd, so we ignore the log file date
|
||||
if ($this->getLogFlag(Flag::per_run)) {
|
||||
$rpl_string = '_' . $this->getLogUniqueId(); // add 8 char unique string
|
||||
// add 8 char unique string and date block with time
|
||||
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogUniqueId();
|
||||
} elseif ($this->getLogFlag(Flag::per_date)) {
|
||||
$rpl_string = '_' . $this->getLogDate(); // add date to file
|
||||
// add date to file
|
||||
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogDate();
|
||||
} else {
|
||||
$rpl_string = '';
|
||||
}
|
||||
@@ -739,7 +746,10 @@ class Logging
|
||||
{
|
||||
if (empty($this->log_file_unique_id) || $override == true) {
|
||||
$this->log_file_unique_id =
|
||||
date('Y-m-d_His') . '_U_'
|
||||
date('Y-m-d_His')
|
||||
. self::LOG_FILE_BLOCK_SEPARATOR
|
||||
. 'U_'
|
||||
// this doesn't have to be unique for everything, just for this logging purpose
|
||||
. substr(hash(
|
||||
'sha1',
|
||||
random_bytes(63)
|
||||
|
||||
@@ -135,30 +135,6 @@ class EditUsers implements Interface\TableArraysInterface
|
||||
'min_edit_acl' => '100',
|
||||
'min_show_acl' => '100',
|
||||
],
|
||||
'debug' => [
|
||||
'value' => $_POST['debug'] ?? '',
|
||||
'output_name' => 'Debug',
|
||||
'type' => 'binary',
|
||||
'int' => 1,
|
||||
'element_list' => [
|
||||
'1' => 'Yes',
|
||||
'0' => 'No'
|
||||
],
|
||||
'min_edit_acl' => '100',
|
||||
'min_show_acl' => '100',
|
||||
],
|
||||
'db_debug' => [
|
||||
'value' => $_POST['db_debug'] ?? '',
|
||||
'output_name' => 'DB Debug',
|
||||
'type' => 'binary',
|
||||
'int' => 1,
|
||||
'element_list' => [
|
||||
'1' => 'Yes',
|
||||
'0' => 'No'
|
||||
],
|
||||
'min_edit_acl' => '100',
|
||||
'min_show_acl' => '100',
|
||||
],
|
||||
'email' => [
|
||||
'value' => $_POST['email'] ?? '',
|
||||
'output_name' => 'E-Mail',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* sets a form token in the _SESSION variable
|
||||
* session must be started for this to work
|
||||
* session must be started and running for this to work
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
@@ -418,9 +418,7 @@ class ProgressBar
|
||||
// if this is percent, we ignore anything, it is auto positioned
|
||||
if ($this->label[$name]['type'] != 'percent') {
|
||||
foreach (['top', 'left', 'width', 'height'] as $pos_name) {
|
||||
if ($$pos_name !== false) {
|
||||
$this->label[$name][$pos_name] = intval($$pos_name);
|
||||
}
|
||||
$this->label[$name][$pos_name] = intval($$pos_name);
|
||||
}
|
||||
|
||||
if ($align != '') {
|
||||
|
||||
408
src/Security/AsymmetricAnonymousEncryption.php
Normal file
408
src/Security/AsymmetricAnonymousEncryption.php
Normal file
@@ -0,0 +1,408 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* very simple asymmetric encryption
|
||||
* Better use:
|
||||
* https://paragonie.com/project/halite
|
||||
* https://github.com/paragonie/halite
|
||||
*
|
||||
* current code is just to encrypt and decrypt
|
||||
*
|
||||
* must use a valid encryption key created with
|
||||
* Secruty\CreateKey class
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Security;
|
||||
|
||||
use CoreLibs\Security\CreateKey;
|
||||
use SodiumException;
|
||||
|
||||
class AsymmetricAnonymousEncryption
|
||||
{
|
||||
/** @var AsymmetricAnonymousEncryption self instance */
|
||||
private static AsymmetricAnonymousEncryption $instance;
|
||||
|
||||
/** @var ?string key pair which holds secret and public key, needed for encryption */
|
||||
private ?string $key_pair = null;
|
||||
/** @var ?string public key, needed for decryption
|
||||
* if not set but key_pair set, this will be extracted from key pair */
|
||||
private ?string $public_key = null;
|
||||
|
||||
/**
|
||||
* init class
|
||||
* if key not passed, key must be set with createKey
|
||||
*
|
||||
* @param string|null $key_pair
|
||||
* @param string|null $public_key
|
||||
*/
|
||||
public function __construct(
|
||||
#[\SensitiveParameter]
|
||||
string|null $key_pair = null,
|
||||
string|null $public_key = null
|
||||
) {
|
||||
if ($public_key !== null) {
|
||||
$this->setPublicKey($public_key);
|
||||
}
|
||||
if ($key_pair !== null) {
|
||||
$this->setKeyPair($key_pair);
|
||||
if (empty($public_key)) {
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$this->setPublicKey($public_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton self object.
|
||||
* For function wrapper use
|
||||
*
|
||||
* @param string|null $key_pair
|
||||
* @param string|null $public_key
|
||||
* @return AsymmetricAnonymousEncryption object
|
||||
*/
|
||||
public static function getInstance(
|
||||
#[\SensitiveParameter]
|
||||
string|null $key_pair = null,
|
||||
string|null $public_key = null
|
||||
): self {
|
||||
// new if no instsance or key is different
|
||||
if (
|
||||
empty(self::$instance) ||
|
||||
self::$instance->key_pair != $key_pair ||
|
||||
self::$instance->public_key != $public_key
|
||||
) {
|
||||
self::$instance = new self($key_pair, $public_key);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (empty($this->key_pair)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// would set it to null, but we we do not want to make key null
|
||||
sodium_memzero($this->key_pair);
|
||||
return;
|
||||
} catch (SodiumException) {
|
||||
// empty catch
|
||||
}
|
||||
if (is_null($this->key_pair)) {
|
||||
return;
|
||||
}
|
||||
$zero = str_repeat("\0", mb_strlen($this->key_pair, '8bit'));
|
||||
$this->key_pair = $this->key_pair ^ (
|
||||
$zero ^ $this->key_pair
|
||||
);
|
||||
unset($zero);
|
||||
unset($this->key_pair); /** @phan-suppress-current-line PhanTypeObjectUnsetDeclaredProperty */
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* MARK: PRIVATE
|
||||
* *************************************************************************/
|
||||
|
||||
/**
|
||||
* Create the internal key pair in binary
|
||||
*
|
||||
* @param ?string $key_pair
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException key pair empty
|
||||
* @throws \UnexpectedValueException invalid hex key pair
|
||||
* @throws \RangeException key pair not correct size
|
||||
*/
|
||||
private function createKeyPair(
|
||||
#[\SensitiveParameter]
|
||||
?string $key_pair
|
||||
): string {
|
||||
if (empty($key_pair)) {
|
||||
throw new \UnexpectedValueException('Key pair cannot be empty');
|
||||
}
|
||||
try {
|
||||
$key_pair = CreateKey::hex2bin($key_pair);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($key_pair);
|
||||
throw new \UnexpectedValueException('Invalid hex key pair: ' . $e->getMessage());
|
||||
}
|
||||
if (mb_strlen($key_pair, '8bit') !== SODIUM_CRYPTO_BOX_KEYPAIRBYTES) {
|
||||
sodium_memzero($key_pair);
|
||||
throw new \RangeException(
|
||||
'Key pair is not the correct size (must be '
|
||||
. SODIUM_CRYPTO_BOX_KEYPAIRBYTES . ' bytes long).'
|
||||
);
|
||||
}
|
||||
return $key_pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the internal public key in binary
|
||||
*
|
||||
* @param ?string $public_key
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException public key empty
|
||||
* @throws \UnexpectedValueException invalid hex key
|
||||
* @throws \RangeException invalid key length
|
||||
*/
|
||||
private function createPublicKey(?string $public_key): string
|
||||
{
|
||||
if (empty($public_key)) {
|
||||
throw new \UnexpectedValueException('Public key cannot be empty');
|
||||
}
|
||||
try {
|
||||
$public_key = CreateKey::hex2bin($public_key);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($public_key);
|
||||
throw new \UnexpectedValueException('Invalid hex public key: ' . $e->getMessage());
|
||||
}
|
||||
if (mb_strlen($public_key, '8bit') !== SODIUM_CRYPTO_BOX_PUBLICKEYBYTES) {
|
||||
sodium_memzero($public_key);
|
||||
throw new \RangeException(
|
||||
'Public key is not the correct size (must be '
|
||||
. SODIUM_CRYPTO_BOX_PUBLICKEYBYTES . ' bytes long).'
|
||||
);
|
||||
}
|
||||
return $public_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypt a message asymmetric with a bpulic key
|
||||
*
|
||||
* @param string $message
|
||||
* @param ?string $public_key
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException create encryption failed
|
||||
* @throws \UnexpectedValueException convert to base64 failed
|
||||
*/
|
||||
private function asymmetricEncryption(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
?string $public_key
|
||||
): string {
|
||||
$public_key = $this->createPublicKey($public_key);
|
||||
try {
|
||||
$encrypted = sodium_crypto_box_seal($message, $public_key);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($message);
|
||||
try {
|
||||
$result = sodium_bin2base64($encrypted, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($encrypted);
|
||||
throw new \UnexpectedValueException("bin2base64 failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($encrypted);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt a message that is asymmetric encrypted with a key pair
|
||||
*
|
||||
* @param string $message
|
||||
* @param ?string $key_pair
|
||||
* @return string
|
||||
* @throws \UnexpectedValueException message string empty
|
||||
* @throws \UnexpectedValueException base64 decoding failed
|
||||
* @throws \UnexpectedValueException decryption failed
|
||||
* @throws \UnexpectedValueException could not decrypt message
|
||||
*/
|
||||
private function asymmetricDecryption(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
?string $key_pair
|
||||
): string {
|
||||
if (empty($message)) {
|
||||
throw new \UnexpectedValueException('Encrypted string cannot be empty');
|
||||
}
|
||||
$key_pair = $this->createKeyPair($key_pair);
|
||||
try {
|
||||
$result = sodium_base642bin($message, SODIUM_BASE64_VARIANT_ORIGINAL);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
sodium_memzero($key_pair);
|
||||
throw new \UnexpectedValueException("base642bin failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($message);
|
||||
$plaintext = false;
|
||||
try {
|
||||
$plaintext = sodium_crypto_box_seal_open($result, $key_pair);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
sodium_memzero($key_pair);
|
||||
sodium_memzero($result);
|
||||
throw new \UnexpectedValueException("Decrypting message failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($key_pair);
|
||||
sodium_memzero($result);
|
||||
if (!is_string($plaintext)) {
|
||||
throw new \UnexpectedValueException('Invalid key pair');
|
||||
}
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* MARK: PUBLIC
|
||||
* *************************************************************************/
|
||||
|
||||
/**
|
||||
* sets the private key for encryption
|
||||
*
|
||||
* @param string $key_pair Key pair in hex
|
||||
* @return AsymmetricAnonymousEncryption
|
||||
* @throws \UnexpectedValueException key pair empty
|
||||
*/
|
||||
public function setKeyPair(
|
||||
#[\SensitiveParameter]
|
||||
string $key_pair
|
||||
): AsymmetricAnonymousEncryption {
|
||||
if (empty($key_pair)) {
|
||||
throw new \UnexpectedValueException('Key pair cannot be empty');
|
||||
}
|
||||
// check if valid;
|
||||
$this->createKeyPair($key_pair);
|
||||
// set new key pair
|
||||
$this->key_pair = $key_pair;
|
||||
sodium_memzero($key_pair);
|
||||
// set public key if not set
|
||||
if (empty($this->public_key)) {
|
||||
$this->public_key = CreateKey::getPublicKey($this->key_pair);
|
||||
// check if valid
|
||||
$this->createPublicKey($this->public_key);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if set key pair matches given one
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @return bool
|
||||
*/
|
||||
public function compareKeyPair(
|
||||
#[\SensitiveParameter]
|
||||
string $key_pair
|
||||
): bool {
|
||||
return $this->key_pair === $key_pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the current set key pair, null if not set
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getKeyPair(): ?string
|
||||
{
|
||||
return $this->key_pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the public key for decryption
|
||||
* if only key pair exists Security\Create::getPublicKey() can be used to
|
||||
* extract the public key from the key pair
|
||||
*
|
||||
* @param string $public_key Public Key in hex
|
||||
* @return AsymmetricAnonymousEncryption
|
||||
* @throws \UnexpectedValueException public key empty
|
||||
*/
|
||||
public function setPublicKey(string $public_key): AsymmetricAnonymousEncryption
|
||||
{
|
||||
if (empty($public_key)) {
|
||||
throw new \UnexpectedValueException('Public key cannot be empty');
|
||||
}
|
||||
// check if valid
|
||||
$this->createPublicKey($public_key);
|
||||
$this->public_key = $public_key;
|
||||
sodium_memzero($public_key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the set public key matches the given one
|
||||
*
|
||||
* @param string $public_key
|
||||
* @return bool
|
||||
*/
|
||||
public function comparePublicKey(string $public_key): bool
|
||||
{
|
||||
return $this->public_key === $public_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the current set public key, null if not set
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPublicKey(): ?string
|
||||
{
|
||||
return $this->public_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a message with a public key
|
||||
* static version
|
||||
*
|
||||
* @param string $message Message to encrypt
|
||||
* @param string $public_key Public key in hex to encrypt message with
|
||||
* @return string Encrypted message as hex string
|
||||
*/
|
||||
public static function encryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
string $public_key
|
||||
): string {
|
||||
return self::getInstance()->asymmetricEncryption($message, $public_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a message
|
||||
*
|
||||
* @param string $message Message to ecnrypt
|
||||
* @return string Encrypted message as hex string
|
||||
*/
|
||||
public function encrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $message
|
||||
): string {
|
||||
return $this->asymmetricEncryption($message, $this->public_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt a message with a key pair
|
||||
* static version
|
||||
*
|
||||
* @param string $message Message to decrypt in hex
|
||||
* @param string $key_pair Key pair in hex to decrypt the message with
|
||||
* @return string Decrypted message
|
||||
*/
|
||||
public static function decryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
string $key_pair
|
||||
): string {
|
||||
return self::getInstance()->asymmetricDecryption($message, $key_pair);
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt a message
|
||||
*
|
||||
* @param string $message Message to decrypt in hex
|
||||
* @return string Decrypted message
|
||||
*/
|
||||
public function decrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $message
|
||||
): string {
|
||||
return $this->asymmetricDecryption($message, $this->key_pair);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -35,14 +35,39 @@ class CreateKey
|
||||
return random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a sodium cyptobox keypair as hex string
|
||||
*
|
||||
* @return string hex string for the keypair
|
||||
*/
|
||||
public static function createKeyPair(): string
|
||||
{
|
||||
return self::bin2hex(sodium_crypto_box_keypair());
|
||||
}
|
||||
|
||||
/**
|
||||
* extracts the public key and returns it as hex string from the hex keypari
|
||||
*
|
||||
* @param string $hex_keypair hex encoded keypair
|
||||
* @return string hex encoded public key
|
||||
*/
|
||||
public static function getPublicKey(
|
||||
#[\SensitiveParameter]
|
||||
string $hex_keypair
|
||||
): string {
|
||||
return self::bin2hex(sodium_crypto_box_publickey(self::hex2bin($hex_keypair)));
|
||||
}
|
||||
|
||||
/**
|
||||
* convert binary key to hex string
|
||||
*
|
||||
* @param string $hex_key Convert binary key string to hex
|
||||
* @return string
|
||||
*/
|
||||
public static function bin2hex(string $hex_key): string
|
||||
{
|
||||
public static function bin2hex(
|
||||
#[\SensitiveParameter]
|
||||
string $hex_key
|
||||
): string {
|
||||
return sodium_bin2hex($hex_key);
|
||||
}
|
||||
|
||||
@@ -52,8 +77,10 @@ class CreateKey
|
||||
* @param string $string_key Convery hex key string to binary
|
||||
* @return string
|
||||
*/
|
||||
public static function hex2bin(string $string_key): string
|
||||
{
|
||||
public static function hex2bin(
|
||||
#[\SensitiveParameter]
|
||||
string $string_key
|
||||
): string {
|
||||
return sodium_hex2bin($string_key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ class Password
|
||||
* @param string $password password
|
||||
* @return string hashed password
|
||||
*/
|
||||
public static function passwordSet(string $password): string
|
||||
{
|
||||
public static function passwordSet(
|
||||
#[\SensitiveParameter]
|
||||
string $password
|
||||
): string {
|
||||
// always use the PHP default for the password
|
||||
// password options ca be set in the password init,
|
||||
// but should be kept as default
|
||||
@@ -31,8 +33,11 @@ class Password
|
||||
* @param string $hash password hash
|
||||
* @return bool true or false
|
||||
*/
|
||||
public static function passwordVerify(string $password, string $hash): bool
|
||||
{
|
||||
public static function passwordVerify(
|
||||
#[\SensitiveParameter]
|
||||
string $password,
|
||||
string $hash
|
||||
): bool {
|
||||
if (password_verify($password, $hash)) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -24,19 +24,19 @@ class SymmetricEncryption
|
||||
/** @var SymmetricEncryption self instance */
|
||||
private static SymmetricEncryption $instance;
|
||||
|
||||
/** @var string bin hex key */
|
||||
private string $key = '';
|
||||
/** @var ?string bin hex key */
|
||||
private ?string $key = null;
|
||||
|
||||
/**
|
||||
* init class
|
||||
* if key not passed, key must be set with createKey
|
||||
*
|
||||
* @param string|null|null $key
|
||||
* @param string|null $key encryption key
|
||||
*/
|
||||
public function __construct(
|
||||
string|null $key = null
|
||||
?string $key = null
|
||||
) {
|
||||
if ($key != null) {
|
||||
if ($key !== null) {
|
||||
$this->setKey($key);
|
||||
}
|
||||
}
|
||||
@@ -45,16 +45,49 @@ class SymmetricEncryption
|
||||
* Returns the singleton self object.
|
||||
* For function wrapper use
|
||||
*
|
||||
* @param string|null $key encryption key
|
||||
* @return SymmetricEncryption object
|
||||
*/
|
||||
public static function getInstance(string|null $key = null): self
|
||||
public static function getInstance(?string $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __deconstruct()
|
||||
{
|
||||
if (empty($this->key)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// would set it to null, but we we do not want to make key null
|
||||
sodium_memzero($this->key);
|
||||
return;
|
||||
} catch (SodiumException) {
|
||||
// empty catch
|
||||
}
|
||||
if (is_null($this->key)) {
|
||||
return;
|
||||
}
|
||||
$zero = str_repeat("\0", mb_strlen($this->key, '8bit'));
|
||||
$this->key = $this->key ^ (
|
||||
$zero ^ $this->key
|
||||
);
|
||||
unset($zero);
|
||||
unset($this->key); /** @phan-suppress-current-line PhanTypeObjectUnsetDeclaredProperty */
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* MARK: PRIVATE
|
||||
* *************************************************************************/
|
||||
@@ -62,11 +95,19 @@ class SymmetricEncryption
|
||||
/**
|
||||
* create key and check validity
|
||||
*
|
||||
* @param string $key The key from which the binary key will be created
|
||||
* @return string Binary key string
|
||||
* @param ?string $key The key from which the binary key will be created
|
||||
* @return string Binary key string
|
||||
* @throws \UnexpectedValueException empty key
|
||||
* @throws \UnexpectedValueException invalid hex key
|
||||
* @throws \RangeException invalid length
|
||||
*/
|
||||
private function createKey(string $key): string
|
||||
{
|
||||
private function createKey(
|
||||
#[\SensitiveParameter]
|
||||
?string $key
|
||||
): string {
|
||||
if (empty($key)) {
|
||||
throw new \UnexpectedValueException('Key cannot be empty');
|
||||
}
|
||||
try {
|
||||
$key = CreateKey::hex2bin($key);
|
||||
} catch (SodiumException $e) {
|
||||
@@ -87,36 +128,42 @@ class SymmetricEncryption
|
||||
* @param string $encrypted Text to decrypt
|
||||
* @param ?string $key Mandatory encryption key, will throw exception if empty
|
||||
* @return string Plain text
|
||||
* @throws \RangeException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException key cannot be empty
|
||||
* @throws \UnexpectedValueException decipher message failed
|
||||
* @throws \UnexpectedValueException invalid key
|
||||
*/
|
||||
private function decryptData(string $encrypted, ?string $key): string
|
||||
{
|
||||
if (empty($key)) {
|
||||
throw new \UnexpectedValueException('Key not set');
|
||||
private function decryptData(
|
||||
#[\SensitiveParameter]
|
||||
string $encrypted,
|
||||
#[\SensitiveParameter]
|
||||
?string $key
|
||||
): string {
|
||||
if (empty($encrypted)) {
|
||||
throw new \UnexpectedValueException('Encrypted string cannot be empty');
|
||||
}
|
||||
$key = $this->createKey($key);
|
||||
$decoded = base64_decode($encrypted);
|
||||
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
|
||||
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
|
||||
|
||||
$plain = false;
|
||||
$plaintext = false;
|
||||
try {
|
||||
$plain = sodium_crypto_secretbox_open(
|
||||
$plaintext = sodium_crypto_secretbox_open(
|
||||
$ciphertext,
|
||||
$nonce,
|
||||
$key
|
||||
);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($ciphertext);
|
||||
sodium_memzero($key);
|
||||
throw new \UnexpectedValueException('Decipher message failed: ' . $e->getMessage());
|
||||
}
|
||||
if (!is_string($plain)) {
|
||||
throw new \UnexpectedValueException('Invalid Key');
|
||||
}
|
||||
sodium_memzero($ciphertext);
|
||||
sodium_memzero($key);
|
||||
return $plain;
|
||||
if (!is_string($plaintext)) {
|
||||
throw new \UnexpectedValueException('Invalid Key');
|
||||
}
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,15 +171,15 @@ class SymmetricEncryption
|
||||
*
|
||||
* @param string $message Message to encrypt
|
||||
* @param ?string $key Mandatory encryption key, will throw exception if empty
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
* @return string Ciphered text
|
||||
* @throws \UnexpectedValueException create message failed
|
||||
*/
|
||||
private function encryptData(string $message, ?string $key): string
|
||||
{
|
||||
if (empty($this->key) || $key === null) {
|
||||
throw new \UnexpectedValueException('Key not set');
|
||||
}
|
||||
private function encryptData(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
?string $key
|
||||
): string {
|
||||
$key = $this->createKey($key);
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
try {
|
||||
@@ -145,6 +192,8 @@ class SymmetricEncryption
|
||||
)
|
||||
);
|
||||
} catch (SodiumException $e) {
|
||||
sodium_memzero($message);
|
||||
sodium_memzero($key);
|
||||
throw new \UnexpectedValueException("Create encrypted message failed: " . $e->getMessage());
|
||||
}
|
||||
sodium_memzero($message);
|
||||
@@ -156,19 +205,49 @@ class SymmetricEncryption
|
||||
* MARK: PUBLIC
|
||||
* *************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* set a new key for encryption
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
* @return SymmetricEncryption
|
||||
* @throws \UnexpectedValueException key cannot be empty
|
||||
*/
|
||||
public function setKey(string $key)
|
||||
{
|
||||
public function setKey(
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): SymmetricEncryption {
|
||||
if (empty($key)) {
|
||||
throw new \UnexpectedValueException('Key cannot be empty');
|
||||
}
|
||||
// check that this is a valid key
|
||||
$this->createKey($key);
|
||||
// set key
|
||||
$this->key = $key;
|
||||
sodium_memzero($key);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if set key is equal to parameter key
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function compareKey(
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): bool {
|
||||
return $key === $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the current set key, null if not set
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getKey(): ?string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,13 +257,13 @@ class SymmetricEncryption
|
||||
* @param string $encrypted Message encrypted with safeEncrypt()
|
||||
* @param string $key Encryption key (as hex string)
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public static function decryptKey(string $encrypted, string $key): string
|
||||
{
|
||||
public static function decryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $encrypted,
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): string {
|
||||
return self::getInstance()->decryptData($encrypted, $key);
|
||||
}
|
||||
|
||||
@@ -193,12 +272,11 @@ class SymmetricEncryption
|
||||
*
|
||||
* @param string $encrypted Message encrypted with safeEncrypt()
|
||||
* @return string
|
||||
* @throws \RangeException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function decrypt(string $encrypted): string
|
||||
{
|
||||
public function decrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $encrypted
|
||||
): string {
|
||||
return $this->decryptData($encrypted, $this->key);
|
||||
}
|
||||
|
||||
@@ -209,11 +287,13 @@ class SymmetricEncryption
|
||||
* @param string $message Message to encrypt
|
||||
* @param string $key Encryption key (as hex string)
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
*/
|
||||
public static function encryptKey(string $message, string $key): string
|
||||
{
|
||||
public static function encryptKey(
|
||||
#[\SensitiveParameter]
|
||||
string $message,
|
||||
#[\SensitiveParameter]
|
||||
string $key
|
||||
): string {
|
||||
return self::getInstance()->encryptData($message, $key);
|
||||
}
|
||||
|
||||
@@ -222,11 +302,11 @@ class SymmetricEncryption
|
||||
*
|
||||
* @param string $message Message to encrypt
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
* @throws \RangeException
|
||||
*/
|
||||
public function encrypt(string $message): string
|
||||
{
|
||||
public function encrypt(
|
||||
#[\SensitiveParameter]
|
||||
string $message
|
||||
): string {
|
||||
return $this->encryptData($message, $this->key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Template;
|
||||
|
||||
// leading slash if this is in lib\Smarty
|
||||
class SmartyExtend extends \Smarty
|
||||
class SmartyExtend extends \Smarty\Smarty
|
||||
{
|
||||
// internal translation engine
|
||||
/** @var \CoreLibs\Language\L10n */
|
||||
/** @var \CoreLibs\Language\L10n language class */
|
||||
public \CoreLibs\Language\L10n $l10n;
|
||||
/** @var \CoreLibs\Logging\Logging $log logging class */
|
||||
public \CoreLibs\Logging\Logging $log;
|
||||
|
||||
// lang & encoding
|
||||
/** @var string */
|
||||
@@ -157,14 +158,18 @@ class SmartyExtend extends \Smarty
|
||||
* calls L10 for pass on internaly in smarty
|
||||
* also registers the getvar caller plugin
|
||||
*
|
||||
* @param \CoreLibs\Language\L10n $l10n l10n language class
|
||||
* @param string|null $cache_id
|
||||
* @param string|null $compile_id
|
||||
* @param \CoreLibs\Language\L10n $l10n l10n language class
|
||||
* @param \CoreLibs\Logging\Logging $log Logger class
|
||||
* @param string|null $cache_id [default=null]
|
||||
* @param string|null $compile_id [default=null]
|
||||
* @param array<string,mixed> $options [default=[]]
|
||||
*/
|
||||
public function __construct(
|
||||
\CoreLibs\Language\L10n $l10n,
|
||||
\CoreLibs\Logging\Logging $log,
|
||||
?string $cache_id = null,
|
||||
?string $compile_id = null
|
||||
?string $compile_id = null,
|
||||
array $options = []
|
||||
) {
|
||||
// trigger deprecation
|
||||
if (
|
||||
@@ -177,14 +182,33 @@ class SmartyExtend extends \Smarty
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
// set variables (to be deprecated)
|
||||
$cache_id = $cache_id ??
|
||||
(defined('CACHE_ID') ? CACHE_ID : '');
|
||||
$compile_id = $compile_id ??
|
||||
(defined('COMPILE_ID') ? COMPILE_ID : '');
|
||||
// set variables from global constants (deprecated)
|
||||
if ($cache_id === null && defined('CACHE_ID')) {
|
||||
trigger_error(
|
||||
'SmartyExtended: No cache_id set and CACHE_ID constant set, this is deprecated',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$cache_id = CACHE_ID;
|
||||
}
|
||||
if ($compile_id === null && defined('COMPILE_ID')) {
|
||||
trigger_error(
|
||||
'SmartyExtended: No compile_id set and COMPILE_ID constant set, this is deprecated',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$compile_id = COMPILE_ID;
|
||||
}
|
||||
if (empty($cache_id)) {
|
||||
throw new \BadMethodCallException('cache_id parameter is not set');
|
||||
}
|
||||
if (empty($compile_id)) {
|
||||
throw new \BadMethodCallException('compile_id parameter is not set');
|
||||
}
|
||||
|
||||
// call basic smarty
|
||||
// or Smarty::__construct();
|
||||
parent::__construct();
|
||||
|
||||
$this->log = $log;
|
||||
|
||||
// init lang
|
||||
$this->l10n = $l10n;
|
||||
// parse and read, legacy stuff
|
||||
@@ -194,7 +218,6 @@ class SmartyExtend extends \Smarty
|
||||
$this->lang_short = $locale['lang_short'];
|
||||
$this->domain = $locale['domain'];
|
||||
$this->lang_dir = $locale['path'];
|
||||
|
||||
// opt load functions so we can use legacy init for smarty run perhaps
|
||||
\CoreLibs\Language\L10n::loadFunctions();
|
||||
_setlocale(LC_MESSAGES, $locale['locale']);
|
||||
@@ -203,7 +226,6 @@ class SmartyExtend extends \Smarty
|
||||
_bind_textdomain_codeset($this->domain, $this->encoding);
|
||||
|
||||
// register smarty variable
|
||||
// $this->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']);
|
||||
$this->registerPlugin(self::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']);
|
||||
|
||||
$this->page_name = \CoreLibs\Get\System::getPageName();
|
||||
@@ -211,6 +233,77 @@ class SmartyExtend extends \Smarty
|
||||
// set internal settings
|
||||
$this->CACHE_ID = $cache_id;
|
||||
$this->COMPILE_ID = $compile_id;
|
||||
// set options
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* set options
|
||||
*
|
||||
* @param array<string,mixed> $options
|
||||
* @return void
|
||||
*/
|
||||
private function setOptions(array $options): void
|
||||
{
|
||||
// set escape html if option is set
|
||||
if (!empty($options['escape_html'])) {
|
||||
$this->setEscapeHtml(true);
|
||||
}
|
||||
// load plugins
|
||||
// plugin array:
|
||||
// 'file': string, path to plugin content to load
|
||||
// 'type': a valid smarty type see Smarty PLUGIN_ constants for correct names
|
||||
// 'tag': the smarty tag
|
||||
// 'callback': the function to call in 'file'
|
||||
if (!empty($options['plugins'])) {
|
||||
foreach ($options['plugins'] as $plugin) {
|
||||
// file is readable
|
||||
if (
|
||||
empty($plugin['file']) ||
|
||||
!is_file($plugin['file']) ||
|
||||
!is_readable($plugin['file'])
|
||||
) {
|
||||
$this->log->warning('SmartyExtended plugin load failed, file not accessable', [
|
||||
'plugin' => $plugin,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
// tag is alphanumeric
|
||||
if (!preg_match("/^\w+$/", $plugin['tag'] ?? '')) {
|
||||
$this->log->warning('SmartyExtended plugin load failed, invalid tag', [
|
||||
'plugin' => $plugin,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
// callback is alphanumeric
|
||||
if (!preg_match("/^\w+$/", $plugin['callback'] ?? '')) {
|
||||
$this->log->warning('SmartyExtended plugin load failed, invalid callback', [
|
||||
'plugin' => $plugin,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
/** @phan-suppress-next-line PhanNoopNew */
|
||||
new \ReflectionClassConstant($this, $plugin['type']);
|
||||
} catch (\ReflectionException $e) {
|
||||
$this->log->error('SmartyExtended plugin load failed, type is not valid', [
|
||||
'message' => $e->getMessage(),
|
||||
'plugin' => $plugin,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
require $plugin['file'];
|
||||
$this->registerPlugin($plugin['type'], $plugin['tag'], $plugin['callback']);
|
||||
} catch (\Smarty\Exception $e) {
|
||||
$this->log->error('SmartyExtended plugin load failed with exception', [
|
||||
'message' => $e->getMessage(),
|
||||
'plugin' => $plugin,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -183,8 +183,9 @@ list($HOST_NAME) = array_pad(explode(':', $_SERVER['HTTP_HOST'], 2), 2, null);
|
||||
define('HOST_NAME', $HOST_NAME);
|
||||
// BAIL ON MISSING MASTER SITE CONFIG
|
||||
if (!isset($SITE_CONFIG[HOST_NAME]['location'])) {
|
||||
echo 'Missing SITE_CONFIG entry for: "' . HOST_NAME . '". Contact Administrator';
|
||||
exit;
|
||||
throw new \InvalidArgumentException(
|
||||
'Missing SITE_CONFIG entry for: "' . HOST_NAME . '". Contact Administrator'
|
||||
);
|
||||
}
|
||||
// BAIL ON MISSING DB CONFIG:
|
||||
// we have either no db selction for this host but have db config entries
|
||||
@@ -200,8 +201,9 @@ if (
|
||||
empty($DB_CONFIG[$SITE_CONFIG[HOST_NAME]['db_host']]))
|
||||
)
|
||||
) {
|
||||
echo 'No matching DB config found for: "' . HOST_NAME . '". Contact Administrator';
|
||||
exit;
|
||||
throw new \InvalidArgumentException(
|
||||
'No matching DB config found for: "' . HOST_NAME . '". Contact Administrator'
|
||||
);
|
||||
}
|
||||
// set SSL on
|
||||
$is_secure = false;
|
||||
|
||||
@@ -48,7 +48,7 @@ header("Content-Type: application/json; charset=UTF-8");
|
||||
if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) {
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// if server request type is get set file_get to null -> no body
|
||||
@@ -57,7 +57,7 @@ if ($_SERVER['REQUEST_METHOD'] == "GET") {
|
||||
} elseif (($file_get = file_get_contents('php://input')) === false) {
|
||||
header("HTTP/1.1 404 Not Found");
|
||||
print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}');
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
print buildContent($http_headers, $file_get);
|
||||
|
||||
@@ -22,8 +22,12 @@ Not yet covered tests:
|
||||
*/
|
||||
final class CoreLibsACLLoginTest extends TestCase
|
||||
{
|
||||
private static $db;
|
||||
private static $log;
|
||||
private static \CoreLibs\DB\IO $db;
|
||||
private static \CoreLibs\Logging\Logging $log;
|
||||
|
||||
private static string $edit_access_cuid;
|
||||
private static string $edit_user_cuid;
|
||||
private static string $edit_user_cuuid;
|
||||
|
||||
/**
|
||||
* start DB conneciton, setup DB, etc
|
||||
@@ -108,21 +112,46 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
self::$db->dbSetMaxQueryCall(-1);
|
||||
// insert additional content for testing (locked user, etc)
|
||||
$queries = [
|
||||
"INSERT INTO edit_access_data "
|
||||
. "(edit_access_id, name, value, enabled) VALUES "
|
||||
. "((SELECT edit_access_id FROM edit_access WHERE uid = 'AdminAccess'), "
|
||||
. "'test', 'value', 1)"
|
||||
<<<SQL
|
||||
INSERT INTO edit_access_data (
|
||||
edit_access_id, name, value, enabled
|
||||
) VALUES (
|
||||
(SELECT edit_access_id FROM edit_access WHERE uid = 'AdminAccess'),
|
||||
'test', 'value', 1
|
||||
)
|
||||
SQL
|
||||
];
|
||||
foreach ($queries as $query) {
|
||||
self::$db->dbExec($query);
|
||||
}
|
||||
// read edit access cuid, edit user cuid and edit user cuuid
|
||||
$row = self::$db->dbReturnRowParams(
|
||||
"SELECT cuid FROM edit_access WHERE uid = $1",
|
||||
["AdminAccess"]
|
||||
);
|
||||
self::$edit_access_cuid = $row['cuid'] ?? '';
|
||||
if (empty(self::$edit_access_cuid)) {
|
||||
self::markTestIncomplete(
|
||||
'Cannot read edit access cuid for "AdminAccess".'
|
||||
);
|
||||
}
|
||||
$row = self::$db->dbReturnRowParams(
|
||||
"SELECT cuid, cuuid FROM edit_user WHERE username = $1",
|
||||
["admin"]
|
||||
);
|
||||
self::$edit_user_cuid = $row['cuid'] ?? '';
|
||||
self::$edit_user_cuuid = $row['cuuid'] ?? '';
|
||||
if (empty(self::$edit_user_cuid) || empty(self::$edit_user_cuuid)) {
|
||||
self::markTestIncomplete(
|
||||
'Cannot read edit user cuid or cuuid for "admin".'
|
||||
);
|
||||
}
|
||||
|
||||
// define mandatory constant
|
||||
// must set
|
||||
// TARGET
|
||||
define('TARGET', 'test');
|
||||
// LOGIN DB SCHEMA
|
||||
// define('LOGIN_DB_SCHEMA', '');
|
||||
|
||||
// SHOULD SET
|
||||
// DEFAULT_ACL_LEVEL (d80)
|
||||
@@ -235,22 +264,25 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'ajax_post_action' => 'login',
|
||||
],
|
||||
],
|
||||
'load, session euid set only, php error' => [
|
||||
'load, session eucuuid set only, php error' => [
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
'EUID' => 1,
|
||||
'LOGIN_EUID' => 1,
|
||||
'LOGIN_EUCUID' => 'abc',
|
||||
'LOGIN_EUCUUID' => '1233456-1234-1234-1234-123456789012',
|
||||
],
|
||||
2,
|
||||
[],
|
||||
],
|
||||
'load, session euid set, all set' => [
|
||||
'load, session eucuuid set, all set' => [
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -259,20 +291,23 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[],
|
||||
[],
|
||||
[
|
||||
'EUID' => 1,
|
||||
'USER_NAME' => '',
|
||||
'GROUP_NAME' => '',
|
||||
'ADMIN' => 1,
|
||||
'GROUP_ACL_LEVEL' => -1,
|
||||
'PAGES_ACL_LEVEL' => [],
|
||||
'USER_ACL_LEVEL' => -1,
|
||||
'USER_ADDITIONAL_ACL' => [],
|
||||
'GROUP_ADDITIONAL_ACL' => [],
|
||||
'UNIT_UID' => [
|
||||
'AdminAccess' => 1,
|
||||
'LOGIN_EUID' => 1,
|
||||
'LOGIN_EUCUID' => 'abc',
|
||||
'LOGIN_EUCUUID' => 'SET_EUCUUID_IN_TEST',
|
||||
'LOGIN_USER_NAME' => '',
|
||||
'LOGIN_GROUP_NAME' => '',
|
||||
'LOGIN_ADMIN' => 1,
|
||||
'LOGIN_GROUP_ACL_LEVEL' => -1,
|
||||
'LOGIN_PAGES_ACL_LEVEL' => [],
|
||||
'LOGIN_USER_ACL_LEVEL' => -1,
|
||||
'LOGIN_USER_ADDITIONAL_ACL' => [],
|
||||
'LOGIN_GROUP_ADDITIONAL_ACL' => [],
|
||||
'LOGIN_UNIT_UID' => [
|
||||
'AdminAccess' => '123456789012',
|
||||
],
|
||||
'UNIT' => [
|
||||
1 => [
|
||||
'LOGIN_UNIT' => [
|
||||
'123456789012' => [
|
||||
'id' => 1,
|
||||
'acl_level' => 80,
|
||||
'name' => 'Admin Access',
|
||||
'uid' => 'AdminAccess',
|
||||
@@ -284,8 +319,8 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'additional_acl' => []
|
||||
],
|
||||
],
|
||||
// 'UNIT_DEFAULT' => '',
|
||||
// 'DEFAULT_ACL_LIST' => [],
|
||||
// 'LOGIN_UNIT_DEFAULT' => '',
|
||||
// 'LOGIN_DEFAULT_ACL_LIST' => [],
|
||||
],
|
||||
0,
|
||||
[
|
||||
@@ -293,6 +328,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -412,6 +448,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_deleted' => true
|
||||
@@ -437,6 +474,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_enabled' => true
|
||||
@@ -462,6 +500,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_locked' => true
|
||||
@@ -487,6 +526,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_get_locked' => true,
|
||||
@@ -511,6 +551,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_locked_period_until' => 'on'
|
||||
@@ -536,6 +577,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -555,6 +597,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -565,6 +608,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_locked_period_after' => 'on'
|
||||
@@ -590,6 +634,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_locked_period_until' => 'on',
|
||||
@@ -616,6 +661,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_login_user_id_locked' => true
|
||||
@@ -641,6 +687,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -659,6 +706,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -669,6 +717,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -688,6 +737,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -698,6 +748,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -717,6 +768,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -727,6 +779,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -746,6 +799,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -777,6 +831,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -800,6 +855,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -810,6 +866,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -833,6 +890,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -843,6 +901,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_login_user_id_revalidate_after' => 'on',
|
||||
@@ -869,6 +928,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -889,6 +949,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -899,6 +960,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_login_user_id_valid_from' => 'on',
|
||||
@@ -925,6 +987,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -945,6 +1008,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -955,6 +1019,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_login_user_id_valid_until' => 'on',
|
||||
@@ -981,6 +1046,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'base_access' => 'list',
|
||||
'page_access' => 'list',
|
||||
'test_login_user_id_valid_from' => 'on',
|
||||
@@ -1008,6 +1074,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
[
|
||||
'page_name' => 'edit_users.php',
|
||||
'edit_access_id' => 1,
|
||||
'edit_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'edit_access_uid' => 'AdminAccess',
|
||||
'edit_access_data' => 'test',
|
||||
'base_access' => 'list',
|
||||
@@ -1038,6 +1105,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'admin_flag' => true,
|
||||
'check_access' => true,
|
||||
'check_access_id' => 1,
|
||||
'check_access_cuid' => 'SET_EDIT_ACCESS_CUID_IN_TEST',
|
||||
'check_access_data' => 'value',
|
||||
'base_access' => true,
|
||||
'page_access' => true,
|
||||
@@ -1085,9 +1153,9 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
/** @var \CoreLibs\Create\Session&MockObject */
|
||||
$session_mock = $this->createPartialMock(
|
||||
\CoreLibs\Create\Session::class,
|
||||
['startSession', 'checkActiveSession', 'sessionDestroy']
|
||||
['getSessionId', 'checkActiveSession', 'sessionDestroy']
|
||||
);
|
||||
$session_mock->method('startSession')->willReturn('ACLLOGINTEST12');
|
||||
$session_mock->method('getSessionId')->willReturn('ACLLOGINTEST12');
|
||||
$session_mock->method('checkActiveSession')->willReturn(true);
|
||||
$session_mock->method('sessionDestroy')->will(
|
||||
$this->returnCallback(function () {
|
||||
@@ -1107,11 +1175,15 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
$_POST[$post_var] = $post_value;
|
||||
}
|
||||
|
||||
// set ingoing session cuuid if requested
|
||||
if (isset($session['LOGIN_EUCUUID']) && $session['LOGIN_EUCUUID'] == 'SET_EUCUUID_IN_TEST') {
|
||||
$session['LOGIN_EUCUUID'] = self::$edit_user_cuuid;
|
||||
}
|
||||
|
||||
// set _SESSION data
|
||||
foreach ($session as $session_var => $session_value) {
|
||||
$_SESSION[$session_var] = $session_value;
|
||||
}
|
||||
|
||||
/** @var \CoreLibs\ACL\Login&MockObject */
|
||||
$login_mock = $this->getMockBuilder(\CoreLibs\ACL\Login::class)
|
||||
->setConstructorArgs([
|
||||
@@ -1130,7 +1202,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
. 'locale' . DIRECTORY_SEPARATOR,
|
||||
]
|
||||
])
|
||||
->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin'])
|
||||
->onlyMethods(['loginTerminate', 'loginReadPageName', 'loginPrintLogin', 'loginEnhanceHttpSecurity'])
|
||||
->getMock();
|
||||
$login_mock->expects($this->any())
|
||||
->method('loginTerminate')
|
||||
@@ -1148,6 +1220,10 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
->method('loginPrintLogin')
|
||||
->willReturnCallback(function () {
|
||||
});
|
||||
$login_mock->expects($this->any())
|
||||
->method('loginEnhanceHttpSecurity')
|
||||
->willReturnCallback(function () {
|
||||
});
|
||||
|
||||
// if mock_settings: enabled OFF
|
||||
// run DB update and set off
|
||||
@@ -1365,6 +1441,19 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
|
||||
// run test
|
||||
try {
|
||||
// preset, we cannot set that in the provider
|
||||
if (
|
||||
isset($expected['check_access_cuid']) &&
|
||||
$expected['check_access_cuid'] == 'SET_EDIT_ACCESS_CUID_IN_TEST'
|
||||
) {
|
||||
$expected['check_access_cuid'] = self::$edit_access_cuid;
|
||||
}
|
||||
if (
|
||||
isset($mock_settings['edit_access_cuid']) &&
|
||||
$mock_settings['edit_access_cuid'] == 'SET_EDIT_ACCESS_CUID_IN_TEST'
|
||||
) {
|
||||
$mock_settings['edit_access_cuid'] = self::$edit_access_cuid;
|
||||
}
|
||||
// if ajax call
|
||||
// check if parameter, or globals (old type)
|
||||
// else normal call
|
||||
@@ -1423,6 +1512,31 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
$login_mock->loginCheckAccessPage($mock_settings['page_access']),
|
||||
'Assert page access'
|
||||
);
|
||||
// - loginCheckEditAccessCuid
|
||||
$this->assertEquals(
|
||||
$expected['check_access'],
|
||||
$login_mock->loginCheckEditAccessCuid($mock_settings['edit_access_cuid']),
|
||||
'Assert check access'
|
||||
);
|
||||
// - loginCheckEditAccessValidCuid
|
||||
$this->assertEquals(
|
||||
$expected['check_access_cuid'],
|
||||
$login_mock->loginCheckEditAccessValidCuid($mock_settings['edit_access_cuid']),
|
||||
'Assert check access cuid valid'
|
||||
);
|
||||
// - loginGetEditAccessCuidFromUid
|
||||
$this->assertEquals(
|
||||
$expected['check_access_cuid'],
|
||||
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_uid']),
|
||||
'Assert check access uid to cuid valid'
|
||||
);
|
||||
// - loginGetEditAccessCuidFromId
|
||||
$this->assertEquals(
|
||||
$expected['check_access_cuid'],
|
||||
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_id']),
|
||||
'Assert check access id to cuid valid'
|
||||
);
|
||||
// Deprecated
|
||||
// - loginCheckEditAccess
|
||||
$this->assertEquals(
|
||||
$expected['check_access'],
|
||||
@@ -1445,7 +1559,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
$this->assertEquals(
|
||||
$expected['check_access_data'],
|
||||
$login_mock->loginGetEditAccessData(
|
||||
$mock_settings['edit_access_id'],
|
||||
$mock_settings['edit_access_uid'],
|
||||
$mock_settings['edit_access_data']
|
||||
),
|
||||
'Assert check access id data value valid'
|
||||
@@ -1476,11 +1590,12 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
// - loginCheckPermissions
|
||||
// - loginGetPermissionOkay
|
||||
} catch (\Exception $e) {
|
||||
// print "[E]: " . $e->getCode() . ", ERROR: " . $login_mock->loginGetLastErrorCode() . "/"
|
||||
// . ($expected['login_error'] ?? 0) . "\n";
|
||||
// print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n";
|
||||
// print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n";
|
||||
// print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n";
|
||||
/* print "[E]: " . $e->getCode() . ", ERROR: " . $login_mock->loginGetLastErrorCode() . "/"
|
||||
. ($expected['login_error'] ?? 0) . "\n";
|
||||
print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n";
|
||||
print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n";
|
||||
print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n";
|
||||
print "POST exit: " . ($_POST['login_exit'] ?? '{0}') . "\n"; */
|
||||
// if this is 100, then we do further error checks
|
||||
if (
|
||||
$e->getCode() == 100 ||
|
||||
@@ -1788,9 +1903,9 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
/** @var \CoreLibs\Create\Session&MockObject */
|
||||
$session_mock = $this->createPartialMock(
|
||||
\CoreLibs\Create\Session::class,
|
||||
['startSession', 'checkActiveSession', 'sessionDestroy']
|
||||
['getSessionId', 'checkActiveSession', 'sessionDestroy']
|
||||
);
|
||||
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('checkActiveSession')->willReturn(true);
|
||||
$session_mock->method('sessionDestroy')->will(
|
||||
$this->returnCallback(function () {
|
||||
@@ -1902,9 +2017,9 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
/** @var \CoreLibs\Create\Session&MockObject */
|
||||
$session_mock = $this->createPartialMock(
|
||||
\CoreLibs\Create\Session::class,
|
||||
['startSession', 'checkActiveSession', 'sessionDestroy']
|
||||
['getSessionId', 'checkActiveSession', 'sessionDestroy']
|
||||
);
|
||||
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('checkActiveSession')->willReturn(true);
|
||||
$session_mock->method('sessionDestroy')->will(
|
||||
$this->returnCallback(function () {
|
||||
@@ -1990,9 +2105,9 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
/** @var \CoreLibs\Create\Session&MockObject */
|
||||
$session_mock = $this->createPartialMock(
|
||||
\CoreLibs\Create\Session::class,
|
||||
['startSession', 'checkActiveSession', 'sessionDestroy']
|
||||
['getSessionId', 'checkActiveSession', 'sessionDestroy']
|
||||
);
|
||||
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('checkActiveSession')->willReturn(true);
|
||||
$session_mock->method('sessionDestroy')->will(
|
||||
$this->returnCallback(function () {
|
||||
@@ -2086,9 +2201,9 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
/** @var \CoreLibs\Create\Session&MockObject */
|
||||
$session_mock = $this->createPartialMock(
|
||||
\CoreLibs\Create\Session::class,
|
||||
['startSession', 'checkActiveSession', 'sessionDestroy']
|
||||
['getSessionId', 'checkActiveSession', 'sessionDestroy']
|
||||
);
|
||||
$session_mock->method('startSession')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('getSessionId')->willReturn('ACLLOGINTEST34');
|
||||
$session_mock->method('checkActiveSession')->willReturn(true);
|
||||
$session_mock->method('sessionDestroy')->will(
|
||||
$this->returnCallback(function () {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1201,6 +1201,91 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
|
||||
'Find next key in array'
|
||||
);
|
||||
}
|
||||
|
||||
public function providerReturnMatchingKeyOnley(): array
|
||||
{
|
||||
return [
|
||||
'limited entries' => [
|
||||
[
|
||||
'a' => 'foo',
|
||||
'b' => 'bar',
|
||||
'c' => 'foobar'
|
||||
],
|
||||
[
|
||||
'a', 'b'
|
||||
],
|
||||
[
|
||||
'a' => 'foo',
|
||||
'b' => 'bar',
|
||||
],
|
||||
],
|
||||
'limited entries, with one wrong key' => [
|
||||
[
|
||||
'a' => 'foo',
|
||||
'b' => 'bar',
|
||||
'c' => 'foobar'
|
||||
],
|
||||
[
|
||||
'a', 'b', 'f'
|
||||
],
|
||||
[
|
||||
'a' => 'foo',
|
||||
'b' => 'bar',
|
||||
],
|
||||
],
|
||||
'wrong keys only' => [
|
||||
[
|
||||
'a' => 'foo',
|
||||
'b' => 'bar',
|
||||
'c' => 'foobar'
|
||||
],
|
||||
[
|
||||
'f', 'f'
|
||||
],
|
||||
[
|
||||
],
|
||||
],
|
||||
'empty keys' => [
|
||||
[
|
||||
'a' => 'foo',
|
||||
'b' => 'bar',
|
||||
'c' => 'foobar'
|
||||
],
|
||||
[],
|
||||
[
|
||||
'a' => 'foo',
|
||||
'b' => 'bar',
|
||||
'c' => 'foobar'
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::arrayReturnMatchingKeyOnly
|
||||
* @dataProvider providerReturnMatchingKeyOnley
|
||||
* @testdox arrayReturnMatchingKeyOnly get only selected key entries from array [$_dataName]
|
||||
*
|
||||
* @param array $input
|
||||
* @param array $key_list
|
||||
* @param array $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayReturnMatchingKeyOnly(
|
||||
array $input,
|
||||
array $key_list,
|
||||
array $expected
|
||||
): void {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\ArrayHandler::arrayReturnMatchingKeyOnly(
|
||||
$input,
|
||||
$key_list
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
@@ -926,48 +926,114 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
public function daysIntervalProvider(): array
|
||||
{
|
||||
return [
|
||||
'valid interval /, not named array' => [
|
||||
'2020/1/1',
|
||||
'2020/1/30',
|
||||
false,
|
||||
[29, 22, 8],
|
||||
// normal and format tests
|
||||
'valid interval / not named array' => [
|
||||
'input_a' => '2020/1/1',
|
||||
'input_b' => '2020/1/30',
|
||||
'return_named' => false, // return_named
|
||||
'include_end_date' => true, // include_end_date
|
||||
'exclude_start_date' => false, // exclude_start_date
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
'valid interval /, named array' => [
|
||||
'2020/1/1',
|
||||
'2020/1/30',
|
||||
true,
|
||||
['overall' => 29, 'weekday' => 22, 'weekend' => 8],
|
||||
'valid interval / named array' => [
|
||||
'input_a' => '2020/1/1',
|
||||
'input_b' => '2020/1/30',
|
||||
'return_named' => true,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => ['overall' => 30, 'weekday' => 22, 'weekend' => 8, 'reverse' => false],
|
||||
],
|
||||
'valid interval -' => [
|
||||
'2020-1-1',
|
||||
'2020-1-30',
|
||||
false,
|
||||
[29, 22, 8],
|
||||
],
|
||||
'valid interval switched' => [
|
||||
'2020/1/30',
|
||||
'2020/1/1',
|
||||
false,
|
||||
[28, 0, 0],
|
||||
'valid interval with "-"' => [
|
||||
'input_a' => '2020-1-1',
|
||||
'input_b' => '2020-1-30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
'valid interval with time' => [
|
||||
'2020/1/1 12:12:12',
|
||||
'2020/1/30 13:13:13',
|
||||
false,
|
||||
[28, 21, 8],
|
||||
'input_a' => '2020/1/1 12:12:12',
|
||||
'input_b' => '2020/1/30 13:13:13',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, false],
|
||||
],
|
||||
// invalid
|
||||
'invalid dates' => [
|
||||
'abc',
|
||||
'xyz',
|
||||
false,
|
||||
[0, 0, 0]
|
||||
'input_a' => 'abc',
|
||||
'input_b' => 'xyz',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [0, 0, 0, false]
|
||||
],
|
||||
// this test will take a long imte
|
||||
// this test will take a long time
|
||||
'out of bound dates' => [
|
||||
'1900-1-1',
|
||||
'9999-12-31',
|
||||
false,
|
||||
[2958463,2113189,845274],
|
||||
'input_a' => '1900-1-1',
|
||||
'input_b' => '9999-12-31',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [2958463, 2113189, 845274, false],
|
||||
],
|
||||
// tests for include/exclude
|
||||
'exclude end date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [29, 21, 8, false],
|
||||
],
|
||||
'exclude start date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [29, 21, 8, false],
|
||||
],
|
||||
'exclude start and end date' => [
|
||||
'input_b' => '2020/1/1',
|
||||
'input_a' => '2020/1/30',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [28, 20, 8, false],
|
||||
],
|
||||
// reverse
|
||||
'reverse: valid interval' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [30, 22, 8, true],
|
||||
],
|
||||
'reverse: exclude end date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => false,
|
||||
'expected' => [29, 21, 8, true],
|
||||
],
|
||||
'reverse: exclude start date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => true,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [29, 21, 8, true],
|
||||
],
|
||||
'reverse: exclude start and end date' => [
|
||||
'input_a' => '2020/1/30',
|
||||
'input_b' => '2020/1/1',
|
||||
'return_named' => false,
|
||||
'include_end_date' => false,
|
||||
'exclude_start_date' => true,
|
||||
'expected' => [28, 20, 8, true],
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -982,20 +1048,52 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
*
|
||||
* @param string $input_a
|
||||
* @param string $input_b
|
||||
* @param bool $flag
|
||||
* @param array $expected
|
||||
* @param bool $return_named
|
||||
* @param array $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testCalcDaysInterval(
|
||||
string $input_a,
|
||||
string $input_b,
|
||||
bool $flag,
|
||||
bool $return_named,
|
||||
bool $include_end_date,
|
||||
bool $exclude_start_date,
|
||||
$expected
|
||||
): void {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysInterval($input_a, $input_b, $flag)
|
||||
\CoreLibs\Combined\DateTime::calcDaysInterval(
|
||||
$input_a,
|
||||
$input_b,
|
||||
return_named:$return_named,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysInterval'
|
||||
);
|
||||
if ($return_named) {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysIntervalNamedIndex(
|
||||
$input_a,
|
||||
$input_b,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysIntervalNamedIndex'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Combined\DateTime::calcDaysIntervalNumIndex(
|
||||
$input_a,
|
||||
$input_b,
|
||||
include_end_date:$include_end_date,
|
||||
exclude_start_date:$exclude_start_date
|
||||
),
|
||||
'call calcDaysIntervalNamedIndex'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1187,7 +1285,38 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
|
||||
'2023-07-03',
|
||||
'2023-07-27',
|
||||
true
|
||||
]
|
||||
],
|
||||
// reverse
|
||||
'reverse: no weekend' => [
|
||||
'2023-07-04',
|
||||
'2023-07-03',
|
||||
false
|
||||
],
|
||||
'reverse: start weekend sat' => [
|
||||
'2023-07-04',
|
||||
'2023-07-01',
|
||||
true
|
||||
],
|
||||
'reverse: start weekend sun' => [
|
||||
'2023-07-04',
|
||||
'2023-07-02',
|
||||
true
|
||||
],
|
||||
'reverse: end weekend sat' => [
|
||||
'2023-07-08',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
'reverse: end weekend sun' => [
|
||||
'2023-07-09',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
'reverse: long period > 6 days' => [
|
||||
'2023-07-27',
|
||||
'2023-07-03',
|
||||
true
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ final class CoreLibsConvertByteTest extends TestCase
|
||||
4 => '1.00 KB',
|
||||
5 => '1.02KiB',
|
||||
],
|
||||
'invalud string number' => [
|
||||
'invalid string number' => [
|
||||
0 => '1024 MB',
|
||||
1 => '1024 MB',
|
||||
2 => '1024 MB',
|
||||
|
||||
@@ -114,6 +114,22 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function hashStandardProvider(): array
|
||||
{
|
||||
$hash_source = 'Some String Text';
|
||||
return [
|
||||
'Long Hash check: ' . \CoreLibs\Create\Hash::STANDARD_HASH => [
|
||||
$hash_source,
|
||||
hash(\CoreLibs\Create\Hash::STANDARD_HASH, $hash_source)
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -136,9 +152,13 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* phpcs:disable Generic.Files.LineLength
|
||||
* @covers ::__sha1Short
|
||||
* @covers ::__crc32b
|
||||
* @covers ::sha1Short
|
||||
* @dataProvider sha1ShortProvider
|
||||
* @testdox __sha1Short $input will be $expected (crc32b) and $expected_sha1 (sha1 short) [$_dataName]
|
||||
* @testdox __sha1Short/__crc32b/sha1short $input will be $expected (crc32b) and $expected_sha1 (sha1 short) [$_dataName]
|
||||
* phpcs:enable Generic.Files.LineLength
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
@@ -149,16 +169,29 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
// uses crc32b
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__sha1Short($input)
|
||||
\CoreLibs\Create\Hash::__sha1Short($input),
|
||||
'__sha1Short depreacted'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, false)
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, false),
|
||||
'__sha1Short (false) depreacted'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__crc32b($input),
|
||||
'__crc32b'
|
||||
);
|
||||
// sha1 type
|
||||
$this->assertEquals(
|
||||
$expected_sha1,
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, true)
|
||||
\CoreLibs\Create\Hash::__sha1Short($input, true),
|
||||
'__sha1Short (true) depreacted'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_sha1,
|
||||
\CoreLibs\Create\Hash::sha1Short($input),
|
||||
'sha1Short'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,8 +199,10 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__hash
|
||||
* @covers ::hashShort
|
||||
* @covers ::hashShort
|
||||
* @dataProvider hashProvider
|
||||
* @testdox __hash $input with $hash_type will be $expected [$_dataName]
|
||||
* @testdox __hash/hashShort/hash $input with $hash_type will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string|null $hash_type
|
||||
@@ -179,12 +214,24 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
if ($hash_type === null) {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__hash($input)
|
||||
\CoreLibs\Create\Hash::__hash($input),
|
||||
'__hash'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashShort($input),
|
||||
'hashShort'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__hash($input, $hash_type)
|
||||
\CoreLibs\Create\Hash::__hash($input, $hash_type),
|
||||
'__hash with hash type'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hash($input, $hash_type),
|
||||
'hash with hash type'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -193,8 +240,9 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__hashLong
|
||||
* @covers ::hashLong
|
||||
* @dataProvider hashLongProvider
|
||||
* @testdox __hashLong $input will be $expected [$_dataName]
|
||||
* @testdox __hashLong/hashLong $input will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
@@ -206,6 +254,52 @@ final class CoreLibsCreateHashTest extends TestCase
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::__hashLong($input)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashLong($input)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::hash
|
||||
* @covers ::hashStd
|
||||
* @dataProvider hashStandardProvider
|
||||
* @testdox hash/hashStd $input will be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testHashStandard(string $input, string $expected): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hashStd($input)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hash($input)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::hash
|
||||
* @testdox hash with invalid type [$_dataName]
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidHashType(): void
|
||||
{
|
||||
$hash_source = 'Some String Text';
|
||||
$expected = hash(\CoreLibs\Create\Hash::STANDARD_HASH, $hash_source);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Create\Hash::hash($hash_source, 'DOES_NOT_EXIST')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
public function sessionProvider(): array
|
||||
{
|
||||
// 0: session name as parameter or for GLOBAL value
|
||||
// 1: type p: parameter, g: global, d: php.ini default
|
||||
// 2: mock data as array
|
||||
// checkCliStatus: true/false,
|
||||
// getSessionStatus: PHP_SESSION_DISABLED for abort,
|
||||
@@ -31,13 +30,10 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
// checkActiveSession: true/false, [1st call, 2nd call]
|
||||
// getSessionId: string or false
|
||||
// 3: exepcted name (session)]
|
||||
// 4: Exception thrown on error
|
||||
// 5: exception code, null for none
|
||||
// 6: expected error string
|
||||
// 4: auto write close flag
|
||||
return [
|
||||
'session parameter' => [
|
||||
'sessionNameParameter',
|
||||
'p',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
@@ -47,12 +43,9 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
],
|
||||
'sessionNameParameter',
|
||||
null,
|
||||
null,
|
||||
'',
|
||||
],
|
||||
'session globals' => [
|
||||
'sessionNameGlobals',
|
||||
'g',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
@@ -61,13 +54,12 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'sessionNameGlobals',
|
||||
null,
|
||||
null,
|
||||
'',
|
||||
[
|
||||
'auto_write_close' => false,
|
||||
],
|
||||
],
|
||||
'session name default' => [
|
||||
'',
|
||||
'd',
|
||||
'auto write close' => [
|
||||
'sessionNameAutoWriteClose',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
@@ -75,109 +67,10 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
'checkActiveSession' => [false, true],
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'',
|
||||
null,
|
||||
null,
|
||||
'',
|
||||
],
|
||||
// error checks
|
||||
// 1: we are in cli
|
||||
'on cli error' => [
|
||||
'',
|
||||
'd',
|
||||
'sessionNameAutoWriteClose',
|
||||
[
|
||||
'checkCliStatus' => true,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
'setSessionName' => true,
|
||||
'checkActiveSession' => [false, true],
|
||||
'getSessionId' => '1234abcd4567'
|
||||
'auto_write_close' => true,
|
||||
],
|
||||
'',
|
||||
'RuntimeException',
|
||||
1,
|
||||
'[SESSION] No sessions in php cli'
|
||||
],
|
||||
// 2: session disabled
|
||||
'session disabled error' => [
|
||||
'',
|
||||
'd',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_DISABLED,
|
||||
'setSessionName' => true,
|
||||
'checkActiveSession' => [false, true],
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'',
|
||||
'RuntimeException',
|
||||
2,
|
||||
'[SESSION] Sessions are disabled'
|
||||
],
|
||||
// 3: invalid session name: string
|
||||
'invalid name chars error' => [
|
||||
'1invalid$session#;',
|
||||
'p',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
'setSessionName' => false,
|
||||
'checkActiveSession' => [false, true],
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'',
|
||||
'UnexpectedValueException',
|
||||
3,
|
||||
'[SESSION] Invalid session name: 1invalid$session#;'
|
||||
],
|
||||
// 3: invalid session name: only numbers
|
||||
'invalid name numbers only error' => [
|
||||
'123',
|
||||
'p',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
'setSessionName' => false,
|
||||
'checkActiveSession' => [false, true],
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'',
|
||||
'UnexpectedValueException',
|
||||
3,
|
||||
'[SESSION] Invalid session name: 123'
|
||||
],
|
||||
// 3: invalid session name: invalid name short
|
||||
// 3: invalid session name: too long (128)
|
||||
// 4: failed to start session (2nd false on check active session)
|
||||
'invalid name numbers only error' => [
|
||||
'',
|
||||
'd',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
'setSessionName' => true,
|
||||
'checkActiveSession' => [false, false],
|
||||
'getSessionId' => '1234abcd4567'
|
||||
],
|
||||
'',
|
||||
'RuntimeException',
|
||||
4,
|
||||
'[SESSION] Failed to activate session'
|
||||
],
|
||||
// 5: get session id return false
|
||||
'invalid name numbers only error' => [
|
||||
'',
|
||||
'd',
|
||||
[
|
||||
'checkCliStatus' => false,
|
||||
'getSessionStatus' => PHP_SESSION_NONE,
|
||||
'setSessionName' => true,
|
||||
'checkActiveSession' => [false, true],
|
||||
'getSessionId' => false
|
||||
],
|
||||
'',
|
||||
'UnexpectedValueException',
|
||||
5,
|
||||
'[SESSION] getSessionId did not return a session id'
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -190,32 +83,24 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
* @testdox startSession $input name for $type will be $expected (error: $expected_error) [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $type
|
||||
* @param array<mixed> $mock_data
|
||||
* @param string $expected
|
||||
* @param string|null $exception
|
||||
* @param string $expected_error
|
||||
* @param array<string,mixed> $options
|
||||
* @return void
|
||||
*/
|
||||
public function testStartSession(
|
||||
string $input,
|
||||
string $type,
|
||||
array $mock_data,
|
||||
string $expected,
|
||||
?string $exception,
|
||||
?int $exception_code,
|
||||
string $expected_error
|
||||
?array $options,
|
||||
): void {
|
||||
// override expected
|
||||
if ($type == 'd') {
|
||||
$expected = ini_get('session.name');
|
||||
}
|
||||
/** @var \CoreLibs\Create\Session&MockObject $session_mock */
|
||||
$session_mock = $this->createPartialMock(
|
||||
\CoreLibs\Create\Session::class,
|
||||
[
|
||||
'checkCliStatus', 'getSessionStatus', 'checkActiveSession',
|
||||
'setSessionName', 'startSessionCall', 'getSessionId',
|
||||
'checkCliStatus',
|
||||
'getSessionStatus', 'checkActiveSession',
|
||||
'getSessionId',
|
||||
'getSessionName'
|
||||
]
|
||||
);
|
||||
@@ -234,12 +119,8 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
$mock_data['checkActiveSession'][0],
|
||||
$mock_data['checkActiveSession'][1],
|
||||
);
|
||||
// dummy set for session name
|
||||
$session_mock->method('setSessionName')->with($input)->willReturn($mock_data['setSessionName']);
|
||||
// set session name & return bsed on request data
|
||||
$session_mock->method('getSessionName')->willReturn($expected);
|
||||
// will not return anything
|
||||
$session_mock->method('startSessionCall');
|
||||
// in test case only return string
|
||||
// false: will return false
|
||||
$session_mock->method('getSessionId')->willReturn($mock_data['getSessionId']);
|
||||
@@ -247,25 +128,7 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
// regex for session id
|
||||
$ression_id_regex = "/^\w+$/";
|
||||
|
||||
if ($exception !== null) {
|
||||
$this->expectException($exception);
|
||||
$this->expectExceptionCode($exception_code);
|
||||
}
|
||||
|
||||
unset($GLOBALS['SET_SESSION_NAME']);
|
||||
$session_id = '';
|
||||
switch ($type) {
|
||||
case 'p':
|
||||
$session_id = $session_mock->startSession($input);
|
||||
break;
|
||||
case 'g':
|
||||
$GLOBALS['SET_SESSION_NAME'] = $input;
|
||||
$session_id = $session_mock->startSession();
|
||||
break;
|
||||
case 'd':
|
||||
$session_id = $session_mock->startSession();
|
||||
break;
|
||||
}
|
||||
$session_id = $session_mock->getSessionId();
|
||||
// asert checks
|
||||
if (!empty($session_id)) {
|
||||
$this->assertMatchesRegularExpression(
|
||||
@@ -284,6 +147,79 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerSessionException(): array
|
||||
{
|
||||
return [
|
||||
'not cli' => [
|
||||
'TEST_EXCEPTION',
|
||||
\RuntimeException::class,
|
||||
1,
|
||||
'/^\[SESSION\] No sessions in php cli$/',
|
||||
],
|
||||
/* 'session disabled ' => [
|
||||
'TEST_EXCEPTION',
|
||||
\RuntimeException::class,
|
||||
2,
|
||||
'/^\[SESSION\] Sessions are disabled/'
|
||||
],
|
||||
'invalid session name' => [
|
||||
'--#as^-292p-',
|
||||
\UnexpectedValueException::class,
|
||||
3,
|
||||
'/^\[SESSION\] Invalid session name: /'
|
||||
],
|
||||
'failed to activate session' => [
|
||||
'TEST_EXCEPTION',
|
||||
\RuntimeException::class,
|
||||
4,
|
||||
'/^\[SESSION\] Failed to activate session/'
|
||||
],
|
||||
'expired session' => [
|
||||
\RuntimeException::class,
|
||||
5,
|
||||
'/^\[SESSION\] Expired session found/'
|
||||
],
|
||||
'not a valid session id returned' => [
|
||||
\UnexpectedValueException::class,
|
||||
6,
|
||||
'/^\[SESSION\] getSessionId did not return a session id/'
|
||||
], */
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* exception checks
|
||||
*
|
||||
* @covers ::initSession
|
||||
* @dataProvider providerSessionException
|
||||
* @testdox create session $session_name with exception $exception ($exception_code) [$_dataName]
|
||||
*
|
||||
* @param string $session_name
|
||||
* @param string $exception
|
||||
* @param int $exception_code
|
||||
* @param string $expected_error
|
||||
* @return void
|
||||
*/
|
||||
public function testSessionException(
|
||||
string $session_name,
|
||||
string $exception,
|
||||
int $exception_code,
|
||||
string $expected_error,
|
||||
): void {
|
||||
//
|
||||
// throws only on new Object creation
|
||||
$this->expectException($exception);
|
||||
$this->expectExceptionCode($exception_code);
|
||||
$this->expectExceptionMessageMatches($expected_error);
|
||||
// cannot set ini after header sent, plus we are on command line there are no headers
|
||||
new \CoreLibs\Create\Session($session_name, ['session_strict' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* provider for session name check
|
||||
*
|
||||
@@ -347,109 +283,147 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sessionDataProvider(): array
|
||||
public function providerSessionData(): array
|
||||
{
|
||||
return [
|
||||
'test' => [
|
||||
'foo',
|
||||
'bar',
|
||||
'bar',
|
||||
null,
|
||||
],
|
||||
'int key test' => [
|
||||
123,
|
||||
'bar',
|
||||
'bar',
|
||||
\UnexpectedValueException::class
|
||||
],
|
||||
// more complex value tests
|
||||
'array values' => [
|
||||
'array',
|
||||
[1, 2, 3],
|
||||
[1, 2, 3],
|
||||
null,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// NOTE: with auto start session, we cannot test this in the command line
|
||||
|
||||
/**
|
||||
* method call test
|
||||
*
|
||||
* @covers ::setS
|
||||
* @covers ::getS
|
||||
* @covers ::issetS
|
||||
* @covers ::unsetS
|
||||
* @dataProvider sessionDataProvider
|
||||
* @testdox setS/getS/issetS/unsetS $name with $input is $expected [$_dataName]
|
||||
* @covers ::set
|
||||
* @covers ::get
|
||||
* @covers ::isset
|
||||
* @covers ::unset
|
||||
* @dataProvider providerSessionData
|
||||
* @testdox set/get/isset/unset $name with $input is $expected ($exception) [$_dataName]
|
||||
*
|
||||
* @param string|int $name
|
||||
* @param mixed $input
|
||||
* @param mixed $expected
|
||||
* @param ?mixed $exception
|
||||
* @return void
|
||||
*/
|
||||
public function testMethodSetGet($name, $input, $expected): void
|
||||
public function testMethodSetGet($name, $input, $expected, $exception): void
|
||||
{
|
||||
$session = new \CoreLibs\Create\Session();
|
||||
$session->setS($name, $input);
|
||||
if (\CoreLibs\Get\System::checkCLI()) {
|
||||
$this->markTestSkipped('Cannot run testMethodSetGet in CLI');
|
||||
}
|
||||
$session = new \CoreLibs\Create\Session('TEST_METHOD');
|
||||
if ($expected !== null) {
|
||||
$this->expectException($exception);
|
||||
}
|
||||
$session->set($name, $input);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$session->getS($name),
|
||||
$session->get($name),
|
||||
'method set assert'
|
||||
);
|
||||
// isset true
|
||||
$this->assertTrue(
|
||||
$session->issetS($name),
|
||||
$session->isset($name),
|
||||
'method isset assert ok'
|
||||
);
|
||||
$session->unsetS($name);
|
||||
$session->unset($name);
|
||||
$this->assertEquals(
|
||||
'',
|
||||
$session->getS($name),
|
||||
$session->get($name),
|
||||
'method unset assert'
|
||||
);
|
||||
// iset false
|
||||
// isset false
|
||||
$this->assertFalse(
|
||||
$session->issetS($name),
|
||||
$session->isset($name),
|
||||
'method isset assert false'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* magic call test
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::__set
|
||||
* @covers ::__get
|
||||
* @covers ::__isset
|
||||
* @covers ::__unset
|
||||
* @dataProvider sessionDataProvider
|
||||
* @testdox __set/__get/__iseet/__unset $name with $input is $expected [$_dataName]
|
||||
* @return array
|
||||
*/
|
||||
public function providerSessionDataMany(): array
|
||||
{
|
||||
return [
|
||||
'valid set' => [
|
||||
[
|
||||
'foo 1' => 'bar 1',
|
||||
'foo 2' => 'bar 1',
|
||||
],
|
||||
[
|
||||
'foo 1' => 'bar 1',
|
||||
'foo 2' => 'bar 1',
|
||||
],
|
||||
null,
|
||||
],
|
||||
'invalid entry' => [
|
||||
[
|
||||
'foo 1' => 'bar 1',
|
||||
123 => 'bar 1',
|
||||
],
|
||||
[
|
||||
'foo 1' => 'bar 1',
|
||||
],
|
||||
\UnexpectedValueException::class
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string|int $name
|
||||
* @param mixed $input
|
||||
* @param mixed $expected
|
||||
* @covers ::setMany
|
||||
* @covers ::getMany
|
||||
* @dataProvider providerSessionDataMany
|
||||
* @testdox setMany/getMany/unsetMany $set is $expected ($exception) [$_dataName]
|
||||
*
|
||||
* @param array<string|int,mixed> $set
|
||||
* @param array<string,mixed> $expected
|
||||
* @param ?mixed $exception
|
||||
* @return void
|
||||
*/
|
||||
public function testMagicSetGet($name, $input, $expected): void
|
||||
public function testMany($set, $expected, $exception): void
|
||||
{
|
||||
$session = new \CoreLibs\Create\Session();
|
||||
$session->$name = $input;
|
||||
if (\CoreLibs\Get\System::checkCLI()) {
|
||||
$this->markTestSkipped('Cannot run testMethodSetGet in CLI');
|
||||
}
|
||||
$session = new \CoreLibs\Create\Session('TEST_METHOD');
|
||||
if ($expected !== null) {
|
||||
$this->expectException($exception);
|
||||
}
|
||||
$session->setMany($set);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$session->$name,
|
||||
'magic set assert'
|
||||
$session->getMany(array_keys($set)),
|
||||
'set many failed'
|
||||
);
|
||||
// isset true
|
||||
$this->assertTrue(
|
||||
isset($session->$name),
|
||||
'magic isset assert ok'
|
||||
);
|
||||
unset($session->$name);
|
||||
$session->unsetMany(array_keys($set));
|
||||
$this->assertEquals(
|
||||
'',
|
||||
$session->$name,
|
||||
'magic unset assert'
|
||||
);
|
||||
// isset true
|
||||
$this->assertFalse(
|
||||
isset($session->$name),
|
||||
'magic isset assert false'
|
||||
[],
|
||||
$session->getMany(array_keys($set)),
|
||||
'unset many failed'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -463,27 +437,30 @@ final class CoreLibsCreateSessionTest extends TestCase
|
||||
*/
|
||||
public function testUnsetAll(): void
|
||||
{
|
||||
if (\CoreLibs\Get\System::checkCLI()) {
|
||||
$this->markTestSkipped('Cannot run testUnsetAll in CLI');
|
||||
}
|
||||
$test_values = [
|
||||
'foo' => 'abc',
|
||||
'bar' => '123'
|
||||
];
|
||||
$session = new \CoreLibs\Create\Session();
|
||||
$session = new \CoreLibs\Create\Session('TEST_UNSET');
|
||||
foreach ($test_values as $name => $value) {
|
||||
$session->setS($name, $value);
|
||||
$session->set($name, $value);
|
||||
// confirm set
|
||||
$this->assertEquals(
|
||||
$value,
|
||||
$session->getS($name),
|
||||
$session->get($name),
|
||||
'set assert: ' . $name
|
||||
);
|
||||
}
|
||||
// unset all
|
||||
$session->unsetAllS();
|
||||
$session->clear();
|
||||
// check unset
|
||||
foreach (array_keys($test_values) as $name) {
|
||||
$this->assertEquals(
|
||||
'',
|
||||
$session->getS($name),
|
||||
$session->get($name),
|
||||
'unsert assert: ' . $name
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ final class CoreLibsCreateUidsTest extends TestCase
|
||||
* must match 7e78fe0d-59b8-4637-af7f-e88d221a7d1e
|
||||
*
|
||||
* @covers ::uuidv4
|
||||
* @covers ::validateUuidv4
|
||||
* @testdox uuidv4 check that return is matching regex [$_dataName]
|
||||
*
|
||||
* @return void
|
||||
@@ -129,13 +130,18 @@ final class CoreLibsCreateUidsTest extends TestCase
|
||||
{
|
||||
$uuid = \CoreLibs\Create\Uids::uuidv4();
|
||||
$this->assertMatchesRegularExpression(
|
||||
'/^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/',
|
||||
$uuid
|
||||
'/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/',
|
||||
$uuid,
|
||||
'Failed regex check'
|
||||
);
|
||||
$this->assertTrue(
|
||||
\CoreLibs\Create\Uids::validateUuuidv4($uuid),
|
||||
'Failed validate regex method'
|
||||
);
|
||||
$this->assertFalse(
|
||||
\CoreLibs\Create\Uids::validateUuuidv4('not-a-uuidv4'),
|
||||
'Failed wrong uuid validated as true'
|
||||
);
|
||||
// $this->assertStringMatchesFormat(
|
||||
// '%4s%4s-%4s-%4s-%4s-%4s%4s%4s',
|
||||
// $uuid
|
||||
// );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
@@ -37,8 +37,9 @@ namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use CoreLibs\Logging\Logger\Level;
|
||||
use CoreLibs\Logging;
|
||||
use CoreLibs\DB\Options\Convert;
|
||||
use CoreLibs\DB\Support\ConvertPlaceholder;
|
||||
|
||||
/**
|
||||
* Test class for DB\IO + DB\SQL\PgSQL
|
||||
@@ -117,7 +118,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
);
|
||||
}
|
||||
// define basic connection set valid and one invalid
|
||||
self::$log = new \CoreLibs\Logging\Logging([
|
||||
self::$log = new Logging\Logging([
|
||||
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
|
||||
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
|
||||
'log_file_id' => 'CoreLibs-DB-IO-Test',
|
||||
@@ -161,13 +162,9 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
// primary key name is table + '_id'
|
||||
<<<SQL
|
||||
CREATE TABLE table_with_primary_key (
|
||||
table_with_primary_key_id SERIAL PRIMARY KEY,
|
||||
table_with_primary_key_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
$base_table
|
||||
SQL
|
||||
/* "CREATE TABLE table_with_primary_key ("
|
||||
// primary key name is table + '_id'
|
||||
. "table_with_primary_key_id SERIAL PRIMARY KEY, "
|
||||
. $base_table */
|
||||
);
|
||||
$db->dbExec(
|
||||
<<<SQL
|
||||
@@ -570,11 +567,11 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
);
|
||||
$db->dbClose();
|
||||
// second conenction with log set NOT debug
|
||||
$log = new \CoreLibs\Logging\Logging([
|
||||
$log = new Logging\Logging([
|
||||
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
|
||||
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
|
||||
'log_file_id' => 'CoreLibs-DB-IO-Test',
|
||||
'log_level' => \CoreLibs\Logging\Logger\Level::Notice,
|
||||
'log_level' => Logging\Logger\Level::Notice,
|
||||
]);
|
||||
$db = new \CoreLibs\DB\IO(
|
||||
self::$db_config[$connection],
|
||||
@@ -3293,6 +3290,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) '
|
||||
. 'VALUES ($1, $2) RETURNING table_with_primary_key_id',
|
||||
'returning_id' => true,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// update
|
||||
@@ -3327,6 +3325,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'query' => 'UPDATE table_with_primary_key SET row_int = $1, '
|
||||
. 'row_varchar = $2 WHERE uid = $3',
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// select
|
||||
@@ -3356,6 +3355,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'count' => 1,
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key WHERE uid = $1',
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// any query but with no parameters
|
||||
@@ -3388,6 +3388,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'count' => 0,
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// no statement name (25)
|
||||
@@ -3411,6 +3412,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'count' => 0,
|
||||
'query' => '',
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// no query (prepare 11)
|
||||
@@ -3435,6 +3437,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'count' => 0,
|
||||
'query' => '',
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// no db connection (prepare/execute 16)
|
||||
@@ -3464,6 +3467,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'count' => 0,
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// prepare with different statement name
|
||||
@@ -3489,6 +3493,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'count' => 0,
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'returning_id' => false,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// insert wrong data count compared to needed (execute 23)
|
||||
@@ -3514,10 +3519,12 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES '
|
||||
. '($1, $2) RETURNING table_with_primary_key_id',
|
||||
'returning_id' => true,
|
||||
'placeholder_converted' => [],
|
||||
],
|
||||
],
|
||||
// execute does not return a result (22)
|
||||
// TODO execute does not return a result
|
||||
// TODO prepared statement with placeholder params auto convert
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3662,7 +3669,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
}
|
||||
|
||||
// check dbGetPrepareCursorValue
|
||||
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
|
||||
foreach (['pk_name', 'count', 'query', 'returning_id', 'placeholder_converted'] as $key) {
|
||||
$this->assertEquals(
|
||||
$prepare_cursor[$key],
|
||||
$db->dbGetPrepareCursorValue($stm_name, $key),
|
||||
@@ -3685,7 +3692,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function preparedProviderValue(): array
|
||||
public function providerDbGetPrepareCursorValue(): array
|
||||
{
|
||||
// 1: query (can be empty for do not set)
|
||||
// 2: stm name
|
||||
@@ -3729,7 +3736,7 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
* test return prepare cursor errors
|
||||
*
|
||||
* @covers ::dbGetPrepareCursorValue
|
||||
* @dataProvider preparedProviderValue
|
||||
* @dataProvider providerDbGetPrepareCursorValue
|
||||
* @testdox prepared query $stm_name with $key expect error id $error_id [$_dataName]
|
||||
*
|
||||
* @param string $query
|
||||
@@ -3762,6 +3769,94 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerDbPreparedCursorStatus(): array
|
||||
{
|
||||
return [
|
||||
'empty statement pararm' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_a',
|
||||
'check_stm_name' => '',
|
||||
'check_query' => '',
|
||||
'expected' => false
|
||||
],
|
||||
'different stm_name' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_b',
|
||||
'check_stm_name' => 'other_name',
|
||||
'check_query' => '',
|
||||
'expected' => 0
|
||||
],
|
||||
'same stm_name' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_c',
|
||||
'check_stm_name' => 'test_stm_c',
|
||||
'check_query' => '',
|
||||
'expected' => 1
|
||||
],
|
||||
'same stm_name and query' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_d',
|
||||
'check_stm_name' => 'test_stm_d',
|
||||
'check_query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'expected' => 2
|
||||
],
|
||||
'same stm_name and different query' => [
|
||||
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
||||
'stm_name' => 'test_stm_e',
|
||||
'check_stm_name' => 'test_stm_e',
|
||||
'check_query' => 'SELECT row_int, uid, row_int FROM table_with_primary_key',
|
||||
'expected' => 1
|
||||
],
|
||||
'insert query test' => [
|
||||
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
|
||||
'stm_name' => 'test_stm_f',
|
||||
'check_stm_name' => 'test_stm_f',
|
||||
'check_query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
|
||||
'expected' => 2
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test cursor status for prepared statement
|
||||
*
|
||||
* @covers ::dbPreparedCursorStatus
|
||||
* @dataProvider providerDbPreparedCursorStatus
|
||||
* @testdox Check prepared $stm_name ($check_stm_name) status is $expected [$_dataName]
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $stm_name
|
||||
* @param string $check_stm_name
|
||||
* @param string $check_query
|
||||
* @param bool|int $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testDbPreparedCursorStatus(
|
||||
string $query,
|
||||
string $stm_name,
|
||||
string $check_stm_name,
|
||||
string $check_query,
|
||||
bool|int $expected
|
||||
): void {
|
||||
$db = new \CoreLibs\DB\IO(
|
||||
self::$db_config['valid'],
|
||||
self::$log
|
||||
);
|
||||
$db->dbPrepare($stm_name, $query);
|
||||
// $db->dbExecute($stm_name);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$db->dbPreparedCursorStatus($check_stm_name, $check_query),
|
||||
'check prepared stement cursor status'
|
||||
);
|
||||
unset($db);
|
||||
}
|
||||
|
||||
// - schema set/get tests
|
||||
// dbGetSchema, dbSetSchema
|
||||
|
||||
@@ -5031,8 +5126,233 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
$db->dbClose();
|
||||
}
|
||||
|
||||
// query placeholder convert
|
||||
// MARK: QUERY PLACEHOLDERS
|
||||
|
||||
// test query placeholder detection for all possible sets
|
||||
// ::dbPrepare
|
||||
|
||||
/**
|
||||
* placeholder sql
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerDbCountQueryParams(): array
|
||||
{
|
||||
return [
|
||||
'one place holder' => [
|
||||
'query' => 'SELECT row_varchar FROM table_with_primary_key WHERE row_varchar = $1',
|
||||
'count' => 1,
|
||||
'convert' => false,
|
||||
],
|
||||
'one place holder, json call' => [
|
||||
'query' => "SELECT row_varchar FROM table_with_primary_key WHERE row_jsonb->>'data' = $1",
|
||||
'count' => 1,
|
||||
'convert' => false,
|
||||
],
|
||||
'one place holder, <> compare' => [
|
||||
'query' => "SELECT row_varchar FROM table_with_primary_key WHERE row_varchar <> $1",
|
||||
'count' => 1,
|
||||
'convert' => false,
|
||||
],
|
||||
'one place holder, named' => [
|
||||
'query' => "SELECT row_varchar FROM table_with_primary_key WHERE row_varchar <> :row_varchar",
|
||||
'count' => 1,
|
||||
'convert' => true,
|
||||
],
|
||||
'no replacement' => [
|
||||
'query' => "SELECT row_varchar FROM table_with_primary_key WHERE row_varchar = '$1'",
|
||||
'count' => 0,
|
||||
'convert' => false,
|
||||
],
|
||||
'insert' => [
|
||||
'query' => "INSERT INTO table_with_primary_key (row_varchar, row_jsonb, row_int) VALUES ($1, $2, $3)",
|
||||
'count' => 3,
|
||||
'convert' => false,
|
||||
],
|
||||
'update' => [
|
||||
'query' => "UPDATE table_with_primary_key SET row_varchar = $1, row_jsonb = $2, row_int = $3 WHERE row_numeric = $4",
|
||||
'count' => 4,
|
||||
'convert' => false,
|
||||
],
|
||||
'multiple, multline' => [
|
||||
'query' => <<<SQL
|
||||
SELECT
|
||||
row_varchar
|
||||
FROM
|
||||
table_with_primary_key
|
||||
WHERE
|
||||
row_varchar = $1 AND row_int = $2
|
||||
AND row_numeric = ANY($3)
|
||||
SQL,
|
||||
'count' => 3,
|
||||
'convert' => false,
|
||||
],
|
||||
'two digit numbers' => [
|
||||
'query' => <<<SQL
|
||||
INSERT INTO table_with_primary_key (
|
||||
row_int, row_numeric, row_varchar, row_varchar_literal, row_json,
|
||||
row_jsonb, row_bytea, row_timestamp, row_date, row_interval
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5,
|
||||
$6, $7, $8, $9, $10
|
||||
)
|
||||
SQL,
|
||||
'count' => 10,
|
||||
'convert' => false,
|
||||
],
|
||||
'things in brackets' => [
|
||||
'query' => <<<SQL
|
||||
SELECT row_varchar
|
||||
FROM table_with_primary_key
|
||||
WHERE
|
||||
row_varchar = $1 AND
|
||||
(row_int = ANY($2) OR row_int = $3)
|
||||
AND row_varchar_literal = $4
|
||||
SQL,
|
||||
'count' => 4,
|
||||
'convert' => false,
|
||||
],
|
||||
'number compare' => [
|
||||
'query' => <<<SQL
|
||||
SELECT row_varchar
|
||||
FROM table_with_primary_key
|
||||
WHERE
|
||||
row_int >= $1 OR row_int <= $2 OR
|
||||
row_int > $3 OR row_int < $4
|
||||
OR row_int = $5 OR row_int <> $6
|
||||
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,
|
||||
],
|
||||
'update with case' => [
|
||||
'query' => <<<SQL
|
||||
UPDATE table_with_primary_key SET
|
||||
row_int = $1::INT,
|
||||
row_varchar = CASE WHEN row_int = 1 THEN $2 ELSE 'bar'::VARCHAR END
|
||||
WHERE
|
||||
row_varchar = $3
|
||||
SQL,
|
||||
'count' => 3,
|
||||
'convert' => false,
|
||||
],
|
||||
'select with case' => [
|
||||
'query' => <<<SQL
|
||||
SELECT row_int
|
||||
FROM table_with_primary_key
|
||||
WHERE
|
||||
row_varchar = CASE WHEN row_int = 1 THEN $1 ELSE $2 END
|
||||
SQL,
|
||||
'count' => 2,
|
||||
'convert' => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder check and convert tests
|
||||
*
|
||||
* @covers ::dbPrepare
|
||||
* @covers ::__dbCountQueryParams
|
||||
* @onvers ::convertPlaceholderInQuery
|
||||
* @dataProvider providerDbCountQueryParams
|
||||
* @testdox Query replacement count test [$_dataName]
|
||||
*
|
||||
* @param string $query
|
||||
* @param int $count
|
||||
* @return void
|
||||
*/
|
||||
public function testDbCountQueryParams(string $query, int $count, bool $convert): void
|
||||
{
|
||||
$db = new \CoreLibs\DB\IO(
|
||||
self::$db_config['valid'],
|
||||
self::$log
|
||||
);
|
||||
$id = sha1($query);
|
||||
$db->dbSetConvertPlaceholder($convert);
|
||||
$db->dbPrepare($id, $query);
|
||||
// print "\n**\n";
|
||||
// print "PCount: " . $db->dbGetPrepareCursorValue($id, 'count') . "\n";
|
||||
// print "\n**\n";
|
||||
$this->assertEquals(
|
||||
$count,
|
||||
$db->dbGetPrepareCursorValue($id, 'count'),
|
||||
'DB count params'
|
||||
);
|
||||
$placeholder = ConvertPlaceholder::convertPlaceholderInQuery($query, null, 'pg');
|
||||
// print "RES: " . print_r($placeholder, true) . "\n";
|
||||
$this->assertEquals(
|
||||
$count,
|
||||
$placeholder['needed'],
|
||||
'convert params'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* query placeholder convert
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function queryPlaceholderReplaceProvider(): array
|
||||
{
|
||||
// WHERE row_varchar = $1
|
||||
@@ -5076,7 +5396,9 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
WHERE row_varchar = $1
|
||||
SQL,
|
||||
'expected_params' => ['string a'],
|
||||
]
|
||||
],
|
||||
// TODO: test with multiple entries
|
||||
// TODO: test with same entry ($1, $1, :var, :var)
|
||||
];
|
||||
}
|
||||
|
||||
@@ -5178,6 +5500,8 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
// - data debug
|
||||
// dbDumpData
|
||||
|
||||
// MARK: ASYNC
|
||||
|
||||
// ASYNC at the end because it has 1s timeout
|
||||
// - asynchronous executions
|
||||
// dbExecAsync, dbCheckAsync
|
||||
|
||||
@@ -568,6 +568,9 @@ final class CoreLibsDebugSupportTest extends TestCase
|
||||
'assert expected 12'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$this->assertTrue(true, 'Default fallback as true');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,341 +21,6 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
|
||||
. 'includes' . DIRECTORY_SEPARATOR
|
||||
. 'locale' . DIRECTORY_SEPARATOR;
|
||||
|
||||
/**
|
||||
* set all constant variables that must be set before call
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
// default web page encoding setting
|
||||
/* if (!defined('DEFAULT_ENCODING')) {
|
||||
define('DEFAULT_ENCODING', 'UTF-8');
|
||||
}
|
||||
if (!defined('DEFAULT_LOCALE')) {
|
||||
// default lang + encoding
|
||||
define('DEFAULT_LOCALE', 'en_US.UTF-8');
|
||||
}
|
||||
// site
|
||||
if (!defined('SITE_ENCODING')) {
|
||||
define('SITE_ENCODING', DEFAULT_ENCODING);
|
||||
}
|
||||
if (!defined('SITE_LOCALE')) {
|
||||
define('SITE_LOCALE', DEFAULT_LOCALE);
|
||||
} */
|
||||
// just set
|
||||
/* if (!defined('BASE')) {
|
||||
define('BASE', str_replace('/configs', '', __DIR__) . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
if (!defined('INCLUDES')) {
|
||||
define('INCLUDES', 'includes' . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
if (!defined('LANG')) {
|
||||
define('LANG', 'lang' . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
if (!defined('LOCALE')) {
|
||||
define('LOCALE', 'locale' . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
if (!defined('CONTENT_PATH')) {
|
||||
define('CONTENT_PATH', 'frontend' . DIRECTORY_SEPARATOR);
|
||||
} */
|
||||
// array session
|
||||
$_SESSION = [];
|
||||
global $_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* all the test data
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
/* public function setLocaleProvider(): array
|
||||
{
|
||||
return [
|
||||
// 0: locale
|
||||
// 1: domain
|
||||
// 2: encoding
|
||||
// 3: path
|
||||
// 4: SESSION: DEFAULT_LOCALE
|
||||
// 5: SESSION: DEFAULT_CHARSET
|
||||
// 6: expected array
|
||||
// 7: deprecation message
|
||||
'no params, all default constants' => [
|
||||
// lang, domain, encoding, path
|
||||
null, null, null, null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'en_US.UTF-8',
|
||||
'lang' => 'en_US',
|
||||
'domain' => 'frontend',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $locale or unset SESSION locale is deprecated',
|
||||
],
|
||||
'no params, session charset and lang' => [
|
||||
// lang, domain, encoding, path
|
||||
null, null, null, null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
'ja_JP', 'UTF-8',
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja_JP',
|
||||
'lang' => 'ja_JP',
|
||||
'domain' => 'frontend',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $domain is deprecated'
|
||||
],
|
||||
'no params, session charset and lang short' => [
|
||||
// lang, domain, encoding, path
|
||||
null, null, null, null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
'ja', 'UTF-8',
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja',
|
||||
'lang' => 'ja',
|
||||
'domain' => 'frontend',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $domain is deprecated',
|
||||
],
|
||||
// param lang (no sessions)
|
||||
'locale param only, no sessions' => [
|
||||
// lang, domain, encoding, path
|
||||
'ja.UTF-8', null, null, null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja.UTF-8',
|
||||
'lang' => 'ja',
|
||||
'domain' => 'frontend',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $domain is deprecated',
|
||||
],
|
||||
// different locale setting
|
||||
'locale complex param only, no sessions' => [
|
||||
// lang, domain, encoding, path
|
||||
'ja_JP.SJIS', null, null, null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja_JP.SJIS',
|
||||
'lang' => 'ja_JP',
|
||||
'domain' => 'frontend',
|
||||
'encoding' => 'SJIS',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $domain is deprecated',
|
||||
],
|
||||
// param lang and domain (no override)
|
||||
'locale, domain params, no sessions' => [
|
||||
// lang, domain, encoding, path
|
||||
'ja.UTF-8', 'admin', null, null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja.UTF-8',
|
||||
'lang' => 'ja',
|
||||
'domain' => 'admin',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $path is deprecated',
|
||||
],
|
||||
// param lang and domain (no override)
|
||||
'locale, domain, encoding params, no sessions' => [
|
||||
// lang, domain, encoding, path
|
||||
'ja.UTF-8', 'admin', 'UTF-8', null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja.UTF-8',
|
||||
'lang' => 'ja',
|
||||
'domain' => 'admin',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $path is deprecated'
|
||||
],
|
||||
// lang, domain, path (no override)
|
||||
'locale, domain and path, no sessions' => [
|
||||
// lang, domain, encoding, path
|
||||
'ja.UTF-8', 'admin', '', __DIR__ . '/locale_other/',
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja.UTF-8',
|
||||
'lang' => 'ja',
|
||||
'domain' => 'admin',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?locale_other\/$/",
|
||||
],
|
||||
null
|
||||
],
|
||||
// all params set (no override)
|
||||
'all parameter, no sessions' => [
|
||||
// lang, domain, encoding, path
|
||||
'ja', 'admin', 'UTF-8', __DIR__ . '/locale_other/',
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'ja',
|
||||
'lang' => 'ja',
|
||||
'domain' => 'admin',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?locale_other\/$/",
|
||||
],
|
||||
null
|
||||
],
|
||||
// param lang and domain (no override)
|
||||
'long locale, domain, encoding params, no sessions' => [
|
||||
// lang, domain, encoding, path
|
||||
'de_CH.UTF-8@euro', 'admin', 'UTF-8', null,
|
||||
// SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
|
||||
null, null,
|
||||
// return array
|
||||
[
|
||||
'locale' => 'de_CH.UTF-8@euro',
|
||||
'lang' => 'de_CH',
|
||||
'domain' => 'admin',
|
||||
'encoding' => 'UTF-8',
|
||||
'path' => "/^\/(.*\/)?includes\/locale\/$/",
|
||||
],
|
||||
'setLocale: Unset $path is deprecated',
|
||||
],
|
||||
// TODO invalid params (bad path) (no override)
|
||||
// TODO param calls, but with override set
|
||||
];
|
||||
} */
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setLocale
|
||||
* @dataProvider setLocaleProvider
|
||||
* @testdox lang settings lang $language, domain $domain, encoding $encoding, path $path; session lang: $SESSION_DEFAULT_LOCALE, session char: $SESSION_DEFAULT_CHARSET [$_dataName]
|
||||
*
|
||||
* @param string|null $language
|
||||
* @param string|null $domain
|
||||
* @param string|null $encoding
|
||||
* @param string|null $path
|
||||
* @param string|null $SESSION_DEFAULT_LOCALE
|
||||
* @param string|null $SESSION_DEFAULT_CHARSET
|
||||
* @param array<mixed> $expected
|
||||
* @param string|null $deprecation_message
|
||||
* @return void
|
||||
*/
|
||||
/* public function testsetLocale(
|
||||
?string $language,
|
||||
?string $domain,
|
||||
?string $encoding,
|
||||
?string $path,
|
||||
?string $SESSION_DEFAULT_LOCALE,
|
||||
?string $SESSION_DEFAULT_CHARSET,
|
||||
array $expected,
|
||||
?string $deprecation_message
|
||||
): void {
|
||||
$return_lang_settings = [];
|
||||
global $_SESSION;
|
||||
// set override
|
||||
if ($SESSION_DEFAULT_LOCALE !== null) {
|
||||
$_SESSION['DEFAULT_LOCALE'] = $SESSION_DEFAULT_LOCALE;
|
||||
}
|
||||
if ($SESSION_DEFAULT_CHARSET !== null) {
|
||||
$_SESSION['DEFAULT_CHARSET'] = $SESSION_DEFAULT_CHARSET;
|
||||
}
|
||||
if ($deprecation_message !== null) {
|
||||
set_error_handler(
|
||||
static function (int $errno, string $errstr): never {
|
||||
throw new \Exception($errstr, $errno);
|
||||
},
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
// catch this with the message
|
||||
$this->expectExceptionMessage($deprecation_message);
|
||||
}
|
||||
// function call
|
||||
if (
|
||||
$language === null && $domain === null &&
|
||||
$encoding === null && $path === null
|
||||
) {
|
||||
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale();
|
||||
} elseif (
|
||||
$language !== null && $domain === null &&
|
||||
$encoding === null && $path === null
|
||||
) {
|
||||
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
|
||||
$language
|
||||
);
|
||||
} elseif (
|
||||
$language !== null && $domain !== null &&
|
||||
$encoding === null && $path === null
|
||||
) {
|
||||
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
|
||||
$language,
|
||||
$domain
|
||||
);
|
||||
} elseif (
|
||||
$language !== null && $domain !== null &&
|
||||
$encoding !== null && $path === null
|
||||
) {
|
||||
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
|
||||
$language,
|
||||
$domain,
|
||||
$encoding
|
||||
);
|
||||
} else {
|
||||
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
|
||||
$language,
|
||||
$domain,
|
||||
$encoding,
|
||||
$path
|
||||
);
|
||||
}
|
||||
restore_error_handler();
|
||||
// print "RETURN: " . print_r($return_lang_settings, true) . "\n";
|
||||
|
||||
foreach (
|
||||
[
|
||||
'locale', 'lang', 'domain', 'encoding', 'path'
|
||||
] as $key
|
||||
) {
|
||||
$value = $expected[$key];
|
||||
if (strpos($value, "/") === 0) {
|
||||
// this is regex
|
||||
$this->assertMatchesRegularExpression(
|
||||
$value,
|
||||
$return_lang_settings[$key],
|
||||
'assert regex failed for ' . $key
|
||||
);
|
||||
} else {
|
||||
// assert equal
|
||||
$this->assertEquals(
|
||||
$value,
|
||||
$return_lang_settings[$key],
|
||||
'assert equal failed for ' . $key
|
||||
);
|
||||
}
|
||||
}
|
||||
// unset all vars
|
||||
$_SESSION = [];
|
||||
unset($GLOBALS['OVERRIDE_LANG']);
|
||||
} */
|
||||
|
||||
/**
|
||||
* all the test data
|
||||
*
|
||||
|
||||
2
test/phpunit/Language/locale_other/.gitignore
vendored
Normal file
2
test/phpunit/Language/locale_other/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -10,7 +10,7 @@ use CoreLibs\Logging\Logger\Level;
|
||||
/**
|
||||
* Test class for Logging
|
||||
* @coversDefaultClass \CoreLibs\Logging\ErrorMessages
|
||||
* @testdox \CoreLibs\Logging\ErrorMEssages method tests
|
||||
* @testdox \CoreLibs\Logging\ErrorMessages method tests
|
||||
*/
|
||||
final class CoreLibsLoggingErrorMessagesTest extends TestCase
|
||||
{
|
||||
|
||||
@@ -395,7 +395,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
}
|
||||
$per_run_id = $log->getLogUniqueId();
|
||||
$this->assertMatchesRegularExpression(
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
|
||||
$per_run_id,
|
||||
'assert per log run id 1st'
|
||||
);
|
||||
@@ -403,7 +403,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$log->setLogUniqueId(true);
|
||||
$per_run_id_2nd = $log->getLogUniqueId();
|
||||
$this->assertMatchesRegularExpression(
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
|
||||
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
|
||||
$per_run_id_2nd,
|
||||
'assert per log run id 2nd'
|
||||
);
|
||||
@@ -824,13 +824,13 @@ final class CoreLibsLoggingLoggingTest extends TestCase
|
||||
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
|
||||
$this->assertEquals(
|
||||
$log->getLogFile(),
|
||||
$log->getLogFileId() . '_DEBUG.log'
|
||||
$log->getLogFileId() . '.DEBUG.log'
|
||||
);
|
||||
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
|
||||
$this->assertTrue($log_ok, 'assert ::log (info) OK');
|
||||
$this->assertEquals(
|
||||
$log->getLogFile(),
|
||||
$log->getLogFileId() . '_INFO.log'
|
||||
$log->getLogFileId() . '.INFO.log'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,838 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Security\CreateKey;
|
||||
use CoreLibs\Security\AsymmetricAnonymousEncryption;
|
||||
|
||||
/**
|
||||
* Test class for Security\AsymmetricAnonymousEncryption and Security\CreateKey
|
||||
* @coversDefaultClass \CoreLibs\Security\AsymmetricAnonymousEncryption
|
||||
* @testdox \CoreLibs\Security\AsymmetricAnonymousEncryption method tests
|
||||
*/
|
||||
final class CoreLibsSecurityAsymmetricAnonymousEncryptionTest extends TestCase
|
||||
{
|
||||
// MARK: key set and compare
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if init class set key pair matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairInitGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'automatic set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'automatic set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if init class set key pair and public key matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairPublicKeyInitGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if init class set public key matches to created public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPublicKeyInitGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
null,
|
||||
$crypt->getKeyPair(),
|
||||
'unset set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setKeyPair
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if set key pair after class init matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairSetGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption();
|
||||
$crypt->setKeyPair($key_pair);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'post class init set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'post class init automatic set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'post class init set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'post class init automatic set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setKeyPair
|
||||
* @covers ::setPublicKey
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if set key pair after class init matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyPairPublicKeySetGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption();
|
||||
$crypt->setKeyPair($key_pair);
|
||||
$crypt->setPublicKey($public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'post class init set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'post class init set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key_pair,
|
||||
$crypt->getKeyPair(),
|
||||
'post class init set key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'post class init set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setPublicKey
|
||||
* @covers ::getKeyPair
|
||||
* @covers ::compareKeyPair
|
||||
* @covers ::getPublicKey
|
||||
* @covers ::comparePublicKey
|
||||
* @testdox Check if set key pair after class init matches to created key pair and public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPublicKeySetGetCompare(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption();
|
||||
$crypt->setPublicKey($public_key);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'post class init set public key not equal to original public key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
null,
|
||||
$crypt->getKeyPair(),
|
||||
'post class init unset key pair returned not equal to original key pair'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$public_key,
|
||||
$crypt->getPublicKey(),
|
||||
'post class init set public key returned not equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @testdox Check different key pair and public key set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDifferentSetKeyPairPublicKey()
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$key_pair_2 = CreateKey::createKeyPair();
|
||||
$public_key_2 = CreateKey::getPublicKey($key_pair_2);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key_2);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'key pair set matches key pair created'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key_2),
|
||||
'alternate public key set matches alternate public key created'
|
||||
);
|
||||
$this->assertFalse(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'alternate public key set does not match key pair public key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @testdox Check if new set privat key does not overwrite set public key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testUpdateKeyPairNotUpdatePublicKey(): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair),
|
||||
'set key pair not equal to original key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'set public key not equal to original public key'
|
||||
);
|
||||
$key_pair_2 = CreateKey::createKeyPair();
|
||||
$public_key_2 = CreateKey::getPublicKey($key_pair_2);
|
||||
$crypt->setKeyPair($key_pair_2);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKeyPair($key_pair_2),
|
||||
'new set key pair not equal to original new key pair'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$crypt->comparePublicKey($public_key),
|
||||
'original set public key not equal to original public key'
|
||||
);
|
||||
$this->assertFalse(
|
||||
$crypt->comparePublicKey($public_key_2),
|
||||
'new public key equal to original public key'
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: empty encrytped string
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @covers ::decrypt
|
||||
* @testdox Test empty encrypted string to decrypt
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEmptyDecryptionString(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Encrypted string cannot be empty');
|
||||
AsymmetricAnonymousEncryption::decryptKey('', CreateKey::generateRandomKey());
|
||||
}
|
||||
|
||||
// MARK: encrypt/decrypt
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerEncryptDecryptSuccess(): array
|
||||
{
|
||||
return [
|
||||
'valid string' => [
|
||||
'input' => 'I am a secret',
|
||||
'expected' => 'I am a secret',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccess(string $input, string $expected): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test class
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$decrypted = $crypt->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class call',
|
||||
);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair, $public_key);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$decrypted = $crypt->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class call botjh set',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test indirect
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
|
||||
$decrypted = AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class Instance call',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt indirect with public key $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccessIndirectPublicKey(string $input, string $expected): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test indirect
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
|
||||
$decrypted = AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Class Instance call public key',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
// test static
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey($input, $public_key);
|
||||
$decrypted = AsymmetricAnonymousEncryption::decryptKey($encrypted, $key_pair);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted,
|
||||
'Static call',
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: invalid decrypt key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerEncryptFailed(): array
|
||||
{
|
||||
return [
|
||||
'wrong decryption key' => [
|
||||
'input' => 'I am a secret',
|
||||
'excpetion_message' => 'Invalid key pair'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailed(string $input, string $exception_message): void
|
||||
{
|
||||
$key_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$wrong_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// wrong key in class call
|
||||
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
|
||||
$encrypted = $crypt->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKeyPair($wrong_key_pair);
|
||||
$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_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$wrong_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// class instance
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance(public_key:$public_key)->encrypt($input);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($wrong_key_pair)->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_pair = CreateKey::createKeyPair();
|
||||
$public_key = CreateKey::getPublicKey($key_pair);
|
||||
$wrong_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// class static
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey($input, $public_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($encrypted, $wrong_key_pair);
|
||||
}
|
||||
|
||||
// MARK: invalid key pair
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongKeyPair(): array
|
||||
{
|
||||
return [
|
||||
'not hex key pair' => [
|
||||
'key_pair' => 'not_a_hex_key_pair',
|
||||
'exception_message' => 'Invalid hex key pair'
|
||||
],
|
||||
'too short hex key pair' => [
|
||||
'key_pair' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
|
||||
'excpetion_message' => 'Key pair is not the correct size (must be '
|
||||
],
|
||||
'empty key pair' => [
|
||||
'key_pair' => '',
|
||||
'excpetion_message' => 'Key pair cannot be empty'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKeyPair
|
||||
* @testdox wrong key pair $key_pair throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyPair(string $key_pair, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// class
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt = new AsymmetricAnonymousEncryption($key_pair);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->encrypt('test');
|
||||
$crypt->setKeyPair($enc_key_pair);
|
||||
$encrypted = $crypt->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKeyPair($key_pair);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKeyPair
|
||||
* @testdox wrong key pair indirect $key_pair throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyPairIndirect(string $key_pair, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// set valid encryption
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance($enc_key_pair)->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($key_pair)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKeyPair
|
||||
* @testdox wrong key pair static $key_pair throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key_pair
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKeyPairStatic(string $key_pair, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
|
||||
// set valid encryption
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey('test', CreateKey::getPublicKey($enc_key_pair));
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($encrypted, $key_pair);
|
||||
}
|
||||
|
||||
// MARK: invalid public key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongPublicKey(): array
|
||||
{
|
||||
return [
|
||||
'not hex public key' => [
|
||||
'public_key' => 'not_a_hex_public_key',
|
||||
'exception_message' => 'Invalid hex public key'
|
||||
],
|
||||
'too short hex public key' => [
|
||||
'public_key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
|
||||
'excpetion_message' => 'Public key is not the correct size (must be '
|
||||
],
|
||||
'empty public key' => [
|
||||
'public_key' => '',
|
||||
'excpetion_message' => 'Public key cannot be empty'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongPublicKey
|
||||
* @testdox wrong public key $public_key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $public_key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongPublicKey(string $public_key, string $exception_message): void
|
||||
{
|
||||
$enc_key_pair = CreateKey::createKeyPair();
|
||||
// $enc_public_key = CreateKey::getPublicKey($enc_key_pair);
|
||||
|
||||
// class
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt = new AsymmetricAnonymousEncryption(public_key:$public_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->decrypt('test');
|
||||
$crypt->setKeyPair($enc_key_pair);
|
||||
$encrypted = $crypt->encrypt('test');
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setPublicKey($public_key);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongPublicKey
|
||||
* @testdox wrong public key indirect $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongPublicKeyIndirect(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::createKeyPair();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance(public_key:$key)->encrypt('test');
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = AsymmetricAnonymousEncryption::getInstance($enc_key)->encrypt('test');
|
||||
// $this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($key)->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongPublicKey
|
||||
* @testdox wrong public key static $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongPublicKeyStatic(string $key, string $exception_message): void
|
||||
{
|
||||
$enc_key = CreateKey::createKeyPair();
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::encryptKey('test', $key);
|
||||
// we must encrypt valid thing first so we can fail with the wrong key
|
||||
$encrypted = AsymmetricAnonymousEncryption::encryptKey('test', $enc_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
// MARK: wrong cipher text
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongCiphertext(): array
|
||||
{
|
||||
return [
|
||||
'invalid cipher text' => [
|
||||
'input' => 'short',
|
||||
'exception_message' => 'base642bin failed: '
|
||||
],
|
||||
'cannot decrypt' => [
|
||||
// phpcs:disable Generic.Files.LineLength
|
||||
'input' => 'Um8tBGiVfFAOg2YoUgA5fTqK1wXPB1S7uxhPNE1lqDxgntkEhYJDOmjXa0DMpBlYHjab6sC4mgzwZSzGCUnXDAgsHckwYwfAzs/r',
|
||||
// phpcs:enable Generic.Files.LineLength
|
||||
'exception_message' => 'Invalid key pair'
|
||||
],
|
||||
'invalid text' => [
|
||||
'input' => 'U29tZSB0ZXh0IGhlcmU=',
|
||||
'exception_message' => 'Invalid key pair'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertext(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::createKeyPair();
|
||||
// class
|
||||
$crypt = new AsymmetricAnonymousEncryption($key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->decrypt($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @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::createKeyPair();
|
||||
|
||||
// class instance
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::getInstance($key)->decrypt($input);
|
||||
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($input, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @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::createKeyPair();
|
||||
// class static
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
AsymmetricAnonymousEncryption::decryptKey($input, $key);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -13,6 +13,11 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
final class CoreLibsSecurityPasswordTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function passwordProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -21,6 +26,11 @@ final class CoreLibsSecurityPasswordTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: we need different hash types for PHP versions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function passwordRehashProvider(): array
|
||||
{
|
||||
return [
|
||||
@@ -63,6 +73,10 @@ final class CoreLibsSecurityPasswordTest extends TestCase
|
||||
*/
|
||||
public function testPasswordRehashCheck(string $input, bool $expected): void
|
||||
{
|
||||
// in PHP 8.4 the length is $12
|
||||
if (PHP_VERSION_ID > 80400) {
|
||||
$input = str_replace('$2y$10$', '$2y$12$', $input);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Security\Password::passwordRehashCheck($input)
|
||||
|
||||
@@ -15,6 +15,77 @@ use CoreLibs\Security\SymmetricEncryption;
|
||||
*/
|
||||
final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
{
|
||||
// MARK: key set compare
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::compareKey
|
||||
* @covers ::getKey
|
||||
* @testdox Check if init class set key matches to created key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeyInitGetCompare(): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$crypt = new SymmetricEncryption($key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKey($key),
|
||||
'set key not equal to original key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key,
|
||||
$crypt->getKey(),
|
||||
'set key returned not equal to original key'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::setKey
|
||||
* @covers ::compareKey
|
||||
* @covers ::getKey
|
||||
* @testdox Check if set key after class init matches to created key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testKeySetGetCompare(): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$crypt = new SymmetricEncryption();
|
||||
$crypt->setKey($key);
|
||||
$this->assertTrue(
|
||||
$crypt->compareKey($key),
|
||||
'set key not equal to original key'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$key,
|
||||
$crypt->getKey(),
|
||||
'set key returned not equal to original key'
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: empty encrypted string
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @covers ::decrypt
|
||||
* @testdox Test empty encrypted string to decrypt
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEmptyDecryptionString(): void
|
||||
{
|
||||
$this->expectExceptionMessage('Encrypted string cannot be empty');
|
||||
SymmetricEncryption::decryptKey('', CreateKey::generateRandomKey());
|
||||
}
|
||||
|
||||
// MARK: encrypt/decrypt compare
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -56,7 +127,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 +153,24 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$decrypted,
|
||||
'Class Instance call',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @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);
|
||||
@@ -77,6 +182,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: invalid key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -114,13 +221,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 ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @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);
|
||||
@@ -128,6 +273,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
SymmetricEncryption::decryptKey($encrypted, $wrong_key);
|
||||
}
|
||||
|
||||
// MARK: wrong key
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -144,6 +291,10 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
|
||||
'excpetion_message' => 'Key is not the correct size (must be '
|
||||
],
|
||||
'empty key' => [
|
||||
'key' => '',
|
||||
'excpetion_message' => 'Key cannot be empty'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -164,6 +315,7 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$enc_key = CreateKey::generateRandomKey();
|
||||
|
||||
// class
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt = new SymmetricEncryption($key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->encrypt('test');
|
||||
@@ -172,6 +324,23 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
$crypt->setKey($key);
|
||||
$crypt->decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -180,6 +349,23 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$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 ::encryptKey
|
||||
* @covers ::decryptKey
|
||||
* @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);
|
||||
@@ -190,6 +376,8 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
SymmetricEncryption::decryptKey($encrypted, $key);
|
||||
}
|
||||
|
||||
// MARK: wrong input
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -232,6 +420,49 @@ final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decryptKey($input, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decryptKey
|
||||
* @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 ::decryptKey
|
||||
* @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__
|
||||
|
||||
@@ -969,44 +969,76 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
|
||||
"query" => ["foo-get" => "bar"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: get response code not matching");
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"GET",'
|
||||
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_FIRST_CALL":"get",'
|
||||
. '"HTTP_ACCEPT":"*\/*"},"REQUEST_TYPE":"GET","PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_FIRST_CALL":"get","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"GET",'
|
||||
. '"PARAMS":{"foo-get":"bar"},"BODY":null}',
|
||||
$response['content'],
|
||||
'multi call: get content not matching'
|
||||
);
|
||||
}
|
||||
// post
|
||||
$response = $curl->post($this->url_basic, [
|
||||
"headers" => ["second-call" => "post"],
|
||||
"body" => ["foo-post" => "baz"]
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: post response code not matching");
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"POST",'
|
||||
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*"},'
|
||||
. '"REQUEST_TYPE":"POST","PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_SECOND_CALL":"post","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"POST",'
|
||||
. '"PARAMS":[],"BODY":{"foo-post":"baz"}}',
|
||||
$response['content'],
|
||||
'multi call: post content not matching'
|
||||
);
|
||||
}
|
||||
// delete
|
||||
$response = $curl->delete($this->url_basic, [
|
||||
"headers" => ["third-call" => "delete"],
|
||||
]);
|
||||
$this->assertEquals("200", $response["code"], "multi call: delete response code not matching");
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"DELETE",'
|
||||
. '"PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_HOST":"soba.egplusww.jp",'
|
||||
. '"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*"},'
|
||||
. '"REQUEST_TYPE":"DELETE","PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
'{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1",'
|
||||
. '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",'
|
||||
. '"HTTP_HOST":"soba.egplusww.jp"},'
|
||||
. '"REQUEST_TYPE":"DELETE",'
|
||||
. '"PARAMS":[],"BODY":[]}',
|
||||
$response['content'],
|
||||
'multi call: delete content not matching'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: auth header set via config
|
||||
|
||||
Reference in New Issue
Block a user