Compare commits

...

40 Commits

Author SHA1 Message Date
Clemens Schwaighofer
53c7dda9a0 JSON Array to convert fix, bool/false return fix 2023-08-02 16:34:57 +09:00
Clemens Schwaighofer
daf2706e7e Release: v9.3.6 2023-07-26 12:13:59 +09:00
Clemens Schwaighofer
6d3a7b7b28 ACL\Login bug fix for acl:base must always be int 2023-07-26 11:52:53 +09:00
Clemens Schwaighofer
745340a7f5 Release: v9.3.5 2023-07-24 09:16:09 +09:00
Clemens Schwaighofer
419c578c46 ACL\Login get edit access id return value fix 2023-07-24 09:14:16 +09:00
Clemens Schwaighofer
6beff9c6ac Release: v9.3.4 2023-07-21 19:07:40 +09:00
Clemens Schwaighofer
79dbd053fa Remove per class loggingn from ACL\Login 2023-07-21 19:06:30 +09:00
Clemens Schwaighofer
74004e5221 Release: v9.3.3 2023-07-21 17:53:18 +09:00
Clemens Schwaighofer
0392187299 DB\IO init with null 2023-07-21 17:52:16 +09:00
Clemens Schwaighofer
edcc65df3e Release: v9.3.2 2023-07-21 17:49:09 +09:00
Clemens Schwaighofer
5dde52a309 DB\IO error/warning log prefix remove, Admin\Backend acl default value check 2023-07-14 15:10:03 +09:00
Clemens Schwaighofer
5f223fb50d Release: v9.3.1 2023-07-04 11:47:24 +09:00
Clemens Schwaighofer
2eaf80b1bd new Combined\DateTime method dateRangeHasWeekend 2023-07-04 11:46:34 +09:00
Clemens Schwaighofer
b5d601aec0 Release: v9.3.0 2023-06-28 15:34:31 +09:00
Clemens Schwaighofer
edcc77a6ab HtmlBuilder classes 2023-06-28 15:33:12 +09:00
Clemens Schwaighofer
0b1df7a901 Release: v9.2.0 2023-06-16 13:28:44 +09:00
Clemens Schwaighofer
e0f8bad2d9 DB\IO Call trace in context array for any debug calls 2023-06-16 13:24:34 +09:00
Clemens Schwaighofer
f5cd71cfbc Release: v9.1.0 2023-06-13 11:57:30 +09:00
Clemens Schwaighofer
1203164d7e DB\IO: add convert types to php type (TTD-606)
Convert DB types to PHP types
Current settings:
- off
- on: int/bool on;y
- json: json strings to array
- numeric: and real/float to php float (possible precision loss)
- bytea: convert PostgreSQL bytea strings to php data strings
2023-06-13 11:54:53 +09:00
Clemens Schwaighofer
b82e08ba05 Release: v9.0.8 2023-06-05 18:32:58 +09:00
Clemens Schwaighofer
6dfb68a6da Logging: internal fixes 2023-06-05 09:34:29 +09:00
Clemens Schwaighofer
5b944cd12d Release: v9.0.7 2023-06-05 09:32:12 +09:00
Clemens Schwaighofer
65cac4c6e2 Logging bug fixes for per_date/per_run flags 2023-06-02 17:45:21 +09:00
Clemens Schwaighofer
1c1ace58db Release: v9.0.6 2023-06-01 13:10:28 +09:00
Clemens Schwaighofer
16e12b5b8f Bug fixes 2023-06-01 13:08:24 +09:00
Clemens Schwaighofer
73063d28b2 Release: v9.0.5 2023-06-01 12:05:03 +09:00
Clemens Schwaighofer
e80d8006a2 print_r methods use mixed paramter 2023-06-01 12:03:38 +09:00
Clemens Schwaighofer
d648e4339a Must give go flag for publish 2023-06-01 11:09:37 +09:00
Clemens Schwaighofer
4b084f8785 Release: v9.0.4 2023-06-01 11:06:36 +09:00
Clemens Schwaighofer
d0d088b354 DB\IO bug fixes 2023-06-01 11:05:25 +09:00
Clemens Schwaighofer
e0d42af1d2 Release: v9.0.3 2023-06-01 09:17:33 +09:00
Clemens Schwaighofer
c1d26f122e phpunit tests updates 2023-06-01 09:16:06 +09:00
Clemens Schwaighofer
2c2826ac24 Bug fixes and clean ups 2023-06-01 08:47:40 +09:00
Clemens Schwaighofer
72f0810898 Release: v9.0.2 2023-05-31 18:49:58 +09:00
Clemens Schwaighofer
b69539b340 Merge branch 'development' 2023-05-31 18:48:58 +09:00
Clemens Schwaighofer
0b133133dd Release: v9.0.1 2023-05-31 18:48:55 +09:00
Clemens Schwaighofer
8fbe855fd4 Logger\Logger Excpetion calls update 2023-05-31 18:48:00 +09:00
Clemens Schwaighofer
d7410dfe78 Merge branch 'development' 2023-05-31 18:46:02 +09:00
Clemens Schwaighofer
5d36ac2f3e CoreLibs update v9.0.1 2023-05-31 18:45:47 +09:00
Clemens Schwaighofer
2e4ace1a39 Release: v9.0.0 2023-05-31 16:52:59 +09:00
33 changed files with 3107 additions and 389 deletions

View File

@@ -16,7 +16,7 @@
],
"minimum-stability": "dev",
"require": {
"php": ">=8.1",
"php": ">=8.2",
"psr/log": "^3.0@dev"
},
"require-dev": {

View File

@@ -1 +1 @@
8.5.0
9.3.6

View File

@@ -3,6 +3,7 @@
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
VERSION=$(git tag --list | sort -V | tail -n1 | sed -e "s/^v//");
file_last_published="${BASE_FOLDER}last.published";
go_flag="$1";
if [ -z "${VERSION}" ]; then
echo "Version must be set in the form x.y.z without any leading characters";
@@ -35,6 +36,13 @@ source .env.deploy;
cd -;
set +o allexport;
if [ "${go_flag}" != "go" ]; then
echo "No go flag given";
echo "Would publish ${VERSION}";
echo "[END]";
exit;
fi;
echo "[START]";
# gitea
if [ ! -z "${GITEA_URL_DL}" ] && [ ! -z "${GITEA_URL_PUSH}" ] &&

View File

@@ -231,8 +231,6 @@ class Login
) {
// attach db class
$this->db = $db;
// log login data for this class only
$log->setLogFlag(\CoreLibs\Logging\Logger\Flag::per_class);
// attach logger
$this->log = $log;
// attach session class
@@ -1060,9 +1058,9 @@ class Login
];
// set the default unit
if ($res['edit_default']) {
$_SESSION['UNIT_DEFAULT'] = $res['edit_access_id'];
$_SESSION['UNIT_DEFAULT'] = (int)$res['edit_access_id'];
}
$_SESSION['UNIT_UID'][$res['uid']] = $res['edit_access_id'];
$_SESSION['UNIT_UID'][$res['uid']] = (int)$res['edit_access_id'];
// sub arrays for simple access
array_push($eauid, $res['edit_access_id']);
$unit_acl[$res['edit_access_id']] = $res['level'];
@@ -1148,18 +1146,18 @@ class Login
// user > page > group
// group ACL 0
if ($_SESSION['GROUP_ACL_LEVEL'] != -1) {
$this->acl['base'] = $_SESSION['GROUP_ACL_LEVEL'];
$this->acl['base'] = (int)$_SESSION['GROUP_ACL_LEVEL'];
}
// page ACL 1
if (
isset($_SESSION['PAGES_ACL_LEVEL'][$this->page_name]) &&
$_SESSION['PAGES_ACL_LEVEL'][$this->page_name] != -1
) {
$this->acl['base'] = $_SESSION['PAGES_ACL_LEVEL'][$this->page_name];
$this->acl['base'] = (int)$_SESSION['PAGES_ACL_LEVEL'][$this->page_name];
}
// user ACL 2
if ($_SESSION['USER_ACL_LEVEL'] != -1) {
$this->acl['base'] = $_SESSION['USER_ACL_LEVEL'];
$this->acl['base'] = (int)$_SESSION['USER_ACL_LEVEL'];
}
}
$_SESSION['BASE_ACL_LEVEL'] = $this->acl['base'];
@@ -2347,7 +2345,10 @@ HTML;
is_array($_SESSION['UNIT']) &&
!array_key_exists($edit_access_id, $_SESSION['UNIT'])
) {
return $_SESSION['UNIT_DEFAULT'] ?? null;
$edit_access_id = null;
if (is_numeric($_SESSION['UNIT_DEFAULT'])) {
$edit_access_id = (int)$_SESSION['UNIT_DEFAULT'];
}
}
return $edit_access_id;
}

View File

@@ -164,6 +164,10 @@ class Backend
);
}
$this->default_acl = $set_default_acl_level ?? DEFAULT_ACL_LEVEL;
// if negative or larger than 100, reset to 0
if ($this->default_acl < 0 || $this->default_acl > 100) {
$this->default_acl = 0;
}
// queue key
if (preg_match("/^(add|save|delete|remove|move|up|down|push_live)$/", $this->action)) {

View File

@@ -41,7 +41,8 @@ class EditBase
/**
* construct form generator
*
* @param array<mixed> $db_config db config array, mandatory
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class, null auto set
* @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set
* @param \CoreLibs\ACL\Login $login login class for ACL settings

View File

@@ -169,10 +169,10 @@ class Email
* @param string $email email string
* @param bool $short default false, if true,
* returns only short type (pc instead of pc_html)
* @return string|bool email type, eg "pc", "docomo", etc,
* @return string|false email type, eg "pc", "docomo", etc,
* false for invalid short type
*/
public static function getEmailType(string $email, bool $short = false)
public static function getEmailType(string $email, bool $short = false): string|false
{
// trip if there is no email address
if (!$email) {
@@ -200,9 +200,9 @@ class Email
* gets the short email type from a long email type
*
* @param string $email_type email string
* @return string|bool short string or false for invalid
* @return string|false short string or false for invalid
*/
public static function getShortEmailType(string $email_type)
public static function getShortEmailType(string $email_type): string|false
{
// check if the short email type exists
if (isset(self::$mobile_email_type_short[$email_type])) {

View File

@@ -452,6 +452,31 @@ class DateTime
return $days;
}
}
/**
* check if a weekend day (sat/sun) is in the given date range
* Can have time too, but is not needed
*
* @param string $start_date Y-m-d
* @param string $end_date Y-m-d
* @return bool True for has weekend, False for has not
*/
public static function dateRangeHasWeekend(
string $start_date,
string $end_date,
): bool {
$dd_start = new \DateTime($start_date);
$dd_end = new \DateTime($end_date);
if (
// starts with a weekend
$dd_start->format('N') >= 6 ||
// start day plus diff will be 6 and so fall into a weekend
((int)$dd_start->format('w') + $dd_start->diff($dd_end)->days) >= 6
) {
return true;
}
return false;
}
}
// __END__

View File

@@ -28,7 +28,7 @@ class Colors
* @param int $green green 0-255
* @param int $blue blue 0-255
* @param bool $hex_prefix default true, prefix with "#"
* @return string|bool rgb in hex values with leading # if set,
* @return string|false rgb in hex values with leading # if set,
* false for invalid color
*/
public static function rgb2hex(
@@ -36,7 +36,7 @@ class Colors
int $green,
int $blue,
bool $hex_prefix = true
): string|bool {
): string|false {
$hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
@@ -58,14 +58,14 @@ class Colors
* @param string $hexStr RGB hexstring
* @param bool $return_as_string flag to return as string
* @param string $seperator string seperator: default: ","
* @return string|array<string,float|int>|bool false on error or array with RGB
* or a string with the seperator
* @return string|array<string,float|int>|false false on error or array with RGB
* or a string with the seperator
*/
public static function hex2rgb(
string $hexStr,
bool $return_as_string = false,
string $seperator = ','
): string|array|bool {
): string|array|false {
$hexStr = preg_replace("/[^0-9A-Fa-f]/", '', $hexStr); // Gets a proper hex string
if (!is_string($hexStr)) {
return false;
@@ -97,13 +97,13 @@ class Colors
* returns:
* array with hue (0-360), sat (0-100%), brightness/value (0-100%)
*
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @return array<int|float>|bool Hue, Sat, Brightness/Value
* false for input value error
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @return array<int|float>|false Hue, Sat, Brightness/Value
* false for input value error
*/
public static function rgb2hsb(int $red, int $green, int $blue): array|bool
public static function rgb2hsb(int $red, int $green, int $blue): array|false
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
@@ -144,13 +144,13 @@ class Colors
* converts HSB/V to RGB values RGB is full INT
* if HSB/V value is invalid, sets this value to 0
*
* @param float $H hue 0-360 (int)
* @param float $S saturation 0-100 (int)
* @param float $V brightness/value 0-100 (int)
* @return array<int>|bool 0 red/1 green/2 blue array as 0-255
* false for input value error
* @param float $H hue 0-360 (int)
* @param float $S saturation 0-100 (int)
* @param float $V brightness/value 0-100 (int)
* @return array<int>|false 0 red/1 green/2 blue array as 0-255
* false for input value error
*/
public static function hsb2rgb(float $H, float $S, float $V): array|bool
public static function hsb2rgb(float $H, float $S, float $V): array|false
{
// check that H is 0 to 359, 360 = 0
// and S and V are 0 to 1
@@ -230,13 +230,13 @@ class Colors
* return:
* array with hue (0-360), saturation (0-100%) and luminance (0-100%)
*
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @return array<float>|bool hue/sat/luminance
* false for input value error
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @return array<float>|false hue/sat/luminance
* false for input value error
*/
public static function rgb2hsl(int $red, int $green, int $blue): array|bool
public static function rgb2hsl(int $red, int $green, int $blue): array|false
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
@@ -284,12 +284,12 @@ class Colors
* converts an HSL to RGB
* if HSL value is invalid, set this value to 0
*
* @param float $hue hue: 0-360 (degrees)
* @param float $sat saturation: 0-100
* @param float $lum luminance: 0-100
* @return array<int,float|int>|bool red/blue/green 0-255 each
* @param float $hue hue: 0-360 (degrees)
* @param float $sat saturation: 0-100
* @param float $lum luminance: 0-100
* @return array<int,float|int>|false red/blue/green 0-255 each
*/
public static function hsl2rgb(float $hue, float $sat, float $lum): array|bool
public static function hsl2rgb(float $hue, float $sat, float $lum): array|false
{
if ($hue == 360) {
$hue = 0;

View File

@@ -48,8 +48,26 @@ class Json
return (array)$json;
}
/**
* convert array to json
* Will set empty json {} on false/error
* Error can be read with jsonGetLastError
* Deos not throw errors
*
* @param array<mixed> $data
* @param int $flags json_encode flags as is
* @return string JSON string or '{}' if false
*/
public static function jsonConvertArrayTo(array $data, int $flags = 0): string
{
$json_string = json_encode($data, $flags) ?: '{}';
self::$json_last_error = json_last_error();
return (string)$json_string;
}
/**
* returns human readable string for json errors thrown in jsonConvertToArray
* Source: https://www.php.net/manual/en/function.json-last-error.php
*
* @param bool $return_string [default=false] if set to true
* it will return the message string and not
@@ -80,6 +98,15 @@ class Json
case JSON_ERROR_UTF8:
$json_error_string = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
case JSON_ERROR_RECURSION:
$json_error_string = 'One or more recursive references in the value to be encoded';
break;
case JSON_ERROR_INF_OR_NAN:
$json_error_string = 'One or more NAN or INF values in the value to be encoded';
break;
case JSON_ERROR_UNSUPPORTED_TYPE:
$json_error_string = ' A value of a type that cannot be encoded was given';
break;
case JSON_ERROR_INVALID_PROPERTY_NAME:
$json_error_string = 'A key starting with \u0000 character was in the string';
break;

View File

@@ -54,7 +54,8 @@ class ArrayIO extends \CoreLibs\DB\IO
* constructor for the array io class, set the
* primary key name automatically (from array)
*
* @param array<mixed> $db_config db connection config
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db connection config
* @param array<mixed> $table_array table array config
* @param string $table_name table name string
* @param \CoreLibs\Logging\Logging $log Logging class
@@ -587,7 +588,7 @@ class ArrayIO extends \CoreLibs\DB\IO
// get it at the end, cause now we can be more sure of no double IDs, etc
reset($this->table_array);
// create select part & addition FK part
foreach ($this->table_array as $column => $data_array) {
foreach (array_keys($this->table_array) as $column) {
// check FK ...
if (
isset($this->table_array[$column]['fk']) &&

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/6/9
* DESCRIPTION:
* DB Options for convert type
*
* off: no conversion (all string)
* on: int/bool only
* json: json/jsonb to array
* numeric: any numeric or float to float
* bytes: decode bytea to string
*/
declare(strict_types=1);
namespace CoreLibs\DB\Options;
enum Convert: int
{
/** do not convert */
case off = 0;
/** convert only int/bool */
case on = 1;
/** also convert json to php array */
case json = 2;
/** also convert any float/real/numeric to php float */
case numeric = 4;
/** also decode bytea string to php string */
case bytea = 8;
/**
* get internal name from string value
*
* @param non-empty-string $name
* @return self
*/
public static function fromName(string $name): self
{
return match ($name) {
'Off', 'off', 'OFF', 'convert_off', 'CONVERT_OFF' => self::off,
'On', 'on', 'ON', 'convert_on', 'CONVERT_ON' => self::on,
'Json', 'json', 'JSON', 'convert_json', 'CONVERT_JSON' => self::json,
'Numeric', 'numeric', 'NUMERIC', 'convert_numeric', 'CONVERT_NUMERIC' => self::numeric,
'Bytea', 'bytea', 'BYTEA', 'convert_bytea', 'CONVERT_BYTEA' => self::bytea,
default => self::off,
};
}
/**
* Get internal name from int value
*
* @param int $value
* @return self
*/
public static function fromValue(int $value): self
{
return self::from($value);
}
}
// __END__

View File

@@ -34,29 +34,29 @@ class Support
}
/**
* prints a html formatted (pre) array
* prints a html formatted (pre) data
*
* @param array<mixed> $array any array
* @param bool $no_html default add <pre>
* @return string formatted array for output with <pre> tag added
* @param mixed $data any data
* @param bool $no_html default add <pre>
* @return string formatted array for output with <pre> tag added
*/
public static function printAr(array $array, bool $no_html = false): string
public static function printAr(mixed $data, bool $no_html = false): string
{
return $no_html ?
print_r($array, true) :
'<pre>' . print_r($array, true) . '</pre>';
print_r($data, true) :
'<pre>' . print_r($data, true) . '</pre>';
}
/**
* alternate name for printAr function
*
* @param array<mixed> $array any array
* @param bool $no_html default add <pre>
* @return string formatted array for output with <pre> tag added
* @param mixed $data any array
* @param bool $no_html default add <pre>
* @return string formatted array for output with <pre> tag added
*/
public static function printArray(array $array, bool $no_html = false): string
public static function printArray(mixed $data, bool $no_html = false): string
{
return self::printAr($array, $no_html);
return self::printAr($data, $no_html);
}
/**
@@ -65,12 +65,12 @@ class Support
* Do not use this without using it in a string in debug function
* Note: for full data debug dumps use Support::dumpVar()
*
* @param array<mixed> $a Array to format
* @return string print_r formated
* @param mixed $data Data to print
* @return string print_r formated
*/
public static function prAr(array $a): string
public static function prAr(mixed $data): string
{
return self::printAr($a, true);
return self::printAr($data, true);
}
/**
@@ -177,7 +177,6 @@ class Support
$caller_level = 1;
$caller_list = self::getCallerMethodList();
if ($caller_list[0] == 'dV') {
echo "Raise caller level<br>: " . $caller_list[0] . "<br>";
$caller_level = 2;
}
// we need to strip the string in <small></small that is

View File

@@ -213,7 +213,7 @@ class Logging
$settings['mandatory'] && !isset($options[$name]) &&
empty($settings['alias'])
) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'Missing mandatory option: "' . $name . '"',
E_USER_WARNING
);
@@ -230,7 +230,7 @@ class Logging
switch ($settings['type']) {
case 'bool':
if (!is_bool($this->options[$name])) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'Option: "' . $name . '" is not of type bool',
E_USER_ERROR
);
@@ -238,7 +238,7 @@ class Logging
break;
case 'string':
if (!is_string($this->options[$name])) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'Option: "' . $name . '" is not of type string',
E_USER_ERROR
);
@@ -249,7 +249,7 @@ class Logging
empty($settings['type_info']) ||
!$this->options[$name] instanceof $settings['type_info']
) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'Option: "' . $name . '" is not of instance '
. ($settings['type_info'] ?? 'NO INSTANCE DEFINED'),
E_USER_ERROR
@@ -407,13 +407,6 @@ class Logging
}
$this->setLogFlag($log_flag_key);
}
// init per run uid
if ($this->getLogFlag(Flag::per_run)) {
$this->setLogUniqueId();
} elseif ($this->getLogFlag(Flag::per_date)) {
// init file date
$this->log_file_date = date('Y-m-d');
}
}
/**
@@ -535,7 +528,7 @@ class Logging
* Prepare the log message with all needed info blocks:
* [timestamp] [host name] [file path + file] [running uid] {class} <debug level/group id> - message
*
* @param string $level_str Log level we will write to
* @param Level $level Log level we will write to
* @param string|Stringable $message The message to write
* @param mixed[] $context Any additional info we want to attach in any format
* @param string $group_id A group id, only used in DEBUG level,
@@ -543,11 +536,15 @@ class Logging
* @return string
*/
private function prepareLog(
string $level_str,
Level $level,
string|\Stringable $message,
array $context = [],
string $group_id = '',
): string {
// only prepare if to write log level is in set log level
if (!$this->checkLogLevel($level)) {
return '';
}
// file + line: call not this but one before (the one that calls this)
$file_line = Support::getCallerFileLine(2) ??
System::getPageName(System::FULL_PATH);
@@ -562,7 +559,7 @@ class Logging
$timestamp = Support::printTime();
// if group id is empty replace it with current level
$group_str = $level_str;
$group_str = $level->getName();
if (!empty($group_id)) {
$group_str .= ':' . $group_id;
}
@@ -570,7 +567,7 @@ class Logging
$context_str = '';
if ($context != []) {
// TODO this here has to be changed to something better
$context_str = ' ' . print_r($context, true);
$context_str = ' :' . print_r($context, true);
}
// build log string
return '[' . $timestamp . '] '
@@ -675,9 +672,11 @@ class Logging
*
* @return bool True, we are at debug level
*/
public function getJsDebug(): bool
public function loggingLevelIsDebug(): bool
{
return $this->log_level === Level::Debug ? true : false;
return $this->getLoggingLevel()->includes(
Level::Debug
);
}
// log file id set (file name prefix)
@@ -774,6 +773,13 @@ class Logging
public function setLogFlag(Flag $flag): void
{
$this->log_flags |= $flag->value;
// init per run uid
if ($this->getLogFlag(Flag::per_run)) {
$this->setLogUniqueId();
} elseif ($this->getLogFlag(Flag::per_date)) {
// init file date
$this->setLogDate();
}
}
/**
@@ -904,6 +910,42 @@ class Logging
// MAIN CALLS
// *********************************************************************
/**
* Commong log interface
*
* extended with group_id, prefix that are ONLY used for debug level
*
* @param Level $level
* @param string|\Stringable $message
* @param mixed[] $context
* @param string $group_id
* @param string $prefix
* @return bool
*/
public function log(
Level $level,
string|\Stringable $message,
array $context = [],
string $group_id = '',
string $prefix = '',
): bool {
// if we are not debug, ignore group_id and prefix
if ($level != Level::Debug) {
$group_id = '';
$prefix = '';
}
return $this->writeErrorMsg(
$level,
$this->prepareLog(
$level,
$prefix . $message,
$context,
$group_id
),
$group_id
);
}
/**
* DEBUG: 100
*
@@ -911,23 +953,23 @@ class Logging
*
* @param string $group_id id for error message, groups messages together
* @param string|Stringable $message the actual error message
* @param mixed[] $context
* @param string $prefix Attach some block before $string.
* Will not be stripped even
* when strip is true
* if strip is false, recommended to add that to $string
* @param mixed[] $context
* @return bool True if logged, false if not logged
*/
public function debug(
string $group_id,
string|\Stringable $message,
string $prefix = '',
array $context = []
array $context = [],
string $prefix = ''
): bool {
return $this->writeErrorMsg(
$this->log_level,
Level::Debug,
$this->prepareLog(
Level::Debug->getName(),
Level::Debug,
$prefix . $message,
$context,
$group_id
@@ -948,7 +990,7 @@ class Logging
return $this->writeErrorMsg(
Level::Info,
$this->prepareLog(
Level::Info->getName(),
Level::Info,
$message,
$context,
)
@@ -967,7 +1009,7 @@ class Logging
return $this->writeErrorMsg(
Level::Notice,
$this->prepareLog(
Level::Notice->getName(),
Level::Notice,
$message,
$context,
)
@@ -986,7 +1028,7 @@ class Logging
return $this->writeErrorMsg(
Level::Warning,
$this->prepareLog(
Level::Warning->getName(),
Level::Warning,
$message,
$context,
)
@@ -1005,7 +1047,7 @@ class Logging
return $this->writeErrorMsg(
Level::Error,
$this->prepareLog(
Level::Error->getName(),
Level::Error,
$message,
$context,
)
@@ -1024,7 +1066,7 @@ class Logging
return $this->writeErrorMsg(
Level::Critical,
$this->prepareLog(
Level::Critical->getName(),
Level::Critical,
$message,
$context,
)
@@ -1043,7 +1085,7 @@ class Logging
return $this->writeErrorMsg(
Level::Alert,
$this->prepareLog(
Level::Alert->getName(),
Level::Alert,
$message,
$context,
)
@@ -1062,7 +1104,7 @@ class Logging
return $this->writeErrorMsg(
Level::Emergency,
$this->prepareLog(
Level::Emergency->getName(),
Level::Emergency,
$message,
$context,
)
@@ -1080,12 +1122,12 @@ class Logging
* But this does not wrap it in <pre></pre>
* Do not use this without using it in a string in debug function
*
* @param array<mixed> $a Array to format
* @return string print_r formated
* @param mixed $data Data to format
* @return string print_r formated
*/
public function prAr(array $a): string
public function prAr(mixed $data): string
{
return Support::printArray($a, true);
return Support::printArray($data, true);
}
/**

View File

@@ -232,13 +232,13 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
/** @var string */
public string $col_name; // the name of the columen (before _<type>) [used for order button]
/** @var int */
public int $yes; // the yes flag that triggers the template to show ALL and not only new/load
public int $yes = 0; // the yes flag that triggers the template to show ALL and not only new/load
/** @var string */
public string $msg; // the error msg
public string $msg = ''; // the error msg
/** @var int */
public int $error; // the error flag set for printing red error msg
public int $error = 0; // the error flag set for printing red error msg
/** @var int */
public int $warning; // warning flag, for information (saved, loaded, etc)
public int $warning = 0; // warning flag, for information (saved, loaded, etc)
/** @var string */
public string $archive_pk_name; // the pk name for the load select form
/** @var string */
@@ -282,7 +282,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
public array $login_acl = [];
// layout publics
/** @var int */
public int $table_width;
public int $table_width = 0;
// internal lang & encoding vars
/** @var string */
public string $lang_dir = '';
@@ -308,7 +308,8 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
/**
* construct form generator
*
* @param array<mixed> $db_config db config array, mandatory
* phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,mixed> $login_acl Login ACL array,

View File

@@ -36,6 +36,7 @@ class Image
): string|false {
// get image type flags
$image_types = [
0 => 'UNKOWN-IMAGE',
1 => 'gif',
2 => 'jpg',
3 => 'png'
@@ -69,7 +70,7 @@ class Image
}
// does this picture exist and is it a picture
if (file_exists($filename) && is_file($filename)) {
[$width, $height, $type] = getimagesize($filename) ?: [];
[$width, $height, $type] = getimagesize($filename) ?: [0, 0, 0];
$convert_prefix = '';
$create_file = false;
$delete_filename = '';
@@ -98,7 +99,7 @@ class Image
if (!is_file($filename)) {
$filename .= '-0';
}
[$width, $height, $type] = getimagesize($filename) ?: [];
[$width, $height, $type] = getimagesize($filename) ?: [0, 0, 0];
}
// if no size given, set size to original
if (!$size_x || $size_x < 1) {
@@ -117,7 +118,7 @@ class Image
$status = exec($convert_string, $output, $return);
// get the size of the converted data, if converted
if (is_file($thumbnail)) {
[$width, $height, $type] = getimagesize($thumbnail) ?: [];
[$width, $height, $type] = getimagesize($thumbnail) ?: [0, 0, 0];
}
}
if ($height > $size_y) {
@@ -217,7 +218,7 @@ class Image
return $thumbnail;
}
// $this->debug('IMAGE PREPARE', "FILENAME OK, THUMB WIDTH/HEIGHT OK");
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [];
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null];
$thumbnail_write_path = null;
$thumbnail_web_path = null;
// path set first
@@ -447,7 +448,7 @@ class Image
if (!function_exists('exif_read_data') || !is_writeable($filename)) {
return;
}
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [];
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null];
// add @ to avoid "file not supported error"
$exif = @exif_read_data($filename);
$orientation = null;

View File

@@ -0,0 +1,264 @@
<?php // phpcs:disable Generic.Files.LineLength
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/6/1
* DESCRIPTION:
* html builder: array
* static build for array lists (not objects)
*
* Recommended to use the Object one or for speed the String Replace
*/
namespace CoreLibs\Template\HtmlBuilder;
use CoreLibs\Template\HtmlBuilder\General\Settings;
use CoreLibs\Template\HtmlBuilder\General\Error;
use CoreLibs\Template\HtmlBuilder\General\HtmlBuilderExcpetion;
class Block
{
/**
* Undocumented function
*
* @param string $tag
* @param string $id
* @param string $content
* @param array<string> $css,
* @param array<string,string> $options
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
* @throws HtmlBuilderExcpetion
*/
public static function cel(
string $tag,
string $id = '',
string $content = '',
array $css = [],
array $options = []
): array {
if (!preg_match("/^[A-Za-z]+$/", $tag)) {
Error::setError(
'201',
'invalid or empty tag',
['tag' => $tag]
);
throw new HtmlBuilderExcpetion('Invalid or empty tag');
}
return [
'tag' => $tag,
'id' => $id,
'name' => $options['name'] ?? '',
'content' => $content,
'css' => $css,
'options' => $options,
'sub' => [],
];
}
/**
* Search element tree for id and add
* if id is empty add at current
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $base
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $attach
* @param string $id
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
*/
public static function ael(
array $base,
array $attach,
string $id = ''
): array {
// no id or matching id
if (
empty($id) ||
$base['id'] == $id
) {
self::addSub($base, $attach);
return $base;
}
// find id in 'id' in all 'sub'
foreach ($base['sub'] as $el) {
$el = self::ael($el, $attach, $id);
}
return $base;
}
/**
* Undocumented function
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $base
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} ...$attach
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
*/
public static function aelx(
array $base,
array ...$attach
): array {
$base = self::addSub($base, ...$attach);
return $base;
}
/**
* Undocumented function
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $element
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $sub
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
*/
public static function addSub(array $element, array ...$sub): array
{
if (!isset($element['sub'])) {
$element['sub'] = [];
}
array_push($element['sub'], ...$sub);
return $element;
}
/**
* Undocumented function
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $element
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
*/
public static function resetSub(array $element): array
{
$element['sub'] = [];
return $element;
}
// CSS Elements
/**
* Undocumented function
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $element
* @param string ...$css
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
*/
public static function acssel(array $element, string ...$css): array
{
$element['css'] = array_unique(array_merge($element['css'] ?? [], $css));
return $element;
}
/**
* Undocumented function
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $element
* @param string ...$css
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
*/
public static function rcssel(array $element, string ...$css): array
{
$element['css'] = array_diff($element['css'] ?? [], $css);
return $element;
}
/**
* Undocumented function
* scssel (switch) is not supported
* use rcssel -> acssel
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $element
* @param array<string> $rcss
* @param array<string> $acss
* @return array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}
*/
public static function scssel(array $element, array $rcss, array $acss): array
{
return self::acssel(
self::rcssel($element, ...$rcss),
...$acss
);
}
/**
* Undocumented function
* alias phfo
*
* @param array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>} $tree
* @param bool $add_nl [default=false]
* @return string
*/
public static function buildHtml(array $tree, bool $add_nl = false): string
{
if (empty($tree['tag'])) {
return '';
}
// print "D01: " . microtime(true) . "<br>";
$line = '<' . $tree['tag'];
if (!empty($tree['id'])) {
$line .= ' id="' . $tree['id'] . '"';
if (in_array($tree['tag'], Settings::NAME_ELEMENTS)) {
$line .= ' name="'
. (!empty($tree['name']) ? $tree['name'] : $tree['id'])
. '"';
}
}
if (count($tree['css'])) {
$line .= ' class="' . join(' ', $tree['css']) . '"';
}
foreach ($tree['options'] ?? [] as $key => $item) {
if (in_array($key, Settings::SKIP_OPTIONS)) {
continue;
}
$line .= ' ' . $key . '="' . $item . '"';
}
$line .= '>';
if (!empty($tree['content'])) {
$line .= $tree['content'];
}
// sub nodes
foreach ($tree['sub'] ?? [] as $sub) {
if ($add_nl === true) {
$line .= "\n";
}
$line .= self::buildHtml($sub, $add_nl);
if ($add_nl === true) {
$line .= "\n";
}
}
// close line if needed
if (!in_array($tree['tag'], Settings::NO_CLOSE)) {
$line .= '</' . $tree['tag'] . '>';
}
return $line;
}
/**
* Undocumented function
* alias phfa
*
* @param array<array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}> $list
* @param bool $add_nl [default=false]
* @return string
*/
public static function buildHtmlFromList(array $list, bool $add_nl = false): string
{
$output = '';
foreach ($list as $el) {
$output .= self::buildHtml($el, $add_nl);
}
return $output;
}
/**
* Undocumented function
* wrapper for buildHtmlFromList
*
* @param array<array{tag:string,id:string,name:string,content:string,css:array<string>,options:array<string,string>,sub:array<mixed>}> $list array of Elements to build string from
* @param bool $add_nl [default=false] Optional output string line break
* @return string build html as string
*/
public static function printHtmlFromArray(array $list, bool $add_nl = false): string
{
return self::buildHtmlFromList($list, $add_nl);
}
}
// __END__

View File

@@ -0,0 +1,559 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/6/1
* DESCRIPTION:
* html builder: element
* nested and connected objects
*/
declare(strict_types=1);
namespace CoreLibs\Template\HtmlBuilder;
use CoreLibs\Template\HtmlBuilder\General\Settings;
use CoreLibs\Template\HtmlBuilder\General\Error;
use CoreLibs\Template\HtmlBuilder\General\HtmlBuilderExcpetion;
class Element
{
/** @var string */
private string $tag = '';
/** @var string */
private string $id = '';
/** @var string */
private string $name = '';
/** @var string */
private string $content = '';
/** @var array<string> */
private array $css = [];
/** @var array<string,mixed> */
private array $options = [];
/** @var array<Element> list of elements */
private array $sub = [];
/**
* create new html element
*
* @param string $tag html tag (eg div, button, etc)
* @param string $id html tag id, used also for name if name
* not set in $options
* @param string $content content text inside, eg <div>Content</div>
* if sub elements exist, they are added after content
* @param array<string> $css array of css names, put style in $options
* @param array<string,string> $options Additional element options in
* key = value format
* eg: onClick => 'something();'
* id, css are skipped
* name only set on input/button
* @throws HtmlBuilderExcpetion
*/
public function __construct(
string $tag,
string $id = '',
string $content = '',
array $css = [],
array $options = []
) {
// exit if not valid tag
try {
$this->setTag($tag);
} catch (HtmlBuilderExcpetion $e) {
throw new HtmlBuilderExcpetion('Could not create Element', 0, $e);
}
$this->setId($id);
$this->setName($options['name'] ?? '');
$this->setContent($content);
$this->addCss(...$css);
$this->setOptions($options);
}
/**
* set tag
*
* @param string $tag
* @return void
* @throws HtmlBuilderExcpetion
*/
public function setTag(string $tag): void
{
// tag must be letters only
if (!preg_match("/^[A-Za-z]+$/", $tag)) {
Error::setError(
'201',
'invalid or empty tag',
['tag' => $tag]
);
throw new HtmlBuilderExcpetion('Invalid or empty tag: ' . $tag);
}
$this->tag = $tag;
}
/**
* get the tag name
*
* @return string HTML element tag
*/
public function getTag(): string
{
return $this->tag;
}
/**
* set the element id
*
* @param string $id
* @return void
*/
public function setId(string $id): void
{
// invalid id and name check too
// be strict: [a-zA-Z0-9], -, _
// cannot start with digit, two hyphens or a hyphen with a digit:
// 0abc
// __abc
// _0abc
if (
!empty($id) &&
!preg_match("/^[A-Za-z][\w-]*$/", $id)
) {
Error::setWarning(
'202',
'possible invalid id',
['id' => $id, 'tag' => $this->getTag()]
);
// TODO: shoud throw error
}
$this->id = $id;
}
/**
* get the html tag id
*
* @return string HTML element id
*/
public function getId(): string
{
return $this->id;
}
/**
* Set name for elements
* only for elements that need it (input/button/form)
*
* @param string $name
* @return void
*/
public function setName(string $name): void
{
if (
!empty($name) &&
!preg_match("/^[A-Za-z][\w-]*$/", $name)
) {
Error::setWarning(
'203',
'possible invalid name',
['name' => $name, 'id' => $this->getId(), 'tag' => $this->getTag()]
);
// TODO: shoud throw error
}
$this->name = $name;
}
/**
* get the name if set
*
* @return string Optional HTML name (eg for input)
*/
public function getName(): string
{
return $this->name;
}
/**
* Set new content for element
*
* @param string $content
* @return void
*/
public function setContent(string $content): void
{
$this->content = $content;
}
/**
* get the elment text content (not sub elements)
*
* @return string HTML content text as is
*/
public function getContent(): string
{
return $this->content;
}
/**
* set or update options
*
* @param array<string,mixed> $options
* @return void
*/
public function setOptions(array $options): void
{
foreach ($options as $key => $value) {
if (empty($key)) {
Error::setError(
'110',
'Cannot set option with empty key',
['id' => $this->getId(), 'tag' => $this->getTag()]
);
// TODO: shoud throw error
continue;
}
// if data is null
if ($value === null) {
if (isset($this->options[$key])) {
unset($this->options[$key]);
} else {
Error::setError(
'210',
'Cannot set option with null value',
['id' => $this->getId(), 'tag' => $this->getTag()]
);
}
// TODO: shoud throw error
continue;
}
$this->options[$key] = $value;
}
}
/**
* get the options array
* also holds "name" option
* anything like: style, javascript, value or any other html tag option
* right side can be empty but not null
*
* @return array<string,string> get options as list html option name and value
*/
public function getOptions(): array
{
return $this->options;
}
// Sub Elements
/**
* get the sub elements (array of Elements)
*
* @return array<Element> Array of Elements (that can have sub elements)
*/
public function getSub(): array
{
return $this->sub;
}
/**
* add one or many sub elements (add at the end)
*
* @param Element $sub One or many elements to add
* @return void
* @throws HtmlBuilderExcpetion
*/
public function addSub(Element ...$sub): void
{
foreach ($sub as $_sub) {
// if one of the elements is the same as this class, ignore it
// with this we avoid self reference loop
if ($_sub == $this) {
Error::setError(
'100',
'Cannot assign Element to itself, this would create an infinite loop',
['id' => $this->getId(), 'tag' => $this->getTag()]
);
throw new HtmlBuilderExcpetion('Cannot assign Element to itself, this would create an infinite loop');
}
array_push($this->sub, $_sub);
}
}
/**
* Remove an element from the sub array
* By pos in array or id set on first level
*
* @param int|string $id String id name or int pos number in array
* @return void
*/
public function removeSub(int|string $id): void
{
// find element with id and remove it
// or when number find pos in sub and remove it
if (is_int($id)) {
if (!isset($this->sub[$id])) {
return;
}
unset($this->sub[$id]);
return;
}
// only on first level
foreach ($this->sub as $pos => $el) {
if (
$el->getId() === $id
) {
unset($this->sub[$pos]);
return;
}
}
}
/**
* remove all sub elements
*
* @return void
*/
public function resetSub(): void
{
$this->sub = [];
}
// CSS Elements
/**
* get the current set css elements
*
* @return array<string> list of css element entries
*/
public function getCss(): array
{
return $this->css;
}
/**
* add one or many new css elements
* Note that we can chain: add/remove/reset
*
* @param string ...$css one or more css strings to add
* @return Element Current element for chaining
*/
public function addCss(string ...$css): Element
{
// should do check for empty/invalid css
$_set_css = [];
foreach ($css as $_css) {
if (empty($_css)) {
Error::setError(
'204',
'cannot have empty css string',
);
// TODO: shoud throw error
continue;
}
// -?[_A-Za-z][_A-Za-z0-9-]*
if (!preg_match("/^-?[_A-Za-z][_A-Za-z0-9-]*$/", $_css)) {
Error::setWarning(
'205',
'possible invalid css string',
['css' => $_css, 'id' => $this->id, 'tag' => $this->tag]
);
// TODO: shoud throw error
}
$_set_css[] = $_css;
}
$this->css = array_unique(array_merge($this->css, $_set_css));
return $this;
}
/**
* remove one or more css elements
* Note that we can chain: add/remove/reset
*
* @param string ...$css one or more css strings to remove
* @return Element Current element for chaining
*/
public function removeCss(string ...$css): Element
{
$this->css = array_diff($this->css, $css);
return $this;
}
/**
* unset all css elements
* Note that we can chain: add/remove/reset
*
* @return Element
*/
public function resetCss(): Element
{
$this->css = [];
return $this;
}
// build output from tree
/**
* build html string from the current element tree (self)
* or from the Element tree given as parameter
* if $add_nl is set then new lines are added before each sub element added
* no indet is done (tab or other)
*
* @param Element|null $tree Different Element tree to build
* if not set (null), self is used
* @param bool $add_nl [default=false] Optional output string line breaks
* @return string HTML as string
*/
public function buildHtml(Element $tree = null, bool $add_nl = false): string
{
// print "D01: " . microtime(true) . "<br>";
if ($tree === null) {
$tree = $this;
}
$line = '<' . $tree->getTag();
if ($tree->getId()) {
$line .= ' id="' . $tree->getId() . '"';
if (in_array($tree->getTag(), Settings::NAME_ELEMENTS)) {
$line .= ' name="'
. (!empty($tree->getName()) ? $tree->getName() : $tree->getId())
. '"';
}
}
if (count($tree->getCss())) {
$line .= ' class="' . join(' ', $tree->getCss()) . '"';
}
foreach ($tree->getOptions() as $key => $item) {
// skip
if (in_array($key, Settings::SKIP_OPTIONS)) {
continue;
}
$line .= ' ' . $key . '="' . $item . '"';
}
$line .= '>';
if (strlen($tree->getContent()) > 0) {
$line .= $tree->getContent();
}
// sub nodes
foreach ($tree->getSub() as $sub) {
if ($add_nl === true) {
$line .= "\n";
}
$line .= $tree->buildHtml($sub, $add_nl);
if ($add_nl === true) {
$line .= "\n";
}
}
// close line if needed
if (!in_array($tree->getTag(), Settings::NO_CLOSE)) {
$line .= '</' . $tree->getTag() . '>';
}
return $line;
}
// this is static
/**
* Builds a single string from an array of elements
* a new line can be added before each new element if $add_nl is set to true
*
* @param array<Element> $list array of Elements, uses buildHtml internal
* @param bool $add_nl [default=false] Optional output string line breaks
* @return string HTML as string
*/
public static function buildHtmlFromList(array $list, bool $add_nl = false): string
{
$output = '';
foreach ($list as $el) {
if (!empty($output) && $add_nl === true) {
$output .= "\n";
}
$output .= $el->buildHtml();
}
return $output;
}
// so we can call builder statically
/**
* Search element tree for id and add
* if id is empty add at element given in parameter $base
*
* @param Element $base Element to attach to
* @param Element $attach Element to attach (single)
* @param string $id Optional id, if empty then attached at the end
* If set will loop through ALL sub elements until
* matching id found. if not found, not added
* @return Element Element with attached sub element
*/
public static function addElementWithId(
Element $base,
Element $attach,
string $id = ''
): Element {
// no id or matching id
if (
empty($id) ||
$base->getId() == $id
) {
$base->addSub($attach);
return $base;
}
// find id in 'id' in all 'sub'
foreach ($base->getSub() as $el) {
self::addElementWithId($el, $attach, $id);
}
return $base;
}
/**
* add one or more elemens to $base
*
* @param Element $base Element to attach to
* @param Element ...$attach Element or Elements to attach
* @return Element Element with attached sub elements
*/
public static function addElement(
Element $base,
Element ...$attach
): Element {
// we must make sure we do not self attach
$base->addSub(...$attach);
return $base;
}
/**
* Static call version for building
* not recommended to be used, rather use "Element->buildHtml()"
* wrapper for buildHtml
*
* @param ?Element $tree Element tree to build
* if not set returns empty string
* @param bool $add_nl [default=false] Optional output string line break
* @return string build html as string
* @deprecated Do not use, use Element->buildHtml() instead
*/
public static function printHtmlFromObject(Element $tree = null, bool $add_nl = false): string
{
// nothing ->bad
if ($tree === null) {
return '';
}
return $tree->buildHtml(add_nl: $add_nl);
}
/**
* Undocumented function
* wrapper for buildHtmlFromList
*
* @param array<Element> $list array of Elements to build string from
* @param bool $add_nl [default=false] Optional output string line break
* @return string build html as string
*/
public static function printHtmlFromArray(array $list, bool $add_nl = false): string
{
return self::buildHtmlFromList($list, $add_nl);
}
}
// __END__

View File

@@ -0,0 +1,127 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/6/27
* DESCRIPTION:
* Error logging for the HtmlBuilder systs
*/
declare(strict_types=1);
namespace CoreLibs\Template\HtmlBuilder\General;
class Error
{
/** @var array<array{level:string,id:string,message:string,context:array<mixed>}> */
private static array $messages = [];
/**
* internal writer for messages
*
* @param string $level
* @param string $id
* @param string $message
* @param array<mixed> $context
* @return void
*/
private static function writeContent(
string $level,
string $id,
string $message,
array $context
): void {
self::$messages[] = [
'level' => $level,
'id' => $id,
'message' => $message,
'context' => $context,
];
}
/**
* warning collector for all internal string errors
* builds an warning with warning id, message text and array with optional content
*
* @param string $id
* @param string $message
* @param array<mixed> $context
* @return void
*/
public static function setWarning(string $id, string $message, array $context = []): void
{
self::writeContent('Warning', $id, $message, $context);
}
/**
* error collector for all internal string errors
* builds an error with error id, message text and array with optional content
*
* @param string $id
* @param string $message
* @param array<mixed> $context
* @return void
*/
public static function setError(string $id, string $message, array $context = []): void
{
self::writeContent('Error', $id, $message, $context);
}
/**
* Return all set errors
*
* @return array<mixed>
*/
public static function getMessages(): array
{
return self::$messages;
}
/**
* Reset all errors
*
* @return void
*/
public static function resetMessages(): void
{
self::$messages = [];
}
/**
* internal level in message array exists check
*
* @param string $level
* @return bool
*/
private static function hasLevel(string $level): bool
{
return array_filter(
self::$messages,
function ($var) use ($level) {
return $var['level'] == $level ? true : false;
}
) === [] ? false : true;
}
/**
* Check if any error is set
*
* @return bool
*/
public static function hasError(): bool
{
return self::hasLevel('Error');
}
/**
* Check if any warning is set
*
* @return bool
*/
public static function hasWarning(): bool
{
return self::hasLevel('Warning');
}
}
// __END__

View File

@@ -0,0 +1,21 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/6/28
* DESCRIPTION:
* Exception class for the HtmlBuilder blocks
*/
declare(strict_types=1);
namespace CoreLibs\Template\HtmlBuilder\General;
/**
* Exception class for HtmlBuilder
*/
class HtmlBuilderExcpetion extends \Exception
{
}
// __END__

View File

@@ -0,0 +1,69 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/7/22
* DESCRIPTION:
* General settings for html elements
*/
declare(strict_types=1);
namespace CoreLibs\Template\HtmlBuilder\General;
class Settings
{
/** @var array<string> list of html elements that can have the name tag */
public const NAME_ELEMENTS = [
'button',
'fieldset',
'form',
'iframe',
'input',
'map',
'meta',
'object',
'output',
'param',
'select',
'textarea',
];
/** @var array<string> options key entries to be skipped in build */
public const SKIP_OPTIONS = [
'id',
'name',
'class',
];
/** @var array<string> html elements that don't need to be closed */
public const NO_CLOSE = [
'input',
'br',
'img',
'hr',
'area',
'col',
'keygen',
'wbr',
'track',
'source',
'param',
'command',
// only in header
'base',
'meta',
'link',
'embed',
];
/** @var array<string> invalid tags, not allowed in body */
public const NOT_IN_BODY_ALLOWED = [
'base',
'meta',
'link',
'embed', // not sure
];
}
// __END__

View File

@@ -0,0 +1,194 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/6/23
* DESCRIPTION:
* Simeple string replace calls for elements
*/
declare(strict_types=1);
namespace CoreLibs\Template\HtmlBuilder;
use CoreLibs\Template\HtmlBuilder\General\Error;
use CoreLibs\Template\HtmlBuilder\General\HtmlBuilderExcpetion;
class StringReplace
{
/** @var array<string,string> */
private static array $elements = [];
/** @var array<string,string> */
private static array $replace = [];
/**
* load html blocks into array for repeated usage
* each array group parameter has 0: index, 1: content
* There is no content check done.
* index must be non empty (but has no fixed format)
* if same index is tried twice it will set an error and skip
*
* @param array{0:string,1:string} ...$element Elements to load
* @return void
* @throws HtmlBuilderExcpetion
*/
public static function loadElements(array ...$element): void
{
foreach ($element as $el) {
$index = $el[0] ?? '';
if (empty($index)) {
Error::setError(
'310',
'Index cannot be an empty string',
[
'element' => $index
]
);
throw new HtmlBuilderExcpetion('Index cannot be an empty string');
}
if (isset(self::$elements[$index])) {
Error::setError(
'311',
'Index already exists',
[
'element' => $index
]
);
throw new HtmlBuilderExcpetion('Index already exists: ' . $index);
}
// content check?
self::$elements[$index] = $el[1];
}
}
/**
* update an element at index
* can also be used to reset (empty string)
*
* @param string $index
* @param string $element
* @return void
*/
public static function updateElement(string $index, string $element): void
{
if (!isset(self::$elements[$index])) {
Error::setError(
'312',
'Index does not exists',
[
'element' => $index
]
);
throw new HtmlBuilderExcpetion('Index does not exists: ' . $index);
}
// allow empty reset
self::$elements[$index] = $element;
}
/**
* get an element block at index
* if not found will return false
*
* @param string $index
* @return string
* @throws HtmlBuilderExcpetion
*/
public static function getElement(string $index): string
{
if (!isset(self::$elements[$index])) {
Error::setError('321', 'Index not found in elements', ['element' => $index]);
throw new HtmlBuilderExcpetion('Index not found in elements array: ' . $index);
}
return self::$elements[$index];
}
/**
* set a replacement block at index
* can be used for setting one block and using it agai
*
* @param string $index
* @param string $content
* @return void
*/
public static function setReplaceBlock(string $index, string $content): void
{
self::$replace[$index] = $content;
}
/**
* get replacement block at index, if not found return empty and set error
*
* @param string $index
* @return string
* @throws HtmlBuilderExcpetion
*/
public static function getReplaceBlock(string $index): string
{
if (!isset(self::$replace[$index])) {
Error::setError('331', 'Index not found in replace block', ['replace' => $index]);
throw new HtmlBuilderExcpetion('Index not found in replace block array: ' . $index);
}
return self::$replace[$index];
}
/**
* build and element on an index and either returns it or also sets it
* into the replace block array
* if index not found in relement list will return false
*
* @param string $index index of set element
* @param array<string,string> $replace array of text to search (key) and replace (value) for
* @return string
* @throws HtmlBuilderExcpetion
*/
public static function buildElement(
string $index,
array $replace,
string $replace_index = ''
): string {
try {
self::getElement($index);
} catch (HtmlBuilderExcpetion $e) {
throw new HtmlBuilderExcpetion('Cannot fetch element with index: ' . $index, 0, $e);
}
if ($replace_index) {
self::setReplaceBlock(
$replace_index,
self::replaceData(self::$elements[$index], $replace)
);
return self::getReplaceBlock($replace_index);
} else {
return self::replaceData(self::$elements[$index], $replace);
}
}
/**
* main replace entries in text string
* elements to be replaced are in {} brackets. if they are missing in the
* replace array they will be added.
* if the replace and content count is not the same then an error will be thrown
*
* @param string $data
* @param array<string,string> $replace
* @return string
* @throws HtmlBuilderExcpetion
*/
public static function replaceData(string $data, array $replace): string
{
$to_replace = array_keys($replace);
// all replace data must have {} around
array_walk($to_replace, function (&$entry) {
if (!str_starts_with($entry, '{')) {
$entry = '{' . $entry;
}
if (!str_ends_with($entry, '}')) {
$entry .= '}';
}
// do some validation?
});
// replace content
return str_replace($to_replace, array_values($replace), $data);
}
}
// __END__

View File

@@ -77,79 +77,79 @@ class SmartyExtend extends \Smarty
public string $COMPILE_ID = '';
// template vars
/** @var string */
public string $MASTER_TEMPLATE_NAME;
public string $MASTER_TEMPLATE_NAME = '';
/** @var string */
public string $PAGE_FILE_NAME;
public string $PAGE_FILE_NAME = '';
/** @var string */
public string $CONTENT_INCLUDE;
public string $CONTENT_INCLUDE = '';
/** @var string */
public string $FORM_NAME;
public string $FORM_NAME = '';
/** @var string */
public string $FORM_ACTION;
public string $FORM_ACTION = '';
/** @var string */
public string $L_TITLE;
public string $L_TITLE = '';
/** @var string|int */
public string|int $PAGE_WIDTH;
// smarty include/set var
/** @var string */
public string $TEMPLATE_PATH;
public string $TEMPLATE_PATH = '';
/** @var string */
public string $TEMPLATE_NAME;
public string $TEMPLATE_NAME = '';
/** @var string */
public string $INC_TEMPLATE_NAME;
public string $INC_TEMPLATE_NAME = '';
/** @var string */
public string $JS_TEMPLATE_NAME;
public string $JS_TEMPLATE_NAME = '';
/** @var string */
public string $CSS_TEMPLATE_NAME;
public string $CSS_TEMPLATE_NAME = '';
/** @var string|null */
public string|null $TEMPLATE_TRANSLATE;
/** @var string|null */
public string|null $JS_TRANSLATE;
// core group
/** @var string */
public string $JS_CORE_TEMPLATE_NAME;
public string $JS_CORE_TEMPLATE_NAME = '';
/** @var string */
public string $CSS_CORE_TEMPLATE_NAME;
public string $CSS_CORE_TEMPLATE_NAME = '';
/** @var string */
public string $JS_CORE_INCLUDE;
public string $JS_CORE_INCLUDE = '';
/** @var string */
public string $CSS_CORE_INCLUDE;
public string $CSS_CORE_INCLUDE = '';
// local names
/** @var string */
public string $JS_SPECIAL_TEMPLATE_NAME = '';
/** @var string */
public string $CSS_SPECIAL_TEMPLATE_NAME = '';
/** @var string */
public string $JS_INCLUDE;
public string $JS_INCLUDE = '';
/** @var string */
public string $CSS_INCLUDE;
public string $CSS_INCLUDE = '';
/** @var string */
public string $JS_SPECIAL_INCLUDE;
public string $JS_SPECIAL_INCLUDE = '';
/** @var string */
public string $CSS_SPECIAL_INCLUDE;
public string $CSS_SPECIAL_INCLUDE = '';
/** @var string */
public string $ADMIN_JAVASCRIPT;
public string $ADMIN_JAVASCRIPT = '';
/** @var string */
public string $ADMIN_STYLESHEET;
public string $ADMIN_STYLESHEET = '';
/** @var string */
public string $FRONTEND_JAVASCRIPT;
public string $FRONTEND_JAVASCRIPT = '';
/** @var string */
public string $FRONTEND_STYLESHEET;
public string $FRONTEND_STYLESHEET = '';
// other smarty folder vars
/** @var string */
public string $INCLUDES;
public string $INCLUDES = '';
/** @var string */
public string $JAVASCRIPT;
public string $JAVASCRIPT = '';
/** @var string */
public string $CSS;
public string $CSS = '';
/** @var string */
public string $FONT;
public string $FONT = '';
/** @var string */
public string $PICTURES;
public string $PICTURES = '';
/** @var string */
public string $CACHE_PICTURES;
public string $CACHE_PICTURES = '';
/** @var string */
public string $CACHE_PICTURES_ROOT;
public string $CACHE_PICTURES_ROOT = '';
// constructor class, just sets the language stuff
/**
@@ -373,7 +373,7 @@ class SmartyExtend extends \Smarty
// check for template include
if (
$this->USE_INCLUDE_TEMPLATE === true &&
!$this->TEMPLATE_NAME
empty($this->TEMPLATE_NAME)
) {
$this->TEMPLATE_NAME = $this->CONTENT_INCLUDE;
// add to cache & compile id
@@ -390,7 +390,7 @@ class SmartyExtend extends \Smarty
exit('MASTER TEMPLATE: ' . $this->MASTER_TEMPLATE_NAME . ' could not be found');
}
if (
$this->TEMPLATE_NAME &&
!empty($this->TEMPLATE_NAME) &&
!file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->TEMPLATE_NAME)
) {
exit('INCLUDE TEMPLATE: ' . $this->TEMPLATE_NAME . ' could not be found');
@@ -418,7 +418,12 @@ class SmartyExtend extends \Smarty
}
}
// if we can't find it, dump it
if (!file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->TEMPLATE_TRANSLATE)) {
if (
!file_exists(
$this->getTemplateDir()[0] . DIRECTORY_SEPARATOR
. $this->TEMPLATE_TRANSLATE
)
) {
$this->TEMPLATE_TRANSLATE = null;
}
if (empty($this->JS_TRANSLATE)) {

View File

@@ -458,6 +458,47 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
/**
* Undocumented function
*
* @return array
*/
public function dateRangeHasWeekendProvider(): array
{
return [
'no weekend' => [
'2023-07-03',
'2023-07-04',
false
],
'start weekend sat' => [
'2023-07-01',
'2023-07-04',
true
],
'start weekend sun' => [
'2023-07-02',
'2023-07-04',
true
],
'end weekend sat' => [
'2023-07-03',
'2023-07-08',
true
],
'end weekend sun' => [
'2023-07-03',
'2023-07-09',
true
],
'long period > 6 days' => [
'2023-07-03',
'2023-07-27',
true
]
];
}
/**
* date string convert test
*
@@ -780,6 +821,29 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
$output
);
}
/**
* Undocumented function
*
* @covers ::dateRangeHasWeekend
* @dataProvider dateRangeHasWeekendProvider
* @testdox dateRangeHasWeekend $start_date and $end_date are expected weekend $expected [$_dataName]
*
* @param string $start_date
* @param string $end_date
* @param bool $expected
* @return void
*/
public function testDateRangeHasWeekend(
string $start_date,
string $end_date,
bool $expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::dateRangeHasWeekend($start_date, $end_date)
);
}
}
// __END__

View File

@@ -16,7 +16,7 @@ final class CoreLibsConvertJsonTest extends TestCase
/**
* test list for json convert tests
*
* @return array
* @return array<mixed>
*/
public function jsonProvider(): array
{
@@ -54,10 +54,36 @@ final class CoreLibsConvertJsonTest extends TestCase
];
}
/**
* Undocumented function
*
* @return array<mixed>
*/
public function jsonArrayProvider(): array
{
return [
'valid json' => [
[
'm' => 2,
'f' => 'sub_2'
],
'{"m":2,"f":"sub_2"}',
],
'empty json array' => [
[],
'[]'
],
'empty json hash' => [
['' => ''],
'{"":""}'
]
];
}
/**
* json error list
*
* @return array JSON error list
* @return array<mixed> JSON error list
*/
public function jsonErrorProvider(): array
{
@@ -127,7 +153,7 @@ final class CoreLibsConvertJsonTest extends TestCase
*
* @param string|null $input
* @param bool $flag
* @param array $expected
* @param array<mixed> $expected
* @return void
*/
public function testJsonConvertToArray(?string $input, bool $flag, array $expected): void
@@ -146,7 +172,8 @@ final class CoreLibsConvertJsonTest extends TestCase
* @testdox jsonGetLastError $input will be $expected_i/$expected_s [$_dataName]
*
* @param string|null $input
* @param string $expected
* @param int $expected_i
* @param string $expected_s
* @return void
*/
public function testJsonGetLastError(?string $input, int $expected_i, string $expected_s): void
@@ -161,6 +188,25 @@ final class CoreLibsConvertJsonTest extends TestCase
\CoreLibs\Convert\Json::jsonGetLastError(true)
);
}
/**
* Undocumented function
*
* @covers ::jsonConvertArrayTo
* @dataProvider jsonArrayProvider
* @testdox jsonConvertArrayTo $input (Override: $flag) will be $expected [$_dataName]
*
* @param array<mixed> $input
* @param string $expected
* @return void
*/
public function testJsonConvertArrayto(array $input, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Json::jsonConvertArrayTo($input)
);
}
}
// __END__

View File

@@ -37,6 +37,8 @@ namespace tests;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use CoreLibs\Logging\Logger\Level;
use CoreLibs\DB\Options\Convert;
/**
* Test class for DB\IO + DB\SQL\PgSQL
@@ -59,20 +61,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
// same as valid, but db debug is off
'valid_debug_false' => [
'db_name' => 'corelibs_db_io_test',
'db_user' => 'corelibs_db_io_test',
'db_pass' => 'corelibs_db_io_test',
'db_host' => 'localhost',
'db_port' => 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => false,
],
// same as valid, but encoding is set
'valid_with_encoding_utf8' => [
@@ -85,7 +73,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => 'UTF-8',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
// valid with no schema set
'valid_no_schema' => [
@@ -98,7 +85,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
// invalid (missing db name)
'invalid' => [
@@ -111,7 +97,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
];
private static $log;
@@ -137,6 +122,7 @@ final class CoreLibsDBIOTest extends TestCase
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'log_file_id' => 'CoreLibs-DB-IO-Test',
]);
// will be true, default logging is true
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
@@ -534,6 +520,9 @@ final class CoreLibsDBIOTest extends TestCase
*/
public function debugSetProvider(): array
{
// 0: db connecdtion
// 1: override log flag, null for default
// 2: set flag
return [
'default debug set' => [
// what base connection
@@ -541,11 +530,6 @@ final class CoreLibsDBIOTest extends TestCase
// actions (set)
null,
// set exepected
self::$db_config['valid']['db_debug'],
],
'set debug to true' => [
'valid_debug_false',
true,
true,
],
'set debug to false' => [
@@ -556,99 +540,46 @@ final class CoreLibsDBIOTest extends TestCase
];
}
/**
* test set for toggleDEbug
*
* @return array
*/
public function debugToggleProvider(): array
{
return [
'default debug set' => [
// what base connection
'valid',
// actions
null,
// toggle is inverse
self::$db_config['valid']['db_debug'] ? false : true,
],
'toggle debug to true' => [
'valid_debug_false',
true,
true,
],
'toggle debug to false' => [
'valid',
false,
false,
]
];
}
/**
* Test dbSetDbug, dbGetDebug
*
* @covers ::dbGetDbug
* @covers ::dbSetDebug
* @dataProvider debugSetProvider
* @testdox Setting debug $set will be $expected [$_dataName]
* @testdox Set and Get Debug flag
*
* @return void
*/
public function testDbSetDebug(
string $connection,
?bool $set,
bool $expected
): void {
public function testDbSetDebug(): void
{
$connection = 'valid';
// default set, expect true
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log
);
$this->assertEquals(
$expected,
$set === null ?
$db->dbSetDebug() :
$db->dbSetDebug($set)
$this->assertTrue(
$db->dbGetDebug()
);
// must always match
$this->assertEquals(
$expected,
// switch off
$db->dbSetDebug(false);
$this->assertFalse(
$db->dbGetDebug()
);
$db->dbClose();
}
/**
* Test dbToggleDebug, dbGetDebug
*
* @covers ::dbGetDbug
* @covers ::dbSetDebug
* @dataProvider debugToggleProvider
* @testdox Toggle debug $toggle will be $expected [$_dataName]
*
* @return void
*/
public function testDbToggleDebug(
string $connection,
?bool $toggle,
bool $expected
): void {
// second conenction with log set NOT debug
$log = new \CoreLibs\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,
]);
$db = new \CoreLibs\DB\IO(
self::$db_config[$connection],
self::$log
$log
);
$this->assertEquals(
$expected,
$toggle === null ?
$db->dbToggleDebug() :
$db->dbToggleDebug($toggle)
);
// must always match
$this->assertEquals(
$expected,
$this->assertFalse(
$db->dbGetDebug()
);
$db->dbClose();
}
// - set max query call sets
@@ -806,7 +737,6 @@ final class CoreLibsDBIOTest extends TestCase
'host' => 'db_host',
'port' => 'db_port',
'ssl' => 'db_ssl',
'debug' => 'db_debug',
'password' => '***',
] as $read => $compare
) {
@@ -4628,6 +4558,176 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbClose();
}
// testing auto convert
/**
* Undocumented function
*
* @covers ::dbSetConvertFlag
* @testdox Check convert type works
*
* @return void
*/
public function testConvertType(): void
{
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$bytea_data = $db->dbEscapeBytea(
file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'CoreLibsDBIOTest.php') ?: ''
);
$query_insert = <<<SQL
INSERT INTO table_with_primary_key (
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
) VALUES (
$1,
$2, $3, $4, $5,
$6, $7, $8, $9,
$10, $11, $12, $13
)
SQL;
$db->dbExecParams(
$query_insert,
[
'CONVERT_TYPE_TEST',
1, 1.5, 'varchar', 'varchar literla',
json_encode(['json', 'a', 1, true, 'sub' => ['b', 'c']]),
json_encode(['jsonb', 'a', 1, true, 'sub' => ['b', 'c']]),
$bytea_data, date('Y-m-d H:i:s'), date('Y-m-d'), date('H:m:s'),
'{1,2,3}', '{"a","b","c"}'
]
);
$type_layout = [
'uid' => 'string',
'row_int' => 'int',
'row_numeric' => 'float',
'row_varchar' => 'string',
'row_varchar_literal' => 'string',
'row_json' => 'json',
'row_jsonb' => 'json',
'row_bytea' => 'bytea',
'row_timestamp' => 'string',
'row_date' => 'string',
'row_interval' => 'string',
'row_array_int' => 'string',
'row_array_varchar' => 'string'
];
$query_select = <<<SQL
SELECT
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
FROM
table_with_primary_key
WHERE
uid = $1
SQL;
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
// all hast to be string
foreach ($res as $key => $value) {
$this->assertIsString($value, 'Aseert string for column: ' . $key);
}
// convert base only
$db->dbSetConvertFlag(Convert::on);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::numeric);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::json);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::bytea);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
case 'bytea':
// for hex types it must not start with \x
$this->assertStringStartsNotWith(
'\x',
$value,
'Aseert bytes not starts with \x for column: ' . $key . '/' . $name
);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
}
// - internal read data (post exec)
// dbGetNumRows, dbGetNumFields, dbGetFieldNames,
// dbGetQuery, dbGetQueryHash, dbGetDbh
@@ -4659,7 +4759,7 @@ final class CoreLibsDBIOTest extends TestCase
. "('Foxtrott', 'Tango', 789, '1982-10-15') ",
null,
//
2,
3,
4,
['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'],
['varchar', 'varchar', 'int4', 'date'],
@@ -4776,9 +4876,16 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbExecParams($query, $params);
}
$this->assertInstanceOf(
'PgSql\Result',
$db->dbGetCursor(),
'Failed assert dbGetCursor'
);
$this->assertEquals(
$compare_query ?? $query,
$db->dbGetQuery()
$db->dbGetQuery(),
'Failed assert dbGetQuery'
);
$this->assertEquals(
// perhaps move that somewhere else?
@@ -4791,7 +4898,8 @@ final class CoreLibsDBIOTest extends TestCase
($params === null ?
$db->dbGetQueryHash($query) :
$db->dbGetQueryHash($query, $params)
)
),
'Failed assertdbGetQueryHash '
);
$this->assertEquals(
$expected_rows,
@@ -4813,6 +4921,35 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbGetFieldTypes(),
'Failed assert dbGetFieldTypes'
);
// check FieldNameTypes matches
$this->assertEquals(
array_combine(
$expected_col_names,
$expected_col_types
),
$db->dbGetFieldNameTypes(),
'Failed assert dbGetFieldNameTypes'
);
// check pos matches name
// name matches type
// pos matches type
foreach ($expected_col_names as $pos => $name) {
$this->assertEquals(
$name,
$db->dbGetFieldName($pos),
'Failed assert dbGetFieldName: ' . $pos . ' => ' . $name
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($name),
'Failed assert dbGetFieldType: ' . $name . ' => ' . $expected_col_types[$pos]
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($pos),
'Failed assert dbGetFieldType: ' . $pos . ' => ' . $expected_col_types[$pos]
);
}
$dbh = $db->dbGetDbh();
$this->assertIsObject(
$dbh

View File

@@ -460,16 +460,18 @@ final class CoreLibsDebugSupportTest extends TestCase
* Undocumented function
*
* @cover ::getCallerFileLine
* @testWith ["/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/www/vendor/phpunit/phpunit/src/Framework/TestCase.php:1608"]
* @testdox getCallerFileLine check if it returns $expected [$_dataName]
* @testWith ["vendor/phpunit/phpunit/src/Framework/TestCase.php:"]
* @testdox getCallerFileLine check based on regex /[\w\-\/]/vendor/phpunit/phpunit/src/Framework/TestCase.php:\d+ [$_dataName]
*
* @param string $expected
* @return void
*/
public function testGetCallerFileLine(string $expected): void
public function testGetCallerFileLine(): void
{
$this->assertEquals(
$expected,
// regex prefix with path "/../" and then fixed vendor + \d+
$regex = "/^\/[\w\-\/]+\/vendor\/phpunit\/phpunit\/src\/Framework\/TestCase.php:\d+$/";
$this->assertMatchesRegularExpression(
$regex,
Support::getCallerFileLine()
);
}
@@ -518,11 +520,17 @@ final class CoreLibsDebugSupportTest extends TestCase
break;
case 11:
// add one "run" before "runBare"
// array_splice(
// $expected,
// 7,
// 0,
// ['run']
// );
array_splice(
$expected,
7,
0,
['run']
0,
['include']
);
$this->assertEquals(
$expected,

View File

@@ -20,10 +20,15 @@ final class CoreLibsLoggingLoggingTest extends TestCase
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
. "\[[\w+\.\/]+:\d+\]\s{1}" // folder/file
. "\[[\w\-\.\/]+:\d+\]\s{1}" // folder/file
. "\[\w+\]\s{1}" // run id
. "{[\w\\\\]+(::\w+)?}\s{1}"; // class
public static function tearDownAfterClass(): void
{
array_map('unlink', glob(self::LOG_FOLDER . '*.log'));
}
/**
* test set for options BASIC
*
@@ -217,14 +222,14 @@ final class CoreLibsLoggingLoggingTest extends TestCase
// setLoggingLevel
// getLoggingLevel
// getJsDebug
// loggingLevelIsDebug
/**
* Undocumented function
*
* @covers ::setLoggingLevel
* @covers ::getLoggingLevel
* @covers ::getJsDebug
* @covers ::loggingLevelIsDebug
* @testdox setLoggingLevel set/get checks
*
* @return void
@@ -242,7 +247,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
$log->getLoggingLevel()
);
$this->assertFalse(
$log->getJsDebug()
$log->loggingLevelIsDebug()
);
// not set, should be debug]
$log = new \CoreLibs\Logging\Logging([
@@ -254,7 +259,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
$log->getLoggingLevel()
);
$this->assertTrue(
$log->getJsDebug()
$log->loggingLevelIsDebug()
);
// invalid, should be debug, will throw excpetion too
$this->expectException(\InvalidArgumentException::class);
@@ -269,7 +274,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
$log->getLoggingLevel()
);
$this->assertTrue(
$log->getJsDebug()
$log->loggingLevelIsDebug()
);
// set valid, then change
$log = new \CoreLibs\Logging\Logging([
@@ -686,6 +691,11 @@ final class CoreLibsLoggingLoggingTest extends TestCase
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerLoggingLevelWrite(): array
{
return [
@@ -791,6 +801,46 @@ final class CoreLibsLoggingLoggingTest extends TestCase
);
}
// check log level that writer writes in correct level
// also that non debug ignores prefix/group
/**
* Undocumented function
*
* @covers ::log
* @testdox log() general call test
*
* @return void
*/
public function testLoggingLog(): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLoggingLog',
'log_folder' => self::LOG_FOLDER,
'log_per_level' => true,
]);
$log_ok = $log->log(Level::Debug, 'DEBUG', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
$this->assertEquals(
$log->getLogFile(),
$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'
);
}
// must test flow:
// init normal
// log -> check file name
// set per date
// log -> check file name
// and same for per_run
// deprecated calls check?
}

View File

@@ -13,7 +13,7 @@ use CoreLibs\Security\SymmetricEncryption;
* @coversDefaultClass \CoreLibs\Security\SymmetricEncryption
* @testdox \CoreLibs\Security\SymmetricEncryption method tests
*/
final class CoreLibsSecuritySymmetricEncryption extends TestCase
final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
{
/**
* Undocumented function

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Template\HtmlBuilder\Block;
/**
* Test class for Template\HtmlBuilder\Block
* @coversDefaultClass \CoreLibs\Template\HtmlBuilder\Block
* @testdox \CoreLibs\Template\HtmlBuilder\Block method tests
*/
final class CoreLibsTemplateHtmlBuilderBlockTest extends TestCase
{
public function testCreateBlock(): void
{
$el = Block::cel('div', 'id', 'content', ['css'], ['onclick' => 'foo();']);
$this->assertEquals(
'<div id="id" class="css" onclick="foo();">content</div>',
Block::buildHtml($el)
);
}
// ael
// aelx|addSub
// resetSub
// acssel/rcssel/scssel
// buildHtml
// buildHtmlFromList|printHtmlFromArray
}
// __END__

View File

@@ -0,0 +1,546 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Template\HtmlBuilder\Element;
use CoreLibs\Template\HtmlBuilder\General\Error;
use CoreLibs\Template\HtmlBuilder\General\HtmlBuilderExcpetion;
/**
* Test class for Template\HtmlBuilder\Element
* @coversDefaultClass \CoreLibs\Template\HtmlBuilder\Element
* @testdox \CoreLibs\Template\HtmlBuilder\Element method tests
*/
final class CoreLibsTemplateHtmlBuilderElementTest extends TestCase
{
public function providerCreateElements(): array
{
return [
'simple div' => [
'tag' => 'div',
'id' => 'id',
'content' => 'content',
'css' => ['css'],
'options' => ['onclick' => 'foo();'],
'expected' => '<div id="id" class="css" onclick="foo();">content</div>'
],
'simple input' => [
'tag' => 'input',
'id' => 'id',
'content' => null,
'css' => ['css'],
'options' => ['name' => 'name', 'onclick' => 'foo();'],
'expected' => '<input id="id" name="name" class="css" onclick="foo();">'
]
];
}
/**
* Undocumented function
*
* @covers ::Element
* @covers ::buildHtml
* @covers ::getTag
* @covers ::getId
* @covers ::getContent
* @covers ::getOptions
* @covers ::getCss
* @dataProvider providerCreateElements
* @testdox create single new Element test [$_dataName]
*
* @param string $tag
* @param string|null $id
* @param string|null $content
* @param array|null $css
* @param array|null $options
* @param string $expected
* @return void
*/
public function testCreateElement(
string $tag,
?string $id,
?string $content,
?array $css,
?array $options,
string $expected
): void {
$el = new Element($tag, $id ?? '', $content ?? '', $css ?? [], $options ?? []);
$this->assertEquals(
$expected,
$el->buildHtml(),
'element creation failed'
);
$this->assertEquals(
$tag,
$el->getTag(),
'get tag failed'
);
if ($id !== null) {
$this->assertEquals(
$id,
$el->getId(),
'get id failed'
);
}
if ($content !== null) {
$this->assertEquals(
$content,
$el->getContent(),
'get content failed'
);
}
if ($css !== null) {
$this->assertEquals(
$css,
$el->getCss(),
'get css failed'
);
}
if ($options !== null) {
$this->assertEquals(
$options,
$el->getOptions(),
'get options failed'
);
}
if (!empty($options['name'])) {
$this->assertEquals(
$options['name'],
$el->getName(),
'get name failed'
);
}
}
/**
* css add/remove
*
* @cover ::getCss
* @cover ::addCss
* @cover ::removeCss
* @testdox test handling of adding and removing css classes
*
* @return void
*/
public function testCssHandling(): void
{
$el = new Element('div', 'css-test', 'CSS content');
$this->assertEqualsCanonicalizing(
[],
$el->getCss(),
'check empty css'
);
$el->addCss('foo');
$this->assertEqualsCanonicalizing(
['foo'],
$el->getCss(),
'check added one css'
);
$el->removeCss('foo');
$this->assertEqualsCanonicalizing(
[],
$el->getCss(),
'check remove added css'
);
// add serveral
// remove some of them
$el->addCss('a', 'b', 'c');
$this->assertEqualsCanonicalizing(
['a', 'b', 'c'],
$el->getCss(),
'check added some css'
);
$el->removeCss('a', 'c');
// $this->assertArray
$this->assertEqualsCanonicalizing(
['b'],
$el->getCss(),
'check remove some css'
);
// chained add and remove
$el->addCss('a', 'b', 'c', 'd')->removeCss('b', 'd');
$this->assertEqualsCanonicalizing(
['a', 'c'],
$el->getCss(),
'check chain add remove some css'
);
$el->resetCss();
$this->assertEqualsCanonicalizing(
[],
$el->getCss(),
'check reset css'
);
// remove something that does not eixst
$el->addCss('exists');
$el->removeCss('not');
$this->assertEqualsCanonicalizing(
['exists'],
$el->getCss(),
'check remove not exitsing'
);
}
/**
* nested test
*
* @testdox nested test and loop assign detection
*
* @return void
*/
public function testBuildNested(): void
{
Error::resetMessages();
$el = new Element('div', 'build-test');
$el_sub = new Element('div', 'sub-1');
$el->addSub($el_sub);
$this->assertEquals(
'<div id="build-test"><div id="sub-1"></div></div>',
$el->buildHtml(),
'nested build failed'
);
// this would create a loop, throws error
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessage("Cannot assign Element to itself, this would create an infinite loop");
$el_sub->addSub($el_sub);
$this->assertEquals(
'<div id="sub-1"></div>',
$el_sub->buildHtml(),
'loop detection failed'
);
$this->assertTrue(
Error::hasError(),
'failed to throw error post loop detection'
);
$this->assertEquals(
[[
'level' => 'Error',
'id' => '100',
'message' => 'Cannot assign Element to itself, this would create an infinite loop',
'context' => ['tag' => 'div', 'id' => 'sub-1']
]],
Error::getMessages(),
'check error is 100 failed'
);
// get sub
$this->assertEquals(
[$el_sub],
$el->getSub(),
'get sub failed'
);
// reset sub
$el->resetSub();
$this->assertEquals(
[],
$el->getSub(),
'reset sub failed'
);
}
/**
* Undocumented function
*
* @testdox updated nested connection
*
* @return void
*/
public function testNestedChangeContent(): void
{
$el = new Element('div', 'build-test');
$el_s_1 = new Element('div', 'sub-1');
$el_s_2 = new Element('div', 'sub-2');
$el_s_3 = new Element('div', 'sub-3');
$el_s_4 = new Element('div', 'sub-4');
$el->addSub($el_s_1, $el_s_2);
// only sub -1, -2
$this->assertEquals(
'<div id="build-test"><div id="sub-1"></div><div id="sub-2"></div></div>',
$el->buildHtml(),
'check basic nested'
);
// now add -3, -4 to both -1 and -2
$el_s_1->addSub($el_s_3, $el_s_4);
$el_s_2->addSub($el_s_3, $el_s_4);
$this->assertEquals(
'<div id="build-test"><div id="sub-1"><div id="sub-3"></div><div id="sub-4">'
. '</div></div><div id="sub-2"><div id="sub-3"></div><div id="sub-4"></div>'
. '</div></div>',
$el->buildHtml(),
'check nested added'
);
// now add some css to el_s_3, will update in both sets
$el_s_3->addCss('red');
$this->assertEquals(
'<div id="build-test"><div id="sub-1"><div id="sub-3" class="red"></div><div id="sub-4">'
. '</div></div><div id="sub-2"><div id="sub-3" class="red"></div><div id="sub-4"></div>'
. '</div></div>',
$el->buildHtml(),
'check nested u@dated'
);
}
/**
* Undocumented function
*
* @testdox test change tag/id/content
*
* @return void
*/
public function testChangeElementData(): void
{
$el = new Element('div', 'id', 'Content');
// content change
$this->assertEquals(
'Content',
$el->getContent(),
'set content'
);
$el->setContent('New Content');
$this->assertEquals(
'New Content',
$el->getContent(),
'changed content'
);
$this->assertEquals(
'div',
$el->getTag(),
'set tag'
);
$el->setTag('span');
$this->assertEquals(
'span',
$el->getTag(),
'changed tag'
);
$this->assertEquals(
'id',
$el->getId(),
'set id'
);
$el->setId('id-2');
$this->assertEquals(
'id-2',
$el->getId(),
'changed id'
);
}
/**
* Undocumented function
*
* @testdox test change options
*
* @return void
*/
public function testChangeOptions(): void
{
$el = new Element('button', 'id', 'Action', ['css'], ['value' => '3']);
$this->assertEquals(
['value' => '3'],
$el->getOptions(),
'set option'
);
$el->setOptions([
'value' => '2'
]);
$this->assertEquals(
['value' => '2'],
$el->getOptions(),
'changed option'
);
// add a new one
$el->setOptions([
'Foo' => 'bar',
'Moo' => 'cow'
]);
$this->assertEquals(
[
'value' => '2',
'Foo' => 'bar',
'Moo' => 'cow'
],
$el->getOptions(),
'changed option'
);
}
// build output
// build from array list
/**
* Undocumented function
*
* @testdox build element tree from object
*
* @return void
*/
public function testBuildHtmlObject(): void
{
// build a simple block
// div -> div -> button
// -> div -> span
// -> div -> input
$el = new Element('div', 'master', '', ['master']);
$el->addSub(
Element::addElement(
new Element('div', 'div-button', '', ['dv-bt']),
new Element('button', 'button-id', 'Click me', ['bt-red'], [
'OnClick' => 'action();',
'value' => 'click',
'type' => 'button'
]),
),
Element::addElement(
new Element('div', 'div-span', '', ['dv-sp']),
new Element('span', 'span-id', 'Big important message<br>other', ['red']),
),
Element::addElement(
new Element('div', 'div-input', '', ['dv-in']),
new Element('input', 'input-id', '', ['in-blue'], [
'OnClick' => 'otherAction();',
'value' => 'Touch',
'type' => 'button'
]),
),
);
$this->assertEquals(
'<div id="master" class="master">'
. '<div id="div-button" class="dv-bt">'
. '<button id="button-id" name="button-id" class="bt-red" OnClick="action();" '
. 'value="click" type="button">Click me</button>'
. '</div>'
. '<div id="div-span" class="dv-sp">'
. '<span id="span-id" class="red">Big important message<br>other</span>'
. '</div>'
. '<div id="div-input" class="dv-in">'
. '<input id="input-id" name="input-id" '
. 'class="in-blue" OnClick="otherAction();" value="Touch" type="button">'
. '</div>'
. '</div>',
$el->buildHtml()
);
}
/**
* Undocumented function
*
* @testdox build elements from array list
*
* @return void
*/
public function testbuildHtmlArray(): void
{
$this->assertEquals(
'<div id="id-1">A</div>'
. '<div id="id-2">B</div>'
. '<div id="id-3">C</div>',
Element::buildHtmlFromList([
new Element('div', 'id-1', 'A'),
new Element('div', 'id-2', 'B'),
new Element('div', 'id-3', 'C'),
])
);
}
/**
* Undocumented function
*
* @testdox check for invalid tag detection, possible invalid id, possible invalid css
*
* @return void
*/
public function testInvalidElement(): void
{
Error::resetMessages();
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessage("Could not create Element");
$el = new Element('');
$this->assertTrue(
Error::hasError(),
'failed to set error invalid tag detection'
);
$this->assertEquals(
[[
'level' => 'Error',
'id' => '201',
'message' => 'invalid or empty tag',
'context' => ['tag' => '']
]],
Error::getMessages(),
'check error message failed'
);
// if we set invalid tag
$el = new Element('div');
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessageMatches("/^Invalid or empty tag: /");
$this->expectExceptionMessage("Invalid or empty tag: 123123");
$el->setTag('123123');
$this->assertTrue(
Error::hasError(),
'failed to set error invalid tag detection'
);
$this->assertEquals(
[[
'level' => 'Error',
'id' => '201',
'message' => 'invalid or empty tag',
'context' => ['tag' => '']
]],
Error::getMessages(),
'check error message failed'
);
// invalid id (warning)
Error::resetMessages();
$el = new Element('div', '-$a15');
$this->assertTrue(
Error::hasWarning(),
'failed to set warning invalid id detection'
);
$this->assertEquals(
[[
'level' => 'Warning',
'id' => '202',
'message' => 'possible invalid id',
'context' => ['id' => '-$a15', 'tag' => 'div']
]],
Error::getMessages(),
'check error message failed'
);
// invalid name
Error::resetMessages();
$el = new Element('div', 'valid', '', [], ['name' => '-$asdf&']);
$this->assertTrue(
Error::hasWarning(),
'failed to set warning invalid name detection'
);
$this->assertEquals(
[[
'level' => 'Warning',
'id' => '203',
'message' => 'possible invalid name',
'context' => ['name' => '-$asdf&', 'id' => 'valid', 'tag' => 'div']
]],
Error::getMessages(),
'check error message failed'
);
}
// static add element
// print object/array
}
// __END__

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Template\HtmlBuilder\StringReplace;
/**
* Test class for Template\HtmlBuilder\StringReplace
* @coversDefaultClass \CoreLibs\Template\HtmlBuilder\StringReplace
* @testdox \CoreLibs\Template\HtmlBuilder\StringReplace method tests
*/
final class CoreLibsTemplateHtmlBuilderStringReplaceTest extends TestCase
{
/**
* Undocumented function
*
* @covers ::replaceData
* @testdox test basic replaceData
*
* @return void
*/
public function testReplaceData(): void
{
$html_block = <<<HTML
<div id="{ID}" class="{CSS}">
{CONTENT}
</div>
HTML;
$this->assertEquals(
<<<HTML
<div id="block-id" class="blue,red">
Some content here<br>with bla bla inside
</div>
HTML,
StringReplace::replaceData(
$html_block,
[
'ID' => 'block-id',
'CSS' => join(',', ['blue', 'red']),
'{CONTENT}' => 'Some content here<br>with bla bla inside',
]
)
);
}
/**
* Undocumented function
*
* @testdox replaceData error
*
* @return void
*/
/* public function testReplaceDataErrors(): void
{
$this->expectException(HtmlBuilderExcpetion::class);
$this->expectExceptionMessage("Replace and content array count differ");
StringReplace::replaceData('<span>{FOO}</span>', ['{FOO}', '{BAR}'], ['foo']);
} */
}
// __END__