Composer path composer packages update

This commit is contained in:
Clemens Schwaighofer
2023-02-28 18:08:57 +09:00
parent e1357f5d39
commit 137fb9a986
187 changed files with 45584 additions and 520 deletions

View File

@@ -10,8 +10,8 @@
}
],
"require": {
"egrajp/corelibs-composer-all": "^7.11",
"egrajp/smarty-extended": "^4.3"
"egrajp/smarty-extended": "^4.3",
"egrajp/corelibs-composer-all": "@dev"
},
"autoload": {
"classmap": [
@@ -19,6 +19,13 @@
]
},
"repositories": {
"CoreLibs-Composer-All": {
"type": "path",
"url": "/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All",
"options": {
"symlink": false
}
},
"git.egplusww.jp.Composer": {
"type": "composer",
"url": "https://git.egplusww.jp/api/packages/Composer/composer"

19
www/composer.lock generated
View File

@@ -4,15 +4,15 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "dc5d89075423c3c3df219079c491d91c",
"content-hash": "a80d1d945e4d8202a444c05b501eaf21",
"packages": [
{
"name": "egrajp/corelibs-composer-all",
"version": "7.12.1",
"version": "dev-development",
"dist": {
"type": "zip",
"url": "https://git.egplusww.jp/api/packages/Composer/composer/files/egrajp%2Fcorelibs-composer-all/7.12.1/egrajp-corelibs-composer-all.7.12.1.zip",
"shasum": "457089ac43cb9fff5091e163877bb56ecb868151"
"type": "path",
"url": "/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All",
"reference": "0a80abe8a4524188b42c5b4cffdd9a813ef407e6"
},
"require": {
"php": ">=8.1"
@@ -39,7 +39,10 @@
}
],
"description": "CoreLibs in a composer package",
"time": "2023-02-28T09:16:32+09:00"
"transport-options": {
"symlink": false,
"relative": false
}
},
{
"name": "egrajp/smarty-extended",
@@ -75,7 +78,9 @@
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"egrajp/corelibs-composer-all": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],

View File

@@ -2,12 +2,12 @@
"packages": [
{
"name": "egrajp/corelibs-composer-all",
"version": "7.12.1",
"version_normalized": "7.12.1.0",
"version": "dev-development",
"version_normalized": "dev-development",
"dist": {
"type": "zip",
"url": "https://git.egplusww.jp/api/packages/Composer/composer/files/egrajp%2Fcorelibs-composer-all/7.12.1/egrajp-corelibs-composer-all.7.12.1.zip",
"shasum": "457089ac43cb9fff5091e163877bb56ecb868151"
"type": "path",
"url": "/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All",
"reference": "0a80abe8a4524188b42c5b4cffdd9a813ef407e6"
},
"require": {
"php": ">=8.1"
@@ -18,7 +18,6 @@
"phpstan/phpstan": "1.10.x-dev",
"phpunit/phpunit": "^9"
},
"time": "2023-02-28T09:16:32+09:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -36,6 +35,10 @@
}
],
"description": "CoreLibs in a composer package",
"transport-options": {
"symlink": false,
"relative": false
},
"install-path": "../egrajp/corelibs-composer-all"
},
{

View File

@@ -11,9 +11,9 @@
),
'versions' => array(
'egrajp/corelibs-composer-all' => array(
'pretty_version' => '7.12.1',
'version' => '7.12.1.0',
'reference' => NULL,
'pretty_version' => 'dev-development',
'version' => 'dev-development',
'reference' => '0a80abe8a4524188b42c5b4cffdd9a813ef407e6',
'type' => 'library',
'install_path' => __DIR__ . '/../egrajp/corelibs-composer-all',
'aliases' => array(),

View File

@@ -6,8 +6,6 @@ parameters:
level: 8 # max is now 9
checkMissingCallableSignature: true
treatPhpDocTypesAsCertain: false
strictRules:
allRules: false
paths:
- %currentWorkingDirectory%/src
bootstrapFiles:
@@ -27,9 +25,9 @@ parameters:
#- # 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
- # this is for 8.1 or newer
message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects PgSql\\\\(Result|Connection(\\|string)?(\\|null)?), object\\|resource given\\.$#"
path: %currentWorkingDirectory%/src/DB/SQL/PgSQL.php
# - # this is for 8.1 or newer
# message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects PgSql\\\\(Result|Connection(\\|string)?(\\|null)?), object\\|resource given\\.$#"
# path: %currentWorkingDirectory%/src/DB/SQL/PgSQL.php
# this is ignored for now
# - '#Expression in empty\(\) is always falsy.#'
# -

View File

@@ -0,0 +1,2 @@
*.zip
.env*

View File

@@ -0,0 +1 @@
8.0.0

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
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";
if [ -z "${VERSION}" ]; then
echo "Version must be set in the form x.y.z without any leading characters";
exit;
fi;
# compare version, if different or newer, deploy
if [ -f "${file_last_published}" ]; then
LAST_PUBLISHED_VERSION=$(cat ${file_last_published});
if $(dpkg --compare-versions "${VERSION}" le "${LAST_PUBLISHED_VERSION}"); then
echo "git tag version ${VERSION} is not newer than previous published version ${LAST_PUBLISHED_VERSION}";
exit;
fi;
fi;
# read in the .env.deploy file and we must have
# GITLAB_USER
# GITLAB_TOKEN
# GITEA_DEPLOY_TOKEN
if [ ! -f "${BASE_FOLDER}.env.deploy" ]; then
echo "Deploy enviroment file .env.deploy is missing";
exit;
fi;
set -o allexport;
cd ${BASE_FOLDER};
source .env.deploy;
cd -;
set +o allexport;
# gitea
if [ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then
curl -LJO \
--output-dir "${BASE_FOLDER}" \
https://git.egplusww.jp/Composer/CoreLibs-Composer-All/archive/v${VERSION}.zip;
curl --user ${GITEA_USER}:${GITEA_TOKEN} \
--upload-file "${BASE_FOLDER}/CoreLibs-Composer-All-v${VERSION}.zip" \
https://git.egplusww.jp/api/packages/Composer/composer?version=${VERSION};
echo "${VERSION}" > "${file_last_published}";
else
echo "Missing either GITEA_USER or GITEA_TOKEN environment variable";
fi;
# gitlab
if [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then
curl --data tag=v${VERSION} \
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer";
curl --data branch=master \
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer";
echo "${VERSION}" > "${file_last_published}";
else
echo "Missing GITLAB_DEPLOY_TOKEN environment variable";
fi;
# __END__

View File

@@ -157,7 +157,7 @@ class Login
private $acl = [];
/** @var array<mixed> */
private $default_acl_list = [];
/** @var array<int|string,mixed> Reverse list to lookup level from type */
/** @var array<string,int> Reverse list to lookup level from type */
private $default_acl_list_type = [];
/** @var int default ACL level to be based on if nothing set */
private $default_acl_level = 0;
@@ -332,7 +332,7 @@ class Login
'type' => $res['type'],
'name' => $res['name']
];
$this->default_acl_list_type[$res['type']] = $res['level'];
$this->default_acl_list_type[(string)$res['type']] = (int)$res['level'];
}
// write that into the session
$_SESSION['DEFAULT_ACL_LIST'] = $this->default_acl_list;
@@ -986,7 +986,7 @@ class Login
* @param string $password the new password
* @return bool true or false if valid password or not
*/
private function loginPasswordChangeValidPassword($password): bool
private function loginPasswordChangeValidPassword(string $password): bool
{
$is_valid_password = true;
// check for valid in regex arrays in list
@@ -1048,7 +1048,7 @@ class Login
$res = $this->db->dbReturnRow($q);
if (
!is_array($res) ||
(is_array($res) && empty($res['edit_user_id']))
empty($res['edit_user_id'])
) {
// username wrong
$this->login_error = 201;
@@ -1068,9 +1068,11 @@ class Login
}
if (
!is_array($res) ||
(is_array($res) &&
(empty($res['edit_user_id']) ||
!$this->loginPasswordCheck($res['old_password_hash'], $this->pw_old_password)))
empty($res['edit_user_id']) ||
!$this->loginPasswordCheck(
$res['old_password_hash'],
$this->pw_old_password
)
) {
// old password wrong
$this->login_error = 202;
@@ -1124,7 +1126,7 @@ class Login
*
* @return string|null html data for login page, or null for nothing
*/
private function loginCreateLoginHTML()
private function loginCreateLoginHTML(): ?string
{
$html_string = null;
// if permission is ok, return null
@@ -1421,8 +1423,12 @@ EOM;
* @param string $username login user username
* @return void has no return
*/
private function writeLog(string $event, string $data, $error = '', string $username = ''): void
{
private function writeLog(
string $event,
string $data,
string|int $error = '',
string $username = ''
): void {
if ($this->login) {
$this->action = 'Login';
} elseif ($this->logout) {
@@ -1794,7 +1800,6 @@ EOM;
// check that numeric, positive numeric, not longer than max input string lenght
// and not short than min password length
if (
is_numeric($length) &&
$length >= $this->password_min_length_max &&
$length <= $this->password_max_length &&
$length <= 255
@@ -2069,9 +2074,12 @@ EOM;
* @param string $type Type name to look in the acl list
* @return int|bool Either int level or false for not found
*/
public function loginGetAclListFromType(string $type)
public function loginGetAclListFromType(string $type): int|bool
{
return $this->default_acl_list_type[$type] ?? false;
if (!isset($this->default_acl_list_type[$type])) {
return false;
}
return (int)$this->default_acl_list_type[$type];
}
/**
@@ -2081,7 +2089,7 @@ EOM;
* @return bool true/false: if the edit access is not
* in the valid list: false
*/
public function loginCheckEditAccess($edit_access_id): bool
public function loginCheckEditAccess(?int $edit_access_id): bool
{
if ($edit_access_id === null) {
return false;
@@ -2122,8 +2130,10 @@ EOM;
* @param string|int $data_key key value to search for
* @return bool|string false for not found or string for found data
*/
public function loginGetEditAccessData(int $edit_access_id, $data_key)
{
public function loginGetEditAccessData(
int $edit_access_id,
string|int $data_key
): bool|string {
if (!isset($_SESSION['UNIT'][$edit_access_id]['data'][$data_key])) {
return false;
}
@@ -2137,9 +2147,12 @@ EOM;
* @param string $uid Edit Access UID to look for
* @return int|bool Either primary key in int or false in bool for not found
*/
public function loginGetEditAccessIdFromUid(string $uid)
public function loginGetEditAccessIdFromUid(string $uid): int|bool
{
return $_SESSION['UNIT_UID'][$uid] ?? false;
if (!isset($_SESSION['UNIT_UID'][$uid])) {
return false;
}
return (int)$_SESSION['UNIT_UID'][$uid];
}
/**
@@ -2204,8 +2217,10 @@ EOM;
* @param string|int $data_key
* @return bool|string
*/
public function loginSetEditAccessData(int $edit_access_id, $data_key)
{
public function loginSetEditAccessData(
int $edit_access_id,
string|int $data_key
): bool|string {
return $this->loginGetEditAccessData($edit_access_id, $data_key);
}
}

View File

@@ -194,7 +194,7 @@ class Backend
*/
public function adbEditLog(
string $event = '',
$data = '',
string|array $data = '',
string $write_type = 'STRING'
): void {
$data_binary = '';
@@ -262,7 +262,7 @@ class Backend
* @param string|int $menu_show_flag
* @return string|int
*/
public function adbSetMenuShowFlag($menu_show_flag)
public function adbSetMenuShowFlag(string|int $menu_show_flag): string|int
{
// must be string or int
$this->menu_show_flag = $menu_show_flag;
@@ -274,7 +274,7 @@ class Backend
*
* @return string|int
*/
public function adbGetMenuShowFlag()
public function adbGetMenuShowFlag(): string|int
{
return $this->menu_show_flag;
}
@@ -437,7 +437,7 @@ class Backend
* @return array<mixed> associative array
* @deprecated \CoreLibs\Combined\ArrayHandler::genAssocArray()
*/
public function adbAssocArray(array $db_array, $key, $value): array
public function adbAssocArray(array $db_array, string|int|bool $key, string|int|bool $value): array
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Combined\ArrayHandler::genAssocArray',
@@ -453,7 +453,7 @@ class Backend
* @return string formatted string
* @deprecated \CoreLibs\Convert\Byte::humanReadableByteFormat()
*/
public function adbByteStringFormat($number): string
public function adbByteStringFormat(string|int|float $number): string
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Convert\Byte::humanReadableByteFormat()',
@@ -482,7 +482,7 @@ class Backend
string $dummy = '',
string $path = '',
string $cache = ''
) {
): string|bool {
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Output\Image::createThumbnail()',
E_USER_DEPRECATED
@@ -569,28 +569,28 @@ class Backend
* Basic class holds exact the same, except the Year/Month/Day/etc strings
* are translated in this call
*
* @param int $year year YYYY
* @param int $month month m
* @param int $day day d
* @param int $hour hour H
* @param int $min min i
* @param string $suffix additional info printed after the date time
* variable in the drop down
* also used for ID in the on change JS call
* @param int $min_steps default is 1 (minute), can set to anything,
* is used as sum up from 0
* @param bool $name_pos_back default false, if set to true,
* the name will be printend
* after the drop down and not before the drop down
* @return string HTML formated strings for drop down lists
* of date and time
* @param int|string $year year YYYY
* @param int|string $month month m
* @param int|string $day day d
* @param int|string $hour hour H
* @param int|string $min min i
* @param string $suffix additional info printed after the date time
* variable in the drop down
* also used for ID in the on change JS call
* @param int $min_steps default is 1 (minute), can set to anything,
* is used as sum up from 0
* @param bool $name_pos_back default false, if set to true,
* the name will be printend
* after the drop down and not before the drop down
* @return string HTML formated strings for drop down lists
* of date and time
*/
public function adbPrintDateTime(
$year,
$month,
$day,
$hour,
$min,
int|string $year,
int|string $month,
int|string $day,
int|string $hour,
int|string $min,
string $suffix = '',
int $min_steps = 1,
bool $name_pos_back = false

View File

@@ -414,6 +414,8 @@ class EditBase
$elements[] = $this->form->formCreateElement('additional_acl');
break;
case 'edit_schemes':
// @deprecated Will be removed
case 'edit_schemas':
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('header_color');
@@ -532,7 +534,7 @@ class EditBase
* @throws Exception
* @throws SmartyException
*/
public function editBaseRun()
public function editBaseRun(): void
{
// set the template dir
// WARNING: this has a special check for the mailing tool layout (old layout)

View File

@@ -672,9 +672,6 @@ class Basic
public static function arrayToString(array $array, string $connect_char): string
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use join()', E_USER_DEPRECATED);
if (!is_array($array)) {
$array = [];
}
return join($connect_char, $array);
}

View File

@@ -42,8 +42,11 @@ class Colors
* @param int|false $hsl_flag flag to check for hsl type
* @return bool True if no error, False if error
*/
private static function rgbHslContentCheck(string $color, $rgb_flag, $hsl_flag): bool
{
private static function rgbHslContentCheck(
string $color,
int|false $rgb_flag,
int|false $hsl_flag
): bool {
// extract string between () and split into elements
preg_match("/\((.*)\)/", $color, $matches);
if (

View File

@@ -25,7 +25,7 @@ class Encoding
* if null is set then "none"
* @return void
*/
public static function setErrorChar($string): void
public static function setErrorChar(string|int|null $string): void
{
if (empty($string)) {
$string = 'none';
@@ -52,7 +52,7 @@ class Encoding
* directly
* @return string|int Set error character
*/
public static function getErrorChar(bool $return_substitute_func = false)
public static function getErrorChar(bool $return_substitute_func = false): string|int
{
// return mb_substitute_character();
if ($return_substitute_func === true) {
@@ -78,14 +78,14 @@ class Encoding
* @param string $string string to test
* @param string $from_encoding encoding of string to test
* @param string $to_encoding target encoding
* @return bool|array<string> false if no error or
* @return array<string>|false false if no error or
* array with failed characters
*/
public static function checkConvertEncoding(
string $string,
string $from_encoding,
string $to_encoding
) {
): array|false {
// convert to target encoding and convert back
$temp = mb_convert_encoding($string, $to_encoding, $from_encoding);
$compare = mb_convert_encoding($temp, $from_encoding, $to_encoding);

View File

@@ -32,7 +32,7 @@ class Jason
* or error string ('' for no error)
* @deprecated Use Json::jsonGetLastError()
*/
public static function jsonGetLastError(bool $return_string = false)
public static function jsonGetLastError(bool $return_string = false): int|string
{
return Json::jsonGetLastError($return_string);
}

View File

@@ -21,12 +21,12 @@ class ArrayHandler
* the needle can be found in the
* haystack array
*/
public static function arraySearchRecursive($needle, array $haystack, ?string $key_search_for = null): array
{
public static function arraySearchRecursive(
string|int $needle,
array $haystack,
?string $key_search_for = null
): array {
$path = [];
if (!is_array($haystack)) {
$haystack = [];
}
if (
$key_search_for != null &&
array_key_exists($key_search_for, $haystack) &&
@@ -72,7 +72,7 @@ class ArrayHandler
*
* @param string|int $needle needle (search for)
* @param array<mixed> $haystack haystack (search in)
* @param string|int $key_search_for the key to look for in
* @param string|int|null $key_search_for the key to look for in
* @param bool $old [true], if set to false will
* return new flat layout
* @param array<mixed>|null $path recursive call for previous path
@@ -80,9 +80,9 @@ class ArrayHandler
* the element was found
*/
public static function arraySearchRecursiveAll(
$needle,
string|int $needle,
array $haystack,
$key_search_for,
string|int|null $key_search_for,
bool $old = true,
?array $path = null
): ?array {
@@ -101,10 +101,6 @@ class ArrayHandler
$path['work'] = [];
}
}
// should not be needed because it would trigger a php mehtod error
if (!is_array($haystack)) {
$haystack = [];
}
// go through the array,
foreach ($haystack as $_key => $_value) {
@@ -152,17 +148,18 @@ class ArrayHandler
* array search simple. looks for key, value combination, if found, returns true
* on default does not strict check, so string '4' will match int 4 and vica versa
*
* @param array<mixed> $array search in as array
* @param string|int $key key (key to search in)
* @param string|int $value value (what to find)
* @param bool $strict [false], if set to true, will strict check key/value
* @return bool true on found, false on not found
* @param array<mixed> $array search in as array
* @param string|int $key key (key to search in)
* @param string|int|bool $value value (what to find)
* @param bool $strict [false], if set to true, will strict check key/value
* @return bool true on found, false on not found
*/
public static function arraySearchSimple(array $array, $key, $value, bool $strict = false): bool
{
if (!is_array($array)) {
$array = [];
}
public static function arraySearchSimple(
array $array,
string|int $key,
string|int|bool $value,
bool $strict = false
): bool {
foreach ($array as $_key => $_value) {
// if value is an array, we search
if (is_array($_value)) {
@@ -189,9 +186,9 @@ class ArrayHandler
* bool key flag: true: handle keys as string or int
* default false: all keys are string
*
* @return array<mixed>|bool merged array
* @return array<mixed>|false merged array
*/
public static function arrayMergeRecursive()
public static function arrayMergeRecursive(): array|false
{
// croak on not enough arguemnts (we need at least two)
if (func_num_args() < 2) {
@@ -264,10 +261,10 @@ class ArrayHandler
* @param array<mixed> $needle elements to search for
* @param array<mixed> $haystack array where the $needle elements should
* be searched int
* @return array<mixed>|bool either the found elements or
* @return array<mixed>|false either the found elements or
* false for nothing found or error
*/
public static function inArrayAny(array $needle, array $haystack)
public static function inArrayAny(array $needle, array $haystack): array|false
{
$found = [];
foreach ($needle as $element) {
@@ -291,8 +288,12 @@ class ArrayHandler
* @param bool $set_only flag to return all (default), or set only
* @return array<mixed> associative array
*/
public static function genAssocArray(array $db_array, $key, $value, bool $set_only = false): array
{
public static function genAssocArray(
array $db_array,
string|int|bool $key,
string|int|bool $value,
bool $set_only = false
): array {
$ret_array = [];
// do this to only run count once
for ($i = 0, $iMax = count($db_array); $i < $iMax; $i++) {
@@ -385,11 +386,8 @@ class ArrayHandler
* and will be pushed up
* @return array<mixed> modified, flattened array
*/
public static function arrayFlatForKey(array $array, $search): array
public static function arrayFlatForKey(array $array, string|int $search): array
{
if (!is_array($array)) {
$array = [];
}
foreach ($array as $key => $value) {
// if it is not an array do just nothing
if (!is_array($value)) {

View File

@@ -74,7 +74,7 @@ class DateTime
* @return string formated date+time in Y-M-D h:m:s ms
*/
public static function dateStringFormat(
$timestamp,
int|float $timestamp,
bool $show_micro = false,
bool $micro_as_float = false
): string {
@@ -100,8 +100,10 @@ class DateTime
* @param bool $show_micro show micro seconds, default true
* @return string interval formatted string or string as is
*/
public static function timeStringFormat($timestamp, bool $show_micro = true): string
{
public static function timeStringFormat(
string|int|float $timestamp,
bool $show_micro = true
): string {
// check if the timestamp has any h/m/s/ms inside, if yes skip
if (!preg_match("/(h|m|s|ms)/", (string)$timestamp)) {
list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 4)), 2, null);
@@ -157,7 +159,7 @@ class DateTime
* @param string|int|float $timestring formatted interval
* @return string|int|float converted float interval, or string as is
*/
public static function stringToTime($timestring)
public static function stringToTime(string|int|float $timestring): string|int|float
{
$timestamp = 0;
if (preg_match("/(d|h|m|s|ms)/", (string)$timestring)) {
@@ -247,9 +249,9 @@ class DateTime
* @param string $date a date string in the format YYYY-MM-DD
* @return bool true if valid date, false if date not valid
*/
public static function checkDate($date): bool
public static function checkDate(string $date): bool
{
if (!$date) {
if (empty($date)) {
return false;
}
list ($year, $month, $day) = array_pad(
@@ -324,7 +326,7 @@ class DateTime
* @return int|bool false on error
* or int -1 (s<e)/0 (s=e)/1 (s>e) as difference
*/
public static function compareDate($start_date, $end_date)
public static function compareDate(string $start_date, string $end_date): int|bool
{
// pre check for empty or wrong
if ($start_date == '--' || $end_date == '--' || !$start_date || !$end_date) {
@@ -367,7 +369,7 @@ class DateTime
* @return int|bool false for error
* or -1 (s<e)/0 (s=e)/1 (s>e) as difference
*/
public static function compareDateTime($start_datetime, $end_datetime)
public static function compareDateTime(string $start_datetime, string $end_datetime): int|bool
{
// pre check for empty or wrong
if ($start_datetime == '--' || $end_datetime == '--' || !$start_datetime || !$end_datetime) {
@@ -402,8 +404,11 @@ class DateTime
* @param bool $return_named return array type, false (default), true for named
* @return array<mixed> 0/overall, 1/weekday, 2/weekend
*/
public static function calcDaysInterval($start_date, $end_date, bool $return_named = false): array
{
public static function calcDaysInterval(
string $start_date,
string $end_date,
bool $return_named = false
): array {
// pos 0 all, pos 1 weekday, pos 2 weekend
$days = [];
// if anything invalid, return 0,0,0

View File

@@ -39,7 +39,7 @@ class Byte
* @return string converted byte number (float) with suffix
* @throws \Exception 1: no valid flag set
*/
public static function humanReadableByteFormat($bytes, int $flags = 0): string
public static function humanReadableByteFormat(string|int|float $bytes, int $flags = 0): string
{
// if not numeric, return as is
if (is_numeric($bytes)) {
@@ -121,7 +121,7 @@ class Byte
* @return string|int|float converted value or original value
* @throws \Exception 1: no valid flag set
*/
public static function stringByteFormat($number, int $flags = 0)
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
{
// use SI 1000 mod and not 1024 mod
if ($flags & self::BYTE_FORMAT_SI) {

View File

@@ -31,8 +31,12 @@ class Colors
* @return string|bool rgb in hex values with leading # if set,
* false for invalid color
*/
public static function rgb2hex(int $red, int $green, int $blue, bool $hex_prefix = true)
{
public static function rgb2hex(
int $red,
int $green,
int $blue,
bool $hex_prefix = true
): string|bool {
$hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
@@ -61,7 +65,7 @@ class Colors
string $hexStr,
bool $return_as_string = false,
string $seperator = ','
) {
): string|array|bool {
$hexStr = preg_replace("/[^0-9A-Fa-f]/", '', $hexStr); // Gets a proper hex string
if (!is_string($hexStr)) {
return false;
@@ -99,7 +103,7 @@ class Colors
* @return array<int|float>|bool Hue, Sat, Brightness/Value
* false for input value error
*/
public static function rgb2hsb(int $red, int $green, int $blue)
public static function rgb2hsb(int $red, int $green, int $blue): array|bool
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
@@ -146,7 +150,7 @@ class Colors
* @return array<int>|bool 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)
public static function hsb2rgb(float $H, float $S, float $V): array|bool
{
// check that H is 0 to 359, 360 = 0
// and S and V are 0 to 1
@@ -232,7 +236,7 @@ class Colors
* @return array<float>|bool hue/sat/luminance
* false for input value error
*/
public static function rgb2hsl(int $red, int $green, int $blue)
public static function rgb2hsl(int $red, int $green, int $blue): array|bool
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
@@ -285,11 +289,8 @@ class Colors
* @param float $lum luminance: 0-100
* @return array<int,float|int>|bool red/blue/green 0-255 each
*/
public static function hsl2rgb(float $hue, float $sat, float $lum)
public static function hsl2rgb(float $hue, float $sat, float $lum): array|bool
{
if (!is_numeric($hue)) {
return false;
}
if ($hue == 360) {
$hue = 0;
}

View File

@@ -19,7 +19,7 @@ class Html
* @param mixed $string string to html encode
* @return mixed if string, encoded, else as is (eg null)
*/
public static function htmlent($string)
public static function htmlent(mixed $string): mixed
{
if (is_string($string)) {
return htmlentities($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
@@ -52,7 +52,7 @@ class Html
* @return ?string returns checked or selected,
* else returns null
*/
public static function checked($haystack, string $needle, int $type = 0): ?string
public static function checked(array|string $haystack, string $needle, int $type = 0): ?string
{
if (is_array($haystack)) {
if (in_array($needle, $haystack)) {

View File

@@ -51,13 +51,13 @@ class Json
/**
* returns human readable string for json errors thrown in jsonConvertToArray
*
* @param bool|boolean $return_string [default=false] if set to true
* it will return the message string and not
* the error number
* @return int|string Either error number (0 for no error)
* or error string ('' for no error)
* @param bool $return_string [default=false] if set to true
* it will return the message string and not
* the error number
* @return int|string Either error number (0 for no error)
* or error string ('' for no error)
*/
public static function jsonGetLastError(bool $return_string = false)
public static function jsonGetLastError(bool $return_string = false): int|string
{
$json_error_string = '';
// valid errors as of php 8.0

View File

@@ -48,7 +48,7 @@ class Math
* @param string|int|float $number string or number to check
* @return float if not number, then returns 0, else original input
*/
public static function initNumeric($number): float
public static function initNumeric(string|int|float $number): float
{
if (!is_numeric($number)) {
return 0;

View File

@@ -32,7 +32,7 @@ class RandomKey
*
* @return void has no return
*/
private static function initRandomKeyData()
private static function initRandomKeyData(): void
{
// random key generation base string
self::$key_range = join('', array_merge(
@@ -52,7 +52,6 @@ class RandomKey
private static function validateRandomKeyLenght(int $key_length): bool
{
if (
is_numeric($key_length) &&
$key_length > 0 &&
$key_length <= self::$max_key_length
) {

View File

@@ -18,19 +18,6 @@ class Session
/** @var string list for errors */
private $session_intern_error_str = '';
/**
* Start session
* startSession should be called for complete check
* If this is called without any name set before the php.ini name is
* used.
*
* @return void
*/
protected function startSessionCall(): void
{
session_start();
}
/**
* init a session, if array is empty or array does not have session_name set
* then no auto init is run
@@ -44,6 +31,19 @@ class Session
}
}
/**
* Start session
* startSession should be called for complete check
* If this is called without any name set before the php.ini name is
* used.
*
* @return void
*/
protected 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
@@ -116,7 +116,7 @@ class Session
* @param string|null $session_name
* @return string|bool
*/
public function startSession(?string $session_name = null)
public function startSession(?string $session_name = null): string|bool
{
// we can't start sessions on command line
if ($this->checkCliStatus()) {
@@ -163,7 +163,7 @@ class Session
*
* @return string|bool
*/
public function getSessionId()
public function getSessionId(): string|bool
{
return session_id();
}
@@ -173,7 +173,7 @@ class Session
*
* @return string|bool
*/
public function getSessionName()
public function getSessionName(): string|bool
{
return session_name();
}
@@ -275,7 +275,7 @@ class Session
* @param mixed $value value to set (can be anything)
* @return void
*/
public function setS($name, $value): void
public function setS(string|int $name, mixed $value): void
{
$_SESSION[$name] = $value;
}
@@ -286,7 +286,7 @@ class Session
* @param string|int $name value key to get from _SESSION
* @return mixed value stored in _SESSION
*/
public function getS($name)
public function getS(string|int $name): mixed
{
return $_SESSION[$name] ?? '';
}
@@ -297,7 +297,7 @@ class Session
* @param string|int $name Name to check for
* @return bool True for set, False fornot set
*/
public function issetS($name): bool
public function issetS(string|int $name): bool
{
return isset($_SESSION[$name]);
}
@@ -308,7 +308,7 @@ class Session
* @param string|int $name _SESSION key name to remove
* @return void
*/
public function unsetS($name): void
public function unsetS(string|int $name): void
{
if (isset($_SESSION[$name])) {
unset($_SESSION[$name]);
@@ -325,7 +325,7 @@ class Session
* @param mixed $value
* @return void
*/
public function __set($name, $value): void
public function __set(string|int $name, mixed $value): void
{
$_SESSION[$name] = $value;
}
@@ -334,13 +334,14 @@ class Session
* Undocumented function
*
* @param string|int $name
* @return mixed
* @return mixed If name is not found, it will return null
*/
public function __get($name)
public function __get(string|int $name): mixed
{
if (isset($_SESSION[$name])) {
return $_SESSION[$name];
}
return null;
}
/**
@@ -349,7 +350,7 @@ class Session
* @param string|int $name
* @return bool
*/
public function __isset($name): bool
public function __isset(string|int $name): bool
{
return isset($_SESSION[$name]);
}
@@ -360,7 +361,7 @@ class Session
* @param string|int $name
* @return void
*/
public function __unset($name): void
public function __unset(string|int $name): void
{
if (isset($_SESSION[$name])) {
unset($_SESSION[$name]);

View File

@@ -137,7 +137,7 @@ class ArrayIO extends \CoreLibs\DB\IO
* @param string $text any html encoded string
* @return string decoded html string
*/
public function convertData($text): string
public function convertData(string $text): string
{
$text = str_replace('&lt;b&gt;', '<b>', $text);
$text = str_replace('&lt;/b&gt;', '</b>', $text);
@@ -156,7 +156,7 @@ class ArrayIO extends \CoreLibs\DB\IO
* @param string $text encoded html string
* @return string decoded html string
*/
public function convertEntities($text): string
public function convertEntities(string $text): string
{
$text = str_replace('&lt;', '<', $text);
$text = str_replace('&gt;', '>', $text);
@@ -172,7 +172,7 @@ class ArrayIO extends \CoreLibs\DB\IO
* @param bool $write write to error message, default false
* @return string the array data as html string entry
*/
public function dbDumpArray($write = false): string
public function dbDumpArray(bool $write = false): string
{
reset($this->table_array);
$string = '';
@@ -192,7 +192,7 @@ class ArrayIO extends \CoreLibs\DB\IO
*
* @return bool true if pk value is set, else false
*/
public function dbCheckPkSet()
public function dbCheckPkSet(): bool
{
// if pk_id is set, overrule ...
if ($this->pk_id) {
@@ -210,10 +210,10 @@ class ArrayIO extends \CoreLibs\DB\IO
/**
* resets the whole array values
* @param boolean $reset_pk true if we want to reset the pk too
* @param bool $reset_pk true if we want to reset the pk too
* @return void has no return
*/
public function dbResetArray($reset_pk = false): void
public function dbResetArray(bool $reset_pk = false): void
{
reset($this->table_array);
foreach (array_keys($this->table_array) as $column) {
@@ -230,10 +230,10 @@ class ArrayIO extends \CoreLibs\DB\IO
*
* @param array<mixed> $table_array optional override for table array set
* set this as new table array too
* @param boolean $acl_limit [false], if set to true, well do ACL limit check
* @param bool $acl_limit [false], if set to true, well do ACL limit check
* @return array<mixed> returns the table array that was deleted
*/
public function dbDelete($table_array = [], $acl_limit = false)
public function dbDelete(array $table_array = [], bool $acl_limit = false): array
{
// is array and has values, override set and set new
if (is_array($table_array) && count($table_array)) {
@@ -294,12 +294,12 @@ class ArrayIO extends \CoreLibs\DB\IO
/**
* reads one row into the array
*
* @param boolean $edit on true convert data, else as is
* @param bool $edit on true convert data, else as is
* @param array<mixed> $table_array optional table array, overwrites
* internal set array
* @return array<mixed> set table array with values
*/
public function dbRead($edit = false, $table_array = [])
public function dbRead(bool $edit = false, array $table_array = []): array
{
// if array give, overrules internal array
if (is_array($table_array) && count($table_array)) {
@@ -381,9 +381,9 @@ class ArrayIO extends \CoreLibs\DB\IO
/**
* writes one set into DB or updates one set (if PK exists)
*
* @param boolean $addslashes old convert entities and set set escape
* @param bool $addslashes old convert entities and set set escape
* @param array<mixed> $table_array optional table array, overwrites internal one
* @param boolean $acl_limit [false], if set to true, well do ACL limit check
* @param bool $acl_limit [false], if set to true, well do ACL limit check
* @return array<mixed> table array or null
*/
public function dbWrite(
@@ -391,7 +391,7 @@ class ArrayIO extends \CoreLibs\DB\IO
array $table_array = [],
bool $acl_limit = false
): array {
if (is_array($table_array) && count($table_array)) {
if (count($table_array)) {
$this->table_array = $table_array;
}
// PK ID check
@@ -475,13 +475,12 @@ class ArrayIO extends \CoreLibs\DB\IO
$this->table_array[$column]['type'] != 'view' &&
strlen($column) > 0 &&
// no acl limiter
($acl_limit === false ||
(
$acl_limit === false ||
// acl limit is true, min edit must be at larger than set
$acl_limit === true &&
$this->base_acl_level >=
($this->table_array[$column]['min_edit_acl'] ?? 100)
))
)
) {
// for password use hidden value if main is not set
if (
@@ -528,7 +527,7 @@ class ArrayIO extends \CoreLibs\DB\IO
}
$q_data .= $_value;
} elseif (isset($this->table_array[$column]['bool'])) {
// boolean storeage (reverse check on ifset)
// bool storage (reverse check on ifset)
$q_data .= "'" . $this->dbBoolean($this->table_array[$column]['value'], true) . "'";
} elseif (
isset($this->table_array[$column]['interval']) ||

View File

@@ -12,7 +12,7 @@
* 2013/10/10, prepare/excute were added, including auto RETURNING primary key if
* possible for any INSERT query in exec or prepare/execute, better debugging and
* data dumping. Proper string escape wrapper, special db exec writer for complex
* array inserts in auto calls. boolean converter from postresql to php
* array inserts in auto calls. bool converter from postresql to php
*
* 2003/12/08, one major change: renamed db_exec_ext to db_return, as it has not
* much in common with the normal db_exec wrapper, as it was written only for
@@ -168,7 +168,7 @@
* - all HTML from the query data (color codes, etc) via flag to debug call
*
* HISTORY:
* 2008/10/25 (cs) add db_boolean to fix the postgres to php boolean var problem
* 2008/10/25 (cs) add db_boolean to fix the postgres to php bool var problem
* (TODO: implement this in any select return)
* 2008/07/03 (cs) add db_write_data function, original written for inventory tool "invSQLWriteData"
* 2008/04/16 (cs) add db_escape_string function for correct string escape
@@ -288,7 +288,7 @@ class IO
private $query; // the query string at the moment
// only inside
// basic vars
/** @var object|resource|bool|int|null */ // replace object with PgSql\Connection|
/** @var \PgSql\Connection|false|null */ // replace object with PgSql\Connection|
private $dbh; // the dbh handler, if disconnected by command is null, bool:false/int:-1 on error,
/** @var bool */
private $db_debug = false; // DB_DEBUG ... (if set prints out debug msgs)
@@ -315,7 +315,7 @@ class IO
/** @var array<mixed,mixed> */
private $cursor_ext; // hash of hashes
// per query vars
/** @var object|resource|bool */ // replace object with PgSql\Result
/** @var \PgSql\Result|false */ // replace object with PgSql\Result
private $cursor; // actual cursor (DBH)
/** @var int */
private $num_rows; // how many rows have been found
@@ -495,15 +495,15 @@ class IO
* OTOH this whole class is so PgSQL specific
* that non PgSQL doesn't make much sense anymore
*
* @return \CoreLibs\DB\SQL\PgSQL|null DB functions object or false on error
* @return SQL\PgSQL|null DB functions object or false on error
*/
private function __loadDBFunctions()
private function __loadDBFunctions(): SQL\PgSQL|null
{
$db_functions = null;
switch ($this->db_type) {
// list of valid DB function objects
case 'pgsql':
$db_functions = new \CoreLibs\DB\SQL\PgSQL();
$db_functions = new SQL\PgSQL();
break;
// if non set or none matching abort
default:
@@ -566,7 +566,7 @@ class IO
*/
private function __closeDB(): void
{
if (!empty($this->dbh) && $this->dbh !== false) {
if (!empty($this->dbh)) {
$this->db_functions->__dbClose();
$this->dbh = null;
}
@@ -632,9 +632,6 @@ class IO
private function __printArray(array $array): string
{
$string = '';
if (!is_array($array)) {
$array = [];
}
foreach ($array as $key => $value) {
$string .= $this->nbsp . '<b>' . $key . '</b> => ';
if (is_array($value)) {
@@ -701,7 +698,7 @@ class IO
/**
* Check if there is a cursor and write this cursors error info
* @param object|resource|bool $cursor current cursor for pg_result_error,
* @param \PgSql\Result|false $cursor current cursor for pg_result_error,
* pg_last_error too, but pg_result_error
* is more accurate (PgSql\Result)
* @return array<mixed> Pos 0: if we could get the method where it was called
@@ -709,7 +706,7 @@ class IO
* Pos 1: if we have the pg_error_string from last error
* if nothing then empty string
*/
private function __dbErrorPreprocessor($cursor = false): array
private function __dbErrorPreprocessor(\PgSql\Result|false $cursor = false): array
{
$pg_error_string = '';
// 1 = self/__dbErrorPreprocessor, 2 = __dbError, __dbWarning,
@@ -774,12 +771,15 @@ class IO
/**
* write an error
* @param integer $error_id Any Error ID, used in debug message string
* @param object|resource|bool $cursor Optional cursor, passed on to preprocessor
* @param \PgSql\Result|false $cursor Optional cursor, passed on to preprocessor
* @param string $msg optional message added to debug
* @return void
*/
protected function __dbError(int $error_id, $cursor = false, string $msg = ''): void
{
protected function __dbError(
int $error_id,
\PgSql\Result|false $cursor = false,
string $msg = ''
): void {
$error_id = (string)$error_id;
[$where_called, $pg_error_string] = $this->__dbErrorPreprocessor($cursor);
// write error msg ...
@@ -798,12 +798,15 @@ class IO
/**
* write a warning
* @param integer $warning_id Integer warning id added to debug
* @param object|resource|bool $cursor Optional cursor, passed on to preprocessor
* @param \PgSql\Result|false $cursor Optional cursor, passed on to preprocessor
* @param string $msg optional message added to debug
* @return void
*/
protected function __dbWarning(int $warning_id, $cursor = false, string $msg = ''): void
{
protected function __dbWarning(
int $warning_id,
\PgSql\Result|false $cursor = false,
string $msg = ''
): void {
$warning_id = (string)$warning_id;
[$where_called, $pg_error_string] = $this->__dbErrorPreprocessor($cursor);
$this->__dbDebug(
@@ -821,18 +824,18 @@ class IO
/**
* if there is the 'to_encoding' var set,
* and the field is in the wrong encoding converts it to the target
* @param array<mixed>|bool|null $row Array from fetch_row
* @return array<mixed>|bool Convert fetch_row array, or false
* @param array<mixed>|false $row Array from fetch_row
* @return array<mixed>|false Convert fetch_row array, or false
*/
private function __dbConvertEncoding($row)
private function __dbConvertEncoding(array|false $row): array|false
{
if ($row === null) {
if (is_bool($row)) {
return false;
}
// only do if array, else pass through row (can be false)
if (
!is_array($row) ||
empty($this->to_encoding)// || empty($this->db_encoding)
empty($this->to_encoding)
) {
return $row;
}
@@ -914,7 +917,7 @@ class IO
* check if there is another query running, or do we hang after a
* PHP error
* @param integer $timeout_seconds For complex timeout waits, default 3 seconds
* @return boolean True for connection OK, else false
* @return bool True for connection OK, else false
*/
private function __dbCheckConnectionOk(int $timeout_seconds = 3): bool
{
@@ -932,7 +935,7 @@ class IO
* dbReturn
* Read data from previous written data cache
* @param string $query_hash The hash for the current query
* @param boolean $assoc_only Only return assoc value (key named)
* @param bool $assoc_only Only return assoc value (key named)
* @return array<mixed> Current position query data from cache
*/
private function __dbReturnCacheRead(string $query_hash, bool $assoc_only): array
@@ -983,10 +986,11 @@ class IO
* - sets internal hash for query
* - checks multiple call count
* @param string $query query string
* @param string $pk_name primary key [if set to NULL no returning will be added]
* @return string|bool queryt hash OR boolean false on error
* @param string $pk_name primary key
* [if set to NULL no returning will be added]
* @return string|false queryt hash OR bool false on error
*/
private function __dbPrepareExec(string $query, string $pk_name)
private function __dbPrepareExec(string $query, string $pk_name): string|false
{
// reset current cursor before exec
$this->cursor = false;
@@ -1152,10 +1156,10 @@ class IO
* insert_id_ext [DEPRECATED, all in insert_id_arr]
* - holds all returning as array
* TODO: Only use insert_id_arr and use functions to get ok array or single
* @param boolean $returning_id
* @param bool $returning_id
* @param string $query
* @param string|null $pk_name
* @param object|resource|bool $cursor (PgSql\Result)
* @param \PgSql\Result|false $cursor (PgSql\Result)
* @param string|null $stm_name If not null, is dbExecutre run
* @return void
*/
@@ -1163,7 +1167,7 @@ class IO
bool $returning_id,
string $query,
?string $pk_name,
$cursor,
\PgSql\Result|false $cursor,
?string $stm_name = null
): void {
// $this->log->debug('DB SET INSERT ID', 'Ret: ' . ($returning_id ? 'Y' : 'N')
@@ -1271,10 +1275,10 @@ class IO
/**
* get certain settings like username, db name
* @param string $name what setting to query
* @return mixed setting value, if not allowed name return false
* @param string $name what setting to query
* @return int|string|bool setting value, if not allowed name return false
*/
public function dbGetSetting(string $name)
public function dbGetSetting(string $name): int|string|bool
{
$setting = '';
switch ($name) {
@@ -1373,7 +1377,7 @@ class IO
* extended version info, can access all additional information data
* @param string $parameter Array parameter name, if not valid returns
* empty string
* @param boolean $strip Strip extended server info string, default true
* @param bool $strip Strip extended server info string, default true
* eg nn.n (other info) will only return nn.n
* @return string Parameter value
*/
@@ -1392,14 +1396,15 @@ class IO
}
/**
* returns boolean true or false if the string matches the database version
* returns bool true or false if the string matches the database version
* @param string $compare string to match in type =X.Y, >X.Y, <X.Y, <=X.Y, >=X.Y
* @return bool true for ok, false on not ok
*/
public function dbCompareVersion(string $compare): bool
{
$matches = [];
// compare has =, >, < prefix, and gets stripped, if the rest is not X.Y format then error
// compare has =, >, < prefix, and gets stripped
// if the rest is not X.Y format then error
preg_match("/^([<>=]{1,})(\d{1,})\.(\d{1,})/", $compare, $matches);
$compare = $matches[1];
$to_master = $matches[2];
@@ -1464,7 +1469,7 @@ class IO
* else current cursor
* @return string Formated string with all the data in the array
*/
public function dbDumpData($query = ''): string
public function dbDumpData(string $query = ''): string
{
// set start array
if ($query) {
@@ -1490,7 +1495,7 @@ class IO
* @param string|int|float|bool $string string to escape
* @return string escaped string
*/
public function dbEscapeString($string): string
public function dbEscapeString(string|int|float|bool $string): string
{
return $this->db_functions->__dbEscapeString($string);
}
@@ -1501,7 +1506,7 @@ class IO
* @param string|int|float|bool $string string to escape
* @return string escaped string
*/
public function dbEscapeLiteral($string): string
public function dbEscapeLiteral(string|int|float|bool $string): string
{
return $this->db_functions->__dbEscapeLiteral($string);
}
@@ -1538,11 +1543,11 @@ class IO
/**
* clear up any data for valid DB insert
* @param int|float|string|null $value to escape data
* @param string $kbn escape trigger type
* @return string escaped value
* @param int|float|string|bool|null $value to escape data
* @param string $kbn escape trigger type
* @return string escaped value
*/
public function dbSqlEscape($value, string $kbn = '')
public function dbSqlEscape(int|float|string|bool|null $value, string $kbn = ''): string
{
switch ($kbn) {
case 'i':
@@ -1551,24 +1556,35 @@ class IO
case 'f':
$value = empty($value) ? 'NULL' : floatval($value);
break;
// string (null is null, else is string)
case 't':
$value = $value === null ?
'NULL' :
"'" . $this->dbEscapeString($value) . "'";
break;
// string litereal (null is null, else is stirng)
case 'tl':
$value = $value === null ?
'NULL' :
$this->dbEscapeLiteral($value);
break;
// what is d?
// escape string, set empty to null
case 'd':
$value = empty($value) ? 'NULL' : "'" . $this->dbEscapeString($value) . "'";
$value = empty($value) ?
'NULL' :
"'" . $this->dbEscapeString($value) . "'";
break;
// escape string literal, set empty to null
case 'dl':
$value = empty($value) ?
'NULL' :
$this->dbEscapeLiteral($value);
break;
// bytea data
case 'by':
$value = empty($value) ? 'NULL' : $this->dbEscapeBytea((string)$value);
break;
// bool
case 'b':
if (is_float($value)) {
$value = (int)$value;
@@ -1577,6 +1593,7 @@ class IO
'NULL' :
"'" . $this->dbBoolean($value, true) . "'";
break;
// int, but with empty value is 0
case 'i2':
$value = empty($value) ? 0 : intval($value);
break;
@@ -1590,14 +1607,14 @@ class IO
/**
* if the input is a single char 't' or 'f
* it will return the boolean value instead
* it will return the bool value instead
* also converts smallint 1/0 to true false
* @param string|bool|int $string 't' / 'f' or any string, or bool true/false
* @param boolean $rev do reverse (bool to string)
* @return bool|string correct php boolean true/false
* @param bool $rev do reverse (bool to string)
* @return bool|string correct php bool true/false
* or postgresql 't'/'f'
*/
public function dbBoolean($string, $rev = false)
public function dbBoolean(string|bool|int $string, bool $rev = false): bool|string
{
if (!$rev) {
if ($string == 't' || $string == 'true') {
@@ -1696,9 +1713,9 @@ class IO
* returns an array of the table with columns and values. FALSE on no table found
* @param string $table table name
* @param string $schema optional schema name
* @return array<mixed>|bool array of table data, false on error (table not found)
* @return array<mixed>|false array of table data, false on error (table not found)
*/
public function dbShowTableMetaData(string $table, string $schema = '')
public function dbShowTableMetaData(string $table, string $schema = ''): array|false
{
$this->__dbErrorReset();
$table = (!empty($schema) ? $schema . '.' : '') . $table;
@@ -1736,14 +1753,14 @@ class IO
* NO_CACHE/3: don't write cache
* @param bool $assoc_only True to only returned the named and not
* index position ones
* @return array<mixed>|bool return array data or false on error/end
* @return array<mixed>|false return array data or false on error/end
* @#suppress PhanTypeMismatchDimFetch
*/
public function dbReturn(
string $query,
int $cache = self::USE_CACHE,
bool $assoc_only = false
) {
): array|false {
$this->__dbErrorReset();
if (!$query) {
$this->__dbError(11);
@@ -1754,7 +1771,7 @@ class IO
// pre declare array
if (!isset($this->cursor_ext[$query_hash])) {
$this->cursor_ext[$query_hash] = [
// cursor, null: unset, 1: finished read/cache, 2: object/resource reading
// cursor, null: unset, 1: finished read/cache, 2: object reading
'cursor' => null,
// cached data
'data' => [],
@@ -2000,9 +2017,9 @@ class IO
* if pk name is table name and _id, pk_name
* is not needed to be set
* if NULL is given here, no RETURNING will be auto added
* @return object|resource|bool cursor for this query or false on error (PgSql\Result)
* @return \PgSql\Result|false cursor for this query or false on error
*/
public function dbExec(string $query = '', string $pk_name = '')
public function dbExec(string $query = '', string $pk_name = ''): \PgSql\Result|false
{
$this->__dbErrorReset();
// prepare and check if we can actually run it
@@ -2031,7 +2048,7 @@ class IO
/**
* executes a cursor and returns the data, if no more data 0 will be returned
* @param object|resource|bool $cursor the cursor from db_exec or
* @param \PgSql\Result|false $cursor the cursor from db_exec or
* pg_query/pg_exec/mysql_query
* if not set will use internal cursor,
* if not found, stops with 0 (error)
@@ -2039,9 +2056,9 @@ class IO
* @param bool $assoc_only false is default,
* if true only named rows,
* not numbered index rows
* @return array<mixed>|bool row array or false on error
* @return array<mixed>|false row array or false on error
*/
public function dbFetchArray($cursor = false, bool $assoc_only = false)
public function dbFetchArray(\PgSql\Result|false $cursor = false, bool $assoc_only = false): array|false
{
$this->__dbErrorReset();
// set last available cursor if none set or false
@@ -2064,9 +2081,9 @@ class IO
* returns the FIRST row of the given query
* @param string $query the query to be executed
* @param bool $assoc_only if true, only return assoc entry (default false)
* @return array<mixed>|bool row array or false on error
* @return array<mixed>|false row array or false on error
*/
public function dbReturnRow(string $query, bool $assoc_only = false)
public function dbReturnRow(string $query, bool $assoc_only = false): array|false
{
$this->__dbErrorReset();
if (!$query) {
@@ -2091,9 +2108,9 @@ class IO
* createds an array of hashes of the query (all data)
* @param string $query the query to be executed
* @param bool $assoc_only if true, only name ref are returned (default true)
* @return array<mixed>|bool array of hashes (row -> fields), false on error
* @return array<mixed>|false array of hashes (row -> fields), false on error
*/
public function dbReturnArray(string $query, bool $assoc_only = true)
public function dbReturnArray(string $query, bool $assoc_only = true): array|false
{
$this->__dbErrorReset();
if (!$query) {
@@ -2151,41 +2168,44 @@ class IO
* returns the full array for cursor ext
* or cursor for one query
* or detail data fonr one query cursor data
* @param string|null $query Query string, if not null convert to hash
* and return set cursor ext for only this
* if not found or null return null
* @return array<mixed>|string|int|resource|object|null
* Cursor Extended array full if no parameter
* Key is hash string from query run
* Or cursor data entry if query field is set
* If nothing found return null
* @param string|null $query Query string, if not null convert to hash
* and return set cursor ext for only this
* if not found or null return null
* @param string $query_field [=''] optional query field to get
* @return array<mixed>|string|int|\PgSql\Result|null
* Cursor Extended array full if no parameter
* Key is hash string from query run
* Or cursor data entry if query field is set
* If nothing found return null
*/
public function dbGetCursorExt($query = null, string $query_field = '')
{
if ($query !== null) {
$query_hash = $this->dbGetQueryHash($query);
if (
is_array($this->cursor_ext) &&
isset($this->cursor_ext[$query_hash])
) {
if (empty($query_field)) {
return $this->cursor_ext[$query_hash];
} else {
return $this->cursor_ext[$query_hash][$query_field] ?? null;
}
} else {
return null;
}
public function dbGetCursorExt(
$query = null,
string $query_field = ''
): array|string|int|\PgSql\Result|null {
if ($query === null) {
return $this->cursor_ext;
}
$query_hash = $this->dbGetQueryHash($query);
if (
is_array($this->cursor_ext) &&
isset($this->cursor_ext[$query_hash])
) {
if (empty($query_field)) {
return $this->cursor_ext[$query_hash];
} else {
return $this->cursor_ext[$query_hash][$query_field] ?? null;
}
} else {
return null;
}
return $this->cursor_ext;
}
/**
* returns the current position the read out
* @param string $query query to find in cursor_ext
* @return int|bool query position (row pos), false on error
* @return int|false query position (row pos), false on error
*/
public function dbGetCursorPos(string $query)
public function dbGetCursorPos(string $query): int|false
{
$this->__dbErrorReset();
if (!$query) {
@@ -2199,9 +2219,9 @@ class IO
/**
* returns the number of rows for the current select query
* @param string $query query to find in cursor_ext
* @return int|bool query position (row pos), false on error
* @return int|false query position (row pos), false on error
*/
public function dbGetCursorNumRows(string $query)
public function dbGetCursorNumRows(string $query): int|false
{
$this->__dbErrorReset();
if (!$query) {
@@ -2253,11 +2273,14 @@ class IO
* @param string $stm_name statement name
* @param string $query queryt string to run
* @param string $pk_name optional primary key
* @return bool|object|resource false on error, true on warning or
* result on full ok (PgSql\Result)
* @return \PgSql\Result|bool false on error, true on warning or
* result on full ok
*/
public function dbPrepare(string $stm_name, string $query, string $pk_name = '')
{
public function dbPrepare(
string $stm_name,
string $query,
string $pk_name = ''
): \PgSql\Result|bool {
$this->__dbErrorReset();
$matches = [];
if (!$query) {
@@ -2361,9 +2384,9 @@ class IO
* runs a prepare query
* @param string $stm_name statement name for the query to run
* @param array<mixed> $data data to run for this query, empty array for none
* @return mixed false on error, or result on OK
* @return \PgSql\Result|false false on error, or result on OK
*/
public function dbExecute(string $stm_name, array $data = [])
public function dbExecute(string $stm_name, array $data = []): \PgSql\Result|false
{
$this->__dbErrorReset();
// if no DB Handler drop out
@@ -2483,11 +2506,10 @@ class IO
/**
* checks a previous async query and returns data if finished
* NEEDS : dbExecAsync
* @return bool|object|resource cursor resource if the query is still running,
* false if an error occured or cursor of that query
* (PgSql\Result)
* @return \PgSql\Result|bool cursor resource if the query is still running,
* false if an error occured or cursor of that query
*/
public function dbCheckAsync()
public function dbCheckAsync(): \PgSql\Result|bool
{
$this->__dbErrorReset();
// if there is actually a async query there
@@ -2547,7 +2569,7 @@ class IO
* @param int $primary_key id key to decide if we write insert or update
* @param string $table name for the target table
* @param array<mixed> $data data array to override _POST data
* @return int|bool primary key
* @return int|false primary key
*/
public function dbWriteData(
array $write_array,
@@ -2555,13 +2577,7 @@ class IO
int $primary_key,
string $table,
array $data = []
) {
if (!is_array($write_array)) {
$write_array = [];
}
if (!is_array($not_write_array)) {
$not_write_array = [];
}
): int|false {
$not_write_update_array = [];
return $this->dbWriteDataExt(
$write_array,
@@ -2586,16 +2602,16 @@ class IO
* to write during update (optional)
* @param array<mixed> $data optional array with data
* if not _POST vars are used
* @return int|bool primary key
* @return int|false primary key
*/
public function dbWriteDataExt(
array $write_array,
$primary_key,
int|string|array $primary_key,
string $table,
array $not_write_array = [],
array $not_write_update_array = [],
array $data = []
) {
): int|false {
if (!is_array($primary_key)) {
$primary_key = [
'row' => $table . '_id',
@@ -2711,7 +2727,10 @@ class IO
$primary_key['value'] = $this->dbGetInsertPK();
}
// if there is not priamry key value field return false
return $primary_key['value'] ?? false;
if (!is_numeric($primary_key['value'])) {
return false;
}
return (int)$primary_key['value'];
}
// ***************************
@@ -2724,7 +2743,7 @@ class IO
* @param bool|null $debug true/false or null for just getting current set
* @return bool Current debug flag as set
*/
public function dbSetDebug($debug = null): bool
public function dbSetDebug(?bool $debug = null): bool
{
if ($debug !== null) {
$this->db_debug = $debug;
@@ -2805,7 +2824,7 @@ class IO
* @return bool False on failure to find schema value or set schema,
* True on successful set
*/
public function dbSetSchema(string $db_schema)
public function dbSetSchema(string $db_schema): bool
{
$this->__dbErrorReset();
if (empty($db_schema)) {
@@ -2934,9 +2953,9 @@ class IO
/**
* Return current database handler
* @return object|resource|bool|int|null
* @return \PgSql\Connection|false|null
*/
public function dbGetDbh()
public function dbGetDbh(): \PgSql\Connection|false|null
{
return $this->dbh;
}
@@ -2992,7 +3011,7 @@ class IO
*
* @return array<mixed>|string|int|null Current insert query primary key
*/
public function dbGetInsertPK()
public function dbGetInsertPK(): array|string|int|null
{
if (empty($this->insert_id_pk_name)) {
return null;
@@ -3016,7 +3035,7 @@ class IO
* @param integer|null $pos
* @return array<mixed>|string|int|null
*/
public function dbGetReturningExt(?string $key = null, ?int $pos = null)
public function dbGetReturningExt(?string $key = null, ?int $pos = null): array|string|int|null
{
// return as is if key is null
if ($key === null) {
@@ -3110,7 +3129,7 @@ class IO
* Not ethat returnin_id also can return false
* but will not set an error entry
*/
public function dbGetPrepareCursorValue(string $stm_name, string $key)
public function dbGetPrepareCursorValue(string $stm_name, string $key): null|string|int|bool
{
// if no statement name
if (empty($stm_name)) {
@@ -3159,8 +3178,8 @@ class IO
* Sets error number that was last
* So we always have the last error number stored even if a new
* one is created
* @param boolean $transform Set to true to transform into id + error message
* @return string Last error number as string or error message
* @param bool $transform Set to true to transform into id + error message
* @return string Last error number as string or error message
*/
public function dbGetLastError(bool $transform = false): string
{
@@ -3180,10 +3199,10 @@ class IO
/**
* Sets warning number that was last
* So we always have the last warning number stored even if a new one is created
* @param boolean $transform Set to true to transform into id + warning message
* @return string Last Warning number as string or warning message
* @param bool $transform Set to true to transform into id + warning message
* @return string Last Warning number as string or warning message
*/
public function dbGetLastWarning(bool $transform = false)
public function dbGetLastWarning(bool $transform = false): string
{
// if no warning, return empty
if (empty($this->warning_id)) {
@@ -3221,7 +3240,7 @@ class IO
* Null for error
* @deprecated Use ->dbGetInsertPK();
*/
public function dbGetReturning()
public function dbGetReturning(): array|string|int|bool|null
{
return $this->dbGetInsertPK();
}
@@ -3271,7 +3290,7 @@ class IO
* @return array<mixed>|string|int|bool|null See above
* @deprecated use getReturningExt($key = null) instead
*/
public function getInsertReturn($key = null)
public function getInsertReturn(?string $key = null): array|string|int|bool|null
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use getReturningExt($key = null)', E_USER_DEPRECATED);
return $this->dbGetReturningExt($key);
@@ -3282,7 +3301,7 @@ class IO
* @return array<mixed>|string|int|bool|null [DEPRECATED]
* @deprecated use dbGetReturning() instead
*/
public function getReturning()
public function getReturning(): array|string|int|bool|null
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetReturning()', E_USER_DEPRECATED);
return $this->dbGetInsertPK();
@@ -3293,7 +3312,7 @@ class IO
* @return array<mixed>|string|int|bool|null [DEPRECATED]
* @deprecated use dbGetInsertPK() instead
*/
public function getInsertPK()
public function getInsertPK(): array|string|int|bool|null
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetInsertPK()', E_USER_DEPRECATED);
return $this->dbGetInsertPK();
@@ -3305,7 +3324,7 @@ class IO
* @return array<mixed>|string|bool|int|null [DEPRECATED]
* @deprecated use dbGetReturningExt($key = null) instead
*/
public function getReturningExt($key = null)
public function getReturningExt(?string $key = null): array|string|int|bool|null
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetReturningExt($key = null)', E_USER_DEPRECATED);
return $this->dbGetReturningExt($key);
@@ -3314,10 +3333,10 @@ class IO
/**
* DEPRECATED: getCursorExt
* @param string|null $q [DEPRECATED]
* @return array<mixed>|string|int|resource|object|null [DEPRECATED]
* @return array<mixed>|string|int|\PgSql\Result|null [DEPRECATED]
* @deprecated use dbGetCursorExt($q = null) instead
*/
public function getCursorExt($q = null)
public function getCursorExt(?string $q = null): array|string|int|\PgSql\Result|null
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetCursorExt($q = null)', E_USER_DEPRECATED);
return $this->dbGetCursorExt($q);
@@ -3328,7 +3347,7 @@ class IO
* @return int|null [DEPRECATED]
* @deprecated use dbGetNumRows() instead
*/
public function getNumRows()
public function getNumRows(): ?int
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use dbGetNumRows()', E_USER_DEPRECATED);
return $this->dbGetNumRows();

View File

@@ -6,7 +6,7 @@
declare(strict_types=1);
namespace CoreLibs\DB\SQL\SqlInterface;
namespace CoreLibs\DB\SQL\Interface;
interface SqlFunctions
{
@@ -21,33 +21,33 @@ interface SqlFunctions
* Undocumented function
*
* @param string $query
* @return object|resource|bool
* @return \PgSql\Result|false
*/
public function __dbQuery(string $query);
public function __dbQuery(string $query): \PgSql\Result|false;
/**
* Undocumented function
*
* @param string $query
* @param array<mixed> $params
* @return object|resource|bool
* @return \PgSql\Result|false
*/
public function __dbQueryParams(string $query, array $params);
public function __dbQueryParams(string $query, array $params): \PgSql\Result|false;
/**
* Undocumented function
*
* @param string $query
* @return boolean
* @return bool
*/
public function __dbSendQuery(string $query): bool;
/**
* Undocumented function
*
* @return object|resource|bool
* @return \PgSql\Result|false
*/
public function __dbGetResult();
public function __dbGetResult(): \PgSql\Result|false;
/**
* Undocumented function
@@ -61,85 +61,85 @@ interface SqlFunctions
*
* @param string $name
* @param string $query
* @return object|resource|bool
* @return \PgSql\Result|false
*/
public function __dbPrepare(string $name, string $query);
public function __dbPrepare(string $name, string $query): \PgSql\Result|false;
/**
* Undocumented function
*
* @param string $name
* @param array<mixed> $data
* @return object|resource|bool
* @return \PgSql\Result|false
*/
public function __dbExecute(string $name, array $data);
public function __dbExecute(string $name, array $data): \PgSql\Result|false;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return integer
* @param \PgSql\Result|false $cursor
* @return int
*/
public function __dbNumRows($cursor): int;
public function __dbNumRows(\PgSql\Result|false $cursor): int;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return integer
* @param \PgSql\Result|false $cursor
* @return int
*/
public function __dbNumFields($cursor): int;
public function __dbNumFields(\PgSql\Result|false $cursor): int;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @param \PgSql\Result|false $cursor
* @param int $i
* @return string|bool
* @return string|false
*/
public function __dbFieldName($cursor, int $i);
public function __dbFieldName(\PgSql\Result|false $cursor, int $i): string|false;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @param \PgSql\Result|false $cursor
* @param int $result_type
* @return array<mixed>|bool
*/
public function __dbFetchArray($cursor, int $result_type = PGSQL_BOTH);
public function __dbFetchArray(\PgSql\Result|false $cursor, int $result_type = PGSQL_BOTH);
/**
* Undocumented function
*
* @param boolean $assoc_type
* @return integer
* @param bool $assoc_type
* @return int
*/
public function __dbResultType(bool $assoc_type = true): int;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @param \PgSql\Result|false $cursor
* @return array<mixed>|bool
*/
public function __dbFetchAll($cursor);
public function __dbFetchAll(\PgSql\Result|false $cursor): array|bool;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return integer
* @param \PgSql\Result|false $cursor
* @return int
*/
public function __dbAffectedRows($cursor): int;
public function __dbAffectedRows(\PgSql\Result|false $cursor): int;
/**
* Undocumented function
*
* @param string $query
* @param string|null $pk_name
* @return string|integer|false
* @return string|int|false
*/
public function __dbInsertId(string $query, ?string $pk_name);
public function __dbInsertId(string $query, ?string $pk_name): string|int|false;
/**
* Undocumented function
@@ -148,7 +148,7 @@ interface SqlFunctions
* @param string $schema
* @return string|bool
*/
public function __dbPrimaryKey(string $table, string $schema = '');
public function __dbPrimaryKey(string $table, string $schema = ''): string|bool;
/**
* Undocumented function
@@ -157,9 +157,9 @@ interface SqlFunctions
* @param string $db_user
* @param string $db_pass
* @param string $db_name
* @param integer $db_port
* @param int $db_port
* @param string $db_ssl
* @return object|resource|bool
* @return \PgSql\Connection|false
*/
public function __dbConnect(
string $db_host,
@@ -168,24 +168,24 @@ interface SqlFunctions
string $db_name,
int $db_port,
string $db_ssl = 'allow'
);
): \PgSql\Connection|false;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @param \PgSql\Result|false $cursor
* @return string
*/
public function __dbPrintError($cursor = false): string;
public function __dbPrintError(\PgSql\Result|false $cursor = false): string;
/**
* Undocumented function
*
* @param string $table
* @param boolean $extended
* @param bool $extended
* @return array<mixed>|bool
*/
public function __dbMetaData(string $table, $extended = true);
public function __dbMetaData(string $table, bool $extended = true): array|bool;
/**
* Undocumented function
@@ -193,7 +193,7 @@ interface SqlFunctions
* @param string|int|float|bool $string
* @return string
*/
public function __dbEscapeString($string): string;
public function __dbEscapeString(string|int|float|bool $string): string;
/**
* Undocumented function
@@ -201,7 +201,7 @@ interface SqlFunctions
* @param string|int|float|bool $string
* @return string
*/
public function __dbEscapeLiteral($string): string;
public function __dbEscapeLiteral(string|int|float|bool $string): string;
/**
* Undocumented function
@@ -230,15 +230,15 @@ interface SqlFunctions
/**
* Undocumented function
*
* @return boolean
* @return bool
*/
public function __dbConnectionBusy(): bool;
/**
* Undocumented function
*
* @param integer $timeout_seconds
* @return boolean
* @param int $timeout_seconds
* @return bool
*/
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool;
@@ -253,8 +253,8 @@ interface SqlFunctions
* Undocumented function
*
* @param string $array_text
* @param integer $start
* @param integer|null $end
* @param int $start
* @param int|null $end
* @return array<mixed>|null
*/
public function __dbArrayParse(
@@ -275,7 +275,7 @@ interface SqlFunctions
* Undocumented function
*
* @param string $db_schema
* @return integer
* @return int
*/
public function __dbSetSchema(string $db_schema): int;
@@ -290,7 +290,7 @@ interface SqlFunctions
* Undocumented function
*
* @param string $db_encoding
* @return integer
* @return int
*/
public function __dbSetEncoding(string $db_encoding): int;

View File

@@ -51,12 +51,11 @@ namespace CoreLibs\DB\SQL;
// as main system. Currently all @var sets are written as object
/** @#phan-file-suppress PhanUndeclaredTypeProperty,PhanUndeclaredTypeParameter,PhanUndeclaredTypeReturnType */
class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
class PgSQL implements Interface\SqlFunctions
{
/** @var string */
private $last_error_query;
// NOTE for PHP 8.1 this is no longer a resource
/** @var object|resource|bool */ // replace object with PgSql\Connection
/** @var \PgSql\Connection|false */
private $dbh;
/**
@@ -77,12 +76,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* wrapper for pg_query, catches error and stores it in class var
*
* @param string $query Query string
* @return object|resource|bool query result (PgSql\Result)
* @return \PgSql\Result|false query result
*/
public function __dbQuery(string $query)
public function __dbQuery(string $query): \PgSql\Result|false
{
$this->last_error_query = '';
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
// read out the query status and save the query if needed
@@ -100,12 +99,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*
* @param string $query Query string with placeholders $1, ..
* @param array<mixed> $params Matching parameters for each placerhold
* @return object|resource|bool Query result (PgSql\Result)
* @return \PgSql\Result|false Query result
*/
public function __dbQueryParams(string $query, array $params)
public function __dbQueryParams(string $query, array $params): \PgSql\Result|false
{
$this->last_error_query = '';
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
// parse query and get all $n entries
@@ -126,7 +125,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*/
public function __dbSendQuery(string $query): bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
$result = pg_send_query($this->dbh, $query);
@@ -136,12 +135,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* wrapper for pg_get_result
*
* @return object|resource|bool resource handler or false for error (PgSql\Result)
* @return \PgSql\Result|false resource handler or false for error
*/
public function __dbGetResult()
public function __dbGetResult(): \PgSql\Result|false
{
$this->last_error_query = '';
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
$result = pg_get_result($this->dbh);
@@ -161,7 +160,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*/
public function __dbClose(): void
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return;
}
if (pg_connection_status($this->dbh) === PGSQL_CONNECTION_OK) {
@@ -175,12 +174,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*
* @param string $name statement name
* @param string $query query string
* @return object|resource|bool prepare statement handler or
* false for error (PgSql\Result)
* @return \PgSql\Result|false prepare statement handler or
* false for error
*/
public function __dbPrepare(string $name, string $query)
public function __dbPrepare(string $name, string $query): \PgSql\Result|false
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
$result = pg_prepare($this->dbh, $name, $query);
@@ -195,11 +194,11 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*
* @param string $name statement name
* @param array<mixed> $data data array
* @return object|resource|bool returns status or false for error (PgSql\Result)
* @return \PgSql\Result|false returns status or false for error
*/
public function __dbExecute(string $name, array $data)
public function __dbExecute(string $name, array $data): \PgSql\Result|false
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
$result = pg_execute($this->dbh, $name, $data);
@@ -212,12 +211,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* wrapper for pg_num_rows
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return int number of rows, -1 on error
* @param \PgSql\Result|false $cursor cursor
* @return int number of rows, -1 on error
*/
public function __dbNumRows($cursor): int
public function __dbNumRows(\PgSql\Result|false $cursor): int
{
if ($cursor === false || is_bool($cursor)) {
if (is_bool($cursor)) {
return -1;
}
return pg_num_rows($cursor);
@@ -226,12 +225,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* wrapper for pg_num_fields
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return int number for fields in result, -1 on error
* @param \PgSql\Result|false $cursor cursor
* @return int number for fields in result, -1 on error
*/
public function __dbNumFields($cursor): int
public function __dbNumFields(\PgSql\Result|false $cursor): int
{
if ($cursor === false || is_bool($cursor)) {
if (is_bool($cursor)) {
return -1;
}
return pg_num_fields($cursor);
@@ -240,13 +239,13 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* wrapper for pg_field_name
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @param int $i field position
* @return string|bool name or false on error
* @param \PgSql\Result|false $cursor cursor
* @param int $i field position
* @return string|false name or false on error
*/
public function __dbFieldName($cursor, int $i)
public function __dbFieldName(\PgSql\Result|false $cursor, int $i): string|false
{
if ($cursor === false || is_bool($cursor)) {
if (is_bool($cursor)) {
return false;
}
return pg_field_name($cursor, $i);
@@ -256,13 +255,13 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* wrapper for pg_fetch_array
* if through/true false, use __dbResultType(true)
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @param int $result_type result type as int number
* @return array<mixed>|bool array result data or false on end/error
* @param \PgSql\Result|false $cursor cursor
* @param int $result_type result type as int number
* @return array<mixed>|false array result data or false on end/error
*/
public function __dbFetchArray($cursor, int $result_type = PGSQL_BOTH)
public function __dbFetchArray(\PgSql\Result|false $cursor, int $result_type = PGSQL_BOTH): array|false
{
if ($cursor === false || is_bool($cursor)) {
if (is_bool($cursor)) {
return false;
}
// result type is passed on as is [should be checked]
@@ -287,12 +286,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* wrapper for pg_fetch_all
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return array<mixed>|bool data array or false for end/error
* @param \PgSql\Result|false $cursor cursor
* @return array<mixed>|false data array or false for end/error
*/
public function __dbFetchAll($cursor)
public function __dbFetchAll(\PgSql\Result|false $cursor): array|false
{
if ($cursor === false || is_bool($cursor)) {
if (is_bool($cursor)) {
return false;
}
return pg_fetch_all($cursor);
@@ -301,12 +300,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* wrapper for pg_affected_rows
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return int affected rows, 0 for none, -1 for error
* @param \PgSql\Result|false $cursor cursor
* @return int affected rows, 0 for none, -1 for error
*/
public function __dbAffectedRows($cursor): int
public function __dbAffectedRows(\PgSql\Result|false $cursor): int
{
if ($cursor === false || is_bool($cursor)) {
if (is_bool($cursor)) {
return -1;
}
return pg_affected_rows($cursor);
@@ -323,7 +322,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* @param string|null $pk_name primary key name, if '' then auto detect
* @return string|int|false primary key value
*/
public function __dbInsertId(string $query, ?string $pk_name)
public function __dbInsertId(string $query, ?string $pk_name): string|int|false
{
// only if an insert has been done
if (preg_match("/^insert /i", $query)) {
@@ -371,7 +370,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* @param string $schema optional schema name, '' for default
* @return string|bool primary key name or false if not found
*/
public function __dbPrimaryKey(string $table, string $schema = '')
public function __dbPrimaryKey(string $table, string $schema = ''): string|bool
{
if ($table) {
// check if schema set is different from schema given,
@@ -426,7 +425,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* @param string $db_name databse name
* @param integer $db_port port (int, 5432 is default)
* @param string $db_ssl SSL (allow is default)
* @return object|resource|bool db handler PgSql\Connection or false on error
* @return \PgSql\Connection|false db handler or false on error
*/
public function __dbConnect(
string $db_host,
@@ -435,7 +434,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
string $db_name,
int $db_port,
string $db_ssl = 'allow'
) {
): \PgSql\Connection|false {
if (empty($db_name)) {
return false;
}
@@ -470,22 +469,22 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* reads the last error for this cursor and returns
* html formatted string with error name
*
* @param bool|object|resource $cursor cursor PgSql\Result (former resource)
* @param \PgSql\Result|false $cursor cursor
* or null
* @return string error string
*/
public function __dbPrintError($cursor = false): string
public function __dbPrintError(\PgSql\Result|false $cursor = false): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return '';
}
// run the query again for the error result here
if (($cursor === false || is_bool($cursor)) && $this->last_error_query) {
if ((is_bool($cursor)) && $this->last_error_query) {
pg_send_query($this->dbh, $this->last_error_query);
$this->last_error_query = '';
$cursor = pg_get_result($this->dbh);
}
if ($cursor && !is_bool($cursor) && $error_str = pg_result_error($cursor)) {
if ($cursor && $error_str = pg_result_error($cursor)) {
return '-PostgreSQL-Error- '
. $error_str;
} else {
@@ -500,9 +499,9 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* @param bool $extended show extended info (default true)
* @return array<mixed>|bool array data for the table info or false on error
*/
public function __dbMetaData(string $table, $extended = true)
public function __dbMetaData(string $table, bool $extended = true): array|bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
// needs to prefixed with @ or it throws a warning on not existing table
@@ -515,9 +514,9 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* @param string|int|float|bool $string any string/int/float/bool
* @return string excaped string
*/
public function __dbEscapeString($string): string
public function __dbEscapeString(string|int|float|bool $string): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return '';
}
return pg_escape_string($this->dbh, (string)$string);
@@ -531,9 +530,9 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* @param string|int|float|bool $string any string/int/float/bool
* @return string excaped string including quites
*/
public function __dbEscapeLiteral($string): string
public function __dbEscapeLiteral(string|int|float|bool $string): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return (string)'';
}
// for phpstan, thinks this is string|false?
@@ -549,7 +548,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*/
public function __dbEscapeIdentifier(string $string): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return '';
}
// for phpstan, thinks this is string|false?
@@ -564,7 +563,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*/
public function __dbEscapeBytea(string $data): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return '';
}
return pg_escape_bytea($this->dbh, $data);
@@ -584,11 +583,12 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* wrapper for pg_connection_busy
*
* @return bool True if connection is busy, False if not or no db connection at all
* @return bool True if connection is busy
* False if not or no db connection at all
*/
public function __dbConnectionBusy(): bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
return pg_connection_busy($this->dbh);
@@ -597,13 +597,13 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
/**
* Experimental wrapper with scoket timetout
*
* @param integer $timeout_seconds Wait how many seconds on timeout
* @return boolean True if connection is busy, or false on
* not busy or no db connection at all
* @param integer $timeout_seconds Wait how many seconds on timeout
* @return bool True if connection is busy, or false on
* not busy or no db connection at all
*/
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
$busy = pg_connection_busy($this->dbh);
@@ -626,14 +626,14 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* On default 'version' will be stripped of any space attached info
* eg 13.5 (other info) will return only 13.5
*
* @param string $parameter Parameter string to extract from array
* @param boolean $strip If parameter is server strip out on default
* Set to false to get original string AS is
* @return string The parameter value
* @param string $parameter Parameter string to extract from array
* @param bool $strip If parameter is server strip out on default
* Set to false to get original string AS is
* @return string The parameter value
*/
public function __dbVersionInfo(string $parameter, bool $strip = true): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return '';
}
// extract element
@@ -655,7 +655,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*/
public function __dbVersionInfoParameterList(): array
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return [];
}
return array_keys(pg_version($this->dbh));
@@ -670,7 +670,7 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
*/
public function __dbVersion(): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return '';
}
// array has client, protocol, server, we just return server stripped
@@ -762,9 +762,9 @@ class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
* @param string $parameter Parameter to query
* @return string|bool Settings value as string
*/
public function __dbParameter(string $parameter)
public function __dbParameter(string $parameter): string|bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
if (is_bool($this->dbh)) {
return false;
}
if (empty($parameter)) {

View File

@@ -413,7 +413,7 @@ class Logging
* @param string $name what variable to return
* @return mixed can be anything, bool, string, int, array
*/
public function getSetting(string $name) //:mixed DOES not work with PHP 7.4
public function getSetting(string $name): mixed
{
// for debug purpose only
return $this->{$name};
@@ -550,9 +550,9 @@ class Logging
* @param string $flag on/off
* @param string|null $level if not null then check if this array entry is set
* else return false
* @return bool|array<mixed> if $level is null, return array, else boolean true/false
* @return array<mixed>|bool if $level is null, return array, else boolean true/false
*/
public function getLogLevel(string $type, string $flag, ?string $level = null)
public function getLogLevel(string $type, string $flag, ?string $level = null): array|bool
{
// abort if not valid type
if (!in_array($type, ['debug', 'echo', 'print'])) {

View File

@@ -122,7 +122,7 @@ class GetTextReader
* @param bool $enable_cache Enable or disable caching
* of strings (default on)
*/
public function __construct($Reader, bool $enable_cache = true)
public function __construct(FileReader|bool $Reader, bool $enable_cache = true)
{
// If there isn't a StreamReader, turn on short circuit mode.
if ((!is_object($Reader) && !$Reader) || (is_object($Reader) && $Reader->error)) {

View File

@@ -46,7 +46,7 @@ class Encoding
* @return void
* @deprecated Use \CoreLibs\Check\Encoding::setErrorChar();
*/
public static function setErrorChar($string): void
public static function setErrorChar(string|int|null $string): void
{
\CoreLibs\Check\Encoding::setErrorChar($string);
}
@@ -60,7 +60,7 @@ class Encoding
* @return string|int Set error character
* @deprecated Use \CoreLibs\Check\Encoding::getErrorChar();
*/
public static function getErrorChar(bool $return_substitute_func = false)
public static function getErrorChar(bool $return_substitute_func = false): string|int
{
return \CoreLibs\Check\Encoding::getErrorChar($return_substitute_func);
}
@@ -81,7 +81,7 @@ class Encoding
* @param string $string string to test
* @param string $from_encoding encoding of string to test
* @param string $to_encoding target encoding
* @return bool|array<string> false if no error or
* @return array<string>|false false if no error or
* array with failed characters
* @deprecated Use \CoreLibs\Check\Encoding::checkConvertEncoding();
*/
@@ -89,7 +89,7 @@ class Encoding
string $string,
string $from_encoding,
string $to_encoding
) {
): array|false {
return \CoreLibs\Check\Encoding::checkConvertEncoding($string, $from_encoding, $to_encoding);
}

View File

@@ -13,27 +13,27 @@ class Elements
/**
* print the date/time drop downs, used in any queue/send/insert at date/time place
*
* @param int $year year YYYY
* @param int $month month m
* @param int $day day d
* @param int $hour hour H
* @param int $min min i
* @param string $suffix additional info printed after the date time
* variable in the drop down
* also used for ID in the on change JS call
* @param int $min_steps default is 1 (minute), can set to anything,
* is used as sum up from 0
* @param bool $name_pos_back default false, if set to true,
* the name will be printend after the drop down
* and not before the drop down
* @return string HTML formated strings for drop down lists of date and time
* @param int|string $year year YYYY
* @param int|string $month month m
* @param int|string $day day d
* @param int|string $hour hour H
* @param int|string $min min i
* @param string $suffix additional info printed after the date time
* variable in the drop down
* also used for ID in the on change JS call
* @param int $min_steps default is 1 (minute), can set to anything,
* is used as sum up from 0
* @param bool $name_pos_back default false, if set to true,
* the name will be printend after the drop down
* and not before the drop down
* @return string HTML formated strings for drop down lists of date and time
*/
public static function printDateTime(
$year,
$month,
$day,
$hour,
$min,
int|string $year,
int|string $month,
int|string $day,
int|string $hour,
int|string $min,
string $suffix = '',
int $min_steps = 1,
bool $name_pos_back = false

View File

@@ -366,7 +366,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
$config_array = $table_arrays[System::getPageName(1)];
} else {
// primary try to load the class
/** @var \CoreLibs\Output\Form\TableArraysInterface|false $content_class */
/** @var TableArrays\Interface\TableArraysInterface|false $content_class */
$content_class = $this->loadTableArray();
if (is_object($content_class)) {
$config_array = $content_class->setTableArray();
@@ -468,9 +468,9 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
/**
* load table array class based on my page name converted to camel case
* class files are in \TableArrays folder in \Output\Form
* @return object|bool Return class object or false on failure
* @return TableArrays\Interface\TableArraysInterface|false Return class object or false on failure
*/
private function loadTableArray()
private function loadTableArray(): TableArrays\Interface\TableArraysInterface|false
{
// note: it schould be Schemas but an original type made it to this
// this file is kept for the old usage, new one should be EditSchemas
@@ -489,7 +489,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
$page_name_camel_case
);
try {
/** @var \CoreLibs\Output\Form\TableArraysInterface|false $class */
/** @var TableArrays\Interface\TableArraysInterface|false $class */
$class = new $class_string($this);
} catch (\Throwable $t) {
$this->log->debug('CLASS LOAD', 'Failed loading: ' . $class_string . ' => ' . $t->getMessage());
@@ -651,7 +651,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
$this->base_acl_level >= $this->security_level['new']
) {
if ($this->really_new == 'yes') {
$this->formUnsetTablearray();
$this->formUnsetTableArray();
} else {
$this->msg .= $this->l->__('You have to select the <b>Checkbox for New</b>!<br>');
$this->error = 2;
@@ -731,9 +731,6 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
empty($this->table_array['protected']['value']) &&
!$this->error
) {
if (!is_array($element_list)) {
$element_list = [];
}
for ($i = 0, $i_max = count($element_list); $i < $i_max; $i++) {
// $this->log->debug('form_error', 'Array: '
// . is_array($this->element_list[$element_list[$i]]['read_data']) . ' | '
@@ -2294,11 +2291,9 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
}
}
}
if (is_array($hidden_array)) {
reset($hidden_array);
foreach ($hidden_array as $key => $value) {
$hidden[] = ['key' => $key, 'value' => $value];
}
reset($hidden_array);
foreach ($hidden_array as $key => $value) {
$hidden[] = ['key' => $key, 'value' => $value];
}
return $hidden;
}

View File

@@ -87,7 +87,9 @@ class EditAccess implements Interface\TableArraysInterface
],
],
'table_name' => 'edit_access',
"load_query" => "SELECT edit_access_id, name FROM edit_access ORDER BY name",
"load_query" => "SELECT edit_access_id, name "
. "FROM edit_access "
. "ORDER BY name",
'show_fields' => [
[
'name' => 'name'

View File

@@ -55,14 +55,19 @@ class EditGroups implements Interface\TableArraysInterface
'mandatory' => 1,
'int' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
'query' => "SELECT edit_access_right_id, name "
. "FROM edit_access_right "
. "ORDER BY level"
],
'edit_scheme_id' => [
'value' => $_POST['edit_scheme_id'] ?? '',
'output_name' => 'Group Scheme',
'int_null' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_scheme_id, name FROM edit_scheme WHERE enabled = 1 ORDER BY name"
'query' => "SELECT edit_scheme_id, name "
. "FROM edit_scheme "
. "WHERE enabled = 1 "
. "ORDER BY name"
],
'additional_acl' => [
'value' => $_POST['additional_acl'] ?? '',
@@ -73,7 +78,9 @@ class EditGroups implements Interface\TableArraysInterface
'cols' => 60
],
],
'load_query' => "SELECT edit_group_id, name, enabled FROM edit_group ORDER BY name",
'load_query' => "SELECT edit_group_id, name, enabled "
. "FROM edit_group "
. "ORDER BY name",
'table_name' => 'edit_group',
'show_fields' => [
[
@@ -115,7 +122,9 @@ class EditGroups implements Interface\TableArraysInterface
'output_name' => 'Access Level',
'int' => 1,
'preset' => 1, // first of the select
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
'query' => "SELECT edit_access_right_id, name "
. "FROM edit_access_right "
. "ORDER BY level"
],
'edit_page_id' => [
'int' => 1,

View File

@@ -55,7 +55,9 @@ class EditMenuGroup implements Interface\TableArraysInterface
],
],
'table_name' => 'edit_menu_group',
'load_query' => "SELECT edit_menu_group_id, name FROM edit_menu_group ORDER BY name",
'load_query' => "SELECT edit_menu_group_id, name "
. "FROM edit_menu_group "
. "ORDER BY name",
'show_fields' => [
[
'name' => 'name'

View File

@@ -140,7 +140,10 @@ class EditPages implements Interface\TableArraysInterface
],
],
'load_query' => "SELECT edit_page_id, "
. "CASE WHEN hostname IS NOT NULL THEN hostname ELSE ''::VARCHAR END || filename AS filename, "
. "CASE "
. "WHEN hostname IS NOT NULL THEN hostname "
. "ELSE ''::VARCHAR "
. "END || filename AS filename, "
. "name, online, menu, popup "
. "FROM edit_page "
. "ORDER BY order_number",
@@ -178,7 +181,8 @@ class EditPages implements Interface\TableArraysInterface
'select_size' => 10,
'selected' => $_POST['edit_visible_group_id'] ?? '',
'query' => "SELECT edit_visible_group_id, 'Name: ' || name || ', ' || 'Flag: ' || flag "
. "FROM edit_visible_group ORDER BY name"
. "FROM edit_visible_group "
. "ORDER BY name"
],
'edit_menu_group' => [
'table_name' => 'edit_page_menu_group',
@@ -188,7 +192,8 @@ class EditPages implements Interface\TableArraysInterface
'select_size' => 10,
'selected' => $_POST['edit_menu_group_id'] ?? '',
'query' => "SELECT edit_menu_group_id, 'Name: ' || name || ', ' || 'Flag: ' || flag "
. "FROM edit_menu_group ORDER BY order_number"
. "FROM edit_menu_group "
. "ORDER BY order_number"
],
],
'element_list' => [
@@ -259,7 +264,9 @@ class EditPages implements Interface\TableArraysInterface
'output_name' => 'Access Level',
'int' => 1,
'preset' => 1, // first of the select
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
'query' => "SELECT edit_access_right_id, name "
. "FROM edit_access_right "
. "ORDER BY level"
],
'edit_page_content_id' => [
'type' => 'hidden',

View File

@@ -68,7 +68,9 @@ class EditSchemas implements Interface\TableArraysInterface
],
],
'table_name' => 'edit_scheme',
'load_query' => "SELECT edit_scheme_id, name, enabled FROM edit_scheme ORDER BY name",
'load_query' => "SELECT edit_scheme_id, name, enabled "
. "FROM edit_scheme "
. "ORDER BY name",
'show_fields' => [
[
'name' => 'name'

View File

@@ -274,7 +274,7 @@ class EditUsers implements Interface\TableArraysInterface
'type' => 'drop_down_db',
'query' => "SELECT edit_language_id, long_name "
. "FROM edit_language "
. "WHERE enabled = 1"
. "WHERE enabled = 1 "
. "ORDER BY order_number",
'min_edit_acl' => '100',
'min_show_acl' => '100',
@@ -284,7 +284,10 @@ class EditUsers implements Interface\TableArraysInterface
'output_name' => 'Scheme',
'int_null' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_scheme_id, name FROM edit_scheme WHERE enabled = 1 ORDER BY name",
'query' => "SELECT edit_scheme_id, name "
. "FROM edit_scheme "
. "WHERE enabled = 1 "
. "ORDER BY name",
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
@@ -293,7 +296,10 @@ class EditUsers implements Interface\TableArraysInterface
'output_name' => 'Group',
'int' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_group_id, name FROM edit_group WHERE enabled = 1 ORDER BY name",
'query' => "SELECT edit_group_id, name "
. "FROM edit_group "
. "WHERE enabled = 1 "
. "ORDER BY name",
'mandatory' => 1,
'min_edit_acl' => '100',
'min_show_acl' => '100',
@@ -304,7 +310,9 @@ class EditUsers implements Interface\TableArraysInterface
'mandatory' => 1,
'int' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level",
'query' => "SELECT edit_access_right_id, name "
. "FROM edit_access_right "
. "ORDER BY level",
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
@@ -434,7 +442,9 @@ class EditUsers implements Interface\TableArraysInterface
'output_name' => 'Access Level',
'preset' => 1, // first of the select
'int' => 1,
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
'query' => "SELECT edit_access_right_id, name "
. "FROM edit_access_right "
. "ORDER BY level"
],
'edit_default' => [
'type' => 'radio_group',

View File

@@ -48,7 +48,9 @@ class EditVisibleGroup implements Interface\TableArraysInterface
],
],
'table_name' => 'edit_visible_group',
'load_query' => "SELECT edit_visible_group_id, name FROM edit_visible_group ORDER BY name",
'load_query' => "SELECT edit_visible_group_id, name "
. "FROM edit_visible_group "
. "ORDER BY name",
'show_fields' => [
[
'name' => 'name'

View File

@@ -22,7 +22,7 @@ class Image
* if empty ROOT is choosen
* @param string $cache_source cache path, if not given TMP is used
* @param bool $clear_cache if set to true, will create thumb all the tame
* @return string|bool thumbnail name, or false for error
* @return string|false thumbnail name, or false for error
*/
public static function createThumbnail(
string $pic,
@@ -32,7 +32,7 @@ class Image
string $path = '',
string $cache_source = '',
bool $clear_cache = false
) {
): string|false {
// get image type flags
$image_types = [
1 => 'gif',
@@ -73,7 +73,7 @@ class Image
$create_file = false;
$delete_filename = '';
// check if we can skip the PDF creation: if we have size, if do not have type, we assume type png
if (!$type && is_numeric($size_x) && is_numeric($size_y)) {
if (!$type) {
$check_thumb = $tmp_src . 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[3];
if (!is_file($check_thumb)) {
$create_file = true;
@@ -100,10 +100,10 @@ class Image
[$width, $height, $type] = getimagesize($filename) ?: [];
}
// if no size given, set size to original
if (!$size_x || $size_x < 1 || !is_numeric($size_x)) {
if (!$size_x || $size_x < 1) {
$size_x = $width;
}
if (!$size_y || $size_y < 1 || !is_numeric($size_y)) {
if (!$size_y || $size_y < 1) {
$size_y = $height;
}
$thumb = 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[$type];
@@ -170,7 +170,7 @@ class Image
* set to false to not use (default true)
* to use quick but less nice version
* @param int $jpeg_quality default 80, set image quality for jpeg only
* @return string|bool thumbnail with path
* @return string|false thumbnail with path
*/
public static function createThumbnailSimple(
string $filename,
@@ -181,7 +181,7 @@ class Image
bool $use_cache = true,
bool $high_quality = true,
int $jpeg_quality = 80
) {
): string|false {
$thumbnail = false;
// $this->debug('IMAGE PREPARE', "FILE: $filename (exists "
// .(string)file_exists($filename)."), WIDTH: $thumb_width, HEIGHT: $thumb_height");
@@ -425,7 +425,7 @@ class Image
* @param string $filename path + filename to rotate. This file must be writeable
* @return void
*/
public static function correctImageOrientation($filename): void
public static function correctImageOrientation(string $filename): void
{
// function exists & file is writeable, else do nothing
if (!function_exists('exif_read_data') || !is_writeable($filename)) {

View File

@@ -1729,7 +1729,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
20
],
'invalud search' => [
'invalid search' => [
12,
'foo',
[],

View File

@@ -107,6 +107,13 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
*/
public function arraySearchRecursiveAllProvider(): array
{
/*
0: $needle,
1: array $input,
2: ?string $key_search_for,
3: bool $flag,
4: array $expected
*/
return [
'find value' => [
0 => 'bar',
@@ -172,6 +179,13 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
*/
public function arraySearchSimpleProvider(): array
{
/*
0: array $input,
1: $key,
2: $value,
3: bool $flag,
4: bool $expected
*/
return [
'key/value exist' => [
0 => self::$array,
@@ -665,7 +679,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
*
* @param array $input
* @param string|int $key
* @param string|int $value
* @param string|int|bool $value
* @param bool $expected
* @return void
*/

View File

@@ -1253,10 +1253,14 @@ final class CoreLibsDBIOTest extends TestCase
'string value literal' => ['string literal', 'tl', '\'string literal\'',],
'empty string value literal' => ['', 'tl', '\'\'',],
'null string value literal' => [null, 'tl', 'NULL',],
// ?d (I have no idea what that does, is like string)
// escape string, but set all empty strings to null ('' is null)
'string value d' => ['string d', 'd', '\'string d\'',],
'empty string value d' => ['', 'd', 'NULL',],
'null string value d' => [null, 'd', 'NULL',],
// escape literal string, but set all empty strings to null ('' is null)
'string value literal d' => ['string d', 'dl', '\'string d\'',],
'empty string value literal d' => ['', 'dl', 'NULL',],
'null string value literal d' => [null, 'dl', 'NULL',],
// by bytea
'string value d' => ['string d', 'by', '\x737472696e672064',],
'empty string value d' => ['', 'by', 'NULL',],
@@ -1281,7 +1285,7 @@ final class CoreLibsDBIOTest extends TestCase
* @dataProvider sqlEscapeProvider
* @testdox Input value $input as $flag to $expected [$_dataName]
*
* @param int|float|string|null $input
* @param int|float|string|bool|null $input
* @param string $flag
* @param int|float|string $expected
* @return void

View File

@@ -0,0 +1,2 @@
vendor
composer.lock

View File

@@ -0,0 +1,120 @@
<?php
/**
* This configuration will be read and overlaid on top of the
* default configuration. Command line arguments will be applied
* after this file is read.
*
* @see src/Phan/Config.php
* See Config for all configurable options.
*
* A Note About Paths
* ==================
*
* Files referenced from this file should be defined as
*
* ```
* Config::projectPath('relative_path/to/file')
* ```
*
* where the relative path is relative to the root of the
* project which is defined as either the working directory
* of the phan executable or a path passed in via the CLI
* '-d' flag.
*/
use Phan\Config;
return [
// "target_php_version" => "8.2",
// turn color on (-C)
"color_issue_messages_if_supported" => true,
// If true, missing properties will be created when
// they are first seen. If false, we'll report an
// error message.
"allow_missing_properties" => false,
// Allow null to be cast as any type and for any
// type to be cast to null.
"null_casts_as_any_type" => false,
// Backwards Compatibility Checking
'backward_compatibility_checks' => false,
// Run a quick version of checks that takes less
// time
"quick_mode" => false,
// Only emit critical issues to start with
// (0 is low severity, 5 is normal severity, 10 is critical)
"minimum_severity" => 0,
// enable for dead code check
// this will spill out errors for all methods never called
// use after all is OK to try to find unused code blocks
// ignore recommended: PhanUnreferencedPublicMethod
// "dead_code_detection" => true,
// default false for include path check
"enable_include_path_checks" => true,
"include_paths" => [
'.',
// '../test/configs/'
],
'ignore_undeclared_variables_in_global_scope' => true,
"file_list" => [
"./test/configs/config.php",
// "./test/configs/config.db.php",
// "./test/configs/config.host.php",
// "./test/configs/config.path.php",
"./test/configs/config.other.php",
"./test/configs/config.master.php",
],
// A list of directories that should be parsed for class and
// method information. After excluding the directories
// defined in exclude_analysis_directory_list, the remaining
// files will be statically analyzed for errors.
//
// Thus, both first-party and third-party code being used by
// your application should be included in this list.
'directory_list' => [
// Change this to include the folders you wish to analyze
// (and the folders of their dependencies)
'src',
// To speed up analysis, we recommend going back later and
// limiting this to only the vendor/ subdirectories your
// project depends on.
// `phan --init` will generate a list of folders for you
'vendor/egrajp/smarty-extended',
],
// A list of directories holding code that we want
// to parse, but not analyze
"exclude_analysis_directory_list" => [
'vendor/egrajp/smarty-extended',
],
'exclude_file_list' => [
],
// what not to show as problem
'suppress_issue_types' => [
// 'PhanUndeclaredMethod',
'PhanEmptyFile',
// ignore unreferences public methods, etc here (for dead code check)
'PhanUnreferencedPublicMethod',
'PhanUnreferencedClass',
'PhanWriteOnlyPublicProperty',
'PhanUnreferencedConstant',
'PhanWriteOnlyPublicProperty',
'PhanReadOnlyPublicProperty'
],
// Override to hardcode existence and types of (non-builtin) globals in the global scope.
// Class names should be prefixed with `\`.
//
// (E.g. `['_FOO' => '\FooClass', 'page' => '\PageClass', 'userId' => 'int']`)
'globals_type_map' => [],
];

View File

@@ -0,0 +1,25 @@
# Composer package from CoreLibs
This is just the lib/CoreLibs folder in a composer package.
For local install only
**Note**: for following classes the `egrajp/smarty-extended` has to be installed
- Template\SmartyExtended
- Admin\EditBase
## Setup from central composer
Setup from gitea internal servers
```sh
composer config repositories.git.egplusww.jp.Composer composer https://git.egplusww.jp/api/packages/Composer/composer
```
Alternative setup composer local zip file repot:
`composer config repositories.composer.egplusww.jp composer http://composer.egplusww.jp`
## Install package
`composer require egrajp/corelibs-composer-all:^7.11`

View File

@@ -0,0 +1,33 @@
{
"name": "egrajp/corelibs-composer-all",
"description": "CoreLibs in a composer package",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"CoreLibs\\": "src/"
}
},
"authors": [
{
"name": "Clemens Schwaighofer",
"email": "clemens.schwaighofer@egplusww.com"
}
],
"minimum-stability": "dev",
"require": {
"php": ">=8.1"
},
"require-dev": {
"phpstan/phpstan": "1.10.x-dev",
"phan/phan": "v5.x-dev",
"phpunit/phpunit": "^9",
"egrajp/smarty-extended": "^4.3"
},
"repositories": {
"git.egplusww.jp.Composer": {
"type": "composer",
"url": "https://git.egplusww.jp/api/packages/Composer/composer"
}
}
}

View File

@@ -0,0 +1,47 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_bytea expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_identifier expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_literal expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_escape_string expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_execute expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_parameter_status expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_prepare expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_query expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_query_params expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: src/DB/SQL/PgSQL.php

View File

@@ -0,0 +1,12 @@
<?php // phpcs:ignore PSR1.Files.SideEffects
// Boostrap file for PHPstand
// sets the _SERVER['HTTP_HOST'] var so we can have DB detection
$_SERVER['HTTP_HOST'] = 'soba.tokyo.tequila.jp';
// so www/includes/edit_base.php works
// require_once('www/lib/Smarty/SmartyBC.class.php');
// for whatever reason it does not load that from the confing.master.php
// for includes/admin_header.php
define('BASE_NAME', '');
// __END__

View File

@@ -0,0 +1,21 @@
<?php
// conditional formats for PHP versions
declare(strict_types=1);
$config = [];
if (PHP_VERSION_ID >= 8_00_00) {
// Change of signature in PHP 8.1
/* $config['parameters']['ignoreErrors'][] = [
'message' => '~Parameter #1 \$(result|connection) of function pg_\w+ '
. 'expects resource(\|null)?, object\|resource given\.~',
'path' => 'www/lib/CoreLibs/DB/SQL/PgSQL.php',
// 'count' => 1,
]; */
}
return $config;
// __END_

View File

@@ -0,0 +1,42 @@
# PHP Stan Config
includes:
- phpstan-conditional.php
parameters:
tmpDir: /tmp/phpstan-corelibs-composer
level: 8 # max is now 9
checkMissingCallableSignature: true
treatPhpDocTypesAsCertain: false
paths:
- %currentWorkingDirectory%/src
bootstrapFiles:
- %currentWorkingDirectory%/phpstan-bootstrap.php
- %currentWorkingDirectory%/vendor/autoload.php
scanDirectories:
- vendor/egrajp/smarty-extended
scanFiles:
- test/configs/config.php
- test/configs/config.master.php
- test/configs/config.other.php
# excludePaths:
# ignore composer
# - vendor
# ignore errores with
ignoreErrors:
#- # 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
# - # this is for 8.1 or newer
# message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects PgSql\\\\(Result|Connection(\\|string)?(\\|null)?), object\\|resource given\\.$#"
# path: %currentWorkingDirectory%/src/DB/SQL/PgSQL.php
# this is ignored for now
# - '#Expression in empty\(\) is always falsy.#'
# -
# message: '#Reflection error: [a-zA-Z0-9\\_]+ not found.#'
# path: www/includes/edit_base.php
#- 'error regex'
#-
# message: 'error regex'
# path: %currentWorkingDirectory%/www/some/*
# paths:
# - ...
# - ...

View File

@@ -0,0 +1,6 @@
<phpunit
cacheResultFile="/tmp/phpunit-corelibs-composer.result.cache"
colors="true"
verbose="true"
>
</phpunit>

View File

@@ -0,0 +1,2 @@
*.zip
.env*

View File

@@ -0,0 +1 @@
8.0.0

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
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";
if [ -z "${VERSION}" ]; then
echo "Version must be set in the form x.y.z without any leading characters";
exit;
fi;
# compare version, if different or newer, deploy
if [ -f "${file_last_published}" ]; then
LAST_PUBLISHED_VERSION=$(cat ${file_last_published});
if $(dpkg --compare-versions "${VERSION}" le "${LAST_PUBLISHED_VERSION}"); then
echo "git tag version ${VERSION} is not newer than previous published version ${LAST_PUBLISHED_VERSION}";
exit;
fi;
fi;
# read in the .env.deploy file and we must have
# GITLAB_USER
# GITLAB_TOKEN
# GITEA_DEPLOY_TOKEN
if [ ! -f "${BASE_FOLDER}.env.deploy" ]; then
echo "Deploy enviroment file .env.deploy is missing";
exit;
fi;
set -o allexport;
cd ${BASE_FOLDER};
source .env.deploy;
cd -;
set +o allexport;
# gitea
if [ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then
curl -LJO \
--output-dir "${BASE_FOLDER}" \
https://git.egplusww.jp/Composer/CoreLibs-Composer-All/archive/v${VERSION}.zip;
curl --user ${GITEA_USER}:${GITEA_TOKEN} \
--upload-file "${BASE_FOLDER}/CoreLibs-Composer-All-v${VERSION}.zip" \
https://git.egplusww.jp/api/packages/Composer/composer?version=${VERSION};
echo "${VERSION}" > "${file_last_published}";
else
echo "Missing either GITEA_USER or GITEA_TOKEN environment variable";
fi;
# gitlab
if [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then
curl --data tag=v${VERSION} \
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer";
curl --data branch=master \
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer";
echo "${VERSION}" > "${file_last_published}";
else
echo "Missing GITLAB_DEPLOY_TOKEN environment variable";
fi;
# __END__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,622 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2006/08/15
* VERSION: 1.0.0
* RELEASED LICENSE: GNU GPL 3
* DESCRIPTION
* Basic Admin interface backend
* - sets action flags
* - menu creation
* - array vars for smarty
*
* CHANGE PLAN:
* loads DB\IO + Logger and returns one group object
* also checks all missing CONFIG vars from Basic class
*
* PUBLIC VARIABLES
*
* PRIVATE VARIABLES
*
* PUBLIC METHODS
*
* PRIVATE METHODS
*
* HISTORY:
*
*********************************************************************/
declare(strict_types=1);
namespace CoreLibs\Admin;
class Backend
{
// page name
/** @var array<mixed> */
public $menu = [];
/** @var int|string */
public $menu_show_flag = 0; // top menu flag (mostly string)
// action ids
/** @var array<string> */
public $action_list = [
'action', 'action_id', 'action_sub_id', 'action_yes', 'action_flag',
'action_menu', 'action_value', 'action_error', 'action_loaded'
];
/** @var string */
public $action;
/** @var string|int */
public $action_id;
/** @var string|int */
public $action_sub_id;
/** @var string|int|bool */
public $action_yes;
/** @var string */
public $action_flag;
/** @var string */
public $action_menu;
/** @var string */
public $action_loaded;
/** @var string */
public $action_value;
/** @var string */
public $action_error;
// ACL array variable if we want to set acl data from outisde
/** @var array<mixed> */
public $acl = [];
/** @var int */
public $default_acl;
// queue key
/** @var string */
public $queue_key;
// the current active edit access id
/** @var int */
public $edit_access_id;
/** @var string */
public $page_name;
// error/warning/info messages
/** @var array<mixed> */
public $messages = [];
/** @var bool */
public $error = false;
/** @var bool */
public $warning = false;
/** @var bool */
public $info = false;
// internal lang & encoding vars
/** @var string */
public $lang_dir = '';
/** @var string */
public $lang;
/** @var string */
public $lang_short;
/** @var string */
public $domain;
/** @var string */
public $encoding;
/** @var \CoreLibs\Debug\Logging logger */
public $log;
/** @var \CoreLibs\DB\IO database */
public $db;
/** @var \CoreLibs\Language\L10n language */
public $l;
/** @var \CoreLibs\Create\Session session class */
public $session;
// smarty publics [end processing in smarty class]
/** @var array<mixed> */
public $DATA;
/** @var array<mixed> */
public $HEADER;
/** @var array<mixed> */
public $DEBUG_DATA;
/** @var array<mixed> */
public $CONTENT_DATA;
// CONSTRUCTOR / DECONSTRUCTOR |====================================>
/**
* main class constructor
*
* @param \CoreLibs\DB\IO $db Database connection class
* @param \CoreLibs\Debug\Logging $log Logging class
* @param \CoreLibs\Create\Session $session Session interface class
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,string> $locale locale data read from setLocale
*/
public function __construct(
\CoreLibs\DB\IO $db,
\CoreLibs\Debug\Logging $log,
\CoreLibs\Create\Session $session,
\CoreLibs\Language\L10n $l10n,
array $locale
) {
// attach db class
$this->db = $db;
// set to log not per class
$log->setLogPer('class', false);
// attach logger
$this->log = $log;
// attach session class
$this->session = $session;
// get the language sub class & init it
$this->l = $l10n;
// parse and read, legacy stuff
$this->encoding = $locale['encoding'];
$this->lang = $locale['lang'];
// get first part from lang
$this->lang_short = explode('_', $locale['lang'])[0];
$this->domain = $this->l->getDomain();
$this->lang_dir = $this->l->getBaseLocalePath();
// set the page name
$this->page_name = \CoreLibs\Get\System::getPageName();
// set the action ids
foreach ($this->action_list as $_action) {
$this->$_action = $_POST[$_action] ?? '';
}
$this->default_acl = DEFAULT_ACL_LEVEL;
// queue key
if (preg_match("/^(add|save|delete|remove|move|up|down|push_live)$/", $this->action)) {
$this->queue_key = \CoreLibs\Create\RandomKey::randomKeyGen(3);
}
}
/**
* class deconstructor
*/
public function __destruct()
{
// NO OP
}
// PUBLIC METHODS |=================================================>
/**
* set internal ACL from login ACL
*
* @param array<mixed> $acl login acl array
*/
public function setACL(array $acl): void
{
$this->acl = $acl;
}
/**
* writes all action vars plus other info into edit_log table
*
* @param string $event any kind of event description,
* @param string|array<mixed> $data any kind of data related to that event
* @param string $write_type write type can bei STRING or BINARY
* @return void
*/
public function adbEditLog(
string $event = '',
string|array $data = '',
string $write_type = 'STRING'
): void {
$data_binary = '';
if ($write_type == 'BINARY') {
$data_binary = $this->db->dbEscapeBytea((string)bzcompress(serialize($data)));
$data = 'see bzip compressed data_binary field';
}
if ($write_type == 'STRING') {
$data_binary = '';
$data = $this->db->dbEscapeString(serialize($data));
}
// check schema
$SCHEMA = 'public';
/** @phpstan-ignore-next-line */
if (defined('LOGIN_DB_SCHEMA') && !empty(LOGIN_DB_SCHEMA)) {
$SCHEMA = LOGIN_DB_SCHEMA;
} elseif ($this->db->dbGetSchema()) {
$SCHEMA = $this->db->dbGetSchema();
} elseif (defined('PUBLIC_SCHEMA')) {
$SCHEMA = PUBLIC_SCHEMA;
}
/** @phpstan-ignore-next-line for whatever reason $SCHEMA is seen as possible array */
$q = "INSERT INTO " . $SCHEMA . ".edit_log "
. "(euid, event_date, event, 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_value, action_error) "
. "VALUES "
. "(" . $this->db->dbEscapeString(isset($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
$_SESSION['EUID'] :
'NULL')
. ", "
. "NOW(), "
. "'" . $this->db->dbEscapeString((string)$event) . "', '" . $data . "', "
. "'" . $data_binary . "', '" . $this->db->dbEscapeString((string)$this->page_name) . "', "
. "'" . @$_SERVER["REMOTE_ADDR"] . "', "
. "'" . $this->db->dbEscapeString(@$_SERVER['HTTP_USER_AGENT']) . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_REFERER'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['SCRIPT_FILENAME'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['QUERY_STRING'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['SERVER_NAME'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_HOST'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_ACCEPT'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_ACCEPT_CHARSET'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_ACCEPT_ENCODING'] ?? '') . "', "
. ($this->session->getSessionId() === false ?
"NULL" :
"'" . $this->session->getSessionId() . "'")
. ", "
. "'" . $this->db->dbEscapeString($this->action) . "', "
. "'" . $this->db->dbEscapeString($this->action_id) . "', "
. "'" . $this->db->dbEscapeString($this->action_yes) . "', "
. "'" . $this->db->dbEscapeString($this->action_flag) . "', "
. "'" . $this->db->dbEscapeString($this->action_menu) . "', "
. "'" . $this->db->dbEscapeString($this->action_loaded) . "', "
. "'" . $this->db->dbEscapeString($this->action_value) . "', "
. "'" . $this->db->dbEscapeString($this->action_error) . "')";
$this->db->dbExec($q, 'NULL');
}
/**
* Set the menu show flag
*
* @param string|int $menu_show_flag
* @return string|int
*/
public function adbSetMenuShowFlag(string|int $menu_show_flag): string|int
{
// must be string or int
$this->menu_show_flag = $menu_show_flag;
return $this->menu_show_flag;
}
/**
* Return the menu show flag
*
* @return string|int
*/
public function adbGetMenuShowFlag(): string|int
{
return $this->menu_show_flag;
}
/**
* menu creater (from login menu session pages)
*
* @param int $flag visible flag trigger
* @return array<mixed> menu array for output on page (smarty)
*/
public function adbTopMenu(int $flag = 0): array
{
if ($this->menu_show_flag) {
$flag = $this->menu_show_flag;
}
// get the session pages array
$PAGES = $_SESSION['PAGES'] ?? null;
if (!isset($PAGES) || !is_array($PAGES)) {
$PAGES = [];
}
$pages = [];
foreach ($PAGES as $PAGE_DATA) {
$pages[] = $PAGE_DATA;
}
// $this->debug('pages', $this->print_ar($pages));
// if flag is 0, then we show all, else, we show only the matching flagges array points
// array is already sorted after correct order
reset($pages);
foreach ($pages as $data) {
// for ($i = 0, $iMax = count($pages); $i < $iMax; $i ++) {
$show = 0;
// is it visible in the menu & is it online
if ($data['menu'] && $data['online']) {
// check if it falls into our flag if we have a flag
if ($flag) {
foreach ($data['visible'] as $name => $key) {
if ($key == $flag) {
$show = 1;
}
}
} else {
// if no flag given, show all menu points
$show = 1;
}
if ($show) {
// if it is popup, write popup arrayound
if (isset($data['popup']) && $data['popup']) {
$type = 'popup';
} else {
$type = 'normal';
$data['popup'] = 0;
}
$query_string = '';
if (
isset($data['query']) &&
is_array($data['query']) &&
count($data['query'])
) {
// for ($j = 0, $jMax = count($pages[$i]['query']); $j < $jMax; $j ++) {
foreach ($data['query'] as $j => $query) {
if (
!empty($query['name']) &&
!empty($query['value'])
) {
if (strlen($query_string)) {
$query_string .= '&';
}
$query_string .= $query['name'] . '=';
if (
isset($query['dynamic']) &&
$query['dynamic']
) {
if (isset($_GET[$query['value']])) {
$query_string .= urlencode($_GET[$query['value']]);
} elseif (isset($_POST[$query['value']])) {
$query_string .= urlencode($_POST[$query['value']]);
}
} else {
$query_string .= urlencode($query['value']);
}
}
}
}
$url = '';
if (isset($data['hostname']) && $data['hostname']) {
$url .= $data['hostname'];
}
$url .= $data['filename'] ?? '';
if (strlen($query_string)) {
$url .= '?' . $query_string;
}
$name = $data['page_name'] ?? '';
// if page name matchs -> set selected flag
$selected = 0;
if (
isset($data['filename']) &&
\CoreLibs\Get\System::getPageName() == $data['filename'] &&
(!isset($data['hostname']) || (
isset($data['hostname']) &&
(!$data['hostname'] || strstr($data['hostname'], CONTENT_PATH) !== false)
))
) {
$selected = 1;
$this->page_name = $name;
}
// last check, is this menu point okay to show
$enabled = 0;
if (
isset($data['filename']) &&
$this->adbShowMenuPoint($data['filename'])
) {
$enabled = 1;
}
// write in to view menu array
array_push($this->menu, [
'name' => $this->l->__($name),
'url' => $url,
'selected' => $selected,
'enabled' => $enabled,
'popup' => $type == 'popup' ? 1 : 0,
'type' => $type
]);
} // show page
} // online and in menu
} // for each page
return $this->menu;
}
/**
* ONLY USED IN adbTopMenu
* checks if this filename is in the current situation (user id, etc) available
*
* @param string|null $filename filename
* @return bool true for visible/accessable menu point, false for not
*/
private function adbShowMenuPoint(?string $filename): bool
{
$enabled = false;
if ($filename === null) {
return $enabled;
}
/** @phan-suppress-next-line PhanNoopSwitchCases */
switch ($filename) {
default:
$enabled = true;
break;
};
return $enabled;
}
/**
* creates out of a normal db_return array an assoc array
*
* @param array<mixed> $db_array input array
* @param string|int|bool $key key
* @param string|int|bool $value value
* @return array<mixed> associative array
* @deprecated \CoreLibs\Combined\ArrayHandler::genAssocArray()
*/
public function adbAssocArray(array $db_array, string|int|bool $key, string|int|bool $value): array
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Combined\ArrayHandler::genAssocArray',
E_USER_DEPRECATED
);
return \CoreLibs\Combined\ArrayHandler::genAssocArray($db_array, $key, $value);
}
/**
* converts bytes into formated string with KB, MB, etc
*
* @param string|int|float $number string or int or number
* @return string formatted string
* @deprecated \CoreLibs\Convert\Byte::humanReadableByteFormat()
*/
public function adbByteStringFormat(string|int|float $number): string
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Convert\Byte::humanReadableByteFormat()',
E_USER_DEPRECATED
);
return \CoreLibs\Convert\Byte::humanReadableByteFormat($number);
}
/**
* converts picture to a thumbnail with max x and max y size
*
* @param string $pic source image file with or without path
* @param int $size_x maximum size width
* @param int $size_y maximum size height
* @param string $dummy empty, or file_type to show an icon
* instead of nothing if file is not found
* @param string $path if source start is not ROOT path
* if empty ROOT is choosen
* @return string|bool thumbnail name, or false for error
* @deprecated \CoreLibs\Output\Image::createThumbnail()
*/
public function adbCreateThumbnail(
string $pic,
int $size_x,
int $size_y,
string $dummy = '',
string $path = '',
string $cache = ''
): string|bool {
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Output\Image::createThumbnail()',
E_USER_DEPRECATED
);
return \CoreLibs\Output\Image::createThumbnail($pic, $size_x, $size_y, $dummy, $path, $cache);
}
/**
* wrapper function to fill up the mssages array
*
* @param string $level info/warning/error
* @param string $msg string, can be printf formated
* @param array<mixed> $vars optional data for a possible printf formated msg
* @return void has no return
*/
public function adbMsg(string $level, string $msg, array $vars = []): void
{
if (!preg_match("/^info|warning|error$/", $level)) {
$level = "info";
}
$this->messages[] = [
'msg' => vsprintf($this->l->__($msg), $vars),
'class' => $level
];
switch ($level) {
case 'info':
$this->info = true;
break;
case 'warning':
$this->warning = true;
break;
case 'error':
$this->error = true;
break;
}
}
/**
* writes live queue
*
* @param string $queue_key string to identfy the queue
* @param string $type [description]
* @param string $target [description]
* @param string $data [description]
* @param string $key_name [description]
* @param string $key_value [description]
* @param ?string $associate [description]
* @param ?string $file [description]
* @return void has no return
*/
public function adbLiveQueue(
string $queue_key,
string $type,
string $target,
string $data,
string $key_name,
string $key_value,
string $associate = null,
string $file = null
): void {
/** @phpstan-ignore-next-line */
if (defined('GLOBAL_DB_SCHEMA') && !empty(GLOBAL_DB_SCHEMA)) {
$SCHEMA = GLOBAL_DB_SCHEMA;
} elseif ($this->db->dbGetSchema()) {
$SCHEMA = $this->db->dbGetSchema();
} elseif (defined('PUBLIC_SCHEMA')) {
$SCHEMA = PUBLIC_SCHEMA;
} else {
$SCHEMA = 'public';
}
$q = "INSERT INTO " . $SCHEMA . ".live_queue ("
. "queue_key, key_value, key_name, type, target, data, group_key, action, associate, file"
. ") VALUES ("
. "'" . $this->db->dbEscapeString($queue_key) . "', '" . $this->db->dbEscapeString($key_value) . "', "
. "'" . $this->db->dbEscapeString($key_name) . "', '" . $this->db->dbEscapeString($type) . "', "
. "'" . $this->db->dbEscapeString($target) . "', '" . $this->db->dbEscapeString($data) . "', "
. "'" . $this->queue_key . "', '" . $this->action . "', "
. "'" . $this->db->dbEscapeString((string)$associate) . "', "
. "'" . $this->db->dbEscapeString((string)$file) . "')";
$this->db->dbExec($q);
}
/**
* Basic class holds exact the same, except the Year/Month/Day/etc strings
* are translated in this call
*
* @param int|string $year year YYYY
* @param int|string $month month m
* @param int|string $day day d
* @param int|string $hour hour H
* @param int|string $min min i
* @param string $suffix additional info printed after the date time
* variable in the drop down
* also used for ID in the on change JS call
* @param int $min_steps default is 1 (minute), can set to anything,
* is used as sum up from 0
* @param bool $name_pos_back default false, if set to true,
* the name will be printend
* after the drop down and not before the drop down
* @return string HTML formated strings for drop down lists
* of date and time
*/
public function adbPrintDateTime(
int|string $year,
int|string $month,
int|string $day,
int|string $hour,
int|string $min,
string $suffix = '',
int $min_steps = 1,
bool $name_pos_back = false
) {
// get the build layout
$html_time = \CoreLibs\Output\Form\Elements::printDateTime(
$year,
$month,
$day,
$hour,
$min,
$suffix,
$min_steps,
$name_pos_back
);
// translate the strings inside
foreach (['Year ', 'Month ', 'Day ', 'Hour ', 'Minute '] as $_time) {
$html_time = str_replace($_time, $this->l->__(str_replace(' ', '', $_time)) . ' ', $html_time);
}
// replace week days in short
foreach (['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as $_date) {
$html_time = str_replace('(' . $_date . ')', '(' . $this->l->__($_date) . ')', $html_time);
}
// return the datetime select string with strings translated
return $html_time;
}
}
// __END__

View File

@@ -0,0 +1,590 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2023/1/6
* DESCRIPTION:
* Original created: 2003/06/10
* This is the edit_base.php data as is moved into a class so we can
* more easy update this and also move to a different AJAX style more
* easy
*********************************************************************/
declare(strict_types=1);
namespace CoreLibs\Admin;
use Exception;
use SmartyException;
class EditBase
{
/** @var array<mixed> */
private $HEADER = [];
/** @var array<mixed> */
private $DATA = [];
/** @var array<mixed> */
private $DEBUG_DATA = [];
/** @var string the template name */
private $EDIT_TEMPLATE = '';
/** @var \CoreLibs\Template\SmartyExtend smarty system */
private $smarty;
/** @var \CoreLibs\Output\Form\Generate form generate system */
private $form;
/** @var \CoreLibs\Debug\Logging */
public $log;
/**
* construct form generator
*
* @param array<mixed> $db_config db config array, mandatory
* @param \CoreLibs\Debug\Logging $log Logging class, null auto set
* @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set
* @param array<string,string> $locale locale array from ::setLocale,
* null auto set
*/
public function __construct(
array $db_config,
\CoreLibs\Debug\Logging $log,
\CoreLibs\Language\L10n $l10n,
array $locale
) {
$this->log = $log;
// smarty template engine (extended Translation version)
$this->smarty = new \CoreLibs\Template\SmartyExtend($l10n, $locale);
// turn off set log per class
$log->setLogPer('class', false);
// create form class
$this->form = new \CoreLibs\Output\Form\Generate(
$db_config,
$log,
$l10n,
$locale
);
if ($this->form->mobile_phone) {
echo "I am sorry, but this page cannot be viewed by a mobile phone";
exit;
}
// $this->form->log->debug('POST', $this->form->log->prAr($_POST));
}
/**
* edit order page
*
* @return void
*/
private function editOrderPage(): void
{
// get is for "table_name" and "where" only
$table_name = $_GET['table_name'] ?? $_POST['table_name'] ?? '';
// not in use
// $where_string = $_GET['where'] ?? $_POST['where'] ?? '';
// order name is _always_ order_number for the edit interface
// follwing arrays do exist here:
// $position ... has the positions of the [0..max], cause in a <select>
// I can't put an number into the array field, in this array,
// there are the POSITION stored,
// that should CHANGE there order (up/down)
// $row_data_id ... has ALL ids from the sorting part
// $row_data_order ... has ALL order positions from the soirting part
$position = $_POST['position'] ?? [];
$row_data_id = $_POST['row_data_id'] ?? [];
$original_id = $row_data_id;
$row_data_order = $_POST['row_data_order'] ?? [];
// direction
$up = $_POST['up'] ?? '';
$down = $_POST['down'] ?? '';
if (count($position)) {
// FIRST u have to put right sort, then read again ...
// hast to be >0 or the first one is selected and then there is no move
if (!empty($up) && isset($position[0]) && $position[0] > 0) {
for ($i = 0; $i < count($position); $i++) {
// change position order
// this gets temp, id before that, gets actual (moves one "down")
// this gets the old before (moves one "up")
// is done for every element in row
// echo "A: ".$row_data_id[$position[$i]]
// ." (".$row_data_order[$position[$i]].") -- ".$row_data_id[$position[$i]-1]
// ." (".$row_data_order[$position[$i]-1].")<br>";
$temp_id = $row_data_id[$position[$i]] ?? null;
$row_data_id[$position[$i]] = $row_data_id[(int)$position[$i] - 1] ?? null;
$row_data_id[(int)$position[$i] - 1] = $temp_id;
// echo "A: ".$row_data_id[$position[$i]]
// ." (".$row_data_order[$position[$i]].") -- "
// .$row_data_id[$position[$i]-1]." (".$row_data_order[$position[$i]-1].")<br>";
} // for
} // if up
// the last position id from position array is not to be the count - 1 of
// row_data_id array, or it is the last element
if (!empty($down) && ($position[count($position) - 1] != (count($row_data_id) - 1))) {
for ($i = count($position) - 1; $i >= 0; $i--) {
// same as up, just up in other way, starts from bottom (last element) and moves "up"
// element before actuel gets temp, this element, becomes element after this,
// element after this, gets this
$temp_id = $row_data_id[(int)$position[$i] + 1] ?? null;
$row_data_id[(int)$position[$i] + 1] = $row_data_id[$position[$i]] ?? null;
$row_data_id[$position[$i]] = $temp_id;
} // for
} // if down
// write data ... (which has to be abstrackt ...)
if (
(!empty($up) && $position[0] > 0) ||
(!empty($down) && ($position[count($position) - 1] != (count($row_data_id) - 1)))
) {
for ($i = 0; $i < count($row_data_id); $i++) {
if (isset($row_data_order[$i]) && isset($row_data_id[$i])) {
$q = "UPDATE " . $table_name
. " SET order_number = " . $row_data_order[$i]
. " WHERE " . $table_name . "_id = " . $row_data_id[$i];
$q = $this->form->dbExec($q);
}
} // for all article ids ...
} // if write
} // if there is something to move
// get ...
$q = "SELECT " . $table_name . "_id, name, order_number FROM " . $table_name . " ";
// /* if (!empty($where_string)) {
// $q .= "WHERE $where_string ";
// } */
$q .= "ORDER BY order_number";
// init arrays
$row_data = [];
$options_id = [];
$options_name = [];
$options_selected = [];
// DB read data for menu
while (is_array($res = $this->form->dbReturn($q))) {
$row_data[] = [
"id" => $res[$table_name . "_id"],
"name" => $res["name"],
"order" => $res["order_number"]
];
} // while read data ...
// html title
$this->HEADER['HTML_TITLE'] = $this->form->l->__('Edit Order');
$messages = [];
$error = $_POST['error'] ?? 0;
// error msg
if (!empty($error)) {
$msg = $_POST['msg'] ?? [];
if (!is_array($msg)) {
$msg = [];
}
$messages[] = [
'msg' => $msg,
'class' => 'error',
'width' => '100%'
];
}
$this->DATA['form_error_msg'] = $messages;
// all the row data
for ($i = 0; $i < count($row_data); $i++) {
$options_id[] = $i;
$options_name[] = $row_data[$i]['name'];
// list of points to order
for ($j = 0; $j < count($position); $j++) {
// if matches, put into select array
if (
isset($original_id[$position[$j]]) && isset($row_data[$i]['id']) &&
$original_id[$position[$j]] == $row_data[$i]['id']
) {
$options_selected[] = $i;
}
}
}
$this->DATA['options_id'] = $options_id;
$this->DATA['options_name'] = $options_name;
$this->DATA['options_selected'] = $options_selected;
// hidden list for the data (id, order number)
$row_data_id = [];
$row_data_order = [];
for ($i = 0; $i < count($row_data); $i++) {
$row_data_id[] = $row_data[$i]['id'];
$row_data_order[] = $row_data[$i]['order'];
}
$this->DATA['row_data_id'] = $row_data_id;
$this->DATA['row_data_order'] = $row_data_order;
// hidden names for the table & where string
$this->DATA['table_name'] = $table_name;
$this->DATA['where_string'] = '';
// $this->DATA['where_string'] = $where_string ?? '';
$this->EDIT_TEMPLATE = 'edit_order.tpl';
}
/**
* all edit pages
*
* @return void
*/
private function editPageFlow(): void
{
// set table width
$table_width = '100%';
// load call only if id is set
if (!empty($_POST[$this->form->archive_pk_name])) {
$this->form->formProcedureLoad($_POST[$this->form->archive_pk_name]);
}
$this->form->formProcedureNew();
$this->form->formProcedureSave();
$this->form->formProcedureDelete();
// delete call only if those two are set
// and we are not in new/save/master delete
if (
!$this->form->new &&
!$this->form->save &&
!$this->form->delete &&
!empty($_POST['element_list']) &&
!empty($_POST['remove_name'])
) {
$this->form->formProcedureDeleteFromElementList(
$_POST['element_list'],
$_POST['remove_name']
);
// run a load post element delete to not end up with empty page
$this->form->formLoadTableArray($_POST[$this->form->archive_pk_name]);
$this->form->yes = 1;
}
$this->DATA['table_width'] = $table_width;
$messages = [];
// write out error / status messages
$messages[] = $this->form->formPrintMsg();
$this->DATA['form_error_msg'] = $messages;
// MENU START
// request some session vars
if (empty($_SESSION['HEADER_COLOR'])) {
$this->DATA['HEADER_COLOR'] = '#E0E2FF';
} else {
$this->DATA['HEADER_COLOR'] = $_SESSION['HEADER_COLOR'];
}
$this->DATA['USER_NAME'] = $_SESSION['USER_NAME'];
$this->DATA['EUID'] = $_SESSION['EUID'];
$this->DATA['GROUP_NAME'] = $_SESSION['GROUP_NAME'];
$this->DATA['GROUP_LEVEL'] = $_SESSION['GROUP_ACL_LEVEL'];
$PAGES = $_SESSION['PAGES'];
//$this->form->log->debug('menu', $this->form->log->prAr($PAGES));
// build nav from $PAGES ...
if (!isset($PAGES) || !is_array($PAGES)) {
$PAGES = [];
}
$menuarray = [];
foreach ($PAGES as $PAGE_CUID => $PAGE_DATA) {
if ($PAGE_DATA['menu'] && $PAGE_DATA['online']) {
$menuarray[] = $PAGE_DATA;
}
}
// split point for nav points
$COUNT_NAV_POINTS = count($menuarray);
$SPLIT_FACTOR = 3;
$START_SPLIT_COUNT = 3;
// WTF ?? I dunno what I am doing here ...
for ($i = 9; $i < $COUNT_NAV_POINTS; $i += $START_SPLIT_COUNT) {
if ($COUNT_NAV_POINTS > $i) {
$SPLIT_FACTOR += 1;
}
}
$position = 0;
$menu_data = [];
// for ($i = 1; $i <= count($menuarray); $i ++) {
foreach ($menuarray as $i => $menu_element) {
// do that for new array
$j = $i + 1;
$menu_data[$i]['pagename'] = htmlentities($menu_element['page_name']);
$menu_data[$i]['filename'] =
// prefix folder or host name
(isset($menu_element['hostname']) && $menu_element['hostname'] ?
$menu_element['hostname'] :
''
)
// filename
. ($menu_element['filename'] ?? '')
// query string
. (isset($menu_element['query_string']) && $menu_element['query_string'] ?
$menu_element['query_string'] :
''
);
if ($j == 1 || !($i % $SPLIT_FACTOR)) {
$menu_data[$i]['splitfactor_in'] = 1;
} else {
$menu_data[$i]['splitfactor_in'] = 0;
}
// on matching, we also need to check if we are in the same folder
if (
isset($menu_element['filename']) &&
$menu_element['filename'] == \CoreLibs\Get\System::getPageName() &&
(!isset($menu_element['hostname']) || (
isset($menu_element['hostname']) &&
(!$menu_element['hostname'] || strstr($menu_element['hostname'], CONTENT_PATH) !== false)
))
) {
$position = $i;
$menu_data[$i]['position'] = 1;
$menu_data[$i]['popup'] = 0;
} else {
// add query stuff
// HAS TO DONE LATER ... set urlencode, etc ...
// check if popup needed
if (isset($menu_element['popup']) && $menu_element['popup'] == 1) {
$menu_data[$i]['popup'] = 1;
$menu_data[$i]['rand'] = uniqid((string)rand());
$menu_data[$i]['width'] = $menu_element['popup_x'];
$menu_data[$i]['height'] = $menu_element['popup_y'];
} else {
$menu_data[$i]['popup'] = 0;
}
$menu_data[$i]['position'] = 0;
} // highlight or not
if (!($j % $SPLIT_FACTOR) || (($j + 1) > count($menuarray))) {
$menu_data[$i]['splitfactor_out'] = 1;
} else {
$menu_data[$i]['splitfactor_out'] = 0;
}
} // for
// $this->form->log->debug('MENU ARRAY', $this->form->log->prAr($menu_data));
$this->DATA['menu_data'] = $menu_data;
$this->DATA['page_name'] = $menuarray[$position]['page_name'] ?? '-Undefined [' . $position . '] -';
$L_TITLE = $this->DATA['page_name'];
// html title
$this->HEADER['HTML_TITLE'] = $this->form->l->__($L_TITLE);
// END MENU
// LOAD AND NEW
$this->DATA['load'] = $this->form->formCreateLoad();
$this->DATA['new'] = $this->form->formCreateNew();
// SHOW DATA PART
if ($this->form->yes) {
$this->DATA['form_yes'] = $this->form->yes;
$this->DATA['form_my_page_name'] = $this->form->my_page_name;
$this->DATA['filename_exist'] = 0;
$this->DATA['drop_down_input'] = 0;
$elements = [];
// depending on the "getPageName()" I show different stuff
switch ($this->form->my_page_name) {
case 'edit_users':
$elements[] = $this->form->formCreateElement('login_error_count');
$elements[] = $this->form->formCreateElement('login_error_date_last');
$elements[] = $this->form->formCreateElement('login_error_date_first');
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('deleted');
$elements[] = $this->form->formCreateElement('protected');
$elements[] = $this->form->formCreateElement('username');
$elements[] = $this->form->formCreateElement('password');
$elements[] = $this->form->formCreateElement('password_change_interval');
$elements[] = $this->form->formCreateElement('login_user_id');
$elements[] = $this->form->formCreateElement('login_user_id_set_date');
$elements[] = $this->form->formCreateElement('login_user_id_last_revalidate');
$elements[] = $this->form->formCreateElement('login_user_id_locked');
$elements[] = $this->form->formCreateElement('login_user_id_revalidate_after');
$elements[] = $this->form->formCreateElement('login_user_id_valid_from');
$elements[] = $this->form->formCreateElement('login_user_id_valid_until');
$elements[] = $this->form->formCreateElement('email');
$elements[] = $this->form->formCreateElement('last_name');
$elements[] = $this->form->formCreateElement('first_name');
$elements[] = $this->form->formCreateElement('edit_group_id');
$elements[] = $this->form->formCreateElement('edit_access_right_id');
$elements[] = $this->form->formCreateElement('strict');
$elements[] = $this->form->formCreateElement('locked');
$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');
$elements[] = $this->form->formCreateElement('additional_acl');
break;
case 'edit_schemes':
// @deprecated Will be removed
case 'edit_schemas':
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('header_color');
$elements[] = $this->form->formCreateElement('template');
break;
case 'edit_pages':
if (!isset($this->form->table_array['edit_page_id']['value'])) {
$q = "DELETE FROM temp_files";
$this->form->dbExec($q);
// gets all files in the current dir and dirs given ending with .php
$folders = ['../admin/', '../frontend/'];
$files = ['*.php'];
$search_glob = [];
foreach ($folders as $folder) {
// make sure this folder actually exists
if (is_dir(ROOT . $folder)) {
foreach ($files as $file) {
$search_glob[] = $folder . $file;
}
}
}
$crap = exec('ls ' . join(' ', $search_glob), $output, $status);
// now get all that are NOT in de DB
$q = "INSERT INTO temp_files (folder, filename) VALUES ";
$t_q = '';
foreach ($output as $output_file) {
// split the ouput into folder and file
$pathinfo = pathinfo($output_file);
if (!empty($pathinfo['dirname'])) {
$pathinfo['dirname'] .= DIRECTORY_SEPARATOR;
} else {
$pathinfo['dirname'] = '';
}
if ($t_q) {
$t_q .= ', ';
}
$t_q .= "('" . $this->form->dbEscapeString($pathinfo['dirname']) . "', '"
. $this->form->dbEscapeString($pathinfo['basename']) . "')";
}
$this->form->dbExec($q . $t_q, 'NULL');
$elements[] = $this->form->formCreateElement('filename');
} else {
// show file menu
// just show name of file ...
$this->DATA['filename_exist'] = 1;
$this->DATA['filename'] = $this->form->table_array['filename']['value'];
} // File Name View IF
$elements[] = $this->form->formCreateElement('hostname');
$elements[] = $this->form->formCreateElement('name');
// $elements[] = $this->form->formCreateElement('tag');
// $elements[] = $this->form->formCreateElement('min_acl');
$elements[] = $this->form->formCreateElement('order_number');
$elements[] = $this->form->formCreateElement('online');
$elements[] = $this->form->formCreateElement('menu');
$elements[] = $this->form->formCreateElementListTable('edit_query_string');
$elements[] = $this->form->formCreateElement('content_alias_edit_page_id');
$elements[] = $this->form->formCreateElementListTable('edit_page_content');
$elements[] = $this->form->formCreateElement('popup');
$elements[] = $this->form->formCreateElement('popup_x');
$elements[] = $this->form->formCreateElement('popup_y');
$elements[] = $this->form->formCreateElementReferenceTable('edit_visible_group');
$elements[] = $this->form->formCreateElementReferenceTable('edit_menu_group');
break;
case 'edit_languages':
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('short_name');
$elements[] = $this->form->formCreateElement('long_name');
$elements[] = $this->form->formCreateElement('iso_name');
break;
case 'edit_groups':
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('edit_access_right_id');
$elements[] = $this->form->formCreateElement('edit_scheme_id');
$elements[] = $this->form->formCreateElementListTable('edit_page_access');
$elements[] = $this->form->formCreateElement('additional_acl');
break;
case 'edit_visible_group':
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('flag');
break;
case 'edit_menu_group':
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('flag');
$elements[] = $this->form->formCreateElement('order_number');
break;
case 'edit_access':
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('protected');
$elements[] = $this->form->formCreateElement('color');
$elements[] = $this->form->formCreateElement('description');
// add name/value list here
$elements[] = $this->form->formCreateElementListTable('edit_access_data');
$elements[] = $this->form->formCreateElement('additional_acl');
break;
default:
print '[No valid page definition given]';
break;
}
// $this->form->log->debug('edit', "Elements: <pre>".$this->form->log->prAr($elements));
$this->DATA['elements'] = $elements;
$this->DATA['hidden'] = $this->form->formCreateHiddenFields();
$this->DATA['save_delete'] = $this->form->formCreateSaveDelete();
} else {
$this->DATA['form_yes'] = 0;
}
$this->EDIT_TEMPLATE = 'edit_body.tpl';
}
/**
* main method that either calls edit order page method or general page
* builds the smarty content and runs smarty display output
*
* @return void
* @throws Exception
* @throws SmartyException
*/
public function editBaseRun(): void
{
// set the template dir
// WARNING: this has a special check for the mailing tool layout (old layout)
if (defined('LAYOUT')) {
$this->smarty->setTemplateDir(BASE . INCLUDES . TEMPLATES . CONTENT_PATH);
$this->DATA['css'] = LAYOUT . CSS;
$this->DATA['js'] = LAYOUT . JS;
} else {
$this->smarty->setTemplateDir(TEMPLATES);
$this->DATA['css'] = CSS;
$this->DATA['js'] = JS;
}
$ADMIN_STYLESHEET = 'edit.css';
// define all needed smarty stuff for the general HTML/page building
$this->HEADER['CSS'] = CSS;
$this->HEADER['DEFAULT_ENCODING'] = DEFAULT_ENCODING;
/** @phpstan-ignore-next-line because ADMIN_STYLESHEET can be null */
$this->HEADER['STYLESHEET'] = $ADMIN_STYLESHEET ?? ADMIN_STYLESHEET;
// main run
if ($this->form->my_page_name == 'edit_order') {
$this->editOrderPage();
} else {
$this->editPageFlow();
}
// debug data, if DEBUG flag is on, this data is print out
// $this->DEBUG_DATA['DEBUG'] = $DEBUG_TMPL ?? '';
$this->DEBUG_DATA['DEBUG'] = '';
// create main data array
$CONTENT_DATA = array_merge($this->HEADER, $this->DATA, $this->DEBUG_DATA);
// data is 1:1 mapping (all vars, values, etc)
foreach ($CONTENT_DATA as $key => $value) {
$this->smarty->assign($key, $value);
}
if (is_dir(BASE . TEMPLATES_C)) {
$this->smarty->setCompileDir(BASE . TEMPLATES_C);
}
if (is_dir(BASE . CACHE)) {
$this->smarty->setCacheDir(BASE . CACHE);
}
$this->smarty->display(
$this->EDIT_TEMPLATE,
'editAdmin_' . $this->smarty->lang,
'editAdmin_' . $this->smarty->lang
);
$this->form->log->debug('DEBUGEND', '==================================== [Form END]');
}
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
<?php
/*
* valid checks for css/html based colors
* # hex
* # hex + alpha
* rgb
* rgba
* hsl
* hsla
*/
declare(strict_types=1);
namespace CoreLibs\Check;
use Exception;
class Colors
{
/** @var int 1 for HEX rgb */
public const HEX_RGB = 1;
/** @var int 2 for HEX rgb with alpha */
public const HEX_RGBA = 2;
/** @var int 4 for rgb() */
public const RGB = 4;
/** @var int 8 for rgba() */
public const RGBA = 8;
/** @var int 16 for hsl() */
public const HSL = 16;
/** @var int 32 for hsla() */
public const HSLA = 32;
/** @var int 63 for all bits set (sum of above) */
public const ALL = 63;
/**
* check rgb/hsl content values in detail
* will abort and return false on first error found
*
* @param string $color html/css tring to check
* @param int|false $rgb_flag flag to check for rgb
* @param int|false $hsl_flag flag to check for hsl type
* @return bool True if no error, False if error
*/
private static function rgbHslContentCheck(
string $color,
int|false $rgb_flag,
int|false $hsl_flag
): bool {
// extract string between () and split into elements
preg_match("/\((.*)\)/", $color, $matches);
if (
!is_array($color_list = preg_split("/,\s*/", $matches[1] ?? ''))
) {
throw new \Exception("Could not extract color list from rgg/hsl", 3);
}
// based on rgb/hsl settings check that entries are valid
// rgb: either 0-255 OR 0-100%
// hsl: first: 0-360
foreach ($color_list as $pos => $color_check) {
if (empty($color_check)) {
return false;
}
$percent_check = false;
if (strrpos($color_check, '%', -1) !== false) {
$percent_check = true;
$color_check = str_replace('%', '', $color_check);
}
// first three normal percent or valid number
if ($rgb_flag !== false) {
if ($percent_check === true) {
// for ALL pos
if ($color_check < 0 || $color_check > 100) {
return false;
}
} elseif (
$pos < 3 &&
($color_check < 0 || $color_check > 255)
) {
return false;
} elseif (
// RGBA set pos 3 if not percent
$pos == 3 &&
($color_check < 0 || $color_check > 1)
) {
return false;
}
} elseif ($hsl_flag !== false) {
// pos 0: 0-360
// pos 1,2: %
// pos 3: % or 0-1 (float)
if (
$pos == 0 &&
($color_check < 0 || $color_check > 360)
) {
return false;
} elseif (
// if pos 1/2 are not percent
($pos == 1 || $pos == 2) &&
($percent_check != true ||
($color_check < 0 || $color_check > 100))
) {
return false;
} elseif (
// 3 is either percent or 0~1
$pos == 3 &&
(
($percent_check == false &&
($color_check < 0 || $color_check > 1)) ||
($percent_check === true &&
($color_check < 0 || $color_check > 100))
)
) {
return false;
}
}
}
return true;
}
/**
* check if html/css color string is valid
* @param string $color A color string of any format
* @param int $flags defaults to ALL, else use | to combined from
* HEX_RGB, HEX_RGBA, RGB, RGBA, HSL, HSLA
* @return bool True if valid, False if not
* @throws Exception 1: no valid flag set
*/
public static function validateColor(string $color, int $flags = self::ALL): bool
{
// blocks for each check
$regex_blocks = [];
// set what to check
if ($flags & self::HEX_RGB) {
$regex_blocks[] = '#[\dA-Fa-f]{6}';
}
if ($flags & self::HEX_RGBA) {
$regex_blocks[] = '#[\dA-Fa-f]{8}';
}
if ($flags & self::RGB) {
$regex_blocks[] = 'rgb\(\d{1,3}%?,\s*\d{1,3}%?,\s*\d{1,3}%?\)';
}
if ($flags & self::RGBA) {
$regex_blocks[] = 'rgba\(\d{1,3}%?,\s*\d{1,3}%?,\s*\d{1,3}%?(,\s*(0\.\d{1,2}|1(\.0)?|\d{1,3}%))?\)';
}
if ($flags & self::HSL) {
$regex_blocks[] = 'hsl\(\d{1,3},\s*\d{1,3}(\.\d{1})?%,\s*\d{1,3}(\.\d{1})?%\)';
}
if ($flags & self::HSLA) {
$regex_blocks[] = 'hsla\(\d{1,3},\s*\d{1,3}(\.\d{1})?%,\s*\d{1,3}'
. '(\.\d{1})?%(,\s*(0\.\d{1,2}|1(\.0)?|\d{1,3}%))?\)';
}
// wrong flag set
if ($flags > self::ALL) {
throw new \Exception("Invalid flags parameter: $flags", 1);
}
if (!count($regex_blocks)) {
throw new \Exception("No regex blocks set: $flags", 2);
}
// build regex
$regex = '^('
. join('|', $regex_blocks)
// close regex
. ')$';
// print "C: $color, F: $flags, R: $regex\n";
if (preg_match("/$regex/", $color)) {
// if valid regex, we now need to check if the content is actually valid
// only for rgb/hsl type
/** @var int|false */
$rgb_flag = strpos($color, 'rgb');
/** @var int|false */
$hsl_flag = strpos($color, 'hsl');
// if both not match, return true
if (
$rgb_flag === false &&
$hsl_flag === false
) {
return true;
}
// run detaul rgb/hsl content check
return self::rgbHslContentCheck($color, $rgb_flag, $hsl_flag);
} else {
return false;
}
}
}
// __END__

View File

@@ -0,0 +1,265 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Check;
class Email
{
// this is for error check parts in where the email regex failed
/** @var array<int,string> */
private static $email_regex_check = [
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
1 => "@(.*)@(.*)", // double @
2 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@", // wrong part before @
3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
5 => "\.([a-zA-Z]{2,6}){1}$", // wrong top level part
6 => "@(.*)\.{2,}", // double .. in domain name part
7 => "@.*\.$" // ends with a dot, top level, domain missing
];
// for above position, description string below
/** @var array<int,string> */
private static $email_regex_check_message = [
0 => 'Invalid email address',
1 => 'Double @ mark in email address',
2 => 'Invalid email part before @ sign',
3 => 'Invalid domain part after @ sign',
4 => 'Invalid domain name part',
5 => 'Wrong domain top level part',
6 => 'Double consecutive dots in domain name (..)',
7 => 'Domain ends with a dot or is missing top level part'
];
// the array with the mobile types that are valid
/** @var array<string,string> */
private static $mobile_email_type = [
'.*@docomo\.ne\.jp$' => 'keitai_docomo',
// correct are a[2-4], b2, c[1-9], e[2-9], h[2-4], t[1-9]
'.*@([a-z0-9]{2}\.)?ezweb\.ne\.jp$' => 'keitai_kddi_ezweb',
// ez[a-j] or nothing
'.*@(ez[a-j]{1}\.)?ido\.ne\.jp$' => 'keitai_kddi_ido',
// (sky group)
'.*@([a-z]{2}\.)?sky\.tu-ka\.ne\.jp$' => 'keitai_kddi_tu-ka',
// (sky group) [tkk,tkc only]
'.*@([a-z]{2}\.)?sky\.tk[kc]{1}\.ne\.jp$' => 'keitai_kddi_sky',
// dtg (sky group)
'.*@([a-z]{2}\.)?sky\.dtg\.ne\.jp$' => 'keitai_kddi_dtg',
// old vodafone [t,k,d,h,c,r,n,s,q]
'.*@[tkdhcrnsq]{1}\.vodafone\.ne\.jp$' => 'keitai_softbank_vodafone',
// very old j-phone (pre vodafone) [d,h,t,k,r,s,n,q,c]
'.*@jp-[dhtkrsnqc]{1}\.ne\.jp$' => 'keitai_softbank_j-phone',
// add i for iphone also as keitai, others similar to the vodafone group
'.*@([dhtcrknsq]{1}\.)?softbank\.ne\.jp$' => 'keitai_softbank',
// add iPhone also as keitai and not as pc
'.*@i{1}\.softbank(\.ne)?\.jp$' => 'smartphone_softbank_iphone',
'.*@disney\.ne\.jp$' => 'keitai_softbank_disney', // (kids)
'.*@willcom\.ne\.jp$' => 'keitai_willcom',
'.*@willcom\.com$' => 'keitai_willcom', // new for pdx.ne.jp address
'.*@wcm\.ne\.jp$' => 'keitai_willcom', // old willcom wcm.ne.jp
'.*@pdx\.ne\.jp$' => 'keitai_willcom_pdx', // old pdx address for willcom
'.*@bandai\.jp$' => 'keitai_willcom_bandai', // willcom paipo! (kids)
'.*@pipopa\.ne\.jp$' => 'keitai_willcom_pipopa', // willcom paipo! (kids)
// actually only di,dj,dk,wm -> all others are "wrong", but none also allowed?
'.*@([a-z0-9]{2,4}\.)?pdx\.ne\.jp$' => 'keitai_willcom_pdx',
// ymobile, ymobile1 techincally not willcom, but I group them there (softbank sub)
'.*@ymobile([1]{1})?\.ne\.jp$' => 'keitai_willcom_ymobile',
// y-mobile techincally not willcom, but I group them there (softbank sub)
'.*@y-mobile\.ne\.jp$' => 'keitai_willcom_ymobile',
'.*@emnet\.ne\.jp$' => 'keitai_willcom_emnet', // e-mobile, group will willcom
'.*@emobile\.ne\.jp$' => 'keitai_willcom_emnet', // e-mobile, group will willcom
'.*@emobile-s\.ne\.jp$' => 'keitai_willcom_emnet' # e-mobile, group will willcom
];
// short list for mobile email types
/** @var array<string,string> */
private static $mobile_email_type_short = [
'keitai_docomo' => 'docomo',
'keitai_kddi_ezweb' => 'kddi',
'keitai_kddi' => 'kddi',
'keitai_kddi_tu-ka' => 'kddi',
'keitai_kddi_sky' => 'kddi',
'keitai_softbank' => 'softbank',
'smartphone_softbank_iphone' => 'iphone',
'keitai_softbank_disney' => 'softbank',
'keitai_softbank_vodafone' => 'softbank',
'keitai_softbank_j-phone' => 'softbank',
'keitai_willcom' => 'willcom',
'keitai_willcom_pdx' => 'willcom',
'keitai_willcom_bandai' => 'willcom',
'keitai_willcom_pipopa' => 'willcom',
'keitai_willcom_ymobile' => 'willcom',
'keitai_willcom_emnet' => 'willcom',
'pc_html' => 'pc',
// old sets -> to be removed later
'docomo' => 'docomo',
'kddi_ezweb' => 'kddi',
'kddi' => 'kddi',
'kddi_tu-ka' => 'kddi',
'kddi_sky' => 'kddi',
'softbank' => 'softbank',
'keitai_softbank_iphone' => 'iphone',
'softbank_iphone' => 'iphone',
'softbank_disney' => 'softbank',
'softbank_vodafone' => 'softbank',
'softbank_j-phone' => 'softbank',
'willcom' => 'willcom',
'willcom_pdx' => 'willcom',
'willcom_bandai' => 'willcom',
'willcom_pipopa' => 'willcom',
'willcom_ymobile' => 'willcom',
'willcom_emnet' => 'willcom',
'pc' => 'pc'
];
/**
* get one position from the regex check list
*
* @param int $type Which position in the regex list to get
* if not set or not valid get default pos 0
* @return string
*/
public static function getEmailRegex(int $type = 0): string
{
// make sure type is in valid range
if ($type < 0 || $type >= count(self::$email_regex_check)) {
$type = 0;
}
return self::$email_regex_check[$type];
}
/**
* get the full check array, except position 0, but preserve keys
* Currently used to add per error level type from
* getEmailRegex to error reporting
* Might be deprecated at some point
*
* @return array<mixed>
*/
public static function getEmailRegexCheck(): array
{
// return all but the first
return array_slice(
self::$email_regex_check,
1,
count(self::$email_regex_check) - 1,
true
);
}
/**
* Returns error message for email ergex error, or empty string if not set
*
* @param int $error
* @return array<string,string|int> Error message and regex
*/
public static function getEmailRegexErrorMessage(int $error): array
{
// return error message and regex
return [
'error' => $error,
'message' => self::$email_regex_check_message[$error] ?? '',
'regex' => self::$email_regex_check[$error] ?? '',
];
}
/**
* guesses the email type (mostly for mobile) from the domain
* if second is set to true, it will return short naming scheme (only provider)
*
* @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,
* false for invalid short type
*/
public static function getEmailType(string $email, bool $short = false)
{
// trip if there is no email address
if (!$email) {
return 'invalid';
}
// loop until we match a mobile type, return this first found type
foreach (self::$mobile_email_type as $email_regex => $email_type) {
if (preg_match("/$email_regex/", $email)) {
if ($short) {
return self::getShortEmailType($email_type);
} else {
return $email_type;
}
}
}
// if no previous return we assume this is a pc address
if ($short) {
return 'pc';
} else {
return 'pc_html';
}
}
/**
* 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
*/
public static function getShortEmailType(string $email_type)
{
// check if the short email type exists
if (isset(self::$mobile_email_type_short[$email_type])) {
return self::$mobile_email_type_short[$email_type];
} else {
// return false on not found
return false;
}
}
/**
* simple email check with the basic email eregex
*
* @param string $email Email address, will be checkd as lower
* @return bool True if email is ok, or false if regex failed
*/
public static function checkEmail(string $email): bool
{
$email_regex = self::getEmailRegex();
if (!preg_match("/$email_regex/", strtolower($email))) {
return false;
}
return true;
}
/**
* check email with all regex checks possible and return errors as array
*
* @param string $email Email address, will be checkd as lower
* @param bool $error_code_only If this is set to true it will only return
* the error pos, instead of detailed array
* @return array<mixed> Errors as array with message and regex
*/
public static function checkEmailFull(string $email, bool $error_code_only = false): array
{
$errors = [];
foreach (self::$email_regex_check as $pos => $email_regex) {
$match = preg_match("/$email_regex/", strtolower($email));
// if the first does not fail, quit as ok
if ($pos == 0 && $match) {
break;
}
// else do error storage
// not that for 1, 6, 7 the regex is matching
if (
(!$match && in_array($pos, [0, 2, 3, 4, 5])) ||
($match && in_array($pos, [1, 6, 7]))
) {
if ($error_code_only === true) {
$errors[] = $pos;
} else {
$errors[] = self::getEmailRegexErrorMessage($pos);
}
}
}
return $errors;
}
}
// __END__

View File

@@ -0,0 +1,117 @@
<?php
/*
* check if string is valid in target encoding
*/
declare(strict_types=1);
namespace CoreLibs\Check;
class Encoding
{
/** @var int<min, -1>|int<1, max>|string */
private static $mb_error_char = '';
/**
* set error char
*
* @param string|int|null $string The character to use to represent
* error chars
* "long" for long, "none" for none
* or a valid code point in int
* like 0x2234 (8756, ∴)
* default character is ? (63)
* if null is set then "none"
* @return void
*/
public static function setErrorChar(string|int|null $string): void
{
if (empty($string)) {
$string = 'none';
}
// if not special string or char but code point
if (in_array($string, ['none', 'long', 'entity'])) {
self::$mb_error_char = $string;
} else {
// always convert to char for internal use
self::$mb_error_char = \IntlChar::chr($string);
// if string convert to code point
if (is_string($string)) {
$string = \IntlChar::ord($string);
}
}
mb_substitute_character($string);
}
/**
* get the current set error character
*
* @param bool $return_substitute_func if set to true return the set
* character from the php function
* directly
* @return string|int Set error character
*/
public static function getErrorChar(bool $return_substitute_func = false): string|int
{
// return mb_substitute_character();
if ($return_substitute_func === true) {
return mb_substitute_character();
} else {
return self::$mb_error_char;
}
}
/**
* test if a string can be safely convert between encodings.
* mostly utf8 to shift jis
* the default compare has a possibility of failure, especially with windows
* it is recommended to the following in the script which uses this method:
* mb_substitute_character(0x2234);
* $class->mb_error_char = '∴';
* if check to Shift JIS
* if check to ISO-2022-JP
* if check to ISO-2022-JP-MS
* set three dots (∴) as wrong character for correct convert error detect
* (this char is used, because it is one of the least used ones)
*
* @param string $string string to test
* @param string $from_encoding encoding of string to test
* @param string $to_encoding target encoding
* @return array<string>|false false if no error or
* array with failed characters
*/
public static function checkConvertEncoding(
string $string,
string $from_encoding,
string $to_encoding
): array|false {
// convert to target encoding and convert back
$temp = mb_convert_encoding($string, $to_encoding, $from_encoding);
$compare = mb_convert_encoding($temp, $from_encoding, $to_encoding);
// if string does not match anymore we have a convert problem
if ($string != $compare) {
$failed = [];
// go through each character and find the ones that do not match
for ($i = 0, $iMax = mb_strlen($string, $from_encoding); $i < $iMax; $i++) {
$char = mb_substr($string, $i, 1, $from_encoding);
$r_char = mb_substr($compare, $i, 1, $from_encoding);
// the ord 194 is a hack to fix the IE7/IE8
// bug with line break and illegal character
if (
(($char != $r_char && (!self::$mb_error_char ||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
ord($char) != 194
) {
$failed[] = $char;
}
}
return $failed;
} else {
return false;
}
}
}
// __END__

View File

@@ -0,0 +1,56 @@
<?php
/*
* various file/file name functions
*/
declare(strict_types=1);
namespace CoreLibs\Check;
class File
{
/**
* quick return the extension of the given file name
*
* @param string $filename file name
* @return string extension of the file name
*/
public static function getFilenameEnding(string $filename): string
{
$page_temp = pathinfo($filename);
return $page_temp['extension'] ?? '';
}
/**
* get lines in a file
*
* @param string $file file for line count read
* @return int number of lines or -1 for non readable file
*/
public static function getLinesFromFile(string $file): int
{
if (
is_file($file) &&
file_exists($file) &&
is_readable($file)
) {
$f = fopen($file, 'rb');
if (!is_resource($f)) {
return 0;
}
$lines = 0;
while (!feof($f)) {
$lines += substr_count(fread($f, 8192) ?: '', "\n");
}
fclose($f);
} else {
// if file does not exist or is not readable, return -1
$lines = -1;
}
// return lines in file
return $lines;
}
}
// __END__

View File

@@ -0,0 +1,41 @@
<?php
/*
* DEPRECATED: Use correct Convert\Json:: instead
*/
declare(strict_types=1);
namespace CoreLibs\Check;
use CoreLibs\Convert\Json;
class Jason
{
/**
* @param string|null $json a json string, or null data
* @param bool $override if set to true, then on json error
* set original value as array
* @return array<mixed> returns an array from the json values
* @deprecated Use Json::jsonConvertToArray()
*/
public static function jsonConvertToArray(?string $json, bool $override = false): array
{
return Json::jsonConvertToArray($json, $override);
}
/**
* @param bool|boolean $return_string [default=false] if set to true
* it will return the message string and not
* the error number
* @return int|string Either error number (0 for no error)
* or error string ('' for no error)
* @deprecated Use Json::jsonGetLastError()
*/
public static function jsonGetLastError(bool $return_string = false): int|string
{
return Json::jsonGetLastError($return_string);
}
}
// __END__

View File

@@ -0,0 +1,59 @@
<?php
/*
* core password set, check and rehash check wrapper functions
*/
declare(strict_types=1);
namespace CoreLibs\Check;
class Password
{
/**
* creates the password hash
*
* @param string $password password
* @return string hashed password
*/
public static function passwordSet(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
return password_hash($password, PASSWORD_DEFAULT);
}
/**
* checks if the entered password matches the hash
*
* @param string $password password
* @param string $hash password hash
* @return bool true or false
*/
public static function passwordVerify(string $password, string $hash): bool
{
if (password_verify($password, $hash)) {
return true;
} else {
return false;
}
}
/**
* checks if the password needs to be rehashed
*
* @param string $hash password hash
* @return bool true or false
*/
public static function passwordRehashCheck(string $hash): bool
{
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
return true;
} else {
return false;
}
}
}
// __END__

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Check;
class PhpVersion
{
/**
* checks if running PHP version matches given PHP version (min or max)
* if either is empty or null it will be ignored
* if no min version (null or empty)
*
* @param string|null $min_version minimum version as string (x, x.y, x.y.x)
* @param string|null $max_version optional maximum version as string (x, x.y, x.y.x)
* @return bool true if ok, false if not matching version
*/
public static function checkPHPVersion(?string $min_version, ?string $max_version = null): bool
{
// exit with false if the min/max strings are wrong
if (
!empty($min_version) &&
!preg_match("/^\d{1,2}(\.\d{1,2})?(\.\d{1,2})?$/", $min_version)
) {
return false;
}
// max is only chcked if it is set
if (
!empty($max_version) &&
!preg_match("/^\d{1,2}(\.\d{1,2})?(\.\d{1,2})?$/", $max_version)
) {
return false;
}
// split up the version strings to calc the compare number
if (!empty($min_version)) {
$version = explode('.', $min_version);
$min_version = (int)$version[0] * 10000 + (int)($version[1] ?? 0) * 100 + (int)($version[2] ?? 0);
}
if (!empty($max_version)) {
$version = explode('.', $max_version);
$max_version = (int)$version[0] * 10000 + (int)($version[1] ?? 0) * 100 + (int)($version[2] ?? 0);
// drop out if min is bigger max, equal size is okay, that would be only THIS
if (!empty($min_version) && $min_version > $max_version) {
return false;
}
}
// set the php version id
if (!defined('PHP_VERSION_ID')) {
$version = explode('.', phpversion() ?: '');
// creates something like 50107
define('PHP_VERSION_ID', (int)$version[0] * 10000 + (int)$version[1] * 100 + (int)$version[2]);
}
// check if matching for version
if (
!empty($min_version) && empty($max_version) &&
PHP_VERSION_ID >= $min_version
) {
return true;
} elseif (
empty($min_version) && !empty($max_version) &&
PHP_VERSION_ID <= $max_version
) {
return true;
} elseif (
!empty($min_version) && !empty($max_version) &&
PHP_VERSION_ID >= $min_version && PHP_VERSION_ID <= $max_version
) {
return true;
}
// if no previous return, fail
return false;
}
}
// __END__

View File

@@ -0,0 +1,410 @@
<?php
/*
* array search and transform functions
*/
declare(strict_types=1);
namespace CoreLibs\Combined;
class ArrayHandler
{
/**
* searches key = value in an array / array
* only returns the first one found
*
* @param string|int $needle needle (search for)
* @param array<mixed> $haystack haystack (search in)
* @param string|null $key_search_for the key to look out for, default empty
* @return array<mixed> array with the elements where
* the needle can be found in the
* haystack array
*/
public static function arraySearchRecursive(
string|int $needle,
array $haystack,
?string $key_search_for = null
): array {
$path = [];
if (
$key_search_for != null &&
array_key_exists($key_search_for, $haystack) &&
$needle === $haystack[$key_search_for]
) {
$path[] = $key_search_for;
} else {
foreach ($haystack as $key => $val) {
if (
is_scalar($val) &&
$val === $needle &&
empty($key_search_for)
) {
$path[] = $key;
break;
} elseif (
is_scalar($val) &&
!empty($key_search_for) &&
$key === $key_search_for &&
$val === $needle
) {
$path[] = $key;
break;
} elseif (
is_array($val) &&
$path = self::arraySearchRecursive(
$needle,
(array)$val,
// to avoid PhanTypeMismatchArgumentNullable
($key_search_for === null ? $key_search_for : (string)$key_search_for)
)
) {
array_unshift($path, $key);
break;
}
}
}
return $path;
}
/**
* recursive array search function, which returns all found not only the first one
*
* @param string|int $needle needle (search for)
* @param array<mixed> $haystack haystack (search in)
* @param string|int|null $key_search_for the key to look for in
* @param bool $old [true], if set to false will
* return new flat layout
* @param array<mixed>|null $path recursive call for previous path
* @return array<mixed>|null all array elements paths where
* the element was found
*/
public static function arraySearchRecursiveAll(
string|int $needle,
array $haystack,
string|int|null $key_search_for,
bool $old = true,
?array $path = null
): ?array {
// init if not set on null
if ($path === null) {
$path = [
'level' => 0,
'work' => []
];
} else {
// init sub sets if not set
if (!isset($path['level'])) {
$path['level'] = 0;
}
if (!isset($path['work'])) {
$path['work'] = [];
}
}
// go through the array,
foreach ($haystack as $_key => $_value) {
if (
is_scalar($_value) &&
$_value === $needle &&
empty($key_search_for)
) {
// only value matches
$path['work'][$path['level'] ?? 0] = $_key;
$path['found'][] = $path['work'];
} elseif (
is_scalar($_value) &&
!empty($key_search_for) &&
$_key === $key_search_for &&
$_value === $needle
) {
// key and value matches
$path['work'][$path['level'] ?? 0] = $_key;
$path['found'][] = $path['work'];
} elseif (is_array($_value)) {
// add position to working
$path['work'][$path['level'] ?? 0] = $_key;
// we will up a level
$path['level'] += 1;
// call recursive
$path = self::arraySearchRecursiveAll($needle, $_value, $key_search_for, $old, $path);
}
}
// be 100% sure the array elements are set
$path['level'] = $path['level'] ?? 0;
$path['work'] = $path['work'] ?? [];
// cut all that is >= level
array_splice($path['work'], $path['level']);
// step back a level
$path['level'] -= 1;
if ($old === false && $path['level'] == -1) {
return $path['found'] ?? [];
} else {
return $path;
}
}
/**
* array search simple. looks for key, value combination, if found, returns true
* on default does not strict check, so string '4' will match int 4 and vica versa
*
* @param array<mixed> $array search in as array
* @param string|int $key key (key to search in)
* @param string|int|bool $value value (what to find)
* @param bool $strict [false], if set to true, will strict check key/value
* @return bool true on found, false on not found
*/
public static function arraySearchSimple(
array $array,
string|int $key,
string|int|bool $value,
bool $strict = false
): bool {
foreach ($array as $_key => $_value) {
// if value is an array, we search
if (is_array($_value)) {
// call recursive, and return result if it is true, else continue
if (($result = self::arraySearchSimple($_value, $key, $value, $strict)) !== false) {
return $result;
}
} elseif ($strict === false && $_key == $key && $_value == $value) {
return true;
} elseif ($strict === true && $_key === $key && $_value === $value) {
return true;
}
}
// no true returned, not found
return false;
}
/**
* correctly recursive merges as an array as array_merge_recursive
* just glues things together
* array first array to merge
* array second array to merge
* ... etc
* bool key flag: true: handle keys as string or int
* default false: all keys are string
*
* @return array<mixed>|false merged array
*/
public static function arrayMergeRecursive(): array|false
{
// croak on not enough arguemnts (we need at least two)
if (func_num_args() < 2) {
trigger_error(__FUNCTION__ . ' needs two or more array arguments', E_USER_WARNING);
return false;
}
// default key is not string
$key_is_string = false;
$arrays = func_get_args();
// if last is not array, then assume it is trigger for key is always string
if (!is_array(end($arrays))) {
if (array_pop($arrays)) {
$key_is_string = true;
}
}
// check that arrays count is at least two, else we don't have enough to do anything
if (count($arrays) < 2) {
trigger_error(__FUNCTION__ . ' needs two or more array arguments', E_USER_WARNING);
return false;
}
$merged = [];
while ($arrays) {
$array = array_shift($arrays);
if (!is_array($array)) {
trigger_error(__FUNCTION__ . ' encountered a non array argument', E_USER_WARNING);
return false;
}
if (!$array) {
continue;
}
foreach ($array as $key => $value) {
// if string or if key is assumed to be string do key match
// else add new entry
if (is_string($key) || $key_is_string === false) {
if (is_array($value) && array_key_exists($key, $merged) && is_array($merged[$key])) {
// $merged[$key] = call_user_func(__METHOD__, $merged[$key], $value, $key_is_string);
$merged[$key] = self::arrayMergeRecursive($merged[$key], $value, $key_is_string);
} else {
$merged[$key] = $value;
}
} else {
$merged[] = $value;
}
}
}
return $merged;
}
/**
* correct array_diff that does an actualy difference between two arrays.
* array_diff only checks elements from A that are not in B, but not the
* other way around.
* Note that like array_diff this only checks first level values not keys
*
* @param array<mixed> $a array to compare a
* @param array<mixed> $b array to compare b
* @return array<mixed> array with missing elements from a & b
*/
public static function arrayDiff(array $a, array $b): array
{
$intersect = array_intersect($a, $b);
return array_merge(array_diff($a, $intersect), array_diff($b, $intersect));
}
/**
* search for the needle array elements in haystack and
* return the ones found as an array,
* is there nothing found, it returns FALSE (boolean)
*
* @param array<mixed> $needle elements to search for
* @param array<mixed> $haystack array where the $needle elements should
* be searched int
* @return array<mixed>|false either the found elements or
* false for nothing found or error
*/
public static function inArrayAny(array $needle, array $haystack): array|false
{
$found = [];
foreach ($needle as $element) {
if (in_array($element, $haystack)) {
$found[] = $element;
}
}
if (count($found) == 0) {
return false;
} else {
return $found;
}
}
/**
* creates out of a normal db_return array an assoc array
*
* @param array<mixed> $db_array return array from the database
* @param string|int|bool $key key set, false for not set
* @param string|int|bool $value value set, false for not set
* @param bool $set_only flag to return all (default), or set only
* @return array<mixed> associative array
*/
public static function genAssocArray(
array $db_array,
string|int|bool $key,
string|int|bool $value,
bool $set_only = false
): array {
$ret_array = [];
// do this to only run count once
for ($i = 0, $iMax = count($db_array); $i < $iMax; $i++) {
// if no key then we make an order reference
if (
$key !== false &&
$value !== false &&
(($set_only && !empty($db_array[$i][$value])) ||
(!$set_only && isset($db_array[$i][$value]))) &&
!empty($db_array[$i][$key])
) {
$ret_array[$db_array[$i][$key]] = $db_array[$i][$value];
} elseif (
$key === false && $value !== false &&
isset($db_array[$i][$value])
) {
$ret_array[] = $db_array[$i][$value];
} elseif (
$key !== false && $value === false &&
!empty($db_array[$i][$key])
) {
$ret_array[$db_array[$i][$key]] = $i;
}
}
return $ret_array;
}
/**
* converts multi dimensional array to a flat array
* does NOT preserve keys
*
* @param array<mixed> $array multi dimensionial array
* @return array<mixed> flattened array
*/
public static function flattenArray(array $array): array
{
$return = [];
array_walk_recursive(
$array,
function ($value) use (&$return) {
$return[] = $value;
}
);
return $return;
}
/**
* will loop through an array recursivly and write the array keys back
*
* @param array<mixed> $array multidemnsional array to flatten
* @param array<mixed> $return recoursive pass on array of keys
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKey(array $array, array $return = []): array
{
foreach ($array as $key => $sub) {
$return[] = $key;
if (is_array($sub) && count($sub) > 0) {
$return = self::flattenArrayKey($sub, $return);
}
}
return $return;
}
/**
* as above will flatten an array, but in this case only the outmost
* leave nodes, all other keyswill be skipped
*
* @param array<mixed> $array multidemnsional array to flatten
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKeyLeavesOnly(array $array): array
{
$return = [];
array_walk_recursive(
$array,
function ($value, $key) use (&$return) {
$return[] = $key;
}
);
return $return;
}
/**
* searches for key -> value in an array tree and writes the value one level up
* this will remove this leaf will all other values
*
* @param array<mixed> $array nested array
* @param string|int $search key to find that has no sub leaf
* and will be pushed up
* @return array<mixed> modified, flattened array
*/
public static function arrayFlatForKey(array $array, string|int $search): array
{
foreach ($array as $key => $value) {
// if it is not an array do just nothing
if (!is_array($value)) {
continue;
}
// probe it has search key
if (isset($value[$search])) {
// set as current
$array[$key] = $value[$search];
} else {
// call up next node down
// $array[$key] = call_user_func(__METHOD__, $value, $search);
$array[$key] = self::arrayFlatForKey($value, $search);
}
}
return $array;
}
}
// __END__

View File

@@ -0,0 +1,457 @@
<?php
/*
* date convert and check functions
*/
declare(strict_types=1);
namespace CoreLibs\Combined;
use Exception;
class DateTime
{
/** @var array<int,string> */
public const DAY_SHORT = [
1 => 'Mon',
2 => 'Tue',
3 => 'Wed',
4 => 'Thu',
5 => 'Fri',
6 => 'Sat',
7 => 'Sun',
];
/** @var array<int,string> */
public const DAY_LONG = [
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
4 => 'Thursday',
5 => 'Friday',
6 => 'Saturday',
7 => 'Sunday',
];
/** @var array<int,string> */
public const MONTH_LONG = [
1 => 'January',
2 => 'February',
3 => 'March',
4 => 'April',
5 => 'May',
6 => 'June',
7 => 'July',
8 => 'August',
9 => 'September',
10 => 'October',
11 => 'November',
12 => 'December',
];
/** @var array<int,string> */
public const MONTH_SHORT = [
1 => 'Jan',
2 => 'Feb',
3 => 'Mar',
4 => 'Apr',
5 => 'May',
6 => 'Jun',
7 => 'Jul',
8 => 'Aug',
9 => 'Sep',
10 => 'Oct',
11 => 'Nov',
12 => 'Dec',
];
/**
* a simple wrapper for the date format
* if an invalid timestamp is give zero timestamp unix time is used
*
* @param int|float $timestamp unix timestamp
* @param bool $show_micro show the micro time (default false)
* @param bool $micro_as_float Add the micro time with . instead
* of ms (default false)
* @return string formated date+time in Y-M-D h:m:s ms
*/
public static function dateStringFormat(
int|float $timestamp,
bool $show_micro = false,
bool $micro_as_float = false
): string {
// split up the timestamp, assume . in timestamp
// array pad $ms if no microtime
list ($timestamp, $ms) = array_pad(explode('.', (string)round($timestamp, 4)), 2, null);
$string = date("Y-m-d H:i:s", (int)$timestamp);
if ($show_micro && $ms) {
if ($micro_as_float == false) {
$string .= ' ' . $ms . 'ms';
} else {
$string .= '.' . $ms;
}
}
return $string;
}
/**
* formats a timestamp into interval, not into a date
*
* @param string|int|float $timestamp interval in seconds and optional
* float micro seconds
* @param bool $show_micro show micro seconds, default true
* @return string interval formatted string or string as is
*/
public static function timeStringFormat(
string|int|float $timestamp,
bool $show_micro = true
): string {
// check if the timestamp has any h/m/s/ms inside, if yes skip
if (!preg_match("/(h|m|s|ms)/", (string)$timestamp)) {
list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 4)), 2, null);
// if negative remember
$negative = false;
if ((int)$timestamp < 0) {
$negative = true;
}
$timestamp = abs((float)$timestamp);
$timegroups = [86400, 3600, 60, 1];
$labels = ['d', 'h', 'm', 's'];
$time_string = '';
// if timestamp is zero, return zero string
if ($timestamp == 0) {
$time_string = '0s';
} else {
for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) {
$output = floor((float)$timestamp / $timegroups[$i]);
$timestamp = (float)$timestamp % $timegroups[$i];
// output has days|hours|min|sec
if ($output || $time_string) {
$time_string .= $output . $labels[$i] . (($i + 1) != count($timegroups) ? ' ' : '');
}
}
}
// only add ms if we have an ms value
if ($ms !== null) {
// if we have ms and it has leading zeros, remove them, but only if it is nut just 0
$ms = preg_replace("/^0+(\d+)$/", '${1}', $ms);
if (!is_string($ms) || empty($ms)) {
$ms = '0';
}
// add ms if there
if ($show_micro) {
$time_string .= ' ' . $ms . 'ms';
} elseif (!$time_string) {
$time_string .= $ms . 'ms';
}
}
if ($negative) {
$time_string = '-' . $time_string;
}
} else {
$time_string = $timestamp;
}
return (string)$time_string;
}
/**
* does a reverse of the timeStringFormat and converts the string from
* xd xh xm xs xms to a timestamp.microtime format
*
* @param string|int|float $timestring formatted interval
* @return string|int|float converted float interval, or string as is
*/
public static function stringToTime(string|int|float $timestring): string|int|float
{
$timestamp = 0;
if (preg_match("/(d|h|m|s|ms)/", (string)$timestring)) {
$timestring = (string)$timestring;
// pos for preg match read + multiply factor
$timegroups = [2 => 86400, 4 => 3600, 6 => 60, 8 => 1];
$matches = [];
// if start with -, strip and set negative
$negative = false;
if (preg_match("/^-/", $timestring)) {
$negative = true;
$timestring = substr($timestring, 1);
}
// preg match: 0: full string
// 2, 4, 6, 8 are the to need values
preg_match("/^((\d+)d ?)?((\d+)h ?)?((\d+)m ?)?((\d+)s ?)?((\d+)ms)?$/", $timestring, $matches);
// multiply the returned matches and sum them up. the last one (ms) is added with .
foreach ($timegroups as $i => $time_multiply) {
if (isset($matches[$i]) && is_numeric($matches[$i])) {
$timestamp += (float)$matches[$i] * $time_multiply;
}
}
if (isset($matches[10]) && is_numeric($matches[10])) {
$timestamp .= '.' . $matches[10];
}
if ($negative) {
// cast to flaot so we can do a negative multiplication
$timestamp = (float)$timestamp * -1;
}
return $timestamp;
} else {
return $timestring;
}
}
/**
* Returns long or short day of week name based on ISO day of week number
* 1: Monday
* ...
* 7: Sunday
*
* @param int $isodow 1: Monday, 7: Sunday
* @param bool $long Default false 'Mon', if true 'Monday'
* @return string Day of week string either short 'Mon' or long 'Monday'
*/
public static function setWeekdayNameFromIsoDow(int $isodow, bool $long = false): string
{
// if not valid, set to invalid
if ($isodow < 1 || $isodow > 7) {
return $long ? 'Invalid' : 'Inv';
}
return date($long ? 'l' : 'D', strtotime("Sunday +{$isodow} days") ?: null);
}
/**
* Get the day of week Name from date
*
* @param string $date Any valid date
* @param bool $long Default false 'Mon', if true 'Monday'
* @return string Day of week string either short 'Mon' or long 'Monday'
*/
public static function setWeekdayNameFromDate(string $date, bool $long = false): string
{
if (!self::checkDate($date)) {
return $long ? 'Invalid' : 'Inv';
}
return date($long ? 'l' : 'D', strtotime($date) ?: null);
}
/**
* Get the day of week Name from date
*
* @param string $date Any valid date
* @return int ISO Weekday number 1: Monday, 7: Sunday, -1 for invalid date
*/
public static function setWeekdayNumberFromDate(string $date): int
{
if (!self::checkDate($date)) {
return -1;
}
return (int)date('N', strtotime($date) ?: null);
}
/**
* splits & checks date, wrap around for check_date function
*
* @param string $date a date string in the format YYYY-MM-DD
* @return bool true if valid date, false if date not valid
*/
public static function checkDate(string $date): bool
{
if (empty($date)) {
return false;
}
list ($year, $month, $day) = array_pad(
preg_split("/[\/-]/", $date) ?: [],
3,
null
);
if (!$year || !$month || !$day) {
return false;
}
if (!checkdate((int)$month, (int)$day, (int)$year)) {
return false;
}
return true;
}
/**
* splits & checks date, wrap around for check_date function
*
* @param string $datetime date (YYYY-MM-DD) + time (HH:MM:SS), SS can be dropped
* @return bool true if valid date, false if date not valid
*/
public static function checkDateTime(string $datetime): bool
{
if (!$datetime) {
return false;
}
// catch last overflow if sec has - in front
list ($year, $month, $day, $hour, $min, $sec, $sec_overflow) = array_pad(
preg_split("/[\/\- :]/", $datetime) ?: [],
7,
null
);
if (!$year || !$month || !$day) {
return false;
}
if (!checkdate((int)$month, (int)$day, (int)$year)) {
return false;
}
if (!is_numeric($hour) || !is_numeric($min)) {
return false;
}
if (!empty($sec) && !is_numeric($sec)) {
return false;
}
if (!empty($sec) && ($sec < 0 || $sec > 60)) {
return false;
};
// in case we have - for seconds
if (!empty($sec_overflow)) {
return false;
}
if (
($hour < 0 || $hour > 24) ||
($min < 0 || $min > 60)
) {
return false;
}
return true;
}
/**
* compares two dates, tries to convert them via strtotime to timestamps
* returns int/bool in:
* -1 if the first date is smaller the last
* 0 if both are equal
* 1 if the first date is bigger than the last
* false if date validation/conversion failed
*
* @param string $start_date start date string in YYYY-MM-DD
* @param string $end_date end date string in YYYY-MM-DD
* @return int|bool false on error
* or int -1 (s<e)/0 (s=e)/1 (s>e) as difference
*/
public static function compareDate(string $start_date, string $end_date): int|bool
{
// pre check for empty or wrong
if ($start_date == '--' || $end_date == '--' || !$start_date || !$end_date) {
return false;
}
// if invalid, quit
if (($start_timestamp = strtotime($start_date)) === false) {
return false;
}
if (($end_timestamp = strtotime($end_date)) === false) {
return false;
}
// convert anything to Y-m-d and then to timestamp
// this is to remove any time parts
$start_timestamp = strtotime(date('Y-m-d', $start_timestamp));
$end_timestamp = strtotime(date('Y-m-d', $end_timestamp));
// compare, or end with false
if ($start_timestamp < $end_timestamp) {
return -1;
} elseif ($start_timestamp == $end_timestamp) {
return 0;
} elseif ($start_timestamp > $end_timestamp) {
return 1;
} else {
return false;
}
}
/**
* compares the two dates + times. if seconds missing in one set,
* adds :00, converts date + times via strtotime to timestamps
* returns int/bool in:
* -1 if the first date is smaller the last
* 0 if both are equal
* 1 if the first date is bigger than the last
* false if date/times validation/conversion failed
*
* @param string $start_datetime start date/time in YYYY-MM-DD HH:mm:ss
* @param string $end_datetime end date/time in YYYY-MM-DD HH:mm:ss
* @return int|bool false for error
* or -1 (s<e)/0 (s=e)/1 (s>e) as difference
*/
public static function compareDateTime(string $start_datetime, string $end_datetime): int|bool
{
// pre check for empty or wrong
if ($start_datetime == '--' || $end_datetime == '--' || !$start_datetime || !$end_datetime) {
return false;
}
// quit if invalid timestamp
if (($start_timestamp = strtotime($start_datetime)) === false) {
return false;
}
if (($end_timestamp = strtotime($end_datetime)) === false) {
return false;
}
// compare, or return false
if ($start_timestamp < $end_timestamp) {
return -1;
} elseif ($start_timestamp == $end_timestamp) {
return 0;
} elseif ($start_timestamp > $end_timestamp) {
return 1;
} else {
return false;
}
}
/**
* calculates the days between two dates
* return: overall days, week days, weekend days as array 0...2 or named
* as overall, weekday and weekend
*
* @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
*/
public static function calcDaysInterval(
string $start_date,
string $end_date,
bool $return_named = false
): array {
// pos 0 all, pos 1 weekday, pos 2 weekend
$days = [];
// if anything invalid, return 0,0,0
try {
$start = new \DateTime($start_date);
$end = new \DateTime($end_date);
} catch (Exception $e) {
if ($return_named === true) {
return [
'overall' => 0,
'weekday' => 0,
'weekend' => 0,
];
} else {
return [0, 0, 0];
}
}
// 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;
// get period for weekends/weekdays
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end);
foreach ($period as $dt) {
$curr = $dt->format('D');
if ($curr == 'Sat' || $curr == 'Sun') {
$days[2] ++;
} else {
$days[1] ++;
}
}
if ($return_named === true) {
return [
'overall' => $days[0],
'weekday' => $days[1],
'weekend' => $days[2],
];
} else {
return $days;
}
}
}
// __END__

View File

@@ -0,0 +1,167 @@
<?php
/*
* byte conversion from and to human readable
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Byte
{
// define byteFormat
public const BYTE_FORMAT_NOSPACE = 1;
public const BYTE_FORMAT_ADJUST = 2;
public const BYTE_FORMAT_SI = 4;
/**
* This function replaces the old byteStringFormat
*
* Converts any number string to human readable byte format
* Maxium is Exobytes and above that the Exobytes suffix is used for all
* If more are needed only the correct short name for the suffix has to be
* added to the labels array
* On no number string it returns string as is
* Source Idea: SOURCE: https://programming.guide/worlds-most-copied-so-snippet.html
*
* The class itself hast the following defined
* BYTE_FORMAT_NOSPACE [1] turn off spaces between number and suffix
* BYTE_FORMAT_ADJUST [2] use sprintf to always print two decimals
* BYTE_FORMAT_SI [4] use si standard 1000 instead of bytes 1024
* To use the constant from outside use class::CONSTANT
*
* @param string|int|float $bytes bytes as string int or pure int
* @param int $flags bitwise flag with use space turned on
* BYTE_FORMAT_NOSPACE: no space between number and suffix
* BYTE_FORMAT_ADJUST: sprintf adjusted two 2 decimals
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string converted byte number (float) with suffix
* @throws \Exception 1: no valid flag set
*/
public static function humanReadableByteFormat(string|int|float $bytes, int $flags = 0): string
{
// if not numeric, return as is
if (is_numeric($bytes)) {
// flags bit wise check
// remove space between number and suffix
if ($flags & self::BYTE_FORMAT_NOSPACE) {
$space = false;
} else {
$space = true;
}
// use sprintf instead of round
if ($flags & self::BYTE_FORMAT_ADJUST) {
$adjust = true;
} else {
$adjust = false;
}
// use SI 1000 mod and not 1024 mod
if ($flags & self::BYTE_FORMAT_SI) {
$si = true;
} else {
$si = false;
}
if ($flags > 7) {
throw new \Exception("Invalid flags parameter: $flags", 1);
}
// si or normal
$unit = $si ? 1000 : 1024;
// always positive
$abs_bytes = $bytes == PHP_INT_MIN ? PHP_INT_MAX : abs((float)$bytes);
// smaller than unit is always B
if ($abs_bytes < $unit) {
return $bytes . 'B';
}
// labels in order of size [Y, Z]
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
// exp position calculation
$exp = floor(log($abs_bytes, $unit));
// avoid printing out anything larger than max labels
if ($exp >= count($labels)) {
$exp = count($labels) - 1;
}
// deviation calculation
$dev = pow($unit, $exp) * ($unit - 0.05);
// shift the exp +1 for on the border units
if (
$exp < 6 &&
$abs_bytes > ($dev - (((int)$dev & 0xfff) == 0xd00 ? 52 : 0))
) {
$exp++;
}
// label name, including leading space if flagged
$pre = ($space ? ' ' : '') . ($labels[$exp] ?? '>E') . ($si ? 'i' : '') . 'B';
$bytes_calc = $abs_bytes / pow($unit, $exp);
// if original is negative, reverse
if ($bytes < 0) {
$bytes_calc *= -1;
}
if ($adjust) {
return sprintf("%.2f%s", $bytes_calc, $pre);
} else {
return round($bytes_calc, 2) . $pre;
}
} else {
// if anything other return as string
return (string)$bytes;
}
}
/**
* calculates the bytes based on a string with nnG, nnGB, nnM, etc
* NOTE: large exabyte numbers will overflow
* flag allowed:
* BYTE_FORMAT_SI [4] use si standard 1000 instead of bytes 1024
*
* @param string|int|float $number any string or number to convert
* @param int $flags bitwise flag with use space turned on
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string|int|float converted value or original value
* @throws \Exception 1: no valid flag set
*/
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
{
// use SI 1000 mod and not 1024 mod
if ($flags & self::BYTE_FORMAT_SI) {
$si = true;
} else {
$si = false;
}
if ($flags != 0 && $flags != 4) {
throw new \Exception("Invalid flags parameter: $flags", 1);
}
// matches in regex
$matches = [];
// all valid units
$valid_units_ = 'bkmgtpezy';
// detects up to exo bytes
preg_match(
"/(-)?([\d.,]*)\s?(eib|pib|tib|gib|mib|kib|eb|pb|tb|gb|mb|kb|e|p|t|g|m|k|b)$/i",
strtolower((string)$number),
$matches
);
if (isset($matches[2]) && isset($matches[3])) {
// remove all non valid characters from the number
$number = preg_replace('/[^0-9\.]/', '', $matches[2]);
// final clean up and convert to float
$number = (float)trim((string)$number);
// convert any mb/gb/etc to single m/b
$unit = preg_replace('/[^bkmgtpezy]/i', '', $matches[3]);
if ($unit) {
$number = $number * pow($si ? 1000 : 1024, stripos($valid_units_, $unit[0]) ?: 0);
}
// convert to INT to avoid +E output
$number = (int)round($number);
// if negative input, keep nnegative
if (!empty($matches[1])) {
$number *= -1;
}
}
// if not matching return as is
return $number;
}
}
// __END__

View File

@@ -0,0 +1,345 @@
<?php
/*
* Convert color spaces
* rgb to hex
* hex to rgb
* rgb to hsb
* hsb to rgb
* rgb to hsl
* hsl to rgb
*/
// TODO: use oklab as base for converting colors
// https://bottosson.github.io/posts/oklab/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Colors
{
/**
* converts the rgb values from int data to the valid rgb html hex string
* optional can turn of leading #
* if one value is invalid, will return false
*
* @param int $red red 0-255
* @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,
* false for invalid color
*/
public static function rgb2hex(
int $red,
int $green,
int $blue,
bool $hex_prefix = true
): string|bool {
$hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
}
foreach (['red', 'green', 'blue'] as $color) {
// if not valid, abort
if ($$color < 0 || $$color > 255) {
return false;
}
// pad left with 0
$hex_color .= str_pad(dechex($$color), 2, '0', STR_PAD_LEFT);
}
return $hex_color;
}
/**
* converts a hex RGB color to the int numbers
*
* @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
*/
public static function hex2rgb(
string $hexStr,
bool $return_as_string = false,
string $seperator = ','
): string|array|bool {
$hexStr = preg_replace("/[^0-9A-Fa-f]/", '', $hexStr); // Gets a proper hex string
if (!is_string($hexStr)) {
return false;
}
$rgbArray = [];
if (strlen($hexStr) == 6) {
// If a proper hex code, convert using bitwise operation.
// No overhead... faster
$colorVal = hexdec($hexStr);
$rgbArray['r'] = 0xFF & ($colorVal >> 0x10);
$rgbArray['g'] = 0xFF & ($colorVal >> 0x8);
$rgbArray['b'] = 0xFF & $colorVal;
} elseif (strlen($hexStr) == 3) {
// If shorthand notation, need some string manipulations
$rgbArray['r'] = hexdec(str_repeat(substr($hexStr, 0, 1), 2));
$rgbArray['g'] = hexdec(str_repeat(substr($hexStr, 1, 1), 2));
$rgbArray['b'] = hexdec(str_repeat(substr($hexStr, 2, 1), 2));
} else {
// Invalid hex color code
return false;
}
// returns the rgb string or the associative array
return $return_as_string ? implode($seperator, $rgbArray) : $rgbArray;
}
/**
* rgb2hsb does not clean convert back to rgb in a round trip
* converts RGB to HSB/V values
* 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
*/
public static function rgb2hsb(int $red, int $green, int $blue): array|bool
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
if ($$c < 0 || $$c > 255) {
return false;
}
$$c = $$c / 255;
}
$MAX = max($red, $green, $blue);
$MIN = min($red, $green, $blue);
$HUE = 0;
if ($MAX == $MIN) {
return [0, 0, round($MAX * 100)];
}
if ($red == $MAX) {
$HUE = ($green - $blue) / ($MAX - $MIN);
} elseif ($green == $MAX) {
$HUE = 2 + (($blue - $red) / ($MAX - $MIN));
} elseif ($blue == $MAX) {
$HUE = 4 + (($red - $green) / ($MAX - $MIN));
}
$HUE *= 60;
if ($HUE < 0) {
$HUE += 360;
}
return [
(int)round($HUE),
(int)round((($MAX - $MIN) / $MAX) * 100),
(int)round($MAX * 100)
];
}
/**
* hsb2rgb does not clean convert back to hsb in a round trip
* 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
*/
public static function hsb2rgb(float $H, float $S, float $V): array|bool
{
// check that H is 0 to 359, 360 = 0
// and S and V are 0 to 1
if ($H == 360) {
$H = 0;
}
if ($H < 0 || $H > 359) {
return false;
}
if ($S < 0 || $S > 100) {
return false;
}
if ($V < 0 || $V > 100) {
return false;
}
// convert to internal 0-1 format
$S /= 100;
$V /= 100;
if ($S == 0) {
$V = (int)round($V * 255);
return [$V, $V, $V];
}
$Hi = floor($H / 60);
$f = ($H / 60) - $Hi;
$p = $V * (1 - $S);
$q = $V * (1 - ($S * $f));
$t = $V * (1 - ($S * (1 - $f)));
switch ($Hi) {
case 0:
$red = $V;
$green = $t;
$blue = $p;
break;
case 1:
$red = $q;
$green = $V;
$blue = $p;
break;
case 2:
$red = $p;
$green = $V;
$blue = $t;
break;
case 3:
$red = $p;
$green = $q;
$blue = $V;
break;
case 4:
$red = $t;
$green = $p;
$blue = $V;
break;
case 5:
$red = $V;
$green = $p;
$blue = $q;
break;
default:
$red = 0;
$green = 0;
$blue = 0;
}
return [
(int)round($red * 255),
(int)round($green * 255),
(int)round($blue * 255)
];
}
/**
* converts a RGB (0-255) to HSL
* 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
*/
public static function rgb2hsl(int $red, int $green, int $blue): array|bool
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
if ($$c < 0 || $$c > 255) {
return false;
}
$$c = $$c / 255;
}
$min = min($red, $green, $blue);
$max = max($red, $green, $blue);
$chroma = $max - $min;
$sat = 0;
$hue = 0;
// luminance
$lum = ($max + $min) / 2;
// achromatic
if ($chroma == 0) {
// H, S, L
return [0.0, 0.0, round($lum * 100, 1)];
} else {
$sat = $chroma / (1 - abs(2 * $lum - 1));
if ($max == $red) {
$hue = fmod((($green - $blue) / $chroma), 6);
if ($hue < 0) {
$hue = (6 - fmod(abs($hue), 6));
}
} elseif ($max == $green) {
$hue = ($blue - $red) / $chroma + 2;
} elseif ($max == $blue) {
$hue = ($red - $green) / $chroma + 4;
}
$hue = $hue * 60;
// $sat = 1 - abs(2 * $lum - 1);
return [
round($hue, 1),
round($sat * 100, 1),
round($lum * 100, 1)
];
}
}
/**
* 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
*/
public static function hsl2rgb(float $hue, float $sat, float $lum): array|bool
{
if ($hue == 360) {
$hue = 0;
}
if ($hue < 0 || $hue > 359) {
return false;
}
if ($sat < 0 || $sat > 100) {
return false;
}
if ($lum < 0 || $lum > 100) {
return false;
}
// calc to internal convert value for hue
$hue = (1 / 360) * $hue;
// convert to internal 0-1 format
$sat /= 100;
$lum /= 100;
// if saturation is 0
if ($sat == 0) {
$lum = (int)round($lum * 255);
return [$lum, $lum, $lum];
} else {
$m2 = $lum < 0.5 ? $lum * ($sat + 1) : ($lum + $sat) - ($lum * $sat);
$m1 = $lum * 2 - $m2;
$hueue = function ($base) use ($m1, $m2) {
// base = hue, hue > 360 (1) - 360 (1), else < 0 + 360 (1)
$base = $base < 0 ? $base + 1 : ($base > 1 ? $base - 1 : $base);
// 6: 60, 2: 180, 3: 240
// 2/3 = 240
// 1/3 = 120 (all from 360)
if ($base * 6 < 1) {
return $m1 + ($m2 - $m1) * $base * 6;
}
if ($base * 2 < 1) {
return $m2;
}
if ($base * 3 < 2) {
return $m1 + ($m2 - $m1) * ((2 / 3) - $base) * 6;
}
return $m1;
};
return [
(int)round(255 * $hueue($hue + (1 / 3))),
(int)round(255 * $hueue($hue)),
(int)round(255 * $hueue($hue - (1 / 3)))
];
}
}
}
// __END__

View File

@@ -0,0 +1,60 @@
<?php
/*
* convert string frmo one encdoing to another with auto detect flags
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Encoding
{
/**
* detects the source encoding of the string and if doesn't match
* to the given target encoding it convert is
* if source encoding is set and auto check is true (default) a second
* check is done so that the source string encoding actually matches
* will be skipped if source encoding detection is ascii
*
* @param string $string string to convert
* @param string $to_encoding target encoding
* @param string $source_encoding optional source encoding, will try to auto detect
* @param bool $auto_check default true, if source encoding is set
* check that the source is actually matching
* to what we sav the source is
* @return string encoding converted string
*/
public static function convertEncoding(
string $string,
string $to_encoding,
string $source_encoding = '',
bool $auto_check = true
): string {
// set if not given
if (!$source_encoding) {
$source_encoding = mb_detect_encoding($string);
} else {
$_source_encoding = mb_detect_encoding($string);
}
if (
$auto_check === true &&
isset($_source_encoding) &&
$_source_encoding == $source_encoding
) {
// trigger check if we have override source encoding.
// if different (_source is all but not ascii) then trigger
// skip if matching
}
if ($source_encoding != $to_encoding) {
if ($source_encoding) {
$string = mb_convert_encoding($string, $to_encoding, $source_encoding);
} else {
$string = mb_convert_encoding($string, $to_encoding);
}
}
return $string;
}
}
// __END__

View File

@@ -0,0 +1,256 @@
<?php
/*
* Run is_<type> checks and return default value if not this type
* This will return default null on invalid entries
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Extends;
class SetVarTypeMain
{
/**
* If input variable is string then returns it, else returns default set
* if not null is true, then null as return is allowed, else return is
* converted to string
*
* @param mixed $val Input variable
* @param string|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return string|null Input var or default value
*/
protected static function setStrMain(
mixed $val,
?string $default = null,
bool $to_null = false
): ?string {
if (is_string($val)) {
return $val;
}
if ($to_null === false) {
return (string)$default;
}
return $default;
}
/**
* Will convert input data to string if possible.
* Runs for string/int/float/bool/null
* Will skip array/object/resource/callable/etc and use default for that
*
* @param mixed $val Input variable
* @param string|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return string|null Converted input data to string/null
*/
protected static function makeStrMain(
mixed $val,
string $default = null,
bool $to_null = false
): ?string {
// int/float/string/bool/null, everything else is ignored
// no: array/object/resource/callable
if (
is_int($val) ||
is_float($val) ||
is_string($val) ||
is_bool($val) ||
is_null($val)
) {
return (string)$val;
}
if ($to_null === false) {
return (string)$default;
}
return $default;
}
/**
* If input variable is int, return it, else return default value. If to_null
* is true then null as return is allowed, else only int is returned
*
* @param mixed $val Input variable
* @param int|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return int|null Input var or default value
*/
protected static function setIntMain(
mixed $val,
?int $default = null,
bool $to_null = false
): ?int {
if (is_int($val)) {
return $val;
}
if ($to_null === false) {
return (int)$default;
}
return $default;
}
/**
* Convert input to int via filter_var. If not convertable return default value.
* If to_null is set to true null return is allowed
* NOTE: this is only a drastic fallback and not recommned for special use.
* It will try to check via filter_var if we can get an int value and then use
* intval to convert it.
* Reason is that filter_var will convert eg 1.5 to 15 instead 1
* One is very wrong, the other is at least better, but not perfect
*
* @param mixed $val Input variable
* @param int|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return int|null Converted input data to int/null
*/
protected static function makeIntMain(
mixed $val,
int $default = null,
bool $to_null = false
): ?int {
// if we can filter it to a valid int, we can convert it
// we so avoid object, array, etc
if (
filter_var(
$val,
FILTER_SANITIZE_NUMBER_INT
) !== false
) {
return intval($val);
}
if ($to_null === false) {
return (int)$default;
}
return $default;
}
/**
* If input is float return it, else set to default value. If to_null is set
* to true, allow null return
*
* @param mixed $val Input variable
* @param float|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return float|null Input var or default value
*/
protected static function setFloatMain(
mixed $val,
?float $default = null,
bool $to_null = false
): ?float {
if (is_float($val)) {
return $val;
}
if ($to_null === false) {
return (float)$default;
}
return $default;
}
/**
* Convert intput var to float via filter_var. If failed to so return default.
* If to_null is set to true allow null return
*
* @param mixed $val Input variable
* @param float|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return float|null Converted intput data to float/null
*/
protected static function makeFloatMain(
mixed $val,
float $default = null,
bool $to_null = false
): ?float {
if (
(
$val = filter_var(
$val,
FILTER_SANITIZE_NUMBER_FLOAT,
FILTER_FLAG_ALLOW_FRACTION
)
) !== false
) {
return (float)$val;
}
if ($to_null === false) {
return (float)$default;
}
return $default;
}
/**
* If input var is array return it, else return default value. If to_null is
* set to true, allow null return
*
* @param mixed $val Input variable
* @param array<mixed>|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return array<mixed>|null Input var or default value
*/
protected static function setArrayMain(
mixed $val,
?array $default = null,
bool $to_null = false
): ?array {
if (is_array($val)) {
return $val;
}
if ($to_null === false) {
return (array)$default;
}
return $default;
}
/**
* If input var is bool return it, else return default value. If to_null is
* set to true will allow null return.
*
* @param mixed $val Input variable
* @param bool|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return bool|null Input var or default value
*/
protected static function setBoolMain(
mixed $val,
?bool $default = null,
bool $to_null = false
): ?bool {
if (is_bool($val)) {
return $val;
}
if ($to_null === false) {
return (bool)$default;
}
return $default;
}
/**
* Convert anything to bool. If it is a string it will try to use the filter_var
* to convert know true/false strings.
* Else it uses (bool) to convert the rest
* If null is allowed, will return null
*
* @param mixed $val Input variable
* @param bool $default Default value if to_null if false
* @param bool $to_null Convert to null (default no)
* @return bool|null Converted input data to bool/ null
*/
protected static function makeBoolMain(
mixed $val,
bool $default = false,
bool $to_null = false
): ?bool {
$boolvar = is_string($val) ?
filter_var(
$val,
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE
) :
(bool)$val;
return $boolvar === null && !$to_null ? $default : $boolvar;
}
}
// __END__

View File

@@ -0,0 +1,70 @@
<?php
/*
* html convert functions
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Html
{
public const SELECTED = 0;
public const CHECKED = 1;
/**
* full wrapper for html entities
*
* @param mixed $string string to html encode
* @return mixed if string, encoded, else as is (eg null)
*/
public static function htmlent(mixed $string): mixed
{
if (is_string($string)) {
return htmlentities($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
} else {
return $string;
}
}
/**
* strips out all line breaks or replaced with given string
* @param string $string string
* @param string $replace replace character, default ' '
* @return string cleaned string without any line breaks
*/
public static function removeLB(string $string, string $replace = ' '): string
{
return str_replace(["\n\r", "\r", "\n"], $replace, $string);
}
/**
* returns 'checked' or 'selected' if okay
* $needle is a var, $haystack an array or a string
* **** THE RETURN: VALUE WILL CHANGE TO A DEFAULT NULL IF NOT FOUND ****
*
* @param array<mixed>|string $haystack (search in) haystack can be
* an array or a string
* @param string $needle needle (search for)
* @param int $type type: 0: returns selected, 1,
* returns checked
* @return ?string returns checked or selected,
* else returns null
*/
public static function checked(array|string $haystack, string $needle, int $type = 0): ?string
{
if (is_array($haystack)) {
if (in_array($needle, $haystack)) {
return $type ? 'checked' : 'selected';
}
} else {
if ($haystack == $needle) {
return $type ? 'checked' : 'selected';
}
}
return null;
}
}
// __END__

View File

@@ -0,0 +1,97 @@
<?php
/*
* Converts a json string to array and stores error for later checking
* can also return empty array on demand
* and self set json as is on error as array
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Json
{
/** @var int */
private static $json_last_error;
/**
* converts a json string to an array
* or inits an empty array on null string
* or failed convert to array
* In ANY case it will ALWAYS return array.
* Does not throw errors
*
* @param string|null $json a json string, or null data
* @param bool $override if set to true, then on json error
* set original value as array
* @return array<mixed> returns an array from the json values
*/
public static function jsonConvertToArray(?string $json, bool $override = false): array
{
if ($json !== null) {
$_json = json_decode($json, true);
if (self::$json_last_error = json_last_error()) {
if ($override == true) {
// init return as array with original as element
$json = [$json];
} else {
$json = [];
}
} else {
$json = $_json;
}
} else {
$json = [];
}
// be sure that we return an array
return (array)$json;
}
/**
* returns human readable string for json errors thrown in jsonConvertToArray
*
* @param bool $return_string [default=false] if set to true
* it will return the message string and not
* the error number
* @return int|string Either error number (0 for no error)
* or error string ('' for no error)
*/
public static function jsonGetLastError(bool $return_string = false): int|string
{
$json_error_string = '';
// valid errors as of php 8.0
switch (self::$json_last_error) {
case JSON_ERROR_NONE:
$json_error_string = '';
break;
case JSON_ERROR_DEPTH:
$json_error_string = 'Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$json_error_string = 'Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$json_error_string = 'Unexpected control character found';
break;
case JSON_ERROR_SYNTAX:
$json_error_string = 'Syntax error, malformed JSON';
break;
case JSON_ERROR_UTF8:
$json_error_string = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
case JSON_ERROR_INVALID_PROPERTY_NAME:
$json_error_string = 'A key starting with \u0000 character was in the string';
break;
case JSON_ERROR_UTF16:
$json_error_string = 'Single unpaired UTF-16 surrogate in unicode escape';
break;
default:
$json_error_string = 'Unknown error';
break;
}
return $return_string === true ? $json_error_string : self::$json_last_error;
}
}
// __END__

View File

@@ -0,0 +1,61 @@
<?php
/*
* various math related function wrappers
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Math
{
/**
* some float numbers will be rounded up even if they have no decimal entries
* this function fixes this by pre-rounding before calling ceil
*
* @param float $number number to round
* @param int|integer $precision intermediat round up decimals (default 10)
* @return float correct ceil number
*/
public static function fceil(float $number, int $precision = 10): float
{
return ceil(round($number, $precision));
}
/**
* round inside an a number, not the decimal part only
* eg 48767 with -2 -> 48700
*
* @param float $number number to round
* @param int $precision negative number for position in number (default -2)
* @return float rounded number
*/
public static function floorp(float $number, int $precision = -2): float
{
// if precision is requal or larger than the number length,
// set precision to length -1
if (abs($precision) >= strlen((string)$number)) {
$precision = (strlen((string)$number) - 1) * -1;
}
$mult = pow(10, $precision); // Can be cached in lookup table
return floor($number * $mult) / $mult;
}
/**
* inits input to 0, if value is not numeric
*
* @param string|int|float $number string or number to check
* @return float if not number, then returns 0, else original input
*/
public static function initNumeric(string|int|float $number): float
{
if (!is_numeric($number)) {
return 0;
} else {
return (float)$number;
}
}
}
// __END__

View File

@@ -0,0 +1,85 @@
<?php
/*
* Translates a mime id string into the actual application or file name
* for example 'text/plain' will output 'Text file'
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class MimeAppName
{
/** @var array<string,string> */
private static $mime_apps = [];
/**
* constructor: init mime list
*/
public function __construct()
{
self::$mime_apps = [
// zip
'application/zip' => 'Zip File',
// Powerpoint
'application/vnd.ms-powerpoint' => 'Microsoft Powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'Microsoft Powerpoint',
// PDF
'pplication/pdf' => 'PDF',
// JPEG
'image/jpeg' => 'JPEG',
// PNG
'image/png' => 'PNG',
// Indesign
'application/x-indesign' => 'Adobe InDesign',
// Photoshop
'image/vnd.adobe.photoshop' => 'Adobe Photoshop',
'application/photoshop' => 'Adobe Photoshop',
// Illustrator
'application/illustrator' => 'Adobe Illustrator',
// Word
'application/vnd.ms-word' => 'Microsoft Word',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'Microsoft Word',
// Excel
'application/vnd.ms-excel' => 'Microsoft Excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'Microsoft Excel',
// plain text
'text/plain' => 'Text file',
// html
'text/html' => 'HTML',
// mp4 (max 45MB each)
'video/mpeg' => 'MPEG Video'
];
}
/**
* Sets or updates a mime type
*
* @param string $mime MIME Name, no validiation
* @param string $app Applicaiton name
* @return void
*/
public static function mimeSetAppName(string $mime, string $app): void
{
// if empty, don't set
if (empty($mime) || empty($app)) {
return;
}
self::$mime_apps[$mime] = $app;
}
/**
* get the application name from mime type
* if not set returns "Other file"
*
* @param string $mime MIME Name
* @return string Application name matching
*/
public static function mimeGetAppName(string $mime): string
{
return self::$mime_apps[$mime] ?? 'Other file';
}
}
// __END__

View File

@@ -0,0 +1,71 @@
<?php
/*
* alternate for header mime encode to void problems with long strings and
* spaces/strange encoding problems.
* Orignal issues during PHP5/7
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class MimeEncode
{
/**
* wrapper function for mb mime convert
* for correct conversion with long strings
*
* @param string $string string to encode
* @param string $encoding target encoding
* @param string $line_break default line break is \r\n
* @return string encoded string
*/
public static function __mbMimeEncode(
string $string,
string $encoding,
string $line_break = "\r\n"
): string {
$current_internal_encoding = mb_internal_encoding();
// set internal encoding, so the mimeheader encode works correctly
mb_internal_encoding($encoding);
// if a subject, make a work around for the broken mb_mimencode
$pos = 0;
// after 36 single bytes characters,
// if then comes MB, it is broken
// has to 2 x 36 < 74 so the mb_encode_mimeheader
// 74 hardcoded split does not get triggered
$split = 36;
$_string = '';
while ($pos < mb_strlen($string, $encoding)) {
$output = mb_strimwidth($string, $pos, $split, "", $encoding);
$pos += mb_strlen($output, $encoding);
// if the strinlen is 0 here, get out of the loop
if (!mb_strlen($output, $encoding)) {
$pos += mb_strlen($string, $encoding);
}
$_string_encoded = mb_encode_mimeheader($output, $encoding);
// only make linebreaks if we have mime encoded code inside
// the space only belongs in the second line
if ($_string && preg_match("/^=\?/", $_string_encoded)) {
$_string .= $line_break . " ";
} elseif (
// hack for plain text with space at the end
mb_strlen($output, $encoding) == $split &&
mb_substr($output, -1, 1, $encoding) == " "
) {
// if output ends with space, add one more
$_string_encoded .= " ";
}
$_string .= $_string_encoded;
}
// strip out any spaces BEFORE a line break
$string = str_replace(" " . $line_break, $line_break, $_string);
// before we end, reset internal encoding
mb_internal_encoding($current_internal_encoding);
// return mime encoded string
return $string;
}
}
// __END__

View File

@@ -0,0 +1,136 @@
<?php
/*
* Run is_<type> checks and return default value if not this type
* This will return a default value as always what is expected and never null
* Use this for santize output from multi return functions where we know what
* will come back
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
use CoreLibs\Convert\Extends\SetVarTypeMain;
class SetVarType extends Extends\SetVarTypeMain
{
/**
* Check is input is string, if not return default string.
* Will always return string
*
* @param mixed $val Input value
* @param string $default Default override value
* @return string Input value or default as string
*/
public static function setStr(mixed $val, string $default = ''): string
{
return (string)SetVarTypeMain::setStrMain($val, $default, false);
}
/**
* Convert input to string if possible.
* Will only work on string/int/float/bool/null types
* Will always return string
*
* @param mixed $val Input value
* @param string $default Default override value
* @return string Input value as string or default as string
*/
public static function makeStr(mixed $val, string $default = ''): string
{
return (string)SetVarTypeMain::makeStrMain($val, $default, false);
}
/**
* Check if input is int, if not return default int value 0.
* Will always return int.
*
* @param mixed $val Input value
* @param int $default Default override value
* @return int Input value or default as int
*/
public static function setInt(mixed $val, int $default = 0): int
{
return (int)SetVarTypeMain::setIntMain($val, $default, false);
}
/**
* Convert intput to int if possible, if not return default value 0.
* Will always return int.
*
* @param mixed $val Input value
* @param int $default Default override value
* @return int Input value as int or default as int
*/
public static function makeInt(mixed $val, int $default = 0): int
{
return (int)SetVarTypeMain::makeIntMain($val, $default, false);
}
/**
* Check if input is float, if not return default value value 0.0.
* Will always return float
*
* @param mixed $val Input value
* @param float $default Default override value
* @return float Input value or default as float
*/
public static function setFloat(mixed $val, float $default = 0.0): float
{
return (float)SetVarTypeMain::setFloatMain($val, $default, false);
}
/**
* Convert input to float, if not possible return default value 0.0.
* Will always return float
*
* @param mixed $val Input value
* @param float $default Default override value
* @return float Input value as float or default as float
*/
public static function makeFloat(mixed $val, float $default = 0.0): float
{
return (float)SetVarTypeMain::makeFloatMain($val, $default, false);
}
/**
* Check if input is array, if not return default empty array.
* Will always return array.
*
* @param mixed $val Input value
* @param array<mixed> $default Default override value
* @return array<mixed> Input value or default as array
*/
public static function setArray(mixed $val, array $default = []): array
{
return (array)SetVarTypeMain::setArrayMain($val, $default, false);
}
/**
* Check if input is bool, if not will return default value false.
* Will aways return bool.
*
* @param mixed $val Input value
* @param bool $default Default override value
* @return bool Input value or default as bool
*/
public static function setBool(mixed $val, bool $default = false): bool
{
return (bool)SetVarTypeMain::setBoolMain($val, $default, false);
}
/**
* Convert anything to bool
*
* @param mixed $val Input value
* @param bool $default Default override value
* @return bool Input value as bool or default as bool
*/
public static function makeBool(mixed $val, bool $default = false): bool
{
return (bool)SetVarTypeMain::makeBoolMain($val, $default, false);
}
}
// __END__

View File

@@ -0,0 +1,130 @@
<?php
/*
* Run is_<type> checks and return default value if not this type
* This will return default null on invalid entries
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
use CoreLibs\Convert\Extends\SetVarTypeMain;
class SetVarTypeNull extends Extends\SetVarTypeMain
{
/**
* Check is input is string, if not return default string.
* Will return null if no string as default.
*
* @param mixed $val Input value
* @param string|null $default Default override value
* @return string|null Input value or default as string/null
*/
public static function setStr(mixed $val, ?string $default = null): ?string
{
return SetVarTypeMain::setStrMain($val, $default, true);
}
/**
* Convert input to string if possible.
* Will only work on string/int/float/bool/null types.
* Will return null if convert failed as default.
*
* @param mixed $val Input value
* @param string|null $default Default override value
* @return string|null Input value as string or default as string/null
*/
public static function makeStr(mixed $val, string $default = null): ?string
{
return SetVarTypeMain::makeStrMain($val, $default, true);
}
/**
* Check if input is int, if not return default value null.
*
* @param mixed $val Input value
* @param int|null $default Default override value
* @return int|null Input value or default as int/null
*/
public static function setInt(mixed $val, ?int $default = null): ?int
{
return SetVarTypeMain::setIntMain($val, $default, true);
}
/**
* Convert intput to int if possible, if not return default value value null.
*
* @param mixed $val Input value $val
* @param int|null $default Default override value
* @return int|null Input value as int or default as int/null
*/
public static function makeInt(mixed $val, int $default = null): ?int
{
return SetVarTypeMain::makeIntMain($val, $default, true);
}
/**
* Check if input is float, if not return default value value null.
*
* @param mixed $val Input value $val
* @param float|null $default Default override value
* @return float|null Input value or default as float/null
*/
public static function setFloat(mixed $val, ?float $default = null): ?float
{
return SetVarTypeMain::setFloatMain($val, $default, true);
}
/**
* Convert input to float, if not possible return default value null.
*
* @param mixed $val Input value $val
* @param float|null $default Default override value
* @return float|null Input value as float or default as float/null
*/
public static function makeFloat(mixed $val, float $default = null): ?float
{
return SetVarTypeMain::makeFloatMain($val, $default, true);
}
/**
* Check if input is array, if not return default value null.
*
* @param mixed $val Input value $val
* @param array<mixed>|null $default Default override value
* @return array<mixed>|null Input value or default as array/null
*/
public static function setArray(mixed $val, ?array $default = null): ?array
{
return SetVarTypeMain::setArrayMain($val, $default, true);
}
/**
* Check if input is bool, if not will return default value null.
*
* @param mixed $val Input value $val
* @param bool|null $default Default override value
* @return bool|null Input value or default as bool/null
*/
public static function setBool(mixed $val, ?bool $default = null): ?bool
{
return SetVarTypeMain::setBoolMain($val, $default, true);
}
/**
* Convert anything to bool
*
* @param mixed $val Input value $val
* @return bool|null Input value as bool or default as bool/null
*/
public static function makeBool(mixed $val): ?bool
{
// note that the default value here is irrelevant, we return null
// on unsetable string var
return SetVarTypeMain::makeBoolMain($val, false, true);
}
}
// __END__

View File

@@ -0,0 +1,123 @@
<?php
/*
* string convert and transform functions
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Strings
{
/**
* return the number of elements in the split list
* 0 if nothing / invalid split
* 1 if no split character found
* n for the numbers in the split list
*
* @param string $split_format
* @param string $split_characters
* @return int
*/
public static function countSplitParts(
string $split_format,
string $split_characters = '-'
): int {
if (
empty($split_format) ||
// non valid characters inside, abort
!preg_match("/^[0-9" . $split_characters . "]/", $split_format) ||
preg_match('/[^\x20-\x7e]/', $split_characters)
) {
return 0;
}
$split_list = preg_split(
// allowed split characters
"/([" . $split_characters . "]{1})/",
$split_format
);
if (!is_array($split_list)) {
return 0;
}
return count(array_filter($split_list));
}
/**
* split format a string base on a split format string
* split format string is eg
* 4-4-4 that means 4 characters DASH 4 characters DASH 4 characters
* So a string in the format of
* ABCD1234EFGH will be ABCD-1234-EFGH
* Note a string LONGER then the maxium will be attached with the LAST
* split character. In above exmaple
* ABCD1234EFGHTOOLONG will be ABCD-1234-EFGH-TOOLONG
*
* @param string $value string value to split
* @param string $split_format split format
* @param string $split_characters list of charcters with which we split
* if not set uses dash ('-')
* @return string split formatted string or original value if not chnaged
*/
public static function splitFormatString(
string $value,
string $split_format,
string $split_characters = '-'
): string {
if (
// abort if split format is empty
empty($split_format) ||
// if not in the valid ASCII character range for any of the strings
preg_match('/[^\x20-\x7e]/', $value) ||
// preg_match('/[^\x20-\x7e]/', $split_format) ||
preg_match('/[^\x20-\x7e]/', $split_characters) ||
// only numbers and split characters in split_format
!preg_match("/[0-9" . $split_characters . "]/", $split_format)
) {
return $value;
}
// split format list
$split_list = preg_split(
// allowed split characters
"/([" . $split_characters . "]{1})/",
$split_format,
-1,
PREG_SPLIT_DELIM_CAPTURE
);
// if this is false, or only one array, abort split
if (!is_array($split_list) || count($split_list) == 1) {
return $value;
}
$out = '';
$pos = 0;
$last_split = '';
foreach ($split_list as $offset) {
if (is_numeric($offset)) {
$_part = substr($value, $pos, (int)$offset);
if (empty($_part)) {
break;
}
$out .= $_part;
$pos += (int)$offset;
} elseif ($pos) { // if first, do not add
$out .= $offset;
$last_split = $offset;
}
}
if (!empty($out) && $pos < strlen($value)) {
$out .= $last_split . substr($value, $pos);
}
// if last is not alphanumeric remove, remove
if (!strcspn(substr($out, -1, 1), $split_characters)) {
$out = substr($out, 0, -1);
}
// overwrite only if out is set
if (!empty($out)) {
return $out;
} else {
return $value;
}
}
}
// __END__

View File

@@ -0,0 +1,291 @@
<?php
/*
* Create email class
*/
declare(strict_types=1);
namespace CoreLibs\Create;
/**
* sending simple text emails
*/
class Email
{
/** @var array<string> allowed list for encodings that can do KV folding */
private static $encoding_kv_allowed = [
'UTF-8',
'EUC-JP',
'SJIS',
'SJIS-win',
'ISO-2022-JP',
'ISO-2022-JP-MS',
'JIS',
'JIS-ms',
];
/** @var string normaly this does not need to be changed */
private static $mb_convert_kana_mode = 'KV';
/**
* create mime encoded email part for to/from emails.
* If encoding is not UTF-8 it will convert the email name to target encoding
* FROM UTF-8
* Source data is ALWAYS seen as utf-8
*
* @param string $email E-Mail address
* @param string $email_name Name for the email address, in UTF-8, if not set, empty
* @param string $encoding Encoding, if not set UTF-8
* @param bool $kv_folding If set to true and a valid encoding, do KV folding
* @return string Correctly encoded and build email string
*/
public static function encodeEmailName(
string $email,
string $email_name = '',
string $encoding = 'UTF-8',
bool $kv_folding = false
): string {
if (empty($email_name)) {
return $email;
}
// if encoding is not UTF-8 then we convert
if ($encoding != 'UTF-8') {
$email_name = mb_convert_encoding($email_name, $encoding, 'UTF-8');
}
$email_name =
mb_encode_mimeheader(
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
mb_convert_kana(
$email_name,
self::$mb_convert_kana_mode,
$encoding
) :
$email_name,
$encoding
);
return '"' . $email_name . '" '
. '<' . (string)$email . '>';
}
/**
* Subject/Body replace sub function
*
* @param string $subject Subject string, in UTF-8
* @param string $body Body string, in UTF-8
* @param array<string,string> $replace Replace the array as key -> value, in UTF-8
* @param string $encoding Encoding for subject encode mime header
* @param bool $kv_folding If set to true and a valid encoding,
* do KV folding
* @return array<string> Pos 0: Subject, Pos 1: Body
*/
private static function replaceContent(
string $subject,
string $body,
array $replace,
string $encoding,
bool $kv_folding
): array {
foreach (['subject', 'body'] as $element) {
$$element = str_replace(
array_map(
function ($key) {
return '{' . $key . '}';
},
array_keys($replace)
),
array_values($replace),
$$element
);
}
// if encoding is NOT UTF-8 convert to target
if ($encoding != 'UTF-8') {
$subject = mb_convert_encoding($subject, $encoding, 'UTF-8');
$body = mb_convert_encoding($body, $encoding, 'UTF-8');
}
// we need to encodde the subject
$subject = mb_encode_mimeheader(
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
// for any non UTF-8 encoding convert kana
mb_convert_kana(
$subject,
self::$mb_convert_kana_mode,
$encoding
) :
$subject,
$encoding
);
return [$subject, $body];
}
/**
* Send plain text email with possible to replace subject/body data
* either global or per to email set.
* replace to tags are in {} in the subject or body
*
* @param string $subject Mail subject, mandatory, in UTF-8
* @param string $body Mail body, mandatory, in UTF-8
* @param string $from_email From email, mandatory
* @param string $from_name From email name, in UTF-8
* if empty '' then not set
* @param array<mixed> $send_to_emails to email or array for email/replace
* If array: name/email/replace[key,value]
* name and replace must be in UTF-8
* At least one must be set
* @param array<string,string> $replace_content Subject/Body replace as
* search -> replace, in UTF-8
* @param string $encoding E-Mail encoding, default UTF-8
* @param bool $kv_folding If set to true and a valid encoding,
* do KV folding
* @param bool $test test flag, default off
* @param \CoreLibs\Debug\Logging|null $log Logging class,
* only used if test flag is true
* @return int 2 test only, no sent
* 1 for ok,
* 0 for send not ok
* -1 for nothing set (emails, subject, body)
* -2 for empty to list
* -3 encoding target not valid or not installed
*/
public static function sendEmail(
string $subject,
string $body,
string $from_email,
string $from_name,
array $send_to_emails,
array $replace_content = [],
string $encoding = 'UTF-8',
bool $kv_folding = false,
bool $test = false,
?\CoreLibs\Debug\Logging $log = null
): int {
/** @var array<string> */
$to_emails = [];
/** @var array<string,array<string,string>> */
$to_replace = [];
/** @var string */
$out_subject = $subject;
/** @var string */
$out_body = $body;
// check basic set
if (empty($subject) || empty($body) || empty($from_email)) {
return -1;
}
if (
$encoding != 'UTF-8' &&
!in_array($encoding, mb_list_encodings())
) {
return -3;
}
// if not one valid to, abort
foreach ($send_to_emails as $to_email) {
// to_email can be string, then only to email
// else expect 'email' & 'name'
if (
is_array($to_email) &&
isset($to_email['email'])
) {
$_to_email = self::encodeEmailName(
$to_email['email'],
$to_email['name'] ?? '',
$encoding,
$kv_folding
);
$to_emails[] = $_to_email;
// if we have to replacement, this override replace content
if (isset($to_email['replace']) && count($to_email['replace'])) {
// merge with original replace content,
// to data will override original data
$to_replace[$_to_email] = array_merge(
$replace_content,
$to_email['replace']
);
}
} elseif (is_string($to_email)) {
$to_emails[] = $to_email;
}
}
if (!count($to_emails)) {
return -2;
}
// the email headers needed
$headers = [
'From' => self::encodeEmailName($from_email, $from_name, $encoding),
'Content-type' => "text/plain; charset=" . $encoding,
'MIME-Version' => "1.0",
];
// if we have a replace string, we need to do replace run
// only if there is no dedicated to replace
// also run replace if there is nothing to replace at all
// this will mime encode the subject
if (!count($to_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$replace_content,
$encoding,
$kv_folding
);
}
$mail_delivery_status = 1;
// send the email
foreach ($to_emails as $to_email) {
// default mail status is success
$mail_status = true;
// if there is a to replace, if not use the original replace content
if (count($to_replace)) {
$_replace = [];
if (!empty($to_replace[$to_email])) {
$_replace = $to_replace[$to_email];
} elseif (count($replace_content)) {
$_replace = $replace_content;
}
if (count($_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$_replace,
$encoding,
$kv_folding
);
}
}
// if we are in test mode, do not send an email and set status to 2
if ($test === false) {
$mail_status = mail($to_email, $out_subject, $out_body, $headers);
} else {
$mail_delivery_status = 2;
}
// log if an log instance exists
if ($log instanceof \CoreLibs\Debug\Logging) {
// build debug strings: convert to UTF-8 if not utf-8
$log->debug('SEND EMAIL', 'HEADERS: ' . $log->prAr($headers) . ', '
. 'ENCODING: ' . $encoding . ', '
. 'KV FOLDING: ' . $log->prBl($kv_folding) . ', '
. 'TO: ' . $to_email . ', '
. 'SUBJECT: ' . $out_subject . ', '
. 'BODY: ' . ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding)));
$log->debug('SEND EMAIL JSON', json_encode([
'encoding' => $encoding,
'kv_folding' => $kv_folding,
'header' => $headers,
'to' => $to_email,
'subject' => $out_subject,
'body' => ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding))
]) ?: '{}');
}
if (!$mail_status) {
$mail_delivery_status = 0;
}
}
return $mail_delivery_status;
}
}
// __END__

View File

@@ -0,0 +1,122 @@
<?php
/*
* hash wrapper functions for old problem fixes
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class Hash
{
public const STANDARD_HASH_LONG = 'ripemd160';
public const STANDARD_HASH_SHORT = 'adler32';
/**
* checks php version and if >=5.2.7 it will flip the string
* can return empty string if none of string sets work
* hash returns false
* preg_replace fails for older php version
* Use __hash with crc32b or hash('crc32b', ...) for correct output
*
* @param string $string string to crc
* @return string crc32b hash (old type)
*/
public static function __crc32b(string $string): string
{
// do normal hash crc32b
$string = hash('crc32b', $string);
// if bigger than 5.2.7, we need to "unfix" the fix
if (\CoreLibs\Check\PhpVersion::checkPHPVersion('5.2.7')) {
// flip it back to old (two char groups)
$string = preg_replace("/^([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})$/", "$4$3$2$1", $string);
}
if (!is_string($string)) {
$string = '';
}
return $string;
}
/**
* replacement for __crc32b call
*
* @param string $string string to hash
* @param bool $use_sha use sha instead of crc32b (default false)
* @return string hash of the string
*/
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);
} else {
return self::__crc32b($string);
}
}
/**
* replacemend for __crc32b call (alternate)
* defaults to adler 32
* allowed crc32b, adler32, fnv132, fnv1a32, joaat
* all that create 8 char long hashes
*
* @param string $string string to hash
* @param string $hash_type hash type (default adler32)
* @return string hash of the string
*/
public static function __hash(
string $string,
string $hash_type = self::STANDARD_HASH_SHORT
): string {
if (
!in_array(
$hash_type,
['crc32b', 'adler32', 'fnv132', 'fnv1a32', 'joaat']
)
) {
$hash_type = 'adler32';
}
return hash($hash_type, $string);
}
/**
* Wrapper function for standard long hashd
*
* @param string $string String to be hashed
* @return string Hashed string
*/
public static function __hashLong(string $string): string
{
return hash(self::STANDARD_HASH_LONG, $string);
}
/**
* create a unique id with the standard hash type defined in __hash
*
* @return string Unique ID with fixed length of 8 characters
* @deprecated Use \CoreLibs\Create\Uids::uniqIdShort() instead
*/
public static function __uniqId(): string
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, '
. '\CoreLibs\Create\Uids::uniqIdShort() class', E_USER_DEPRECATED);
return \CoreLibs\Create\Uids::uniqIdShort();
}
/**
* create a unique id with the standard long hash type
* defined in __hashLong
*
* @return string Unique ID with length of current default long hash
* @deprecated Use \CoreLibs\Create\Uids::uniqIdLong() instead
*/
public static function __uniqIdLong(): string
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, '
. '\CoreLibs\Create\Uids::uniqIdLong() class', E_USER_DEPRECATED);
return \CoreLibs\Create\Uids::uniqIdLong();
}
}
// __END__

View File

@@ -0,0 +1,120 @@
<?php
/*
* random key functions
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class RandomKey
{
// key generation
/** @var string */
private static $key_range = '';
/** @var int */
private static $one_key_length;
/** @var int */
private static $key_length = 4; // default key length
/** @var int */
private static $max_key_length = 256; // max allowed length
/**
* if launched as class, init random key data first
*/
public function __construct()
{
$this->initRandomKeyData();
}
/**
* sets the random key range with the default values
*
* @return void has no return
*/
private static function initRandomKeyData(): void
{
// random key generation base string
self::$key_range = join('', array_merge(
range('A', 'Z'),
range('a', 'z'),
range('0', '9')
));
self::$one_key_length = strlen(self::$key_range);
}
/**
* validates they key length for random string generation
*
* @param int $key_length key length
* @return bool true for valid, false for invalid length
*/
private static function validateRandomKeyLenght(int $key_length): bool
{
if (
$key_length > 0 &&
$key_length <= self::$max_key_length
) {
return true;
} else {
return false;
}
}
/**
* sets the key length and checks that they key given is valid
* if failed it will not change the default key length and return false
*
* @param int $key_length key length
* @return bool true/false for set status
*/
public static function setRandomKeyLength(int $key_length): bool
{
// only if valid int key with valid length
if (self::validateRandomKeyLenght($key_length) === true) {
self::$key_length = $key_length;
return true;
} else {
return false;
}
}
/**
* get the current set random key length
*
* @return int Current set key length
*/
public static function getRandomKeyLength(): int
{
return self::$key_length;
}
/**
* creates a random key based on the key_range with key_length
* if override key length is set, it will check on valid key and use this
* this will not set the class key length variable
*
* @param int $key_length key length override, -1 for use default
* @return string random key
*/
public static function randomKeyGen(int $key_length = -1): string
{
// init random key strings if not set
if (!is_numeric(self::$one_key_length)) {
self::initRandomKeyData();
}
$use_key_length = 0;
// only if valid int key with valid length
if (self::validateRandomKeyLenght($key_length) === true) {
$use_key_length = $key_length;
} else {
$use_key_length = self::$key_length;
}
// create random string
$random_string = '';
for ($i = 1; $i <= $use_key_length; $i++) {
$random_string .= self::$key_range[random_int(0, self::$one_key_length - 1)];
}
return $random_string;
}
}

View File

@@ -0,0 +1,372 @@
<?php
/*
* AUTHOR: Clemens Schwaighofer
* DESCRIPTION:
* start a php sesseion
* name can be given via startSession parameter
* if not set tries to read $SET_SESSION_NAME from global
* else will use default set in php.ini
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class Session
{
/** @var string list for errors */
private $session_intern_error_str = '';
/**
* 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
*/
public function __construct(string $session_name = '')
{
if (!empty($session_name)) {
$this->startSession($session_name);
}
}
/**
* Start session
* startSession should be called for complete check
* If this is called without any name set before the php.ini name is
* used.
*
* @return void
*/
protected 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
*
* @return bool True if we are in a CLI enviroment, or false for everything else
*/
public function checkCliStatus(): bool
{
return \CoreLibs\Get\System::checkCLI();
}
/**
* Set session name call. If not valid session name, will return false
*
* @param string $session_name A valid string for session name
* @return bool True if session name is valid,
* False if not
*/
public function setSessionName(string $session_name): bool
{
if (!$this->checkValidSessionName($session_name)) {
return false;
}
session_name($session_name);
return true;
}
/**
* Return set error string, empty if none set
* Error strings are only set in the startSession method
*
* @return string Last error string
*/
public function getErrorStr(): string
{
return $this->session_intern_error_str;
}
/**
* check if session name is valid
*
* As from PHP 8.1/8.0/7.4 error
* INVALID CHARS: =,; \t\r\n\013\014
* NOTE: using . will fail even thought valid
* we allow only alphanumeric with - (dash) and 1 to 128 characters
*
* @param string $session_name any string, not null
* @return bool True for valid, False for invalid
*/
public static function checkValidSessionName(string $session_name): bool
{
// check
if (
// must only have those
!preg_match('/^[-a-zA-Z0-9]{1,128}$/', $session_name) ||
// cannot be only numbers
preg_match('/^[0-9]+$/', $session_name)
) {
return false;
}
return true;
}
/**
* start 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
*/
public function startSession(?string $session_name = null): string|bool
{
// we can't start sessions on command line
if ($this->checkCliStatus()) {
$this->session_intern_error_str = '[SESSION] No sessions in php cli';
return false;
}
// if session are OFF
if ($this->getSessionStatus() === PHP_SESSION_DISABLED) {
$this->session_intern_error_str = '[SESSION] Sessions are disabled';
return false;
}
// 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)) {
$this->session_intern_error_str = '[SESSION] Invalid session name: ' . $session_name;
return false;
}
$this->setSessionName($session_name);
}
// start session
$this->startSessionCall();
}
// if we still have no active session
if (!$this->checkActiveSession()) {
$this->session_intern_error_str = '[SESSION] Failed to activate session';
return false;
}
if (false === ($session_id = $this->getSessionId())) {
$this->session_intern_error_str = '[SESSION] getSessionId did not return a session id';
}
return $session_id;
}
/**
* get current set session id or false if none started
*
* @return string|bool
*/
public function getSessionId(): string|bool
{
return session_id();
}
/**
* get set session name or false if none started
*
* @return string|bool
*/
public function getSessionName(): string|bool
{
return session_name();
}
/**
* Checks if there is an active session.
* Does not check if we can have a session
*
* @return bool True if there is an active session, else false
*/
public function checkActiveSession(): bool
{
if ($this->getSessionStatus() === PHP_SESSION_ACTIVE) {
return true;
} else {
return false;
}
}
/**
* unlock the session file, so concurrent AJAX requests can be done
* NOTE: after this has been called, no changes in _SESSION will be stored
* NOTE: a new session with a different name can be started after this one is called
* if problem, run ob_flush() and flush() too
*
* @return bool True und sucess, false on failure
*/
public function writeClose(): bool
{
return session_write_close();
}
/**
* Proper destroy a session
* - unset the _SESSION array
* - unset cookie if cookie on and we have not strict mode
* - destroy session
*
* @return bool
*/
public function sessionDestroy(): bool
{
$_SESSION = [];
if (
ini_get('session.use_cookies') &&
!ini_get('session.use_strict_mode')
) {
$session_name = $this->getSessionName();
if ($session_name === false) {
$session_name = '';
}
$params = session_get_cookie_params();
setcookie(
(string)$session_name,
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}
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
/**
* unset all _SESSION entries
*
* @return void
*/
public function unsetAllS(): void
{
foreach (array_keys($_SESSION ?? []) as $name) {
unset($_SESSION[$name]);
}
}
/**
* set _SESSION entry 'name' with any value
*
* @param string|int $name array name in _SESSION
* @param mixed $value value to set (can be anything)
* @return void
*/
public function setS(string|int $name, mixed $value): void
{
$_SESSION[$name] = $value;
}
/**
* 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
*/
public function getS(string|int $name): mixed
{
return $_SESSION[$name] ?? '';
}
/**
* 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
*/
public function issetS(string|int $name): bool
{
return isset($_SESSION[$name]);
}
/**
* unset one _SESSION entry 'name' if exists
*
* @param string|int $name _SESSION key name to remove
* @return void
*/
public function unsetS(string|int $name): 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]);
}
}
}
// __END__

View File

@@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Create;
class Uids
{
// what to use as a default hash if non ise set and no DEFAULT_HASH is defined
public const DEFAULT_HASH = 'sha256';
public const STANDARD_HASH_LONG = 'ripemd160';
public const STANDARD_HASH_SHORT = 'adler32';
/**
* creates psuedo random uuid v4
* Code take from class here:
* https://www.php.net/manual/en/function.uniqid.php#94959
*
* @return string pseudo random uuid v4
*/
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)
);
}
/**
* TODO: make this a proper uniq ID creation
* add uuidv4 subcall to the uuid function too
* creates a uniq id
*
* @param string $type uniq id type, currently md5 or sha256 allowed
* if not set will use DEFAULT_HASH if set
* @return string uniq id
*/
public static function uniqId(string $type = ''): string
{
$uniq_id = '';
switch ($type) {
case 'md5':
$uniq_id = md5(uniqid((string)rand(), true));
break;
case self::DEFAULT_HASH:
$uniq_id = hash(self::DEFAULT_HASH, uniqid((string)rand(), true));
break;
case self::STANDARD_HASH_LONG:
$uniq_id = hash(self::STANDARD_HASH_LONG, uniqid((string)rand(), true));
break;
case self::STANDARD_HASH_SHORT:
$uniq_id = hash(self::STANDARD_HASH_SHORT, uniqid((string)rand(), true));
break;
default:
// if not empty, check if in valid list
if (
!empty($type) &&
in_array($type, hash_algos())
) {
$hash = $type;
} else {
// fallback to default hash type if none set or invalid
$hash = self::DEFAULT_HASH;
}
$uniq_id = hash($hash, uniqid((string)rand(), true));
break;
}
return $uniq_id;
}
/**
* create a unique id with the standard hash type defined in __hash
*
* @return string Unique ID with fixed length of 8 characters
*/
public static function uniqIdShort(): string
{
return self::uniqId(self::STANDARD_HASH_SHORT);
}
/**
* create a unique id with the standard long hash type
* defined in __hashLong
*
* @return string Unique ID with length of current default long hash
*/
public static function uniqIdLong(): string
{
return self::uniqId(self::STANDARD_HASH_LONG);
}
}
// __END__

View File

@@ -0,0 +1,654 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2002/12/17
* VERSION: 1.0.0
* RELEASED LICENSE: GNU GPL 3
* SHORT DESC :RIPTION:
* DB Array IO Class:
* writes, reads or deletes a complete array (one data set) in/out a
* table from the connected DB.
* you don't have to write any SQL queries, worry over update/insert
*
* HISTORY:
* 2019/9/11 (cs) error string 21->1021, 22->1022 for not overlapping with IO
* 2005/07/07 (cs) updated array class for postgres: set 0 & NULL if int field given, insert uses () values () syntax
* 2005/03/31 (cs) fixed the class call with all debug vars
* 2003-03-10: error_ids where still wrong chagned 11->21 and 12->22
* 2003-02-26: db_array_io is no longer single class but extens db_io,
* as it needs it anyway
* moved the class info vars into class_info array into
* the constructor, removed info function
* 2003-02-24: in db_delete moved query build to top, or pk_name/value
* will be reset before delete is done
* 2002-12-20: just added info() method
* 2002-12-17: splitted the class from other file (with main db wrapper)
*********************************************************************/
// picture upload should be taken out from here and out in media_class
// as it actually has nothing to do with this one here ? (or at least
// put into separete function in this class)
declare(strict_types=1);
namespace CoreLibs\DB\Extended;
// subclass for one array handling
class ArrayIO extends \CoreLibs\DB\IO
{
// main calss variables
/** @var array<mixed> */
public $table_array; // the array from the table to work on
/** @var string */
public $table_name; // the table_name
/** @var string */
public $pk_name = ''; // the primary key from this table
/** @var int|string|null */
public $pk_id; // the PK id
// security values
/** @var int base acl for current page */
private $base_acl_level = 0;
/**
* constructor for the array io class, set the
* primary key name automatically (from array)
*
* @param array<mixed> $db_config db connection config
* @param array<mixed> $table_array table array config
* @param string $table_name table name string
* @param \CoreLibs\Debug\Logging|null $log Logging class, default set if not set
* @param int $base_acl_level Set base acl level, if needed
* @param int $acl_admin Flag if this is an admin ACL access level
*/
public function __construct(
array $db_config,
array $table_array,
string $table_name,
\CoreLibs\Debug\Logging $log = null,
int $base_acl_level = 0,
int $acl_admin = 0
) {
// instance db_io class
parent::__construct($db_config, $log ?? new \CoreLibs\Debug\Logging());
// more error vars for this class
$this->error_string['1999'] = 'No table array or table name set';
$this->error_string['1021'] = 'No Primary Key given';
$this->error_string['1022'] = 'Could not run Array Query';
$this->table_array = $table_array;
$this->table_name = $table_name;
// error abort if no table array or no table name
if (empty($table_array) || empty($table_name)) {
$this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing');
}
// set primary key for given table_array
foreach ($this->table_array as $key => $value) {
if (!empty($value['pk'])) {
$this->pk_name = $key;
}
}
$this->dbArrayIOSetAcl($base_acl_level, $acl_admin);
}
/**
* class deconstructor
*/
public function __destruct()
{
parent::__destruct();
}
/**
* set the base acl level and admin acl flag
* This is needed for table array ACL checks
* if not set I assume 0 (non write/non read/non admin)
*
* @param int $base_acl_level ACL Level from 0 to 100, -1 is not allowed
* Will sett 0 if invalid
* @param int $acl_admin 0 for non admin, 1 for admin (base acl is 100)
* @return void
*/
public function dbArrayIOSetAcl(int $base_acl_level, int $acl_admin): void
{
// default not allowed, must be 0 at least
if ($base_acl_level < 0) {
$base_acl_level = 0;
}
// only 0 or 1 allowed
if (!in_array($acl_admin, [0, 1])) {
$acl_admin = 0;
}
// if the user is admin flagged, auto set to 100, if not already set to 100
if ($acl_admin == 1) {
$base_acl_level = 100;
}
$this->base_acl_level = $base_acl_level;
}
/**
* changes all previously alterd HTML code into visible one,
* works for <b>,<i>, and <a> (thought <a> can be / or should
* be handled with the magic links functions
* used with the read function
*
* @param string $text any html encoded string
* @return string decoded html string
*/
public function convertData(string $text): string
{
$text = str_replace('&lt;b&gt;', '<b>', $text);
$text = str_replace('&lt;/b&gt;', '</b>', $text);
$text = str_replace('&lt;i&gt;', '<i>', $text);
$text = str_replace('&lt;/i&gt;', '</i>', $text);
// my need a change
$text = str_replace('&lt;a href=&quot;', '<a target="_blank" href="', $text);
$text = str_replace('&quot;&gt;', '">', $text);
$text = str_replace('&lt;/a&gt;', '</a>', $text);
return $text;
}
/**
* changeds all HTML entities into non HTML ones
*
* @param string $text encoded html string
* @return string decoded html string
*/
public function convertEntities(string $text): string
{
$text = str_replace('&lt;', '<', $text);
$text = str_replace('&gt;', '>', $text);
$text = str_replace('&amp;', '&', $text);
$text = str_replace('&quot;', '"', $text);
$text = str_replace('&#039;', "'", $text);
return $text;
}
/**
* dumps the current data
*
* @param bool $write write to error message, default false
* @return string the array data as html string entry
*/
public function dbDumpArray(bool $write = false): string
{
reset($this->table_array);
$string = '';
foreach ($this->table_array as $column => $data_array) {
$string .= '<b>' . $column . '</b> -> ' . $data_array['value'] . '<br>';
}
// add output to internal error_msg
if ($write === true) {
$this->__dbDebug('dbArray', $string);
}
return $string;
}
/**
* checks if pk is set and if not, set from pk_id and
* if this also not set return 0
*
* @return bool true if pk value is set, else false
*/
public function dbCheckPkSet(): bool
{
// if pk_id is set, overrule ...
if ($this->pk_id) {
$this->table_array[$this->pk_name]['value'] = $this->pk_id;
}
// if not set ... produce error
if (!$this->table_array[$this->pk_name]['value']) {
// if no PK found, error ...
$this->__dbError(1021);
return false;
} else {
return true;
}
}
/**
* resets the whole array values
* @param bool $reset_pk true if we want to reset the pk too
* @return void has no return
*/
public function dbResetArray(bool $reset_pk = false): void
{
reset($this->table_array);
foreach (array_keys($this->table_array) as $column) {
if (!$this->table_array[$column]['pk']) {
unset($this->table_array[$column]['value']);
} elseif ($reset_pk) {
unset($this->table_array[$column]['value']);
}
}
}
/**
* deletes one dataset
*
* @param array<mixed> $table_array optional override for table array set
* set this as new table array too
* @param bool $acl_limit [false], if set to true, well do ACL limit check
* @return array<mixed> returns the table array that was deleted
*/
public function dbDelete(array $table_array = [], bool $acl_limit = false): array
{
// is array and has values, override set and set new
if (is_array($table_array) && count($table_array)) {
$this->table_array = $table_array;
}
if (!$this->dbCheckPkSet()) {
return $this->table_array;
}
if ($acl_limit === true && $this->base_acl_level < 100) {
$this->log->debug('DB DELETE ERROR', 'ACL Limit on, Delete, '
. 'but base ACL level of 100 not met: ' . $this->base_acl_level);
return $this->table_array;
}
// delete query
$q = 'DELETE FROM ' . $this->table_name . ' WHERE ';
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
// delete files and build FK query
reset($this->table_array);
$q_where = '';
foreach (array_keys($this->table_array) as $column) {
// suchen nach bildern und lschen ...
if (
!empty($this->table_array[$column]['file']) &&
file_exists($this->table_array[$column]['url'] . $this->table_array[$column]['value'])
) {
if (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
unlink($this->table_array[$column]['path'] . $this->table_array[$column]['value']);
}
$file_name = str_replace('_tn', '', $this->table_array[$column]['value']);
if (file_exists($this->table_array[$column]['path'] . $file_name)) {
unlink($this->table_array[$column]['path'] . $file_name);
}
}
// if we have a foreign key
if (!empty($this->table_array[$column]['fk'])) {
// create FK constraint checks
if ($q_where) {
$q_where .= ' AND ';
}
$q_where .= $column . ' = ' . $this->table_array[$column]['value'];
}
// allgemeines zurcksetzen des arrays
unset($this->table_array[$column]['value']);
}
// attach fk row if there ...
if ($q_where) {
$q .= ' AND ' . $q_where;
}
// if 0, error
$this->pk_id = null;
if (!$this->dbExec($q)) {
$this->__dbError(1022);
}
return $this->table_array;
}
/**
* reads one row into the array
*
* @param bool $edit on true convert data, else as is
* @param array<mixed> $table_array optional table array, overwrites
* internal set array
* @return array<mixed> set table array with values
*/
public function dbRead(bool $edit = false, array $table_array = []): array
{
// if array give, overrules internal array
if (is_array($table_array) && count($table_array)) {
$this->table_array = $table_array;
}
if (!$this->dbCheckPkSet()) {
return $this->table_array;
}
reset($this->table_array);
$q_select = '';
$q_where = '';
// create select part & addition FK part
foreach ($this->table_array as $column => $data_array) {
if ($q_select) {
$q_select .= ', ';
}
if (
!empty($data_array['type']) && $data_array['type'] == 'datetime' &&
!empty($data_array['sql_read'])
) {
// convert tom different timestamp type
$q_select .= "TO_CHAR($column, '" . $data_array['sql_read'] . "') AS $column";
} else {
$q_select .= $column;
}
// check FK ...
if (
isset($this->table_array[$column]['fk']) &&
isset($this->table_array[$column]['value'])
) {
if (!empty($q_where)) {
$q_where .= ' AND ';
}
$q_where .= $column .= ' = ' . $this->table_array[$column]['value'];
}
}
$q = 'SELECT ';
$q .= $q_select;
$q .= ' FROM ' . $this->table_name . ' WHERE ';
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
if ($q_where) {
$q .= ' AND ' . $q_where;
}
// if query was executed okay, else set error
if ($this->dbExec($q)) {
if (is_array($res = $this->dbFetchArray())) {
reset($this->table_array);
foreach ($this->table_array as $column => $data_array) {
// wenn "edit" dann gib daten wie in DB zurück, ansonten aufbereiten fr ausgabe
// ?? sollte das nicht drauen ??? man weis ja net was da drin steht --> is noch zu berlegen
// $this->log->debug('DB READ', 'EDIT: $edit | Spalte: $column | type: '
// .$this->table_array[$column]['type'].' | Res: '.$res[$column]);
if ($edit) {
$this->table_array[$column]['value'] = $res[$column];
// if password, also write to hidden
if (
isset($this->table_array[$column]['type']) &&
$this->table_array[$column]['type'] == 'password'
) {
$this->table_array[$column]['HIDDEN_value'] = $res[$column];
}
} else {
$this->table_array[$column]['value'] = $this->convertData(nl2br($res[$column]));
// had to put out the htmlentities from the line above as it breaks japanese characters
}
}
}
// possible dbFetchArray errors ...
$this->pk_id = $this->table_array[$this->pk_name]['value'];
} else {
$this->__dbError(1022);
}
return $this->table_array;
}
/**
* writes one set into DB or updates one set (if PK exists)
*
* @param bool $addslashes old convert entities and set set escape
* @param array<mixed> $table_array optional table array, overwrites internal one
* @param bool $acl_limit [false], if set to true, well do ACL limit check
* @return array<mixed> table array or null
*/
public function dbWrite(
bool $addslashes = false,
array $table_array = [],
bool $acl_limit = false
): array {
if (count($table_array)) {
$this->table_array = $table_array;
}
// PK ID check
// if ($this->pk_id && !$this->table_array[$this->pk_name]["value"]) {
// $this->table_array[$this->pk_name]["value"]=$this->pk_id;
// }
// checken ob PKs gesetzt, wenn alle -> update, wenn keiner -> insert, wenn ein paar -> ERROR!
if (!$this->table_array[$this->pk_name]['value']) {
$insert = 1;
} else {
$insert = 0;
}
// early abort for new write with not enough ACL level
if ($insert && $acl_limit === true && $this->base_acl_level < 100) {
$this->log->debug('DB WRITE ERROR', 'ACL Limit on, Insert, '
. 'but base ACL level of 100 not met: ' . $this->base_acl_level);
return $this->table_array;
}
reset($this->table_array);
$q_data = '';
$q_vars = '';
$q_where = '';
foreach ($this->table_array as $column => $data_array) {
/********************************* START FILE *************************************/
// file upload
if (isset($this->table_array[$column]['file'])) {
// falls was im tmp drinnen, sprich ein upload, datei kopieren, Dateinamen in db schreiben
// falls datei schon am server (physischer pfad), dann einfach url in db schreiben (update)
// falls in 'delete' 'ja' dann loeschen (und gibts eh nur beim update)
if ($this->table_array[$column]['delete']) {
unset($this->table_array[$column]['delete']);
if (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
unlink($this->table_array[$column]['path'] . $this->table_array[$column]['value']);
}
$file_name = str_replace('_tn', '', $this->table_array[$column]['value']);
if (file_exists($this->table_array[$column]['path'] . $file_name)) {
unlink($this->table_array[$column]['path'] . $file_name);
}
$this->table_array[$column]['value'] = '';
} else {
if ($this->table_array[$column]['tmp'] != 'none' && $this->table_array[$column]['tmp']) {
// mozilla, patch
$fn_name = explode('/', $this->table_array[$column]['dn']);
$this->table_array[$column]['dn'] = $fn_name[count($fn_name) - 1];
$filename_parts = explode('.', $this->table_array[$column]['dn']);
$ext = end($filename_parts);
array_splice($filename_parts, -1, 1);
$name = str_replace(' ', '_', implode('.', $filename_parts));
$file_name = $name . '.' . $ext;
//echo 'Dn: $file_name';
copy($this->table_array[$column]['tmp'], $this->table_array[$column]['path'] . $file_name);
// automatisch thumbnail generieren, geht nur mit convert (ImageMagic!!!), aber nur bei bild ..
if (in_array(strtolower($ext), ['jpeg', 'jpg', 'gif', 'png'])) {
$file_name_tn = $name . '_tn.' . $ext;
$input = $this->table_array[$column]['path'] . $file_name;
$output = $this->table_array[$column]['path'] . $file_name_tn;
$com = 'convert -geometry 115 ' . $input . ' ' . $output;
exec($com);
$this->table_array[$column]['value'] = $file_name_tn;
} else {
$this->table_array[$column]['value'] = $file_name;
}
} elseif (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
// mach gar nix, wenn bild schon da ???
}
} // delete or upload
} // file IF
/********************************* END FILE **************************************/
// do not write 'pk' (primary key) or 'view' values
// also do not write UPDATE for elements that are
// acl flagged, not if we have an ACL limiter, don't insert
// $this->log->debug('DB WRITE', 'C: ' . $column . ', '
// . 'ACL Level ' . $this->log->prBl($acl_limit) . ', '
// . 'TA ACL: ' . ($this->table_array[$column]['min_edit_acl'] ?? 100) . ', '
// . 'Base ACL: ' . $this->base_acl_level);
if (
!isset($this->table_array[$column]['pk']) &&
isset($this->table_array[$column]['type']) &&
$this->table_array[$column]['type'] != 'view' &&
strlen($column) > 0 &&
// no acl limiter
(
$acl_limit === false ||
// acl limit is true, min edit must be at larger than set
$this->base_acl_level >=
($this->table_array[$column]['min_edit_acl'] ?? 100)
)
) {
// for password use hidden value if main is not set
if (
isset($this->table_array[$column]['type']) &&
$this->table_array[$column]['type'] == 'password' &&
empty($this->table_array[$column]['value'])
) {
$this->table_array[$column]['value'] = $this->table_array[$column]['HIDDEN_value'];
}
if (!$insert) {
if (strlen($q_data)) {
$q_data .= ', ';
}
$q_data .= $column . ' = ';
} else {
// this is insert
if (strlen($q_data)) {
$q_data .= ', ';
}
if (strlen($q_vars)) {
$q_vars .= ', ';
}
$q_vars .= $column;
}
// integer is different
if (isset($this->table_array[$column]['int']) || isset($this->table_array[$column]['int_null'])) {
$this->log->debug('WRITE CHECK', '[' . $column . '][' . $this->table_array[$column]['value'] . ']'
. '[' . $this->table_array[$column]['type'] . '] '
. 'VALUE SET: ' . (string)isset($this->table_array[$column]['value'])
. ' | INT NULL: ' . (string)isset($this->table_array[$column]['int_null']));
if (
isset($this->table_array[$column]['value']) &&
!$this->table_array[$column]['value'] &&
isset($this->table_array[$column]['int_null'])
) {
$_value = 'NULL';
} elseif (
!isset($this->table_array[$column]['value']) ||
(isset($this->table_array[$column]['value']) && !$this->table_array[$column]['value'])
) {
$_value = 0;
} else {
$_value = $this->table_array[$column]['value'];
}
$q_data .= $_value;
} elseif (isset($this->table_array[$column]['bool'])) {
// bool storage (reverse check on ifset)
$q_data .= "'" . $this->dbBoolean($this->table_array[$column]['value'], true) . "'";
} elseif (
isset($this->table_array[$column]['interval']) ||
isset($this->table_array[$column]['date']) ||
isset($this->table_array[$column]['datetime']) ||
isset($this->table_array[$column]['emptynull'])
) {
// for interval we check if no value, then we set null
if (
!isset($this->table_array[$column]['value']) ||
(isset($this->table_array[$column]['value']) && !$this->table_array[$column]['value'])
) {
$_value = 'NULL';
} elseif (isset($this->table_array[$column]['value'])) {
$_value = $this->dbEscapeLiteral($this->table_array[$column]['value']);
} else {
// fallback
$_value = 'NULL';
}
$q_data .= $_value;
} else {
// if the error check is json, we set field to null if NOT set
// else normal string write
if (
isset($this->table_array[$column]['error_check']) &&
$this->table_array[$column]['error_check'] == 'json' &&
(
!isset($this->table_array[$column]['value']) ||
(isset($this->table_array[$column]['value']) &&
!$this->table_array[$column]['value'])
)
) {
$q_data .= 'NULL';
} else {
// normal string
$q_data .= "'";
// if add slashes do convert & add slashes else write AS is
if ($addslashes) {
$q_data .= $this->dbEscapeString(
$this->convertEntities($this->table_array[$column]['value'])
);
} else {
$q_data .= $this->dbEscapeString($this->table_array[$column]['value']);
}
$q_data .= "'";
}
}
}
} // while ...
if (empty($q_data)) {
$this->log->debug('DB WRITE ERROR', 'No data to write, possible through ACL');
return $this->table_array;
}
// NOW get PK, and FK settings (FK only for update query)
// 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) {
// check FK ...
if (
isset($this->table_array[$column]['fk']) &&
isset($this->table_array[$column]['value'])
) {
if (!empty($q_where)) {
$q_where .= ' AND ';
}
$q_where .= $column .= ' = ' . $this->table_array[$column]['value'];
}
}
// if no PK set, then get max ID from DB
if (!$this->table_array[$this->pk_name]['value']) {
// max id, falls INSERT
$q = 'SELECT MAX(' . $this->pk_name . ') + 1 AS pk_id FROM ' . $this->table_name;
if (is_array($res = $this->dbReturnRow($q))) {
$pk_id = $res['pk_id'];
} else {
$pk_id = 1;
}
$this->table_array[$this->pk_name]['value'] = $pk_id;
}
if (!$insert) {
$q = 'UPDATE ' . $this->table_name . ' SET ';
$q .= $q_data;
$q .= ' WHERE ';
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
if (!empty($q_where)) {
$q .= ' AND ' . $q_where;
}
// set pk_id ... if it has changed or so
$this->pk_id = $this->table_array[$this->pk_name]['value'];
} else {
$q = 'INSERT INTO ' . $this->table_name . ' ';
$q .= '(' . $q_vars . ') ';
$q .= 'VALUES (' . $q_data . ')';
// write primary key too
// if ($q_data)
// $q .= ", ";
// $q .= $this->pk_name." = ".$this->table_array[$this->pk_name]['value']." ";
// $this->pk_id = $this->table_array[$this->pk_name]['value'];
}
// return success or not
if (!$this->dbExec($q)) {
$this->__dbError(1022);
}
// set primary key
if ($insert) {
$insert_id = $this->dbGetInsertPK();
if (is_array($insert_id)) {
$insert_id = 0;
}
$this->table_array[$this->pk_name]['value'] = $insert_id;
$this->pk_id = $insert_id;
}
// return the table if needed
return $this->table_array;
}
// end of class
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,305 @@
<?php
/**
* Intrface for all SQL\* functions
*/
declare(strict_types=1);
namespace CoreLibs\DB\SQL\Interface;
interface SqlFunctions
{
/**
* Undocumented function
*
* @return bool
*/
public function __dbLastErrorQuery(): bool;
/**
* Undocumented function
*
* @param string $query
* @return \PgSql\Result|false
*/
public function __dbQuery(string $query): \PgSql\Result|false;
/**
* Undocumented function
*
* @param string $query
* @param array<mixed> $params
* @return \PgSql\Result|false
*/
public function __dbQueryParams(string $query, array $params): \PgSql\Result|false;
/**
* Undocumented function
*
* @param string $query
* @return bool
*/
public function __dbSendQuery(string $query): bool;
/**
* Undocumented function
*
* @return \PgSql\Result|false
*/
public function __dbGetResult(): \PgSql\Result|false;
/**
* Undocumented function
*
* @return void
*/
public function __dbClose(): void;
/**
* Undocumented function
*
* @param string $name
* @param string $query
* @return \PgSql\Result|false
*/
public function __dbPrepare(string $name, string $query): \PgSql\Result|false;
/**
* Undocumented function
*
* @param string $name
* @param array<mixed> $data
* @return \PgSql\Result|false
*/
public function __dbExecute(string $name, array $data): \PgSql\Result|false;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @return int
*/
public function __dbNumRows(\PgSql\Result|false $cursor): int;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @return int
*/
public function __dbNumFields(\PgSql\Result|false $cursor): int;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @param int $i
* @return string|false
*/
public function __dbFieldName(\PgSql\Result|false $cursor, int $i): string|false;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @param int $result_type
* @return array<mixed>|bool
*/
public function __dbFetchArray(\PgSql\Result|false $cursor, int $result_type = PGSQL_BOTH);
/**
* Undocumented function
*
* @param bool $assoc_type
* @return int
*/
public function __dbResultType(bool $assoc_type = true): int;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @return array<mixed>|bool
*/
public function __dbFetchAll(\PgSql\Result|false $cursor): array|bool;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @return int
*/
public function __dbAffectedRows(\PgSql\Result|false $cursor): int;
/**
* Undocumented function
*
* @param string $query
* @param string|null $pk_name
* @return string|int|false
*/
public function __dbInsertId(string $query, ?string $pk_name): string|int|false;
/**
* Undocumented function
*
* @param string $table
* @param string $schema
* @return string|bool
*/
public function __dbPrimaryKey(string $table, string $schema = ''): string|bool;
/**
* Undocumented function
*
* @param string $db_host
* @param string $db_user
* @param string $db_pass
* @param string $db_name
* @param int $db_port
* @param string $db_ssl
* @return \PgSql\Connection|false
*/
public function __dbConnect(
string $db_host,
string $db_user,
string $db_pass,
string $db_name,
int $db_port,
string $db_ssl = 'allow'
): \PgSql\Connection|false;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @return string
*/
public function __dbPrintError(\PgSql\Result|false $cursor = false): string;
/**
* Undocumented function
*
* @param string $table
* @param bool $extended
* @return array<mixed>|bool
*/
public function __dbMetaData(string $table, bool $extended = true): array|bool;
/**
* Undocumented function
*
* @param string|int|float|bool $string
* @return string
*/
public function __dbEscapeString(string|int|float|bool $string): string;
/**
* Undocumented function
*
* @param string|int|float|bool $string
* @return string
*/
public function __dbEscapeLiteral(string|int|float|bool $string): string;
/**
* Undocumented function
*
* @param string $string
* @return string
*/
public function __dbEscapeIdentifier(string $string): string;
/**
* Undocumented function
*
* @param string $data
* @return string
*/
public function __dbEscapeBytea(string $data): string;
/**
* Undocumented function
*
* @param string $bytea
* @return string
*/
public function __dbUnescapeBytea(string $bytea): string;
/**
* Undocumented function
*
* @return bool
*/
public function __dbConnectionBusy(): bool;
/**
* Undocumented function
*
* @param int $timeout_seconds
* @return bool
*/
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool;
/**
* Undocumented function
*
* @return string
*/
public function __dbVersion(): string;
/**
* Undocumented function
*
* @param string $array_text
* @param int $start
* @param int|null $end
* @return array<mixed>|null
*/
public function __dbArrayParse(
string $array_text,
int $start = 0,
?int &$end = null
): ?array;
/**
* Undocumented function
*
* @param string $show_string
* @return string
*/
public function __dbShow(string $show_string): string;
/**
* Undocumented function
*
* @param string $db_schema
* @return int
*/
public function __dbSetSchema(string $db_schema): int;
/**
* Undocumented function
*
* @return string
*/
public function __dbGetSchema(): string;
/**
* Undocumented function
*
* @param string $db_encoding
* @return int
*/
public function __dbSetEncoding(string $db_encoding): int;
/**
* Undocumented function
*
* @return string
*/
public function __dbGetEncoding(): string;
}
// __END__

View File

@@ -0,0 +1,883 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2003/04/09
* SHORT DESCRIPTION:
* 2018/3/23, the whole class system is transformed to namespaces
* also all internal class calls are converted to camel case
*
* pgsql wrapper calls
*
* HISTORY:
* 2008/04/16 (cs) wrapper for pg escape string
* 2007/01/11 (cs) add prepare/execute for postgres
* 2006/09/12 (cs) in case db_query retuns false, save the query and
* run the query through the send/get procedure to get
* correct error data from the db
* 2006/06/26 (cs) added port for db connection
* 2006/04/03 (cs) added meta data for table
* 2005/07/25 (cs) removed the plural s remove, not needed and not 100% working
* 2005/07/07 (cs) the default it is table_name _ id
* 2005/01/19 (cs) changed the pgsql connect, so it dies if it can't connect to the DB
* 2004/09/30 (cs) layout cleanup
*
*
* collection of PostgreSQL wrappers
*
* pg_prepare
* pg_execute
* pg_num_rows
* pg_num_fields
* pg_field_name
* pg_affected_rows (*)
* pg_fetch_array
* pg_query
* pg_send_query
* pg_get_result
* pg_connection_busy
* pg_close
* pg_connect (*)
* pg_meta_data
* pg_escape_string
*
*/
declare(strict_types=1);
namespace CoreLibs\DB\SQL;
// 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 */
class PgSQL implements Interface\SqlFunctions
{
/** @var string */
private $last_error_query;
/** @var \PgSql\Connection|false */
private $dbh;
/**
* queries last error query and returns true or false if error was set
*
* @return bool true/false if last error is set
*/
public function __dbLastErrorQuery(): bool
{
if ($this->last_error_query) {
return true;
} else {
return false;
}
}
/**
* wrapper for pg_query, catches error and stores it in class var
*
* @param string $query Query string
* @return \PgSql\Result|false query result
*/
public function __dbQuery(string $query): \PgSql\Result|false
{
$this->last_error_query = '';
if (is_bool($this->dbh)) {
return false;
}
// read out the query status and save the query if needed
$result = pg_query($this->dbh, $query);
if ($result === false) {
$this->last_error_query = $query;
}
return $result;
}
/**
* Proposed
* wrapperf or pg_query_params for queries in the style of
* SELECT foo FROM bar WHERE foobar = $1
*
* @param string $query Query string with placeholders $1, ..
* @param array<mixed> $params Matching parameters for each placerhold
* @return \PgSql\Result|false Query result
*/
public function __dbQueryParams(string $query, array $params): \PgSql\Result|false
{
$this->last_error_query = '';
if (is_bool($this->dbh)) {
return false;
}
// parse query and get all $n entries
// TODO count of $n must match params
// read out the query status and save the query if needed
$result = pg_query_params($this->dbh, $query, $params);
if ($result === false) {
$this->last_error_query = $query;
}
return $result;
}
/**
* sends an async query to the server
*
* @param string $query query string
* @return bool true/false if query was sent successful
*/
public function __dbSendQuery(string $query): bool
{
if (is_bool($this->dbh)) {
return false;
}
$result = pg_send_query($this->dbh, $query);
return $result ? true : false;
}
/**
* wrapper for pg_get_result
*
* @return \PgSql\Result|false resource handler or false for error
*/
public function __dbGetResult(): \PgSql\Result|false
{
$this->last_error_query = '';
if (is_bool($this->dbh)) {
return false;
}
$result = pg_get_result($this->dbh);
if ($result === false) {
return false;
}
if ($error = pg_result_error($result)) {
$this->last_error_query = $error;
}
return $result;
}
/**
* wrapper for pg_close
*
* @return void has no return
*/
public function __dbClose(): void
{
if (is_bool($this->dbh)) {
return;
}
if (pg_connection_status($this->dbh) === PGSQL_CONNECTION_OK) {
// in 8.1 this throws an error, and we don't need that anyway
// pg_close($this->dbh);
}
}
/**
* wrapper for pg_prepare
*
* @param string $name statement name
* @param string $query query string
* @return \PgSql\Result|false prepare statement handler or
* false for error
*/
public function __dbPrepare(string $name, string $query): \PgSql\Result|false
{
if (is_bool($this->dbh)) {
return false;
}
$result = pg_prepare($this->dbh, $name, $query);
if (!$result) {
$this->last_error_query = $query;
}
return $result;
}
/**
* wrapper for pg_execute for running a prepared statement
*
* @param string $name statement name
* @param array<mixed> $data data array
* @return \PgSql\Result|false returns status or false for error
*/
public function __dbExecute(string $name, array $data): \PgSql\Result|false
{
if (is_bool($this->dbh)) {
return false;
}
$result = pg_execute($this->dbh, $name, $data);
if (!$result) {
$this->last_error_query = $name;
}
return $result;
}
/**
* wrapper for pg_num_rows
*
* @param \PgSql\Result|false $cursor cursor
* @return int number of rows, -1 on error
*/
public function __dbNumRows(\PgSql\Result|false $cursor): int
{
if (is_bool($cursor)) {
return -1;
}
return pg_num_rows($cursor);
}
/**
* wrapper for pg_num_fields
*
* @param \PgSql\Result|false $cursor cursor
* @return int number for fields in result, -1 on error
*/
public function __dbNumFields(\PgSql\Result|false $cursor): int
{
if (is_bool($cursor)) {
return -1;
}
return pg_num_fields($cursor);
}
/**
* wrapper for pg_field_name
*
* @param \PgSql\Result|false $cursor cursor
* @param int $i field position
* @return string|false name or false on error
*/
public function __dbFieldName(\PgSql\Result|false $cursor, int $i): string|false
{
if (is_bool($cursor)) {
return false;
}
return pg_field_name($cursor, $i);
}
/**
* wrapper for pg_fetch_array
* if through/true false, use __dbResultType(true)
*
* @param \PgSql\Result|false $cursor cursor
* @param int $result_type result type as int number
* @return array<mixed>|false array result data or false on end/error
*/
public function __dbFetchArray(\PgSql\Result|false $cursor, int $result_type = PGSQL_BOTH): array|false
{
if (is_bool($cursor)) {
return false;
}
// result type is passed on as is [should be checked]
return pg_fetch_array($cursor, null, $result_type);
}
/**
* simple match up between assoc true/false
*
* @param bool $assoc_type true (default) for PGSQL_ASSOC, false for PGSQL_BOTH
* @return int valid result type for fetch array
*/
public function __dbResultType(bool $assoc_type = true): int
{
if ($assoc_type == true) {
return PGSQL_ASSOC;
}
// fallback to default
return PGSQL_BOTH;
}
/**
* wrapper for pg_fetch_all
*
* @param \PgSql\Result|false $cursor cursor
* @return array<mixed>|false data array or false for end/error
*/
public function __dbFetchAll(\PgSql\Result|false $cursor): array|false
{
if (is_bool($cursor)) {
return false;
}
return pg_fetch_all($cursor);
}
/**
* wrapper for pg_affected_rows
*
* @param \PgSql\Result|false $cursor cursor
* @return int affected rows, 0 for none, -1 for error
*/
public function __dbAffectedRows(\PgSql\Result|false $cursor): int
{
if (is_bool($cursor)) {
return -1;
}
return pg_affected_rows($cursor);
}
/**
* reads the last inserted primary key for the query
* if there is no pk_name tries to auto built it from the table name
* this only works if db schema is after no plural names
* and pk name is table name + _id
*
* detects schema prefix in table name
* @param string $query query string
* @param string|null $pk_name primary key name, if '' then auto detect
* @return string|int|false primary key value
*/
public function __dbInsertId(string $query, ?string $pk_name): string|int|false
{
// only if an insert has been done
if (preg_match("/^insert /i", $query)) {
$schema = '';
// get table name from insert
$array = explode(' ', $query);
$_table = $array[2];
// if there is a dot inside, we need to split
if (strstr($_table, '.')) {
list($schema, $table) = explode('.', $_table);
} else {
$table = $_table;
}
// 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";
// 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))) {
list($id) = $res;
} else {
return false;
}
} else {
$id = [-1, $q];
}
return $id;
} else {
//if not insert, return false
return false;
}
}
/**
* queries database for the primary key name to this table in the selected schema
*
* @param string $table table name
* @param string $schema optional schema name, '' for default
* @return string|bool primary key name or false if not found
*/
public function __dbPrimaryKey(string $table, string $schema = ''): string|bool
{
if ($table) {
// check if schema set is different from schema given,
// only needed if schema is not empty
$table_prefix = '';
if ($schema) {
$search_path = $this->__dbGetSchema();
if ($search_path != $schema) {
$table_prefix = $schema . '.';
}
}
// 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 ";
if ($schema) {
$q .= ", pg_namespace ";
}
$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);
if ($cursor !== false) {
$__db_fetch_array = $this->__dbFetchArray($cursor);
if (!is_array($__db_fetch_array)) {
return false;
}
return $__db_fetch_array['column_name'] ?? false;
} else {
return false;
}
} else {
return false;
}
}
/**
* wrapper for pg_connect, writes out failure to screen if error occurs (hidden var)
*
* @param string $db_host host name
* @param string $db_user user name
* @param string $db_pass password
* @param string $db_name databse name
* @param integer $db_port port (int, 5432 is default)
* @param string $db_ssl SSL (allow is default)
* @return \PgSql\Connection|false db handler or false on error
*/
public function __dbConnect(
string $db_host,
string $db_user,
string $db_pass,
string $db_name,
int $db_port,
string $db_ssl = 'allow'
): \PgSql\Connection|false {
if (empty($db_name)) {
return false;
}
// if there is no host, leave it empty, this will try default unix path
// same for port (defaults to 5432 if not set)
// must set is db name
// if no user name, db name is used
$connection_string = [];
if (!empty($db_host)) {
$connection_string[] = 'host=' . $db_host;
}
if (!empty($db_port)) {
$connection_string[] = 'port=' . $db_port;
}
if (!empty($db_user)) {
$connection_string[] = 'user=' . $db_user;
}
if (!empty($db_pass)) {
$connection_string[] = 'password=' . $db_pass;
}
// we must have at least db name set
$connection_string[] = 'dbname=' . $db_name;
if (!empty($db_ssl)) {
$connection_string[] = 'sslmode=' . $db_ssl;
}
// connect
$this->dbh = pg_connect(join(' ', $connection_string));
return $this->dbh;
}
/**
* reads the last error for this cursor and returns
* html formatted string with error name
*
* @param \PgSql\Result|false $cursor cursor
* or null
* @return string error string
*/
public function __dbPrintError(\PgSql\Result|false $cursor = false): string
{
if (is_bool($this->dbh)) {
return '';
}
// run the query again for the error result here
if ((is_bool($cursor)) && $this->last_error_query) {
pg_send_query($this->dbh, $this->last_error_query);
$this->last_error_query = '';
$cursor = pg_get_result($this->dbh);
}
if ($cursor && $error_str = pg_result_error($cursor)) {
return '-PostgreSQL-Error- '
. $error_str;
} else {
return '';
}
}
/**
* wrapper for pg_meta_data
*
* @param string $table table name
* @param bool $extended show extended info (default true)
* @return array<mixed>|bool array data for the table info or false on error
*/
public function __dbMetaData(string $table, bool $extended = true): array|bool
{
if (is_bool($this->dbh)) {
return false;
}
// needs to prefixed with @ or it throws a warning on not existing table
return @pg_meta_data($this->dbh, $table, $extended);
}
/**
* wrapper for pg_escape_string
*
* @param string|int|float|bool $string any string/int/float/bool
* @return string excaped string
*/
public function __dbEscapeString(string|int|float|bool $string): string
{
if (is_bool($this->dbh)) {
return '';
}
return pg_escape_string($this->dbh, (string)$string);
}
/**
* wrapper for pg_escape_literal
* difference to escape string is that this one adds quotes ('') around
* the string too
*
* @param string|int|float|bool $string any string/int/float/bool
* @return string excaped string including quites
*/
public function __dbEscapeLiteral(string|int|float|bool $string): string
{
if (is_bool($this->dbh)) {
return (string)'';
}
// for phpstan, thinks this is string|false?
return (string)pg_escape_literal($this->dbh, (string)$string);
}
/**
* wrapper for pg_escape_identifier
* Only used for table names, column names
*
* @param string $string any string
* @return string escaped string
*/
public function __dbEscapeIdentifier(string $string): string
{
if (is_bool($this->dbh)) {
return '';
}
// for phpstan, thinks this is string|false?
return (string)pg_escape_identifier($this->dbh, (string)$string);
}
/**
* wrapper for pg_escape_byte
*
* @param string $data data stream
* @return string escaped bytea string
*/
public function __dbEscapeBytea(string $data): string
{
if (is_bool($this->dbh)) {
return '';
}
return pg_escape_bytea($this->dbh, $data);
}
/**
* unescape bytea data from postgesql
*
* @param string $bytea Bytea data stream
* @return string Unescaped bytea data
*/
public function __dbUnescapeBytea(string $bytea): string
{
return pg_unescape_bytea($bytea);
}
/**
* wrapper for pg_connection_busy
*
* @return bool True if connection is busy
* False if not or no db connection at all
*/
public function __dbConnectionBusy(): bool
{
if (is_bool($this->dbh)) {
return false;
}
return pg_connection_busy($this->dbh);
}
/**
* Experimental wrapper with scoket timetout
*
* @param integer $timeout_seconds Wait how many seconds on timeout
* @return bool True if connection is busy, or false on
* not busy or no db connection at all
*/
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool
{
if (is_bool($this->dbh)) {
return false;
}
$busy = pg_connection_busy($this->dbh);
/** @var array<resource>|null */
$socket = [pg_socket($this->dbh)];
$null = null;
while ($busy) {
// Will wait on that socket until that happens or the timeout is reached
stream_select($socket, $null, $null, $timeout_seconds);
$busy = pg_connection_busy($this->dbh);
}
return $busy;
}
/**
* extended wrapper for pg_version
* can return any setting in this array block
* If no connection, return empty string,
* if not in array return empty string
* On default 'version' will be stripped of any space attached info
* eg 13.5 (other info) will return only 13.5
*
* @param string $parameter Parameter string to extract from array
* @param bool $strip If parameter is server strip out on default
* Set to false to get original string AS is
* @return string The parameter value
*/
public function __dbVersionInfo(string $parameter, bool $strip = true): string
{
if (is_bool($this->dbh)) {
return '';
}
// extract element
$return_string = (string)(pg_version($this->dbh)[$parameter] ?? '');
// for version, strip if requested
if (
in_array($parameter, ['server']) &&
$strip === true
) {
$return_string = explode(' ', $return_string, 2)[0] ?? '';
}
return $return_string;
}
/**
* Returns all parameters that are possible from the db_version
*
* @return array<mixed> Parameter key names from pg_version
*/
public function __dbVersionInfoParameterList(): array
{
if (is_bool($this->dbh)) {
return [];
}
return array_keys(pg_version($this->dbh));
}
/**
* wrapper for pg_version
* Note: this only returns server version
* not connection version OR client version
*
* @return string version string
*/
public function __dbVersion(): string
{
if (is_bool($this->dbh)) {
return '';
}
// array has client, protocol, server, we just return server stripped
return $this->__dbVersionInfo('server', true);
}
/**
* Returns a numeric version eg 90506 or 130006, etc
* Note that this calls a show command on the server
* Note:
* Old version is 9.5.6 where 9.5 is the major version
* Newer Postgresql (10 on) have only one major version so eg 13.5
* is returned as 130005
*
* @return integer Server version
*/
public function __dbVersionNumeric(): int
{
return (int)$this->__dbShow('server_version_num');
}
/**
* NOTE: it is recommended to convert arrays to json and parse the json in
* PHP itself, array_to_json(array)
* This is a fallback for old PostgreSQL versions
* postgresql array to php array
* https://stackoverflow.com/a/27964420
*
* @param string $array_text Array text from PostgreSQL
* @param int $start Start string position
* @param int|null $end End string position from recursive call
* @return array<mixed>|null PHP type array
*/
public function __dbArrayParse(
string $array_text,
int $start = 0,
?int &$end = null
): ?array {
if (empty($array_text) || $array_text[0] != '{') {
return null;
}
$return = [];
$string = false;
$quote = '';
$len = strlen($array_text);
$v = '';
// start from offset
for ($array_pos = $start + 1; $array_pos < $len; $array_pos += 1) {
$ch = $array_text[$array_pos];
// check wher ein the string are we
// end, one down
if (!$string && $ch == '}') {
if ($v !== '' || !empty($return)) {
$return[] = $v;
}
$end = $array_pos;
break;
// open new array, jump recusrive up
} elseif (!$string && $ch == '{') {
// full string + poff set and end
$v = $this->__dbArrayParse($array_text, $array_pos, $array_pos);
// next array element
} elseif (!$string && $ch == ',') {
$return[] = $v;
$v = '';
// flag that this is a string
} elseif (!$string && ($ch == '"' || $ch == "'")) {
$string = true;
$quote = $ch;
// quoted string
} elseif ($string && $ch == $quote && $array_text[$array_pos - 1] == "\\") {
$v = substr($v, 0, -1) . $ch;
} elseif ($string && $ch == $quote && $array_text[$array_pos - 1] != "\\") {
$string = false;
} else {
// build string
$v .= $ch;
}
}
return $return;
}
/**
* Returns any server setting
* if no connection or empty parameter or other error returns false
* else returns a string
*
* @param string $parameter Parameter to query
* @return string|bool Settings value as string
*/
public function __dbParameter(string $parameter): string|bool
{
if (is_bool($this->dbh)) {
return false;
}
if (empty($parameter)) {
return false;
}
return pg_parameter_status($this->dbh, $parameter);
}
/**
* wrapper for any SHOW data blocks
* eg search_path or client_encoding
*
* @param string $show_string Part to show, if invalid will return empty string
* @return string Found part as is
*/
public function __dbShow(string $show_string): string
{
// get search path
$cursor = $this->__dbQuery("SHOW " . $this->__dbEscapeIdentifier($show_string));
// abort on failure and return empty
if ($cursor === false) {
return '';
}
// get result
$db_schema = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
return $db_schema[$show_string] ?? '';
}
/**
* Sets a new database schema/search_path
* Checks if schema exits and if not aborts with error code 2
*
* @param string $db_schema Schema to set
* @return int Returns 0 if no error
* 1 for check query failed
* 2 for schema not found in database
*/
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);
// abort if execution fails
if ($cursor === false) {
return 1;
}
// check if schema does not exists
$row = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
if (empty($row['exists']) || $row['exists'] == 'f') {
return 2;
}
$query = "SET search_path TO " . $this->__dbEscapeLiteral($db_schema);
$this->__dbQuery($query);
return 0;
}
/**
* Returns current set schema/search_path
*
* @return string Search Path as currently set in DB
*/
public function __dbGetSchema(): string
{
return $this->__dbShow('search_path');
}
/**
* set the client encoding
* Returns 0 on set ok, or 3 if the client encoding could not be set
*
* @param string $db_encoding
* @return int Returns 0 for no error
* [not used] 1 for check query failed
* [not used] 2 for invalid client encoding
* 3 client encoding could not be set
*/
public function __dbSetEncoding(string $db_encoding): int
{
// check if ecnoding is valid first
// does not take into account aliases
// TODO lookup with alisaes so eg ShiftJIS does not get a false
// $query = "SELECT EXISTS("
// . "SELECT pg_catalog.pg_encoding_to_char(conforencoding) "
// . "FROM pg_catalog.pg_conversion "
// . "WHERE pg_catalog.pg_encoding_to_char(conforencoding) = "
// . $this->dbEscapeLiteral($db_encoding)
// . ")";
// $cursor = $this->__dbQuery($query);
// if ($cursor === false) {
// return 1;
// }
// $row = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
// if ($row['exists'] == 'f') {
// return 2;
// }
$query = "SET client_encoding TO " . $this->__dbEscapeLiteral($db_encoding);
if ($this->__dbQuery($query) === false) {
return 3;
}
return 0;
}
/**
* Returns current set client encoding
* @return string Client encoding string, empty if not set
*/
public function __dbGetEncoding(): string
{
return $this->__dbShow('client_encoding');
}
}
// __END__

View File

@@ -0,0 +1,99 @@
<?php
/*
* direct write to log file
* must have BASE folder and LOG foder defined
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
class FileWriter
{
/** @var string */
private static $debug_filename = 'debug_file.log'; // where to write output
/** @var string */
private static $debug_folder;
/**
* Set a debug log folder, if not set BASE+LOG folders are set
* if they are defined
* This folder name must exist and must be writeable
*
* @param string $folder Folder name to where the log file will be written
* @return boolean True for valid folder name, False for invalid
*/
public static function fsetFolder(string $folder): bool
{
if (!preg_match("/^[\w\-\/]+/", $folder)) {
return false;
}
if (!is_writeable($folder)) {
return false;
}
// if last is not / then add
if (substr($folder, -1, 1) != DIRECTORY_SEPARATOR) {
$folder .= DIRECTORY_SEPARATOR;
}
self::$debug_folder = $folder;
return true;
}
/**
* set new debug file name
* Must be alphanumeric and underscore only.
* Must end with .log
*
* @param string $filename File name to set
* @return bool True for valid file name, False if invalid
*/
public static function fsetFilename(string $filename): bool
{
// valid file. must be only ascii & _, must end with .log
if (!preg_match("/^[\w\-]+\.log$/", $filename)) {
return false;
}
self::$debug_filename = $filename;
return true;
}
/**
* writes a string to a file immediatly, for fast debug output
*
* @param string $string string to write to the file
* @param boolean $enter default true, if set adds a linebreak \n at the end
* @return bool True for log written, false for not wirrten
*/
public static function fdebug(string $string, bool $enter = true): bool
{
if (empty(self::$debug_filename)) {
return false;
}
// if empty try to set base log folder
if (
empty(self::$debug_folder) &&
defined('BASE') && defined('LOG')
) {
self::$debug_folder = BASE . LOG;
}
if (!is_writeable(self::$debug_folder)) {
return false;
}
$filename = self::$debug_folder . self::$debug_filename;
$fh = fopen($filename, 'a');
if ($fh === false) {
return false;
}
if ($enter === true) {
$string .= "\n";
}
$string = "[" . \CoreLibs\Debug\Support::printTime() . "] "
. "[" . \CoreLibs\Get\System::getPageName(2) . "] - " . $string;
fwrite($fh, $string);
fclose($fh);
return true;
}
}
// __END__

View File

@@ -0,0 +1,881 @@
<?php
/*
* Debug support functions
*
* These are if there is any debug to print out at all at the end
 * debug_output_all - general yes no
 * It's recommended to use the method "debug_for" to turn on of the array vars
 * debug_output - turn on for one level (Array)
 * debug_output_not - turn off for one level (array)
 *
 * Print out the debug at thend of the html
 * echo_output_all
 * echo_output
 * echo_output_not
 *
 * Write debug to file
 * print_output_all
 * print_output
 * print_output_not
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
use CoreLibs\Debug\Support;
use CoreLibs\Create\Uids;
use CoreLibs\Get\System;
use CoreLibs\Convert\Html;
class Logging
{
// options
/** @var array<mixed> */
private $options = [];
// page and host name
/** @var string */
private $page_name;
/** @var string */
private $host_name;
/** @var int */
private $host_port;
// internal error reporting vars
/** @var array<mixed> */
private $error_msg = []; // the "connection" to the outside errors
// debug output prefix
/** @var string */
private $error_msg_prefix = ''; // prefix to the error string (the class name)
// debug flags
/** @var array<mixed> */
private $debug_output = []; // if this is true, show debug on desconstructor
/** @var array<mixed> */
private $debug_output_not = [];
/** @var bool */
private $debug_output_all = false;
/** @var array<mixed> */
private $echo_output = []; // errors: echo out, default is 1
/** @var array<mixed> */
private $echo_output_not = [];
/** @var bool */
private $echo_output_all = false;
/** @var array<mixed> */
private $print_output = []; // errors: print to file, default is 0
/** @var array<mixed> */
private $print_output_not = [];
/** @var bool */
private $print_output_all = false;
// debug flags/settings
/** @var string */
private $running_uid = ''; // unique ID set on class init and used in logging as prefix
// log file name
/** @var string */
private $log_folder = '';
/** @var string */
private $log_file_name_ext = 'log'; // use this for date rotate
/** @var string */
private $log_file_name = '';
/** @var int */
private $log_max_filesize = 0; // set in kilobytes
/** @var string */
private $log_print_file = 'error_msg##LOGID####LEVEL####CLASS####PAGENAME####DATE##';
/** @var string */
private $log_file_unique_id; // a unique ID set only once for call derived from this class
/** @var string */
private $log_file_date = ''; // Y-m-d file in file name
/** @var bool */
private $log_print_file_date = true; // if set add Y-m-d and do automatic daily rotation
/** @var string */
private $log_file_id = ''; // a alphanumeric name that has to be set as global definition
/** @var bool */
private $log_per_level = false; // set, it will split per level (first parameter in debug call)
/** @var bool */
private $log_per_class = false; // set, will split log per class
/** @var bool */
private $log_per_page = false; // set, will split log per called file
/** @var bool */
private $log_per_run = false; // create a new log file per run (time stamp + unique ID)
// script running time
/** @var float */
private $script_starttime;
/**
* Init logger
*
* global vars that can be used
* - BASE
* - LOG
* - LOG_FILE_ID
* options array layout
* - log_folder:
* - print_file_date:
* - file_id:
* - unique_id:
* - log_per_level:
* - log_per_class:
* - log_per_page:
* - log_per_run:
* - debug_all:
* - echo_all:
* - print_all:
* - debug (array):
* - echo (array):
* - print (array):
* - debug_not (array):
* - echo_not (array):
* - print_not (array):
*
* @param array<mixed> $options Array with settings options
*/
public function __construct(array $options = [])
{
// copy the options over
$this->options = $options;
// set log folder from options
$this->log_folder = $this->options['log_folder'] ?? '';
// legacy flow, check must set constants
if (empty($this->log_folder) && defined('BASE') && defined('LOG')) {
// make sure this is writeable, else skip
$this->log_folder = BASE . LOG;
}
// fallback + notice
if (empty($this->log_folder)) {
/* trigger_error(
'options or constant not set or folder not writable. fallback to: ' . getcwd(),
E_USER_NOTICE
); */
$this->log_folder = getcwd() . DIRECTORY_SEPARATOR;
}
// if folder is not writeable, abort
if (!is_writeable($this->log_folder)) {
trigger_error(
'Folder: ' . $this->log_folder . ' is not writeable for logging',
E_USER_ERROR
);
}
// check if log_folder has a trailing /
if (substr($this->log_folder, -1, 1) != DIRECTORY_SEPARATOR) {
$this->log_folder .= DIRECTORY_SEPARATOR;
}
// running time start for script
$this->script_starttime = microtime(true);
// set per run UID for logging
$this->running_uid = Uids::uniqIdShort();
// set the page name
$this->page_name = System::getPageName();
// set host name
list($this->host_name , $this->host_port) = System::getHostName();
// add port to host name if not port 80
if ($this->host_port != 80) {
$this->host_name .= ':' . $this->host_port;
}
// can be overridden with basicSetLogFileId later
if (!empty($this->options['file_id'])) {
$this->setLogId($this->options['file_id']);
} elseif (!empty($GLOBALS['LOG_FILE_ID'])) {
// legacy flow, should be removed and only set via options
$this->setLogId($GLOBALS['LOG_FILE_ID']);
// TODO trigger deprecation error
// trigger_error(
// 'Debug\Logging: Do not use globals LOG_FILE_ID to set log id for Logging',
// E_USER_DEPRECATED
// );
} elseif (defined('LOG_FILE_ID')) {
// legacy flow, should be removed and only set via options
$this->setLogId(LOG_FILE_ID);
// trigger deprecation error
// trigger_error(
// 'Debug\Logging: Do not use constant LOG_FILE_ID to set log id for Logging',
// E_USER_DEPRECATED
// );
}
// init the log levels
$this->initLogLevels();
}
// *** PRIVATE ***
/**
* init the basic log levels based on global set variables
*
* @return void
*/
private function initLogLevels(): void
{
// if given via parameters, only for all
// globals overrule given settings, for one (array), eg $ECHO['db'] = 1;
foreach (['debug', 'echo', 'print'] as $type) {
// include or exclude (off) from output
foreach (['on', 'off'] as $flag) {
$in_type = $type;
if ($flag == 'off') {
$in_type .= '_not';
}
$up_type = strtoupper($in_type);
if (
isset($this->options[$in_type]) &&
is_array($this->options[$in_type])
) {
$this->setLogLevel($type, $flag, $this->options[$in_type]);
} elseif (
isset($GLOBALS[$up_type]) &&
is_array($GLOBALS[$up_type])
) {
// TODO trigger deprecation error
$this->setLogLevel($type, $flag, $GLOBALS[$up_type]);
}
}
}
// TODO remove all $GLOBALS call and only use options
// all overrule
$this->setLogLevelAll(
'debug',
$this->options['debug_all'] ??
// for user login, should be handled outside like globals
$_SESSION['DEBUG_ALL'] ??
$GLOBALS['DEBUG_ALL'] ??
false
);
$this->setLogLevelAll(
'print',
$this->options['print_all'] ??
// for user login, should be handled outside like globals
$_SESSION['DEBUG_ALL'] ??
$GLOBALS['PRINT_ALL'] ??
false
);
$this->setLogLevelAll(
'echo',
$this->options['echo_all'] ??
$GLOBALS['ECHO_ALL'] ??
false
);
// GLOBAL rules for log writing
// add file date is default on
$this->setGetLogPrintFileDate(
$this->options['print_file_date'] ??
$GLOBALS['LOG_PRINT_FILE_DATE'] ??
true
);
// all other logging file name flags are off
$this->setLogPer(
'level',
$this->options['per_level'] ??
$GLOBALS['LOG_PER_LEVEL'] ??
false
);
$this->setLogPer(
'class',
$this->options['per_class'] ??
$GLOBALS['LOG_PER_CLASS'] ??
false
);
$this->setLogPer(
'page',
$this->options['per_page'] ??
$GLOBALS['LOG_PER_PAGE'] ??
false
);
$this->setLogPer(
'run',
$this->options['per_run'] ??
$GLOBALS['LOG_PER_RUN'] ??
false
);
// set log per date
if ($this->setGetLogPrintFileDate()) {
$this->log_file_date = date('Y-m-d');
}
// set per run ID
if ($this->log_per_run) {
$this->setLogUniqueId();
}
}
/**
* checks if we have a need to work on certain debug output
* Needs debug/echo/print ad target for which of the debug flag groups we check
* also needs level string to check in the per level output flag check.
* In case we have invalid target it will return false
*
* @param string $target target group to check debug/echo/print
* @param string $level level to check in detailed level flag
* @return bool true on access allowed or false on no access
*/
private function doDebugTrigger(string $target, string $level): bool
{
$access = false;
// check if we do debug, echo or print
if (
(
$this->getLogLevel($target, 'on', $level) ||
$this->getLogLevelAll($target)
) &&
!$this->getLogLevel($target, 'off', $level)
) {
$access = true;
}
return $access;
}
/**
* writes error msg data to file for current level
*
* @param string $level the level to write
* @param string $error_string error string to write
* @return bool True if message written, FAlse if not
*/
private function writeErrorMsg(string $level, string $error_string): bool
{
// only write if write is requested
if (
!($this->doDebugTrigger('debug', $level) &&
$this->doDebugTrigger('print', $level))
) {
return false;
}
// init base file path
$fn = $this->log_folder . $this->log_print_file . '.' . $this->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;
} else {
$rpl_string = '';
}
$fn = str_replace('##LOGID##', $rpl_string, $fn); // log id (like a log file prefix)
if ($this->log_per_run) {
$rpl_string = '_' . $this->log_file_unique_id; // add 8 char unique string
} elseif ($this->setGetLogPrintFileDate()) {
$rpl_string = '_' . $this->log_file_date; // add date to file
} else {
$rpl_string = '';
}
$fn = str_replace('##DATE##', $rpl_string, $fn); // create output filename
// write per level
$rpl_string = !$this->log_per_level ? '' :
// normalize level, replace all non alphanumeric characters with -
'_' . (
// if return is only - then set error string
preg_match(
"/^-+$/",
$level_string = preg_replace("/[^A-Za-z0-9-_]/", '-', $level) ?? ''
) ?
'INVALID-LEVEL-STRING' :
$level_string
);
$fn = str_replace('##LEVEL##', $rpl_string, $fn); // create output filename
// set per class, but don't use get_class as we will only get self
$rpl_string = !$this->log_per_class ? '' : '_'
// set sub class settings
. str_replace('\\', '-', Support::getCallerClass());
$fn = str_replace('##CLASS##', $rpl_string, $fn); // create output filename
// if request to write to one file
$rpl_string = !$this->log_per_page ?
'' :
'_' . System::getPageName(System::NO_EXTENSION);
$fn = str_replace('##PAGENAME##', $rpl_string, $fn); // create output filename
// write to file
// first check if max file size is is set and file is bigger
if (
$this->log_max_filesize > 0 &&
((filesize($fn) / 1024) > $this->log_max_filesize)
) {
// for easy purpose, rename file only to attach timestamp, nur sequence numbering
rename($fn, $fn . '.' . date("YmdHis"));
}
$this->log_file_name = $fn;
$fp = fopen($this->log_file_name, 'a');
if ($fp !== false) {
fwrite($fp, $error_string);
fclose($fp);
return true;
} else {
echo "<!-- could not open file: " . $this->log_file_name . " //-->";
return false;
}
}
// *** PUBLIC ***
/**
* Temporary method to read all class variables for testing purpose
*
* @param string $name what variable to return
* @return mixed can be anything, bool, string, int, array
*/
public function getSetting(string $name): mixed
{
// for debug purpose only
return $this->{$name};
}
/**
* sets the internal log file prefix id
* string must be a alphanumeric string
* if non valid string is given it returns the previous set one only
*
* @param string $string log file id string value
* @return string returns the set log file id string
* @deprecated Use $log->setLogId()
*/
public function basicSetLogId(string $string): string
{
return $this->setLogId($string);
}
/**
* sets the internal log file prefix id
* string must be a alphanumeric string
* if non valid string is given it returns the previous set one only
*
* @param string $string log file id string value
* @return string returns the set log file id string
*/
public function setLogId(string $string): string
{
if (preg_match("/^[\w\-]+$/", $string)) {
$this->log_file_id = $string;
}
return $this->log_file_id;
}
/**
* return current set log file id
* @return string
*/
public function getLogId(): string
{
return $this->log_file_id;
}
/**
* old name for setLogLevel
*
* @param string $type debug, echo, print
* @param string $flag on/off
* array $array of levels to turn on/off debug
* @return bool Return false if type or flag is invalid
* @deprecated Use setLogLevel
*/
public function debugFor(string $type, string $flag): bool
{
/** @phan-suppress-next-line PhanTypeMismatchArgumentReal, PhanParamTooFew @phpstan-ignore-next-line */
return $this->setLogLevel(...[func_get_args()]);
}
/**
* set log level settings for All types
* if invalid type, skip
*
* @param string $type Type to get: debug, echo, print
* @param bool $set True or False
* @return bool Return false if type invalid
*/
public function setLogLevelAll(string $type, bool $set): bool
{
// skip set if not valid
if (!in_array($type, ['debug', 'echo', 'print'])) {
return false;
}
$this->{$type . '_output_all'} = $set;
return true;
}
/**
* get the current log level setting for All level blocks
*
* @param string $type Type to get: debug, echo, print
* @return bool False on failure, or the boolean flag from the all var
*/
public function getLogLevelAll(string $type): bool
{
// type check for debug/echo/print
if (!in_array($type, ['debug', 'echo', 'print'])) {
return false;
}
return $this->{$type . '_output_all'};
}
/**
* passes list of level names, to turn on debug
* eg $foo->debugFor('print', 'on', ['LOG', 'DEBUG', 'INFO']);
*
* @param string $type debug, echo, print
* @param string $flag on/off
* @param array<mixed> $debug_on Array of levels to turn on/off debug
* To turn off a level set 'Level' => false,
* If not set, switches to on
* @return bool Return false if type or flag invalid
* also false if debug array is empty
*/
public function setLogLevel(string $type, string $flag, array $debug_on): bool
{
// abort if not valid type
if (!in_array($type, ['debug', 'echo', 'print'])) {
return false;
}
// invalid flag type
if (!in_array($flag, ['on', 'off'])) {
return false;
}
if (count($debug_on) >= 1) {
foreach ($debug_on as $level => $set) {
$switch = $type . '_output' . ($flag == 'off' ? '_not' : '');
if (!is_bool($set)) {
$level = $set;
$set = true;
}
$this->{$switch}[$level] = $set;
}
} else {
return false;
}
return true;
}
/**
* return the log level for the array type normal and not (disable)
*
* @param string $type debug, echo, print
* @param string $flag on/off
* @param string|null $level if not null then check if this array entry is set
* else return false
* @return array<mixed>|bool if $level is null, return array, else boolean true/false
*/
public function getLogLevel(string $type, string $flag, ?string $level = null): array|bool
{
// abort if not valid type
if (!in_array($type, ['debug', 'echo', 'print'])) {
return false;
}
// invalid flag type
if (!in_array($flag, ['on', 'off'])) {
return false;
}
$switch = $type . '_output' . ($flag == 'off' ? '_not' : '');
// log level direct check must be not null or not empty string
if (!empty($level)) {
return $this->{$switch}[$level] ?? false;
}
// array
return $this->{$switch};
}
/**
* set flags for per log level type
* - level: set per sub group level
* - class: split by class
* - page: split per page called
* - run: for each run
*
* @param string $type Type to get: level, class, page, run
* @param bool $set True or False
* @return bool Return false if type invalid
*/
public function setLogPer(string $type, bool $set): bool
{
if (!in_array($type, ['level', 'class', 'page', 'run'])) {
return false;
}
$this->{'log_per_' . $type} = $set;
// if per run set unique id
if ($type == 'run' && $set == true) {
$this->setLogUniqueId();
}
return true;
}
/**
* return current set log per flag in bool
*
* @param string $type Type to get: level, class, page, run
* @return bool True of false for turned on or off
*/
public function getLogPer(string $type): bool
{
if (!in_array($type, ['level', 'class', 'page', 'run'])) {
return false;
}
return $this->{'log_per_' . $type};
}
/**
* Sets a unique id based on current date (y/m/d, h:i:s) and a unique id (8 chars)
* if override is set to true it will be newly set, else if already set nothing changes
*
* @param bool $override True to force new set
* @return void
*/
public function setLogUniqueId(bool $override = false): void
{
if (!$this->log_file_unique_id || $override == true) {
$this->log_file_unique_id =
date('Y-m-d_His') . '_U_'
. substr(hash('sha1', uniqid((string)mt_rand(), true)), 0, 8);
}
}
/**
* Return current set log file unique id,
* empty string for not set
*
* @return string
*/
public function getLogUniqueId(): string
{
return $this->log_file_unique_id;
}
/**
* Set or get the log file date extension flag
* if null or empty parameter gets current flag
*
* @param boolean|null $set Set the date suffix for log files
* If set to null return current set
* @return boolean Current set flag
*/
public function setGetLogPrintFileDate(?bool $set = null): bool
{
if ($set !== null) {
$this->log_print_file_date = $set;
}
return $this->log_print_file_date;
}
/**
* Return current set log file name
*
* @return string Filename set set after the last time debug was called
*/
public function getLogFileName(): string
{
return $this->log_file_name;
}
/**
* A replacement for the \CoreLibs\Debug\Support::printAr
* But this does not wrap it in <pre></pre>
* It uses some special code sets so we can convert that to pre flags
* for echo output {##HTMLPRE##} ... {##/HTMLPRE##}
* 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
*/
public function prAr(array $a): string
{
return '##HTMLPRE##' . print_r($a, true) . '##/HTMLPRE##';
}
/**
* Convert bool value to string value
*
* @param bool $bool Bool value to be transformed
* @param string $true Override default string 'true'
* @param string $false Override default string 'false'
* @return string $true or $false string for true/false bool
*/
public function prBl(
bool $bool,
string $true = 'true',
string $false = 'false'
): string {
return $bool ? $true : $false;
}
/**
* write debug data to error_msg array
*
* @param string $level id for error message, groups messages together
* @param string $string the actual error message
* @param bool $strip default on false, if set to true,
* all html tags will be stripped and <br> changed to \n
* this is only used for debug output
* @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
* @return bool True if logged, false if not logged
*/
public function debug(
string $level,
string $string,
bool $strip = false,
string $prefix = ''
): bool {
$status = false;
// must be debug on and either echo or print on
if (
!$this->doDebugTrigger('debug', $level) ||
(
// if debug is on, either print or echo must be set to on
!$this->doDebugTrigger('print', $level) &&
!$this->doDebugTrigger('echo', $level)
)
) {
return $status;
}
// get the last class entry and wrie that
$class = Support::getCallerClass();
// get timestamp
$timestamp = Support::printTime();
// same string put for print (no html data inside)
// write to file if set
$status = $this->writeErrorMsg(
$level,
'[' . $timestamp . '] '
. '[' . $this->host_name . '] '
. '[' . System::getPageName(System::FULL_PATH) . '] '
. '[' . $this->running_uid . '] '
. '{' . $class . '} '
. '<' . $level . '> - '
// strip the htmlpre special tags if exist
. str_replace(
['##HTMLPRE##', '##/HTMLPRE##'],
'',
// if stripping all html, etc is requested, only for write error msg
($strip ?
// find any <br> and replace them with \n
// strip rest of html elements (base only)
preg_replace(
"/(<\/?)(\w+)([^>]*>)/",
'',
str_replace('<br>', "\n", $prefix . $string)
) :
$prefix . $string
) ?: ''
)
. "\n"
);
// write to error level msg array if there is an echo request
if ($this->doDebugTrigger('echo', $level)) {
// init if not set
if (!isset($this->error_msg[$level])) {
$this->error_msg[$level] = [];
}
// HTML string
$this->error_msg[$level][] = '<div>'
. '[<span style="font-weight: bold; color: #5e8600;">' . $timestamp . '</span>] '
. '[<span style="font-weight: bold; color: #c56c00;">' . $level . '</span>] '
. '[<span style="color: #b000ab;">' . $this->host_name . '</span>] '
. '[<span style="color: #08b369;">' . $this->page_name . '</span>] '
. '[<span style="color: #0062A2;">' . $this->running_uid . '</span>] '
. '{<span style="font-style: italic; color: #928100;">' . $class . '</span>} - '
// as is prefix, allow HTML
. $prefix
// we replace special HTMLPRE with <pre> entries
. str_replace(
['##HTMLPRE##', '##/HTMLPRE##'],
['<pre>', '</pre>'],
Html::htmlent($string)
)
. "</div><!--#BR#-->";
$status = true;
}
return $status;
}
/**
* for ECHO ON only
* returns error data as string so it can be echoed out
*
* @param string $header_prefix prefix string for header
* @return string error msg for all levels
*/
public function printErrorMsg(string $header_prefix = ''): string
{
$string_output = '';
// if not debug && echo on, do not return anything
if (
!$this->getLogLevelAll('debug') ||
!$this->getLogLevelAll('echo')
) {
return $string_output;
}
if ($this->error_msg_prefix) {
$header_prefix = $this->error_msg_prefix;
}
$script_end = microtime(true) - $this->script_starttime;
foreach ($this->error_msg as $level => $temp_debug_output) {
if ($this->doDebugTrigger('debug', $level)) {
if ($this->doDebugTrigger('echo', $level)) {
$string_output .= '<div style="font-size: 12px;">'
. '[<span style="font-style: italic; color: #c56c00;">' . $level . '</span>] '
. ($header_prefix ? "<b>**** " . Html::htmlent($header_prefix) . " ****</br>\n" : '')
. '</div>'
. join('', $temp_debug_output);
} // echo it out
} // do printout
} // for each level
// create the output wrapper around
// so we have a nice formated output per class
if ($string_output) {
$string_prefix = '<div style="text-align: left; padding: 5px; font-size: 10px; '
. 'font-family: sans-serif; border-top: 1px solid black; '
. 'border-bottom: 1px solid black; margin: 10px 0 10px 0; '
. 'background-color: white; color: black;">'
. '<div style="font-size: 12px;">{<span style="font-style: italic; color: #928100;">'
. Support::getCallerClass() . '</span>}</div>';
$string_output = $string_prefix . $string_output
. '<div><span style="font-style: italic; color: #108db3;">Script Run Time:</span> '
. $script_end . '</div>'
. '</div>';
}
// }
return $string_output;
}
/**
* for ECHO ON only
* unsests the error message array
* can be used if writing is primary to file
* if no level given resets all
*
* @param string $level optional level
* @return void has no return
*/
public function resetErrorMsg(string $level = ''): void
{
if (!$level) {
$this->error_msg = [];
} elseif (isset($this->error_msg[$level])) {
unset($this->error_msg[$level]);
}
}
/**
* for ECHO ON only
* Get current error message array
*
* @return array<mixed> error messages collected
*/
public function getErrorMsg(): array
{
return $this->error_msg;
}
/**
* for ECHO ON only
* merges the given error array with the one from this class
* only merges visible ones
*
* @param array<mixed> $error_msg error array
* @return void has no return
*/
public function mergeErrors(array $error_msg = []): void
{
array_push($this->error_msg, ...$error_msg);
}
}
// __END__

View File

@@ -0,0 +1,129 @@
<?php
/*
* dump memory usage
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
use CoreLibs\Convert\Byte;
class MemoryUsage
{
/** @var int */
private static $start_memory = 0;
/** @var int */
private static $set_memory = 0;
/** @var int */
private static $previous_memory = 0;
/** @var bool */
private static $debug_memory = false;
/**
* set memory flag, or return set memory flag
*
* @param bool|null $set_debug
* @return bool
*/
public static function debugMemoryFlag(?bool $set_debug = null): bool
{
if ($set_debug === null) {
return self::$debug_memory;
}
self::$debug_memory = $set_debug;
return self::$debug_memory;
}
/**
* Reset all memory variables to 0
*
* @return void
*/
public static function resetMemory(): void
{
self::$start_memory = 0;
self::$set_memory = 0;
self::$previous_memory = 0;
}
/**
* set the start memory velue, or reset to a new start value
*
* @return void
*/
public static function setStartMemory(): void
{
self::$start_memory = memory_get_usage();
}
/**
* set the and independent memory set for a sub tracking outside main
*
* @return void
*/
public static function setMemory(): void
{
self::$set_memory = memory_get_usage();
}
/**
* calculate and set memory usage values
* this will return an array with all the data that can be used in
* printMemoryUsage for human readable output
*
* @param string $prefix A prefix tag
* @return array<string,int|string> return array
*/
public static function memoryUsage(string $prefix): array
{
// skip if DEBUG is off
if (self::$debug_memory === false) {
return [];
}
if (empty(self::$start_memory)) {
self::$start_memory = memory_get_usage();
}
$memory_usage = memory_get_usage();
$data = [
'prefix' => $prefix,
'peak' => memory_get_peak_usage(),
'usage' => $memory_usage,
'start' => $memory_usage - self::$start_memory,
'last' => $memory_usage - self::$previous_memory,
'set' => $memory_usage - self::$set_memory
];
self::$previous_memory = $memory_usage;
return $data;
}
/**
* returns a human readable output from the memoryUsage function
* can be used for logging purpose
*
* @param array<string,int|string> $data Data array from memoryUsage
* @param bool $raw Flag to shaw unconverted memory numbers
* @return string Return debug string with memory usage
*/
public static function printMemoryUsage(array $data, bool $raw = false): string
{
return
'[' . $data['prefix'] . '] Peak/Curr/Change: '
. Byte::humanReadableByteFormat($data['peak'])
. '/'
. Byte::humanReadableByteFormat($data['usage'])
// . ($raw === true ? ' [' . $data['usage'] . ']' : '')
. '/Since Start: '
. Byte::humanReadableByteFormat($data['start'])
. ($raw === true ? ' [' . $data['start'] . ']' : '')
. ' | Since Last: '
. Byte::humanReadableByteFormat($data['last'])
. ($raw === true ? ' [' . $data['last'] . ']' : '')
. ' | Since Set: '
. Byte::humanReadableByteFormat($data['set'])
. ($raw === true ? ' [' . $data['set'] . ']' : '');
}
}
// __END__

View File

@@ -0,0 +1,173 @@
<?php
/*
* various running time checkers
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
class RunningTime
{
// hr
/** @var float */
private static $hr_start_time;
/** @var float */
private static $hr_end_time;
/** @var float */
private static $hr_last_time;
// normal
/** @var float */
private static $start_time;
/** @var float */
private static $end_time;
/** @var string */
private static $running_time_string;
/**
* sub calculation for running time based on out time.
* If no running time set, return 0
*
* @param string $out_time
* @return float
*/
private static function hrRunningTimeCalc(
float $run_time,
string $out_time = 'ms'
): float {
// init divisor, just in case
$divisor = 1;
// check through valid out time, if nothing matches default to ms
switch ($out_time) {
case 'n':
case 'ns':
$divisor = 1;
break;
case 'y':
case 'ys':
$divisor = 1000;
break;
case 'm':
case 'ms':
$divisor = 1000000;
break;
case 's':
$divisor = 1000000000;
break;
// default is ms
default:
$divisor = 1000000;
break;
}
// return the run time in converted format
return $run_time /= $divisor;
}
/**
* for messure run time between two calls for this method
* uses the hrtime() for running time
* first call sets start time and returns 0,
* every other call sets end time and returns the run time since start
* the out_time parameter can be:
* n/ns (nano), y/ys (micro), m/ms (milli), s (seconds)
* default is milliseconds
*
* @param string $out_time set return time adjustment calculation
* @return float running time without out_time suffix
*/
public static function hrRunningTime(string $out_time = 'ms'): float
{
// if start time not set, set start time
if (!self::$hr_start_time) {
self::$hr_start_time = hrtime(true);
self::$hr_last_time = self::$hr_start_time;
$run_time = 0;
} else {
self::$hr_end_time = hrtime(true);
$run_time = self::$hr_end_time - self::$hr_last_time;
self::$hr_last_time = self::$hr_end_time;
}
return self::hrRunningTimeCalc($run_time, $out_time);
}
/**
* print overall end time , can only be called after hrRunningtime
* see $out_time parameter description in hrRunningtime.
* Does not record a new timestamp, only prints different between start and
* last recoreded timestamp
*
* @param string $out_time set return time adjustment calculation
* @return float overall running time without out_time suffix
*/
public static function hrRunningTimeFromStart(string $out_time = 'ms'): float
{
if (!self::$hr_start_time) {
return (float)0;
}
$time = self::hrRunningTimeCalc(
self::$hr_end_time - self::$hr_start_time,
$out_time
);
return $time;
}
/**
* reset hr running time internal variables (start, end, last)
*
* @return void
*/
public static function hrRunningTimeReset(): void
{
self::$hr_start_time = 0;
self::$hr_end_time = 0;
self::$hr_last_time = 0;
}
/**
* prints start or end time in text format. On first call sets start time
* on second call it sends the end time and then also prints the running time
* Sets the internal runningtime_string variable with Start/End/Run time string
* NOTE: for pure running time check it is recommended to use hrRunningTime method
* @param bool $simple if true prints HTML strings, default text only
* @return float running time as float number
*/
public static function runningTime(bool $simple = false): float
{
list($micro, $timestamp) = explode(' ', microtime());
$running_time = 0;
// set start & end time
if (!self::$start_time) {
// always reset running time string on first call
self::$running_time_string = '';
self::$start_time = ((float)$micro + (float)$timestamp);
self::$running_time_string .= $simple ? 'Start: ' : "<b>Started at</b>: ";
} else {
self::$end_time = ((float)$micro + (float)$timestamp);
self::$running_time_string .= $simple ? 'End: ' : "<b>Stopped at</b>: ";
}
self::$running_time_string .= date('Y-m-d H:i:s', (int)$timestamp);
self::$running_time_string .= ' ' . $micro . ($simple ? ', ' : '<br>');
// if both are set
if (self::$start_time && self::$end_time) {
$running_time = self::$end_time - self::$start_time;
self::$running_time_string .= ($simple ? 'Run: ' : "<b>Script running time</b>: ") . $running_time . " s";
// reset start & end time after run
self::$start_time = 0;
self::$end_time = 0;
}
return $running_time;
}
/**
* get the runningTime string (for debug visual)
*
* @return string
*/
public static function runningTimeString(): string
{
return self::$running_time_string;
}
}
// __END__

View File

@@ -0,0 +1,219 @@
<?php
/*
* Debug support functions
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
use CoreLibs\Convert\Html;
class Support
{
/**
* wrapper around microtime function to print out y-m-d h:i:s.ms
*
* @param int $set_microtime -1 to set micro time, 0 for none,
* positive for rounding
* @return string formated datetime string with microtime
*/
public static function printTime(int $set_microtime = -1): string
{
list($microtime, $timestamp) = explode(' ', microtime());
$string = date("Y-m-d H:i:s", (int)$timestamp);
// if microtime flag is -1 no round, if 0, no microtime, if >= 1, round that size
if ($set_microtime == -1) {
$string .= substr($microtime, 1);
} elseif ($set_microtime >= 1) {
// in round case we run this through number format to always get the same amount of digits
$string .= substr(number_format(round((float)$microtime, $set_microtime), $set_microtime), 1);
}
return $string;
}
/**
* prints a html formatted (pre) array
*
* @param array<mixed> $array any array
* @param bool $no_html set to true to use ##HTMLPRE##
* @return string formatted array for output with <pre> tag added
*/
public static function printAr(array $array, bool $no_html = false): string
{
if ($no_html === false) {
return "<pre>" . print_r($array, true) . "</pre>";
} else {
return '##HTMLPRE##' . print_r($array, true) . '##/HTMLPRE##';
}
}
/**
* alternate name for printAr function
*
* @param array<mixed> $array any array
* @param bool $no_html set to true to use ##HTMLPRE##
* @return string formatted array for output with <pre> tag added
*/
public static function printArray(array $array, bool $no_html = false): string
{
return self::printAr($array, $no_html);
}
/**
* convert bool value to string
* if $name is set prefix with nae
* default true: true, false: false
*
* @param bool $bool Variable to convert
* @param string $name [default: ''] Prefix name
* @param string $true [default: true] True string
* @param string $false [default: false] False string
* @return string String with converted bool text for debug
*/
public static function printBool(
bool $bool,
string $name = '',
string $true = 'true',
string $false = 'false'
): string {
$string = (!empty($name) ? '<b>' . $name . '</b>: ' : '')
. ($bool ? $true : $false);
return $string;
}
/**
* print out any data as string
* will convert boolean to TRUE/FALSE
* if object return get_class
* for array use printAr function, can be controlled with no_html for
* Debug\Logging compatible output
*
* @param mixed $mixed
* @param bool $no_html set to true to use ##HTMLPRE##or html escape
* @return string
*/
public static function printToString(mixed $mixed, bool $no_html = false): string
{
if (is_null($mixed)) {
return (string)'NULL';
} elseif (is_bool($mixed)) {
return self::printBool($mixed, '', 'TRUE', 'FALSE');
} elseif (is_resource($mixed)) {
return (string)$mixed;
} elseif (is_object($mixed)) {
return get_class($mixed);
} elseif (is_array($mixed)) {
// use the pre one OR debug one
return self::printAr($mixed, $no_html);
} elseif (is_string($mixed)) {
if ($no_html) {
return Html::htmlent((string)$mixed);
} else {
return (string)$mixed;
}
} else {
// should be int/float/string
return (string)$mixed;
}
}
/**
* if there is a need to find out which parent method called a child method,
* eg for debugging, this function does this
*
* call this method in the child method and you get the parent function that called
* @param int $level debug level, default 1
* @return ?string null or the function that called the function
* where this method is called
*/
public static function getCallerMethod(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print \CoreLibs\Debug\Support::printAr($traces);
// We should check from top down if unset?
// sets the start point here, and in level two (the sub call) we find this
if (isset($traces[$level])) {
return $traces[$level]['function'];
}
return null;
}
/**
* Returns array with all methods in the call stack in the order so that last
* called is last in order
* Will start with start_level to skip unwanted from stack
* Defaults to skip level 0 wich is this methid
*
* @param integer $start_level From what level on, as defaul starts with 1
* to exclude self
* @return array<mixed> All method names in list where max is last called
*/
public static function getCallerMethodList(int $start_level = 1): array
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$methods = [];
foreach ($traces as $level => $data) {
if ($level >= $start_level) {
if (!empty($data['function'])) {
array_unshift($methods, $data['function']);
}
}
}
return $methods;
}
/**
* Get the current class where this function is called
* Is mostly used in debug log statements to get the class where the debug
* was called
* gets top level class
* loops over the debug backtrace until if finds the first class (from the end)
*
* @return string Class name with namespace
*/
public static function getCallerClass(): string
{
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// ?? [['class' => get_called_class()]];
// TODO make sure that this doesn't loop forver
$class = null;
while ($class === null && count($backtrace) > 0) {
// if current is
// [function] => debug
// [class] => CoreLibs\Debug\Logging
// then return
// (OUTSIDE) because it was not called from a class method
// or return file name
$get_class = array_pop($backtrace);
$class = $get_class['class'] ?? null;
}
// on null or empty return empty string
return empty($class) ? '' : $class;
}
/**
* If a string is empty, sets '-' for return, or if given any other string
*
* @param string|null $string The string to check
* @param string $replace [default '-'] What to replace the empty string with
* @return string String itself or the replaced value
*/
public static function debugString(
?string $string,
string $replace = '-',
bool $no_html = false
): string {
if (empty($string)) {
$string = $replace;
}
if ($no_html) {
return Html::htmlent($string);
} else {
return $string;
}
}
}
// __END__

View File

@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Get;
class DotEnv
{
/** @var string constant comment char, set to # */
private const COMMENT_CHAR = '#';
/**
* parses .env file
*
* Rules for .env file
* variable is any alphanumeric string followed by = on the same line
* content starts with the first non space part
* strings can be contained in "
* strings MUST be contained in " if they are multiline
* if string starts with " it will match until another " is found
* anything AFTER " is ignored
* if there are two variables with the same name only the first is used
* variables are case sensitive
*
* @param string $path Folder to file, default is __DIR__
* @param string $env_file What file to load, default is .env
* @return int -1 other error
* 0 for success full load
* 1 for file loadable, no data or data already loaded
* 2 for file not readable or open failed
* 3 for file not found
*/
public static function readEnvFile(
string $path = __DIR__,
string $env_file = '.env'
): int {
// default -1;
$status = -1;
$env_file_target = $path . DIRECTORY_SEPARATOR . $env_file;
// this is not a file -> abort
if (!is_file($env_file_target)) {
$status = 3;
return $status;
}
// cannot open file -> abort
if (!is_readable($env_file_target)) {
$status = 2;
return $status;
}
// open file
if (($fp = fopen($env_file_target, 'r')) === false) {
$status = 2;
return $status;
}
// set to readable but not yet any data loaded
$status = 1;
$block = false;
$var = '';
while ($line = fgets($fp)) {
// main match for variable = value part
if (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) {
$var = $matches[1];
$value = $matches[2];
$quotes = $matches[3];
// write only if env is not set yet, and write only the first time
if (empty($_ENV[$var])) {
if (!empty($quotes)) {
// match greedy for first to last so we move any " if there are
if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) {
$value = $matches[1];
} else {
// this is a multi line
$block = true;
// first " in string remove
// add removed new line back because this is a multi line
$value = ltrim($value, '"') . PHP_EOL;
}
} else {
// strip any quotes at end for unquoted single line
// an right hand spaces are removed too
$value = false !== ($pos = strpos($value, self::COMMENT_CHAR)) ?
rtrim(substr($value, 0, $pos)) : $value;
}
// if block is set, we strip line of slashes
$_ENV[$var] = $block === true ? stripslashes($value) : $value;
// set successful load
$status = 0;
}
} elseif ($block === true) {
// read line until there is a unescaped "
// this also strips everything after the last "
if (preg_match("/(.*[^\\\])\"/", $line, $matches)) {
$block = false;
// strip ending " and EVERYTHING that follows after that
$line = $matches[1];
}
// just be sure it is init before we fill
if (!isset($_ENV[$var])) {
$_ENV[$var] = '';
}
// strip line of slashes
$_ENV[$var] .= stripslashes($line);
}
}
fclose($fp);
return $status;
}
}
// __END__

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Get;
/**
* @deprecated use \CoreLibs\Get\DotEnv instead
*/
class ReadEnvFile
{
/**
* parses .env file
*
* Rules for .env file
* variable is any alphanumeric string followed by = on the same line
* content starts with the first non space part
* strings can be contained in "
* strings MUST be contained in " if they are multiline
* if string starts with " it will match until another " is found
* anything AFTER " is ignored
* if there are two variables with the same name only the first is used
* variables are case sensitive
*
* @param string $path Folder to file, default is __DIR__
* @param string $env_file What file to load, default is .env
* @return int -1 other error
* 0 for success full load
* 1 for file loadable, but no data inside
* 2 for file not readable or open failed
* 3 for file not found
* @deprecated Use \CoreLibs\Get\DotEnv::readEnvFile() instead
*/
public static function readEnvFile(
string $path = __DIR__,
string $env_file = '.env'
): int {
return \CoreLibs\Get\DotEnv::readEnvFile($path, $env_file);
}
}
// __END__

View File

@@ -0,0 +1,120 @@
<?php
/*
* system related functions to get self name, host name, error strings
*/
declare(strict_types=1);
namespace CoreLibs\Get;
class System
{
public const WITH_EXTENSION = 0;
public const NO_EXTENSION = 1;
public const FULL_PATH = 2;
private const DEFAULT_PORT = '80';
/**
* helper function for PHP file upload error messgaes to messge string
*
* @param int $error_code integer _FILE upload error code
* @return string message string, translated
*/
public static function fileUploadErrorMessage(int $error_code): string
{
switch ($error_code) {
case UPLOAD_ERR_INI_SIZE:
$message = 'The uploaded file exceeds the upload_max_filesize directive in php.ini';
break;
case UPLOAD_ERR_FORM_SIZE:
$message = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
break;
case UPLOAD_ERR_PARTIAL:
$message = 'The uploaded file was only partially uploaded';
break;
case UPLOAD_ERR_NO_FILE:
$message = 'No file was uploaded';
break;
case UPLOAD_ERR_NO_TMP_DIR:
$message = 'Missing a temporary folder';
break;
case UPLOAD_ERR_CANT_WRITE:
$message = 'Failed to write file to disk';
break;
case UPLOAD_ERR_EXTENSION:
$message = 'File upload stopped by extension';
break;
default:
$message = 'Unknown upload error';
break;
}
return $message;
}
/**
* get the host name without the port as given by the SELF var
*
* @return array<mixed> host name/port name
*/
public static function getHostName(): array
{
$host = $_SERVER['HTTP_HOST'] ?? 'NOHOST:NOPORT';
list($host_name, $port) = array_pad(explode(':', $host), 2, self::DEFAULT_PORT);
return [$host_name, $port];
}
/**
* get the page name of the curronte page
*
* @param int $strip_ext WITH_EXTENSION: keep filename as is (default)
* NO_EXTENSION: strip page file name extension
* FULL_PATH: keep filename as is, but add dirname too
* @return string filename
*/
public static function getPageName(int $strip_ext = self::WITH_EXTENSION): string
{
// get the file info
$page_temp = pathinfo($_SERVER['PHP_SELF']);
if ($strip_ext == self::NO_EXTENSION) {
// no extension
return $page_temp['filename'];
} elseif ($strip_ext == self::FULL_PATH) {
// full path
return $_SERVER['PHP_SELF'];
} else {
// with extension
return $page_temp['basename'];
}
}
/**
* similar to getPageName, but it retuns the raw array
*
* @return array<string> pathinfo array from PHP SELF
*/
public static function getPageNameArray(): array
{
return pathinfo($_SERVER['PHP_SELF']);
}
/**
* Check if the php sapi interface has cli inside
*
* @return bool True for CLI type PHP, else false
*/
public static function checkCLI(): bool
{
return substr(
// if return is false, use empty string
(($sapi_name = php_sapi_name()) === false ?
'' :
$sapi_name
),
0,
3
) === 'cli' ? true : false;
}
}
// __END__

View File

@@ -0,0 +1,58 @@
<?php
/*
Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>.
This file is part of PHP-gettext.
PHP-gettext is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PHP-gettext is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PHP-gettext; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
declare(strict_types=1);
namespace CoreLibs\Language\Core;
// Preloads entire file in memory first, then creates a StringReader
// over it (it assumes knowledge of StringReader internals)
class CachedFileReader extends \CoreLibs\Language\Core\StringReader
{
/** @var int */
public $error = 0;
/** @var string */
public $fd_str = '';
/**
* Undocumented function
*
* @param string $filename
*/
public function __construct(string $filename)
{
parent::__construct();
if (file_exists($filename)) {
$fd = fopen($filename, 'rb');
if (!is_resource($fd)) {
$this->error = 3; // Cannot read file, probably permissions
} else {
$this->fd_str = fread($fd, filesize($filename) ?: 0) ?: '';
fclose($fd);
}
} else {
$this->error = 2; // File doesn't exist
}
}
}
// __END__

View File

@@ -0,0 +1,135 @@
<?php
/*
Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>.
This file is part of PHP-gettext.
PHP-gettext is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PHP-gettext is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PHP-gettext; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
declare(strict_types=1);
namespace CoreLibs\Language\Core;
class FileReader
{
/** @var int */
public $fr_pos;
/** @var resource|bool */
public $fr_fd;
/** @var int */
public $fr_length;
/** @var int */
public $error = 0;
/**
* file read constructor
*
* @param string $filename file name to load
*/
public function __construct(string $filename)
{
if (file_exists($filename)) {
$this->fr_length = filesize($filename) ?: 0;
$this->fr_pos = 0;
$this->fr_fd = fopen($filename, 'rb');
if (!is_resource($this->fr_fd)) {
$this->error = 3; // Cannot read file, probably permissions
}
} else {
$this->error = 2; // File doesn't exist
}
}
/**
* read byte data length
*
* @param int $bytes how many bytes to read
* @return string read data as string
*/
public function read(int $bytes): string
{
if (!$bytes || !is_resource($this->fr_fd)) {
return '';
}
fseek($this->fr_fd, $this->fr_pos);
// PHP 5.1.1 does not read more than 8192 bytes in one fread()
// the discussions at PHP Bugs suggest it's the intended behaviour
$data = '';
while ($bytes > 0) {
$chunk = fread($this->fr_fd, $bytes);
if ($chunk === false) {
break;
}
$data .= $chunk;
$bytes -= strlen($chunk);
}
$this->fr_pos = ftell($this->fr_fd) ?: 0;
return $data;
}
/**
* seek to a position in the file
*
* @param int $pos position where to go to
* @return int file position after seek done
*/
public function seekto(int $pos): int
{
if (!is_resource($this->fr_fd)) {
return 0;
}
fseek($this->fr_fd, $pos);
$this->fr_pos = ftell($this->fr_fd) ?: 0;
return $this->fr_pos;
}
/**
* get current position in file
*
* @return int current position in bytes
*/
public function currentpos(): int
{
return $this->fr_pos;
}
/**
* file length/size
*
* @return int file size in bytes
*/
public function length(): int
{
return $this->fr_length;
}
/**
* close open file handler
*
* @return void has no return
*/
public function close(): void
{
if (is_resource($this->fr_fd)) {
fclose($this->fr_fd);
}
}
}
// __END__

Some files were not shown because too many files have changed in this diff Show More