Compare commits

..

27 Commits

Author SHA1 Message Date
0c1f060759 Merge branch 'development' 2023-04-11 11:03:24 +09:00
aad46ec80a DB\IO: add missing debug query, clean up not needed code
in dbReturn with params on not matching param the system exited on fail
without printing the query making it hard to find where the error is.
Added debug output in case the params count is not matching.
Same move in the dbExecute call

removed param count check from dbReturnRow/dbReturnArray as this check
is done in the dbExecParams call anyway
2023-04-11 11:03:04 +09:00
f5e9f0610d Publish: v8.2.1 2023-04-10 17:24:47 +09:00
14a5250cd7 DB\IO: Bug fix for missing query params replacement in debug messages 2023-04-10 17:23:27 +09:00
6e6edef57d Release: v8.2.0 2023-04-10 14:38:50 +09:00
d3810db965 Add ACL\Login additional acl fields to export acl array 2023-04-10 14:37:44 +09:00
187a012284 Published v8.1.4 2023-04-10 09:05:35 +09:00
b3d2662fd2 DB\IO params detection fix 2023-04-07 14:39:00 +09:00
1189aecae9 New release v8.1.3 2023-04-03 15:08:43 +09:00
024d6d2d7a Bug fix in DB\IO returning call check 2023-04-03 15:07:29 +09:00
f2d5377347 Release v8.1.2 2023-03-29 10:07:12 +09:00
af11bd8199 DB\IO dbReturn and dbReturnParams set NO_CACHE as default 2023-03-29 10:05:09 +09:00
0e6a43a2c2 Release v8.1.1 2023-03-28 16:49:55 +09:00
94eeaaaa51 DB\IO Debug output update for parameter queries 2023-03-28 16:49:06 +09:00
4a246bec5f Release v8.1.0 2023-03-28 16:47:38 +09:00
46b2b60718 CoreLibs DB\IO dbExec*, dbReturn* params methods add 2023-03-28 15:41:02 +09:00
9616d956cb Publish v8.0.7 2023-03-13 11:35:13 +09:00
df401b9add Update SmartyExtend set vars calls with parameter changes
Frontend: drop $cms, add $smarty_data array
Backend: if $cms is set content_path options array entry must be set
2023-03-13 11:33:23 +09:00
4b9e393971 Published v8.0.6 2023-03-13 10:52:15 +09:00
6cda319ed0 Bug fix for SmartyExtend set var call 2023-03-13 10:51:09 +09:00
583edbfe0a Published v8.0.5 2023-03-13 09:29:30 +09:00
67a8e1a533 Switch from parameter list to options list for SmartyExtend var set
The SmartyExtend var set mothod calls switched to options list
2023-03-13 09:27:46 +09:00
38788dddce CoreLibs v8.0.5 release 2023-03-10 15:30:06 +09:00
cf196d56dd Bug fix in SmartyExtend set vars calls 2023-03-10 15:27:45 +09:00
0cb76c8db2 CoreLibs v8.0.3 release 2023-03-10 15:13:43 +09:00
db02bdd102 CoreLibs data update for v8.0.3 2023-03-10 15:11:58 +09:00
d9b71a64b3 Published v8.0.2 2023-03-09 16:59:18 +09:00
18 changed files with 2219 additions and 520 deletions

View File

@@ -1 +1 @@
8.0.1
8.2.1

View File

@@ -31,6 +31,7 @@ source .env.deploy;
cd -;
set +o allexport;
echo "[START]";
# gitea
if [ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then
curl -LJO \
@@ -56,5 +57,7 @@ if [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then
else
echo "Missing GITLAB_DEPLOY_TOKEN environment variable";
fi;
echo "";
echo "[DONE]";
# __END__

View File

@@ -69,6 +69,7 @@ declare(strict_types=1);
namespace CoreLibs\ACL;
use CoreLibs\Check\Password;
use CoreLibs\Convert\Json;
class Login
{
@@ -196,7 +197,12 @@ class Login
/** @var array<string,mixed> options */
private $options = [];
/** @var array<string,string> locale options: locale, domain, encoding (opt), path */
private $locale = [];
private $locale = [
'locale' => '',
'domain' => '',
'encoding' => '',
'path' => '',
];
/** @var \CoreLibs\Debug\Logging logger */
public $log;
@@ -423,7 +429,7 @@ class Login
/**
* Set options
* Current allowed
* Current allowed:
* target <string>: site target
* debug <bool>
* auto_login <bool>: self start login process
@@ -555,12 +561,12 @@ class Login
);
// set path
$options['locale_path'] = BASE . INCLUDES . LOCALE;
$_SESSION['LOCALE_PATH'] = $options['locale_path'];
}
$_SESSION['LOCALE_PATH'] = $options['locale_path'];
// LANG: LOCALE
if (empty($options['site_locale'])) {
trigger_error(
'loginMainCall: SITE_LOCALE or DEFAULT_LOCALE should not be used',
'loginMainCall: SITE_LOCALE should not be used',
E_USER_DEPRECATED
);
$options['site_locale'] = defined('SITE_LOCALE') && !empty(SITE_LOCALE) ?
@@ -580,7 +586,6 @@ class Login
);
// set domain
$options['site_domain'] = SITE_DOMAIN;
$_SESSION['DEFAULT_DOMAIN'] = $options['site_domain'];
} elseif (
defined('CONTENT_PATH')
) {
@@ -592,6 +597,16 @@ class Login
$options['set_domain'] = str_replace(DIRECTORY_SEPARATOR, '', CONTENT_PATH);
}
}
$_SESSION['DEFAULT_DOMAIN'] = $options['site_domain'];
// LANG: ENCODING
if (empty($options['site_encoding'])) {
trigger_error(
'loginMainCall: SITE_ENCODING should not be used',
E_USER_DEPRECATED
);
$options['site_encoding'] = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
SITE_ENCODING : 'UTF-8';
}
// write array to options
$this->options = $options;
@@ -739,7 +754,10 @@ class Login
// we have to get the themes in here too
$q = "SELECT eu.edit_user_id, eu.username, eu.password, "
. "eu.edit_group_id, "
. "eg.name AS edit_group_name, admin, "
. "eg.name AS edit_group_name, eu.admin, "
// additinal acl lists
. "eu.additional_acl AS user_additional_acl, "
. "eg.additional_acl AS group_additional_acl, "
// login error + locked
. "eu.login_error_count, eu.login_error_date_last, "
. "eu.login_error_date_first, eu.strict, eu.locked, "
@@ -887,8 +905,10 @@ class Login
$_SESSION['GROUP_NAME'] = $res['edit_group_name'];
$_SESSION['USER_ACL_LEVEL'] = $res['user_level'];
$_SESSION['USER_ACL_TYPE'] = $res['user_type'];
$_SESSION['USER_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['user_additional_acl']);
$_SESSION['GROUP_ACL_LEVEL'] = $res['group_level'];
$_SESSION['GROUP_ACL_TYPE'] = $res['group_type'];
$_SESSION['GROUP_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['group_additional_acl']);
// deprecated TEMPLATE setting
$_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : '';
$_SESSION['HEADER_COLOR'] = !empty($res['second_header_color']) ?
@@ -905,6 +925,7 @@ class Login
// rgb: nnn.n for each
// hsl: nnn.n for first, nnn.n% for 2nd, 3rd
// Check\Colors::validateColor()
// LANGUAGE/LOCALE/ENCODING:
$_SESSION['LANG'] = $res['locale'] ?? 'en';
$_SESSION['DEFAULT_CHARSET'] = $res['encoding'] ?? 'UTF-8';
$_SESSION['DEFAULT_LOCALE'] = $_SESSION['LANG']
@@ -1006,7 +1027,8 @@ class Login
$_SESSION['PAGES'] = $pages;
$_SESSION['PAGES_ACL_LEVEL'] = $pages_acl;
// load the edit_access user rights
$q = "SELECT ea.edit_access_id, level, type, ea.name, ea.color, ea.uid, edit_default "
$q = "SELECT ea.edit_access_id, level, type, ea.name, "
. "ea.color, ea.uid, edit_default, ea.additional_acl "
. "FROM edit_access_user eau, edit_access_right ear, edit_access ea "
. "WHERE eau.edit_access_id = ea.edit_access_id "
. "AND eau.edit_access_right_id = ear.edit_access_right_id "
@@ -1033,6 +1055,7 @@ class Login
'uid' => $res['uid'],
'color' => $res['color'],
'default' => $res['edit_default'],
'additional_acl' => Json::jsonConvertToArray($res['additional_acl']),
'data' => $ea_data
];
// set the default unit
@@ -1107,6 +1130,11 @@ class Login
// username (login), group name
$this->acl['user_name'] = $_SESSION['USER_NAME'];
$this->acl['group_name'] = $_SESSION['GROUP_NAME'];
// set additional acl
$this->acl['additional_acl'] = [
'user' => $_SESSION['USER_ADDITIONAL_ACL'],
'group' => $_SESSION['GROUP_ADDITIONAL_ACL'],
];
// we start with the default acl
$this->acl['base'] = $this->default_acl_level;
@@ -1169,7 +1197,8 @@ class Login
'uid' => $unit['uid'],
'level' => $this->default_acl_list[$this->acl['unit'][$ea_id]]['name'] ?? -1,
'default' => $unit['default'],
'data' => $unit['data']
'data' => $unit['data'],
'additional_acl' => $unit['additional_acl']
];
// set default
if (!empty($unit['default'])) {
@@ -1195,7 +1224,8 @@ class Login
}
/**
* set locale and load mo translator
* set locale
* if invalid, set to empty string
*
* @return void
*/
@@ -1204,22 +1234,52 @@ class Login
// ** LANGUAGE SET AFTER LOGIN **
// set the locale
if (
!empty($_SESSION['DEFAULT_LOCALE'])
!empty($_SESSION['DEFAULT_LOCALE']) &&
preg_match("/^[-A-Za-z0-9_.@]+$/", $_SESSION['DEFAULT_LOCALE'])
) {
$locale = $_SESSION['DEFAULT_LOCALE'];
} else {
} elseif (
!preg_match("/^[-A-Za-z0-9_.@]+$/", $this->options['site_locale'])
) {
$locale = $this->options['site_locale'];
} else {
$locale = '';
}
// set the charset
preg_match('/(?:\\.(?P<charset>[-A-Za-z0-9_]+))/', $locale, $matches);
$locale_encoding = $matches['charset'] ?? '';
if (!empty($locale_encoding)) {
$encoding = strtoupper($locale_encoding);
} elseif (
!empty($_SESSION['DEFAULT_CHARSET']) &&
preg_match("/^[-A-Za-z0-9_]+$/", $_SESSION['DEFAULT_CHARSET'])
) {
$encoding = $_SESSION['DEFAULT_CHARSET'];
} elseif (
!preg_match("/^[-A-Za-z0-9_]+$/", $this->options['site_encoding'])
) {
$encoding = $this->options['site_encoding'];
} else {
$encoding = '';
}
// check domain
$domain = $this->options['site_domain'];
if (
!preg_match("/^\w+$/", $this->options['site_domain'])
) {
$domain = '';
}
$path = $this->options['locale_path'];
if (!is_dir($path)) {
$path = '';
}
// domain and path are a must set from class options
$this->locale = [
'locale' => $locale,
'domain' => $this->options['site_domain'],
'path' => $this->options['locale_path'],
'domain' => $domain,
'encoding' => $encoding,
'path' => $path,
];
$this->l = new \CoreLibs\Language\L10n(
$this->locale['locale'],
$this->locale['domain'],
$this->locale['path']
);
}
/**
@@ -1824,6 +1884,12 @@ EOM;
$this->loginLogoutUser();
// ** LANGUAGE SET AFTER LOGIN **
$this->loginSetLocale();
// load translator
$this->l = new \CoreLibs\Language\L10n(
$this->locale['locale'],
$this->locale['domain'],
$this->locale['path']
);
// if the password change flag is okay, run the password change method
if ($this->password_change) {
$this->loginPasswordChange();
@@ -2396,6 +2462,53 @@ EOM;
): bool|string {
return $this->loginGetEditAccessData($edit_access_id, $data_key);
}
/**
* Return locale settings with
* locale
* domain
* encoding
* path
*
* empty string if not set
*
* @return array<string,string> Locale settings
*/
public function loginGetLocale(): array
{
return $this->locale;
}
/**
* return header color or null for not set
*
* @return string|null Header color in RGB hex with leading sharp
*/
public function loginGetHeaderColor(): ?string
{
return $_SESSION['HEADER_COLOR'] ?? null;
}
/**
* Return the current loaded list of pages the user can access
*
* @return array<mixed>
*/
public function loginGetPages(): array
{
return $_SESSION['PAGES'] ?? [];
}
/**
* Get the current set EUID (edit user id)
*
* @return string EUID as string
*/
public function loginGetEuid(): string
{
return $this->euid;
}
}
// __END__

View File

@@ -121,14 +121,13 @@ class Backend
* @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
* @param int|null $set_default_acl_level Default ACL level
*/
public function __construct(
\CoreLibs\DB\IO $db,
\CoreLibs\Debug\Logging $log,
\CoreLibs\Create\Session $session,
\CoreLibs\Language\L10n $l10n,
array $locale,
?int $set_default_acl_level = null
) {
// attach db class
@@ -142,12 +141,12 @@ class Backend
// get the language sub class & init it
$this->l = $l10n;
// parse and read, legacy stuff
$locale = $this->l->getLocaleAsArray();
$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();
$this->lang_short = $locale['lang_short'];
$this->domain = $locale['domain'];
$this->lang_dir = $locale['path'];
// set the page name
$this->page_name = \CoreLibs\Get\System::getPageName();

View File

@@ -35,6 +35,8 @@ class EditBase
private $form;
/** @var \CoreLibs\Debug\Logging */
public $log;
/** @var \CoreLibs\ACL\Login */
public $login;
/**
* construct form generator
@@ -42,18 +44,24 @@ class EditBase
* @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
* @param \CoreLibs\ACL\Login $login login class for ACL settings
* @param array<string,mixed> $options Various settings options
*/
public function __construct(
array $db_config,
\CoreLibs\Debug\Logging $log,
\CoreLibs\Language\L10n $l10n,
array $locale
\CoreLibs\ACL\Login $login,
array $options
) {
$this->log = $log;
$this->login = $login;
// smarty template engine (extended Translation version)
$this->smarty = new \CoreLibs\Template\SmartyExtend($l10n, $locale);
$this->smarty = new \CoreLibs\Template\SmartyExtend(
$l10n,
$options['cache_id'] ?? '',
$options['compile_id'] ?? '',
);
// turn off set log per class
$log->setLogPer('class', false);
@@ -62,7 +70,7 @@ class EditBase
$db_config,
$log,
$l10n,
$locale
$this->login->loginGetAcl()
);
if ($this->form->mobile_phone) {
echo "I am sorry, but this page cannot be viewed by a mobile phone";
@@ -272,23 +280,16 @@ class EditBase
// 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->DATA['HEADER_COLOR'] = $this->login->loginGetHeaderColor() ?? '#E0E2FF';
$this->DATA['USER_NAME'] = $this->login->loginGetAcl()['user_name'] ?? '';
$this->DATA['EUID'] = $this->login->loginGetEuid();
$this->DATA['GROUP_NAME'] = $this->login->loginGetAcl()['group_name'] ?? '';
$this->DATA['ACCESS_LEVEL'] = $this->login->loginGetAcl()['base'] ?? '';
// below is old and to removed when edit_body.tpl is updates
$this->DATA['GROUP_LEVEL'] = $this->DATA['ACCESS_LEVEL'];
$PAGES = $this->login->loginGetPages();
//$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']) {
@@ -574,7 +575,7 @@ class EditBase
$set_admin_stylesheet = $set_admin_stylesheet ?? ADMIN_STYLESHEET;
$set_default_encoding = $set_default_encoding ?? DEFAULT_ENCODING;
$set_css = $set_css ?? LAYOUT . CSS;
$set_css = $set_js ?? LAYOUT . JS;
$set_js = $set_js ?? LAYOUT . JS;
$set_root = $set_root ?? ROOT;
$set_content_path = $set_content_path ?? CONTENT_PATH;

View File

@@ -69,12 +69,16 @@
* - sets the SQL query (will be set with the $query parameter from method)
* if u leave the parameter free the class will try to use this var, but this
* method is not so reccomended
* $params
* - array for query parameters, if not set, ignored
* $num_rows
* - the number of rows returned by a SELECT or alterd bei UPDATE/INSERT
* $num_fields
* - the number of fields from the SELECT, is usefull if you do a SELECT *
* $field_names
* - array of field names (in the order of the return)
* $field_types
* - array of field types
* $insert_id
* - for INSERT with auto_increment PK, the ID is stored here
* $error_msg
@@ -258,7 +262,6 @@ use CoreLibs\Create\Uids;
// 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 IO
{
@@ -276,8 +279,20 @@ class IO
public const NO_CACHE = 3;
/** @var string default hash type */
public const ERROR_HASH_TYPE = 'adler32';
/**
* @var string regex for params: only stand alone $number allowed
* never allowed to start with '
* must be after space/tab, =, (
*/
public const REGEX_PARAMS = '/[^\'][\s(=](\$[0-9]{1,})/';
/** @var string regex to get returning with matches at position 1 */
public const REGEX_RETURNING = '/\s+returning\s+(.+?);?$/i';
public const REGEX_RETURNING = '/\s+returning\s+(.+\s*(?:.+\s*)+);?$/i';
// REGEX_SELECT
// REGEX_UPDATE
// REGEX INSERT
// REGEX_INSERT_UPDATE_DELETE
// REGEX_FROM_TABLE
// REGEX_INSERT_UPDATE_DELETE_TABLE
// recommend to set private/protected and only allow setting via method
// can bet set from outside
@@ -286,10 +301,12 @@ class IO
private $to_encoding = '';
/** @var string */
private $query; // the query string at the moment
/** @var array<mixed> */
private $params; // current params for query
// only inside
// basic vars
/** @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,
private $dbh; // the dbh handler, if disconnected by command is null, bool:false on error,
/** @var bool */
private $db_debug = false; // DB_DEBUG ... (if set prints out debug msgs)
/** @var string */
@@ -321,8 +338,10 @@ class IO
private $num_rows; // how many rows have been found
/** @var int */
private $num_fields; // how many fields has the query
/** @var array<mixed> */
private $field_names = []; // array with the field names of the current query
/** @var array<string> array with the field names of the current query */
private $field_names = [];
/** @var array<string> field type names */
private $field_types = [];
/** @var array<mixed> */
private $insert_id_arr = []; // always return as array, even if only one
/** @var string */
@@ -405,9 +424,9 @@ class IO
$db_debug_override ??
// from db config setting
$db_config['db_debug'] ??
// should be handled from outside
// [DEPRECATED] should be handled from outside
$_SESSION['DB_DEBUG'] ??
// globals should be deprecated
// [DEPRECATED] globals should be deprecated
$GLOBALS['DB_DEBUG'] ??
false
);
@@ -652,13 +671,16 @@ class IO
* @param string $error_string error message or debug data
* @param string $id db debug group
* @param string $type query identifier (Q, I, etc)
* @return void has no return
* @param array<mixed> $error_data Optional error data as array
* Will be printed after main error string
* @return void
*/
protected function __dbDebug(
string $debug_id,
string $error_string,
string $id = '',
string $type = ''
string $type = '',
array $error_data = []
): void {
// NOTE prefix allows html for echo output, will be stripped on file print
$prefix = '';
@@ -679,6 +701,11 @@ class IO
if ($prefix) {
$prefix .= '- ';
}
if ($error_data !== []) {
$error_string .= '<br>['
. $this->log->prAr($error_data)
. ']';
}
$this->log->debug($debug_id, $error_string, true, $prefix);
}
@@ -860,20 +887,33 @@ class IO
/**
* for debug purpose replaces $1, $2, etc with actual data
* @param string $stm_name prepared statement name
* @param array<mixed> $data the data array
* @param string $query Query to replace values in
* @param array<mixed> $data The data array
* @return string string of query with data inside
*/
private function __dbDebugPrepare(string $stm_name, array $data = []): string
private function __dbDebugPrepare(string $query, array $data = []): string
{
// skip anything if there is no data
if ($data === []) {
return $query;
}
// get the keys from data array
$keys = array_keys($data);
// because the placeholders start with $ and at 1, we need to increase each key and prefix it with a $ char
// because the placeholders start with $ and at 1,
// we need to increase each key and prefix it with a $ char
for ($i = 0, $iMax = count($keys); $i < $iMax; $i++) {
$keys[$i] = '$' . ($keys[$i] + 1);
// prefix data set with parameter pos
$data[$i] = $keys[$i] . ':' . ($data[$i] === null ?
'"NULL"' : (string)$data[$i]
);
}
// simply replace the $1, $2, ... with the actual data and return it
return str_replace(array_reverse($keys), array_reverse($data), $this->prepare_cursor[$stm_name]['query']);
return str_replace(
array_reverse($keys),
array_reverse($data),
$query
);
}
/**
@@ -977,6 +1017,33 @@ class IO
return $return;
}
/**
* Checks if the placeholder count in the query matches the params given
* on call
*
* @param string $query Query to check
* @param int $params_count The parms count expected
* @return bool True for params count ok, else false
*/
private function __dbCheckQueryParams(string $query, int $params_count): bool
{
// search for $1, $2, in the query and push it into the control array
// skip counts for same eg $1, $1, $2 = 2 and not 3
preg_match_all(self::REGEX_PARAMS, $query, $match);
$placeholder_count = count(array_unique($match[1]));
if ($params_count != $placeholder_count) {
$this->__dbError(
23,
false,
'Array data count does not match prepared fields. Need: '
. $placeholder_count . ', has: '
. $params_count
);
return false;
}
return true;
}
/**
* sub function for dbExec and dbExecAsync
* - checks query is set
@@ -985,13 +1052,17 @@ class IO
* - checks for insert if returning is set/pk name
* - sets internal hash for query
* - checks multiple call count
* @param string $query query string
* @param string $query Query string
* @param array<mixed> $params Query params, needed for hash creation
* @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): string|false
{
private function __dbPrepareExec(
string $query,
array $params,
string $pk_name
): string|false {
// reset current cursor before exec
$this->cursor = false;
// clear matches for regex lookups
@@ -1001,6 +1072,8 @@ class IO
$this->returning_id = false;
// set the query
$this->query = $query;
// current params
$this->params = $params;
// no query set
if (empty($this->query)) {
$this->__dbError(11);
@@ -1066,10 +1139,18 @@ class IO
// $this->debug('DB IO', 'Q: '.$this->query.', RETURN: '.$this->returning_id);
// for DEBUG, only on first time ;)
if ($this->db_debug) {
$this->__dbDebug('db', $this->query, '__dbPrepareExec', 'Q');
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->query,
$this->params
),
'__dbPrepareExec',
($this->params === [] ? 'Q' : 'Qp')
);
}
// import protection, hash needed
$query_hash = $this->dbGetQueryHash($this->query);
$query_hash = $this->dbGetQueryHash($this->query, $this->params);
// if the array index does not exists set it 0
if (!array_key_exists($query_hash, $this->query_called)) {
$this->query_called[$query_hash] = 0;
@@ -1085,7 +1166,15 @@ class IO
$this->query_called[$query_hash] > $this->MAX_QUERY_CALL
) {
$this->__dbError(30, false, $this->query);
$this->__dbDebug('db', $this->query, 'dbExec', 'Q[nc]');
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->query,
$this->params
),
'dbExec',
($this->params === [] ? 'Q[nc]' : 'Qp[nc]')
);
return false;
}
$this->query_called[$query_hash] ++;
@@ -1121,7 +1210,11 @@ class IO
// set field names
$this->field_names = [];
for ($i = 0; $i < $this->num_fields; $i++) {
$this->field_names[] = $this->db_functions->__dbFieldName($this->cursor, $i);
$this->field_names[] = $this->db_functions->__dbFieldName($this->cursor, $i) ?: '';
}
$this->field_types = [];
for ($i = 0; $i < $this->num_fields; $i++) {
$this->field_types[] = $this->db_functions->__dbFieldType($this->cursor, $i) ?: '';
}
} elseif ($this->__checkQueryForInsert($this->query)) {
// if not select do here
@@ -1745,8 +1838,10 @@ class IO
* the stored array will be deleted ...
* - if set to 3, after EACH row, the data will be reset,
* no caching is done except for basic (count, etc)
* Wrapper for dbReturnParams
*
* @param string $query Query string
* @param int $cache reset status: default: USE_CACHE
* @param int $cache reset status: default: NO_CACHE
* USE_CACHE/0: normal read from cache on second run
* READ_NEW/1: write to cache, clean before new run
* CLEAR_CACHE/2: write cache, clean after finished
@@ -1758,7 +1853,42 @@ class IO
*/
public function dbReturn(
string $query,
int $cache = self::USE_CACHE,
int $cache = self::NO_CACHE,
bool $assoc_only = false
): array|false {
return $this->dbReturnParams($query, [], $cache, $assoc_only);
}
/**
* single running function, if called creates hash from
* query string and so can itself call exec/return calls
* caches data, so next time called with IDENTICAL (!!!!)
* [this means 1:1 bit to bit identical query] returns cached
* data, or with reset flag set calls data from DB again
* NOTE on $cache param:
* - if set to 0, if same query run again, will read from cache
* - if set to 1, the data will be read new and cached, cache reset a new run
* (wheres 1 reads cache AND destroys at end of read)
* - if set to 2, at the end of the query (last row returned),
* the stored array will be deleted ...
* - if set to 3, after EACH row, the data will be reset,
* no caching is done except for basic (count, etc)
*
* @param string $query Query string
* @param array<mixed> $params Query parameters
* @param int $cache reset status: default: NO_CACHE
* USE_CACHE/0: normal read from cache on second run
* READ_NEW/1: write to cache, clean before new run
* CLEAR_CACHE/2: write cache, clean after finished
* 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>|false return array data or false on error/end
*/
public function dbReturnParams(
string $query,
array $params = [],
int $cache = self::NO_CACHE,
bool $assoc_only = false
): array|false {
$this->__dbErrorReset();
@@ -1767,7 +1897,7 @@ class IO
return false;
}
// create hash from query ...
$query_hash = $this->dbGetQueryHash($query);
$query_hash = $this->dbGetQueryHash($query, $params);
// pre declare array
if (!isset($this->cursor_ext[$query_hash])) {
$this->cursor_ext[$query_hash] = [
@@ -1787,6 +1917,8 @@ class IO
'pos' => 0,
// the query used in this call
'query' => '',
// parameter
'params' => [],
// cache flag from method call
'cache_flag' => $cache,
// flag if we only have assoc data
@@ -1816,6 +1948,25 @@ class IO
$this->__dbError(17, false, $this->cursor_ext[$query_hash]['query']);
return false;
}
// set the query parameters
$this->cursor_ext[$query_hash]['params'] = $params;
// check if params count matches
// checks if the params count given matches the expected count
if ($this->__dbCheckQueryParams($query, count($params)) === false) {
// in case we got an error print out query
if ($this->db_debug) {
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->query,
$this->params
),
'dbReturn',
($this->params === [] ? 'Q[e]' : 'Qp[e]')
);
}
return false;
}
// set first call to false
$first_call = false;
// init return als false
@@ -1837,7 +1988,15 @@ class IO
$this->cursor_ext[$query_hash]['log'][] = 'No cursor';
// for DEBUG, print out each query executed
if ($this->db_debug) {
$this->__dbDebug('db', $this->cursor_ext[$query_hash]['query'], 'dbReturn', 'Q');
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->cursor_ext[$query_hash]['query'],
$this->cursor_ext[$query_hash]['params']
),
'dbReturn',
($this->cursor_ext[$query_hash]['params'] === [] ? 'Q' : 'Qp'),
);
}
// if no DB Handler try to reconnect
if (!$this->dbh) {
@@ -1851,12 +2010,30 @@ class IO
if (!$this->__dbCheckConnectionOk()) {
return false;
}
if ($this->cursor_ext[$query_hash]['params'] === []) {
$this->cursor_ext[$query_hash]['cursor'] =
$this->db_functions->__dbQuery($this->cursor_ext[$query_hash]['query']);
$this->db_functions->__dbQuery(
$this->cursor_ext[$query_hash]['query']
);
} else {
$this->cursor_ext[$query_hash]['cursor'] =
$this->db_functions->__dbQueryParams(
$this->cursor_ext[$query_hash]['query'],
$this->cursor_ext[$query_hash]['params']
);
}
// if still no cursor ...
if (!$this->cursor_ext[$query_hash]['cursor']) {
if ($this->db_debug) {
$this->__dbDebug('db', $this->cursor_ext[$query_hash]['query'], 'dbReturn', 'Q');
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->cursor_ext[$query_hash]['query'],
$this->cursor_ext[$query_hash]['params']
),
'dbReturn',
($this->cursor_ext[$query_hash]['params'] === [] ? 'Q[e]' : 'Qp[e]'),
);
}
// internal error handling
$this->__dbError(13, $this->cursor_ext[$query_hash]['cursor']);
@@ -1889,6 +2066,16 @@ class IO
);
}
$this->field_names = $this->cursor_ext[$query_hash]['field_names'];
// field types
$this->cursor_ext[$query_hash]['field_types'] = [];
for ($i = 0; $i < $this->cursor_ext[$query_hash]['num_fields']; $i++) {
$this->cursor_ext[$query_hash]['field_types'][] =
$this->db_functions->__dbFieldType(
$this->cursor_ext[$query_hash]['cursor'],
$i
);
}
$this->field_types = $this->cursor_ext[$query_hash]['field_types'];
// reset first call var
$first_call = false;
// reset the internal pos counter
@@ -2009,9 +2196,10 @@ class IO
* like num_rows, num_fields, etc depending on query
* for INSERT INTO queries it is highly recommended to set the pk_name to avoid an
* additional read from the database for the PK NAME
* Wrapper for dbExecParams without params
* @param string $query the query, if not given,
* the query class var will be used
* if this was not set, method will quit with a 0 (failure)
* if this was not set, method will quit with false
* @param string $pk_name optional primary key name, for insert id
* return if the pk name is very different
* if pk name is table name and _id, pk_name
@@ -2019,16 +2207,50 @@ class IO
* if NULL is given here, no RETURNING will be auto added
* @return \PgSql\Result|false cursor for this query or false on error
*/
public function dbExec(string $query = '', string $pk_name = ''): \PgSql\Result|false
{
public function dbExec(
string $query = '',
string $pk_name = ''
): \PgSql\Result|false {
// just calls the same without any params
// which will trigger normal pg_query call
return $this->dbExecParams($query, [], $pk_name);
}
/**
* Execute any query, but with the use of placeholders
*
* @param string $query Query, if not given, query class var will be used
* if this was not set, method will quit with false
* @param array<mixed> $params Parameters to be replaced.
* NOTE: bytea data cannot be used here (pg_query_params)
* @param string $pk_name Optional primary key name, for insert id
* return if the pk name is very different
* 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 \PgSql\Result|false cursor for this query or false on error
*/
public function dbExecParams(
string $query = '',
array $params = [],
string $pk_name = ''
): \PgSql\Result|false {
$this->__dbErrorReset();
// prepare and check if we can actually run it
if ($this->__dbPrepareExec($query, $pk_name) === false) {
if ($this->__dbPrepareExec($query, $params, $pk_name) === false) {
// bail if no query hash set
return false;
}
// checks if the params count given matches the expected count
if ($this->__dbCheckQueryParams($query, count($params)) === false) {
return false;
}
// ** actual db exec call
if ($params === []) {
$cursor = $this->db_functions->__dbQuery($this->query);
} else {
$cursor = $this->db_functions->__dbQueryParams($this->query, $params);
}
// if we faield, just set the master cursors to false too
$this->cursor = $cursor;
if ($cursor === false) {
@@ -2079,12 +2301,30 @@ class IO
/**
* returns the FIRST row of the given query
* wrapper for dbReturnRowParms
* @param string $query the query to be executed
* @param bool $assoc_only if true, only return assoc entry (default false)
* @return array<mixed>|false row array or false on error
*/
public function dbReturnRow(string $query, bool $assoc_only = false): array|false
{
return $this->dbReturnRowParams($query, [], $assoc_only);
}
/**
* Returns the first row only for the given query
* Uses db_query_params
*
* @param string $query the query to be executed
* @param array<mixed> $params params to be used in query
* @param bool $assoc_only if true, only return assoc entry (default false)
* @return array<mixed>|false row array or false on error
*/
public function dbReturnRowParams(
string $query,
array $params = [],
bool $assoc_only = false
): array|false {
$this->__dbErrorReset();
if (!$query) {
$this->__dbError(11);
@@ -2096,7 +2336,7 @@ class IO
$this->__dbError(17, false, $query);
return false;
}
$cursor = $this->dbExec($query);
$cursor = $this->dbExecParams($query, $params);
if ($cursor === false) {
return false;
}
@@ -2105,13 +2345,31 @@ class IO
}
/**
* createds an array of hashes of the query (all data)
* creates an array of hashes of the query (all data)
* Wrapper for dbReturnArrayParams
* @param string $query the query to be executed
* @param bool $assoc_only if true, only name ref are returned (default true)
* @return array<mixed>|false array of hashes (row -> fields), false on error
*/
public function dbReturnArray(string $query, bool $assoc_only = true): array|false
{
return $this->dbReturnArrayParams($query, [], $assoc_only);
}
/**
* Creates an array of hashes of all data returned from the query
* uses db_query_param
*
* @param string $query the query to be executed
* @param array<mixed> $params params to be used in query
* @param bool $assoc_only if true, only name ref are returned (default true)
* @return array<mixed>|false array of hashes (row -> fields), false on error
*/
public function dbReturnArrayParams(
string $query,
array $params = [],
bool $assoc_only = true
): array|false {
$this->__dbErrorReset();
if (!$query) {
$this->__dbError(11);
@@ -2122,17 +2380,12 @@ class IO
$this->__dbError(17, false, $query);
return false;
}
$cursor = $this->dbExec($query);
$cursor = $this->dbExecParams($query, $params);
if ($cursor === false) {
return false;
}
$rows = [];
while (is_array($res = $this->dbFetchArray($cursor, $assoc_only))) {
// $data = [];
// for ($i = 0; $i < $this->num_fields; $i++) {
// $data[$this->field_names[$i]] = $res[$this->field_names[$i]] ?? null;
// }
// $rows[] = $data;
$rows[] = $res;
}
return $rows;
@@ -2145,12 +2398,14 @@ class IO
/**
* resets all data stored to this query
* @param string $query The Query whose cache should be cleaned
* @return bool false if query not found, true if success
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return bool False if query not found, true if success
*/
public function dbCacheReset(string $query): bool
public function dbCacheReset(string $query, array $params = []): bool
{
$this->__dbErrorReset();
$query_hash = $this->dbGetQueryHash($query);
$query_hash = $this->dbGetQueryHash($query, $params);
// clears cache for this query
if (empty($this->cursor_ext[$query_hash]['query'])) {
$this->__dbError(18);
@@ -2168,9 +2423,11 @@ 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
* @param array<mixed> $params Optional params for query hash get
* @param string $query_field [=''] optional query field to get
* @return array<mixed>|string|int|\PgSql\Result|null
* Cursor Extended array full if no parameter
@@ -2179,13 +2436,14 @@ class IO
* If nothing found return null
*/
public function dbGetCursorExt(
$query = null,
?string $query = null,
array $params = [],
string $query_field = ''
): array|string|int|\PgSql\Result|null {
if ($query === null) {
return $this->cursor_ext;
}
$query_hash = $this->dbGetQueryHash($query);
$query_hash = $this->dbGetQueryHash($query, $params);
if (
is_array($this->cursor_ext) &&
isset($this->cursor_ext[$query_hash])
@@ -2202,33 +2460,39 @@ class IO
/**
* returns the current position the read out
* @param string $query query to find in cursor_ext
*
* @param string $query Query to find in cursor_ext
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return int|false query position (row pos), false on error
*/
public function dbGetCursorPos(string $query): int|false
public function dbGetCursorPos(string $query, array $params = []): int|false
{
$this->__dbErrorReset();
if (!$query) {
$this->__dbError(11);
return false;
}
$query_hash = $this->dbGetQueryHash($query);
$query_hash = $this->dbGetQueryHash($query, $params);
return (int)$this->cursor_ext[$query_hash]['pos'];
}
/**
* returns the number of rows for the current select query
* @param string $query query to find in cursor_ext
*
* @param string $query Query to find in cursor_ext
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return int|false query position (row pos), false on error
*/
public function dbGetCursorNumRows(string $query): int|false
public function dbGetCursorNumRows(string $query, array $params = []): int|false
{
$this->__dbErrorReset();
if (!$query) {
$this->__dbError(11);
return false;
}
$query_hash = $this->dbGetQueryHash($query);
$query_hash = $this->dbGetQueryHash($query, $params);
return (int)$this->cursor_ext[$query_hash]['num_rows'];
}
@@ -2239,22 +2503,27 @@ class IO
/**
* resets the call times for the max query called to 0
* USE CAREFULLY: rather make the query prepare -> execute
*
* @param string $query query string
* @return void has no return
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return void
*/
public function dbResetQueryCalled(string $query): void
public function dbResetQueryCalled(string $query, array $params = []): void
{
$this->query_called[$this->dbGetQueryHash($query)] = 0;
$this->query_called[$this->dbGetQueryHash($query, $params)] = 0;
}
/**
* gets how often a query was called already
* @param string $query query string
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return int count of times the query was executed
*/
public function dbGetQueryCalled(string $query): int
public function dbGetQueryCalled(string $query, array $params = []): int
{
$query_hash = $this->dbGetQueryHash($query);
$query_hash = $this->dbGetQueryHash($query, $params);
if (!empty($this->query_called[$query_hash])) {
return $this->query_called[$query_hash];
} else {
@@ -2268,7 +2537,8 @@ class IO
/**
* prepares a query
* for INSERT INTO queries it is highly recommended to set the pk_name to avoid an additional
* for INSERT INTO queries it is highly recommended
* to set the pk_name to avoid an additional
* read from the database for the PK NAME
* @param string $stm_name statement name
* @param string $query queryt string to run
@@ -2358,7 +2628,7 @@ class IO
$match = [];
// search for $1, $2, in the query and push it into the control array
// skip counts for same eg $1, $1, $2 = 2 and not 3
preg_match_all('/(\$[0-9]{1,})/', $query, $match);
preg_match_all(self::REGEX_PARAMS, $query, $match);
$this->prepare_cursor[$stm_name]['count'] = count(array_unique($match[1]));
$this->prepare_cursor[$stm_name]['query'] = $query;
$result = $this->db_functions->__dbPrepare($stm_name, $query);
@@ -2419,6 +2689,17 @@ class IO
);
return false;
}
if ($this->db_debug) {
$this->__dbDebug(
'db',
$this->__dbDebugPrepare(
$this->prepare_cursor[$stm_name]['query'],
$data
),
'dbExecPrep',
'Qpe'
);
}
// if the count does not match
if ($this->prepare_cursor[$stm_name]['count'] != count($data)) {
$this->__dbError(
@@ -2431,9 +2712,6 @@ class IO
);
return false;
}
if ($this->db_debug) {
$this->__dbDebug('db', $this->__dbDebugPrepare($stm_name, $data), 'dbExecPrep', 'Q');
}
$result = $this->db_functions->__dbExecute($stm_name, $data);
if ($result === false) {
$this->log->debug('ExecuteData', 'ERROR in STM[' . $stm_name . '|'
@@ -2471,27 +2749,58 @@ class IO
// ***************************
/**
* executres the query async so other methods can be run during this
* for INSERT INTO queries it is highly recommended to set the pk_name
* to avoid an additional read from the database for the PK NAME
* executes the query async so other methods can be run at the same time
* Wrapper for dbExecParamsAsync
* NEEDS : dbCheckAsync
* @param string $query query to run
* @param string $pk_name optional primary key name, only used with
* insert for returning call
* @return bool true if async query was sent ok,
* false if error happened
* false on error
*/
public function dbExecAsync(string $query, string $pk_name = ''): bool
{
return $this->dbExecParamsAsync($query, [], $pk_name);
}
/**
* eexecutes the query async so other methods can be run at the same time
* Runs with db_send_query_params
* NEEDS : dbCheckAsync
*
* @param string $query query to run
* @param array<mixed> $params
* @param string $pk_name optional primary key name, only used with
* insert for returning call
* @return bool true if async query was sent ok,
* false on error
*/
public function dbExecParamsAsync(
string $query,
array $params = [],
string $pk_name = ''
): bool {
$this->__dbErrorReset();
// prepare and check if we can actually run the query
if (($query_hash = $this->__dbPrepareExec($query, $pk_name)) === false) {
if (
($query_hash = $this->__dbPrepareExec($query, $params, $pk_name)) === false
) {
// bail if no hash set
return false;
}
// checks if the params count given matches the expected count
if ($this->__dbCheckQueryParams($query, count($params)) === false) {
return false;
}
// ** actual db exec call
if ($params === []) {
$status = $this->db_functions->__dbSendQuery($this->query);
} else {
$status = $this->db_functions->__dbSendQueryParams($this->query, $params);
}
// run the async query, this just returns true or false
// the actually result is in dbCheckAsync
if (!$this->db_functions->__dbSendQuery($this->query)) {
if (!$status) {
// if failed, process here
$this->__dbError(40);
return false;
@@ -2503,6 +2812,42 @@ class IO
}
}
/**
* TODO write dbPrepareAsync
* Asnychronus prepare call
* NEEDS : dbCheckAsync
*
* @param string $stm_name
* @param string $query
* @param string $pk_name
* @return bool
*/
public function dbPrepareAsync(
string $stm_name,
string $query,
string $pk_name = ''
): bool {
$status = $this->db_functions->__dbSendPrepare($stm_name, $query);
return $status;
}
/**
* TODO write dbExecuteAsync
* Asynchronus execute call
* NEEDS : dbCheckAsync
*
* @param string $stm_name
* @param array<mixed> $data
* @return bool
*/
public function dbExecuteAsync(
string $stm_name,
array $data = []
): bool {
$status = $this->db_functions->__dbSendExecute($stm_name, $data);
return $status;
}
/**
* checks a previous async query and returns data if finished
* NEEDS : dbExecAsync
@@ -2953,6 +3298,7 @@ class IO
/**
* Return current database handler
*
* @return \PgSql\Connection|false|null
*/
public function dbGetDbh(): \PgSql\Connection|false|null
@@ -2963,16 +3309,25 @@ class IO
/**
* Returns hash for query
* Hash is used in all internal storage systems for return data
*
* @param string $query The query to create the hash from
* @return string Hash, as set by hash lpng
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return string Hash, as set by hash long
*/
public function dbGetQueryHash(string $query): string
public function dbGetQueryHash(string $query, array $params = []): string
{
return Hash::__hashLong($query);
return Hash::__hashLong(
$query . (
$params !== [] ?
'#' . json_encode($params) : ''
)
);
}
/**
* Get current set query
*
* @return string Current set query string
*/
public function dbGetQuery(): string
@@ -2982,6 +3337,7 @@ class IO
/**
* Clear current query
*
* @return void
*/
public function dbResetQuery(): void
@@ -2989,6 +3345,26 @@ class IO
$this->query = '';
}
/**
* Get current set params
*
* @return array<mixed>
*/
public function dbGetParams(): array
{
return $this->params;
}
/**
* Rset current set params
*
* @return void
*/
public function dbResetParams(): void
{
$this->params = [];
}
// ***************************
// INTERNAL VARIABLES READ POST QUERY RUN
// ***************************
@@ -3031,12 +3407,14 @@ class IO
*
* Replacement for insert_id_ext array access before
*
* @param string|null $key
* @param integer|null $pos
* @return array<mixed>|string|int|null
* @param string|null $key Key to find in insert_id_arr
* @param integer|null $pos Multiple in array, which row to search in
* @return array<mixed>|string|int|null Return value, null for error/not found
*/
public function dbGetReturningExt(?string $key = null, ?int $pos = null): array|string|int|null
{
public function dbGetReturningExt(
?string $key = null,
?int $pos = null
): array|string|int|null {
// return as is if key is null
if ($key === null) {
if (count($this->insert_id_arr) == 1) {
@@ -3080,6 +3458,7 @@ class IO
/**
* Always returns the returning block as an array
*
* @return array<mixed> All returning data as array. even if one row only
*/
public function dbGetReturningArray(): array
@@ -3091,6 +3470,7 @@ class IO
* returns current number of rows that where
* affected by UPDATE/SELECT, etc
* null on empty
*
* @return int|null Number of rows or null if not set
*/
public function dbGetNumRows(): ?int
@@ -3100,6 +3480,7 @@ class IO
/**
* Number of fields in select query
*
* @return integer|null Number of fields in select or null if not set
*/
public function dbGetNumFields(): ?int
@@ -3109,13 +3490,26 @@ class IO
/**
* Return field names from query
* @return array<mixed> Field names as array
* Order based on order in query
*
* @return array<string> Field names as array
*/
public function dbGetFieldNames(): array
{
return $this->field_names;
}
/**
* Return field types from query
* Order based on order in query, use field names to get position
*
* @return array<string> Field types as array
*/
public function dbGetFieldTypes(): array
{
return $this->field_types;
}
/**
* Returns the value for given key in statement
* Will write error if statemen id does not exist
@@ -3129,8 +3523,10 @@ 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): null|string|int|bool
{
public function dbGetPrepareCursorValue(
string $stm_name,
string $key
): null|string|int|bool {
// if no statement name
if (empty($stm_name)) {
$this->__dbError(

View File

@@ -42,6 +42,15 @@ interface SqlFunctions
*/
public function __dbSendQuery(string $query): bool;
/**
* Undocumented function
*
* @param string $query
* @param array<mixed> $params
* @return bool
*/
public function __dbSendQueryParams(string $query, array $params): bool;
/**
* Undocumented function
*
@@ -74,6 +83,24 @@ interface SqlFunctions
*/
public function __dbExecute(string $name, array $data): \PgSql\Result|false;
/**
* Undocumented function
*
* @param string $name
* @param string $query
* @return bool
*/
public function __dbSendPrepare(string $name, string $query): bool;
/**
* Undocumented function
*
* @param string $name
* @param array<mixed> $params
* @return bool
*/
public function __dbSendExecute(string $name, array $params): bool;
/**
* Undocumented function
*
@@ -99,6 +126,15 @@ interface SqlFunctions
*/
public function __dbFieldName(\PgSql\Result|false $cursor, int $i): string|false;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @param int $i
* @return string|false
*/
public function __dbFieldType(\PgSql\Result|false $cursor, int $i): string|false;
/**
* Undocumented function
*

View File

@@ -33,7 +33,11 @@
* pg_affected_rows (*)
* pg_fetch_array
* pg_query
* pg_query_params
* pg_send_query
* pg_send_query_params
* pg_send_prepare
* pg_send_execute
* pg_get_result
* pg_connection_busy
* pg_close
@@ -50,6 +54,7 @@ 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 */
/** @phan-file-suppress PhanTypeMismatchArgumentInternal, PhanTypeMismatchReturn */
class PgSQL implements Interface\SqlFunctions
{
@@ -93,7 +98,6 @@ class PgSQL implements Interface\SqlFunctions
}
/**
* Proposed
* wrapper for pg_query_params for queries in the style of
* SELECT foo FROM bar WHERE foobar = $1
*
@@ -132,6 +136,22 @@ class PgSQL implements Interface\SqlFunctions
return $result ? true : false;
}
/**
* sends an async query to the server with params
*
* @param string $query Query string with placeholders $1, ..
* @param array<mixed> $params Matching parameters for each placerhold
* @return bool true/false Query sent successful status
*/
public function __dbSendQueryParams(string $query, array $params): bool
{
if (is_bool($this->dbh)) {
return false;
}
$result = pg_send_query_params($this->dbh, $query, $params);
return $result ? true : false;
}
/**
* wrapper for pg_get_result
*
@@ -208,6 +228,38 @@ class PgSQL implements Interface\SqlFunctions
return $result;
}
/**
* Asnyc send for a prepared statement
*
* @param string $name
* @param string $query
* @return bool
*/
public function __dbSendPrepare(string $name, string $query): bool
{
if (is_bool($this->dbh)) {
return false;
}
$result = pg_send_prepare($this->dbh, $name, $query);
return $result ? true : false;
}
/**
* Asnyc ssend for a prepared statement execution
*
* @param string $name
* @param array<mixed> $params
* @return bool
*/
public function __dbSendExecute(string $name, array $params): bool
{
if (is_bool($this->dbh)) {
return false;
}
$result = pg_send_execute($this->dbh, $name, $params);
return $result ? true : false;
}
/**
* wrapper for pg_num_rows
*
@@ -251,6 +303,21 @@ class PgSQL implements Interface\SqlFunctions
return pg_field_name($cursor, $i);
}
/**
* wrapper for pg_field_name
*
* @param \PgSql\Result|false $cursor cursor
* @param int $i field position
* @return string|false field type name or false
*/
public function __dbFieldType(\PgSql\Result|false $cursor, int $i): string|false
{
if (is_bool($cursor)) {
return false;
}
return pg_field_type($cursor, $i);
}
/**
* wrapper for pg_fetch_array
* if through/true false, use __dbResultType(true)

View File

@@ -251,22 +251,22 @@ class Logging
'debug',
$this->options['debug_all'] ??
// for user login, should be handled outside like globals
$_SESSION['DEBUG_ALL'] ??
$GLOBALS['DEBUG_ALL'] ??
$_SESSION['DEBUG_ALL'] ?? // DEPRECATED
$GLOBALS['DEBUG_ALL'] ?? // DEPRECATED
false
);
$this->setLogLevelAll(
'print',
$this->options['print_all'] ??
// for user login, should be handled outside like globals
$_SESSION['DEBUG_ALL'] ??
$GLOBALS['PRINT_ALL'] ??
$_SESSION['DEBUG_ALL'] ?? // DEPRECATED
$GLOBALS['PRINT_ALL'] ?? // DEPRECATED
false
);
$this->setLogLevelAll(
'echo',
$this->options['echo_all'] ??
$GLOBALS['ECHO_ALL'] ??
$GLOBALS['ECHO_ALL'] ?? // DEPRECATED
false
);
@@ -274,32 +274,32 @@ class Logging
// add file date is default on
$this->setGetLogPrintFileDate(
$this->options['print_file_date'] ??
$GLOBALS['LOG_PRINT_FILE_DATE'] ??
$GLOBALS['LOG_PRINT_FILE_DATE'] ?? // DEPRECATED
true
);
// all other logging file name flags are off
$this->setLogPer(
'level',
$this->options['per_level'] ??
$GLOBALS['LOG_PER_LEVEL'] ??
$GLOBALS['LOG_PER_LEVEL'] ?? // DEPRECATED
false
);
$this->setLogPer(
'class',
$this->options['per_class'] ??
$GLOBALS['LOG_PER_CLASS'] ??
$GLOBALS['LOG_PER_CLASS'] ?? // DEPRECATED
false
);
$this->setLogPer(
'page',
$this->options['per_page'] ??
$GLOBALS['LOG_PER_PAGE'] ??
$GLOBALS['LOG_PER_PAGE'] ?? // DEPRECATED
false
);
$this->setLogPer(
'run',
$this->options['per_run'] ??
$GLOBALS['LOG_PER_RUN'] ??
$GLOBALS['LOG_PER_RUN'] ?? // DEPRECATED
false
);
// set log per date

View File

@@ -21,6 +21,7 @@ class GetLocale
* @param string|null $encoding override encoding
* @param string|null $path override path
* @return array<string,string> locale, domain, encoding, path
* @deprecated use GetLocale::setLocaleSession(...) instead
*/
public static function setLocale(
?string $locale = null,
@@ -28,6 +29,10 @@ class GetLocale
?string $encoding = null,
?string $path = null
): array {
trigger_error(
'Use \CoreLibs\Language\GetLocale::setLocaleSession(...) instead',
E_USER_DEPRECATED
);
// locale must match at least basic rules
if (
empty($locale) ||
@@ -137,6 +142,113 @@ class GetLocale
'path' => $path,
];
}
/**
* NOTE: For getting the login info via login class use ->loginGetLocale()
*
* Set locale from session or from override parameters
* This is the prefered version to setLocale
* It usese the following SESSION VARIABLES
* DEFAULT_LOCALE
* DEFAULT_DOMAIN
* DEFAULT_CHARSET (should be set from DEFAULT_LOCALE)
* LOCALE_PATH
* in the return array, null set invalid information
*
* @param string $locale override locale
* @param string $domain override domain
* @param string $encoding override encoding
* @param string $path override path
* @return array<string,string> locale, domain, encoding, path
* @return array<string,string|null> Return list of set locale information
* @deprecated This version will be removed in a future version use ACL\Login->loginGetLocale() instead
*/
public static function setLocaleFromSession(
string $locale,
string $domain,
string $encoding,
string $path
): array {
// locale must match at least basic rules
if (
!empty($_SESSION['DEFAULT_LOCALE']) &&
preg_match("/^[-A-Za-z0-9_.@]+$/", $_SESSION['DEFAULT_LOCALE'])
) {
// parse from session (logged in)
$locale = $_SESSION['DEFAULT_LOCALE'];
} elseif (
empty($locale) ||
!preg_match("/^[-A-Za-z0-9_.@]+$/", $locale)
) {
$locale = null;
}
// if domain is set, must be alphanumeric, if not unset
if (
!empty($_SESSION['DEFAULT_DOMAIN']) &&
preg_match("/^\w+$/", $_SESSION['DEFAULT_DOMAIN'])
) {
$domain = $_SESSION['DEFAULT_DOMAIN'];
} elseif (
empty($domain) ||
!preg_match("/^\w+$/", $domain)
) {
$domain = null;
}
// check that override encoding matches locale encoding
// if locale encoding is set
preg_match('/(?:\\.(?P<charset>[-A-Za-z0-9_]+))/', $locale ?? '', $matches);
$locale_encoding = $matches['charset'] ?? null;
if (!empty($locale_encoding)) {
$encoding = strtoupper($locale_encoding);
} elseif (
!empty($_SESSION['DEFAULT_CHARSET']) &&
preg_match("/^[-A-Za-z0-9_]+$/", $_SESSION['DEFAULT_CHARSET'])
) {
$encoding = $_SESSION['DEFAULT_CHARSET'];
} elseif (
empty($encoding) ||
// not valid encoding
!preg_match("/^[-A-Za-z0-9_]+$/", $encoding)
) {
$encoding = null;
}
// path checks if set, if not valid path unset to default BASE path
if (
!empty($_SESSION['LOCALE_PATH']) &&
is_dir($_SESSION['LOCALE_PATH'])
) {
$path = $_SESSION['LOCALE_PATH'];
} elseif (
empty($path) ||
!is_dir($path)
) {
$path = null;
}
// extract lang & country from locale string, else set to en
if (
preg_match(
// lang
'/^(?P<lang>[a-z]{2,3})'
// country code
. '(?:_(?P<country>[A-Z]{2}))?/',
$locale ?? '',
$matches
)
) {
$lang = ($matches['lang'] ?? 'en')
// add country only if set
. (!empty($matches['country']) ? '_' . $matches['country'] : '');
} else {
$lang = null;
}
return [
'locale' => $locale,
'lang' => $lang,
'domain' => $domain,
'encoding' => $encoding,
'path' => $path,
];
}
}
// __END__

View File

@@ -32,12 +32,18 @@ use CoreLibs\Language\Core\GetTextReader;
class L10n
{
/** @var string the default fallback encoding if nothing is set */
public const DEFAULT_CHARSET = 'UTF-8';
/** @var string the current locale */
private $locale = '';
/** @var string the SET locale as WHERE the domain file is */
private $locale_set = '';
/** @var string the default selected/active domain */
private $domain = '';
/** @var string encoding, as from locale or set from outside */
private $override_encoding = self::DEFAULT_CHARSET;
/** @var string encoding set during the parse Locale */
private $encoding = '';
/** @var array<string,array<string,GetTextReader>> locale > domain = translator */
private $domains = [];
/** @var array<string,string> bound paths for domains */
@@ -75,11 +81,14 @@ class L10n
* will return self instance
* @param string $domain override CONTENT_PATH . $encoding name for mo file
* @param string $path path, if empty fallback on default internal path
* @param string $encoding Optional encoding, should be set if locale has
* no encoding, defaults to UTF-8
*/
public function __construct(
string $locale = '',
string $domain = '',
string $path = ''
string $path = '',
string $encoding = ''
) {
// auto load language only if at least locale and domain is set
// New: path must be set too, or we fall through
@@ -103,7 +112,7 @@ class L10n
$path = $domain;
$domain = $_domain;
}
$this->getTranslator($locale, $domain, $path);
$this->getTranslator($locale, $domain, $path, $encoding);
}
}
@@ -138,12 +147,14 @@ class L10n
* @param string $locale language name, if not set, try previous set
* @param string $domain set name for mo file, if not set, try previous set
* @param string $path path, if not set try to get from paths array, else self
* @param string $override_encoding if locale does not env encoding set, use this one
* @return GetTextReader the main gettext reader object
*/
public function getTranslator(
string $locale = '',
string $domain = '',
string $path = ''
string $path = '',
string $override_encoding = '',
): GetTextReader {
// set local if not from parameter
if (empty($locale)) {
@@ -153,11 +164,16 @@ class L10n
if (empty($domain)) {
$domain = $this->domain;
}
// override encoding for unset
if (!empty($override_encoding)) {
$this->override_encoding = $override_encoding;
}
// store old settings
$old_mofile = $this->mofile;
$old_lang = $this->locale;
$old_lang_set = $this->locale_set;
$old_domain = $this->domain;
$old_encoding = $this->encoding;
$old_base_locale_path = $this->base_locale_path;
$old_base_content_path = $this->base_content_path;
@@ -186,6 +202,7 @@ class L10n
// now we loop over lang compositions to get the base path
// then we check
$locales = $this->listLocales($locale);
$encoding = $this->getEncodingFromLocale($locale);
foreach ($locales as $_locale) {
$this->base_content_path = $_locale . DIRECTORY_SEPARATOR
. 'LC_MESSAGES' . DIRECTORY_SEPARATOR;
@@ -202,6 +219,7 @@ class L10n
if (is_readable($this->mofile)) {
// locale and domain current wanted
$this->locale = $locale;
$this->encoding = $encoding;
$this->domain = $domain;
// set empty domains path with current locale
if (empty($this->domains[$locale])) {
@@ -225,6 +243,7 @@ class L10n
$this->mofile = $old_mofile;
$this->locale = $old_lang;
$this->locale_set = $old_lang_set;
$this->encoding = $old_encoding;
$this->domain = $old_domain;
$this->base_locale_path = $old_base_locale_path;
$this->base_content_path = $old_base_content_path;
@@ -258,21 +277,36 @@ class L10n
return $this->l10n;
}
/**
* Extract encoding from Locale, or fallback to override one if not set
*
* @param string $locale
* @return string
*/
private function getEncodingFromLocale(string $locale): string
{
// extract charset from $locale
// if not set get override encoding
preg_match('/(?:\\.(?P<charset>[-A-Za-z0-9_]+))/', $locale, $matches);
return $matches['charset'] ?? $this->override_encoding;
}
/**
* Get the local as array same to the GetLocale::setLocale return
* This does not set from outside, but only what is set in the l10n class
*
* @return array{locale: string, lang: string|null, domain: string, encoding: string|null, path: string}
* @return array{locale: string, lang: string, lang_short: string, domain: string, encoding: string, path: string}
*/
public function getLocaleAsArray(): array
{
$locale = L10n::parseLocale($this->getLocale());
return [
'locale' => $this->getLocale(),
'lang' => $locale['lang']
'lang' => ($locale['lang'] ?? '')
. (!empty($locale['country']) ? '_' . $locale['country'] : ''),
'lang_short' => $locale['lang'] ?? '',
'domain' => $this->getDomain(),
'encoding' => $locale['charset'],
'encoding' => $this->getEncoding(),
'path' => $this->getBaseLocalePath(),
];
}
@@ -515,6 +549,37 @@ class L10n
return $this->locale_set;
}
/**
* Set override encoding
*
* @param string $encoding
* @return void
*/
public function setOverrideEncoding(string $encoding): void
{
$this->override_encoding = $encoding;
}
/**
* return current set override encoding
*
* @return string
*/
public function getOverrideEncoding(): string
{
return $this->override_encoding;
}
/**
* Current set encoding
*
* @return string
*/
public function getEncoding(): string
{
return $this->encoding;
}
/**
* get current set language
*

View File

@@ -277,6 +277,8 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
private $acl_admin = 0;
/** @var array<mixed> */
public $security_level;
/** @var array<string,mixed> Login ACL */
public $login_acl = [];
// layout publics
/** @var int */
public $table_width;
@@ -308,7 +310,8 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
* @param array<mixed> $db_config db config array, mandatory
* @param \CoreLibs\Debug\Logging $log Logging class
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,string> $locale locale array from ::setLocale
* @param array<string,mixed> $login_acl Login ACL array,
* at least base/admin should be set
* @param array<mixed>|null $table_arrays Override table array data
* instead of try to load from
* include file
@@ -318,7 +321,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
array $db_config,
\CoreLibs\Debug\Logging $log,
\CoreLibs\Language\L10n $l10n,
array $locale,
array $login_acl,
?array $table_arrays = null,
) {
// init logger if not set
@@ -327,19 +330,19 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
$this->log->setLogPer('class', false);
// init the language class
$this->l = $l10n;
// legacy lang vars set
// parse and read, legacy stuff
$locale = $this->l->getLocaleAsArray();
$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();
$this->lang_short = $locale['lang_short'];
$this->domain = $locale['domain'];
$this->lang_dir = $locale['path'];
// load config array
// get table array definitions for current page name
$this->login_acl = $login_acl;
// security settings
$this->base_acl_level = (int)$_SESSION['BASE_ACL_LEVEL'];
$this->acl_admin = (int)$_SESSION['ADMIN'];
$this->base_acl_level = $this->login_acl['base'] ?? 0;
$this->acl_admin = $this->login_acl['admin'] ?? 0;
// replace any non valid variable names and set my page name
$this->my_page_name = str_replace(
@@ -377,7 +380,6 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
$this->base_acl_level,
$this->acl_admin
);
// $this->log->debug('SESSION FORM', 'sessin: ' . $this->log->prAr($_SESSION));
// here should be a check if the config_array is correct ...
if (isset($config_array['show_fields']) && is_array($config_array['show_fields'])) {
$this->field_array = $config_array['show_fields'];

View File

@@ -160,13 +160,11 @@ class SmartyExtend extends \Smarty
* also registers the getvar caller plugin
*
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,string> $locale locale data read from setLocale
* @param string|null $cache_id
* @param string|null $compile_id
*/
public function __construct(
\CoreLibs\Language\L10n $l10n,
array $locale,
?string $cache_id = null,
?string $compile_id = null
) {
@@ -192,13 +190,12 @@ class SmartyExtend extends \Smarty
// iinit lang
$this->l10n = $l10n;
// parse and read, legacy stuff
$locale = $this->l10n->getLocaleAsArray();
$this->encoding = $locale['encoding'];
$this->lang = $locale['lang'];
// get first part from lang
$this->lang_short = explode('_', $locale['lang'])[0];
$this->domain = $this->l10n->getDomain();
$this->locale_set = $this->l10n->getLocaleSet();
$this->lang_dir = $this->l10n->getBaseLocalePath();
$this->lang_short = $locale['lang_short'];
$this->domain = $locale['domain'];
$this->lang_dir = $locale['path'];
// opt load functions so we can use legacy init for smarty run perhaps
\CoreLibs\Language\L10n::loadFunctions();
@@ -455,92 +452,95 @@ class SmartyExtend extends \Smarty
* wrapper call for setSmartyVars
* this is for frontend type and will not set any only admin needed variables
*
* @param string|null $compile_dir BASE . TEMPLATES_C
* @param string|null $cache_dir BASE . CACHE
* @param string|null $set_js JS
* @param string|null $set_css CSS
* @param string|null $set_font FONT
* @param string|null $set_default_encoding DEFAULT_ENCODING
* @param string|null $set_g_title G_TITLE
* @param string|null $set_stylesheet STYLESHEET
* @param string|null $set_javascript JAVASCRIPT
* @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for
* smarty variables merge
* @param array<string,string> $options list with the following value:
* compile_dir :BASE . TEMPLATES_C
* cache_dir :BASE . CACHE
* js :JS
* css :CSS
* font :FONT
* default_encoding :DEFAULT_ENCODING
* g_title :G_TITLE
* stylesheet :STYLESHEET
* javascript :JAVASCRIPT
* @param array<string,mixed> $smarty_data array of three keys
* that hold smarty set strings
* HEADER, DATA, DEBUG_DATA
* @return void
*/
public function setSmartyVarsFrontend(
?string $compile_dir = null,
?string $cache_dir = null,
?string $set_js = null,
?string $set_css = null,
?string $set_font = null,
?string $set_default_encoding = null,
?string $set_g_title = null,
?string $set_stylesheet = null,
?string $set_javascript = null,
?\CoreLibs\Admin\Backend $cms = null
array $options,
array $smarty_data
): void {
$this->setSmartyVars(
false,
$cms,
$compile_dir,
$cache_dir,
$set_js,
$set_css,
$set_font,
$set_default_encoding,
$set_g_title,
$smarty_data,
null,
$options['compile_dir'] ?? null,
$options['cache_dir'] ?? null,
$options['js'] ?? null,
$options['css'] ?? null,
$options['font'] ?? null,
$options['default_encoding'] ?? null,
$options['g_title'] ?? null,
null,
null,
null,
$set_stylesheet,
$set_javascript
null,
null,
$options['stylesheet'] ?? null,
$options['javascript'] ?? null
);
}
/**
* wrapper call for setSmartyVars
* this is only for admin interface and will set additional variables
* @param string|null $compile_dir BASE . TEMPLATES_C
* @param string|null $cache_dir BASE . CACHE
* @param string|null $set_js JS
* @param string|null $set_css CSS
* @param string|null $set_font FONT
* @param string|null $set_default_encoding DEFAULT_ENCODING
* @param string|null $set_g_title G_TITLE
* @param string|null $set_admin_stylesheet ADMIN_STYLESHEET
* @param string|null $set_admin_javascript ADMIN_JAVASCRIPT
* @param string|null $set_page_width PAGE_WIDTH
* @param array<string,string> $options list with the following value:
* compile_dir :BASE . TEMPLATES_C
* cache_dir :BASE . CACHE
* js :JS
* css :CSS
* font :FONT
* default_encoding :DEFAULT_ENCODING
* g_title :G_TITLE
* admin_stylesheet :ADMIN_STYLESHEET
* admin_javascript :ADMIN_JAVASCRIPT
* page_width :PAGE_WIDTH
* content_path :CONTENT_PATH
* user_name :_SESSION['USER_NAME']
* @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for
* smarty variables merge
* @return void
*/
public function setSmartyVarsAdmin(
?string $compile_dir = null,
?string $cache_dir = null,
?string $set_js = null,
?string $set_css = null,
?string $set_font = null,
?string $set_default_encoding = null,
?string $set_g_title = null,
?string $set_admin_stylesheet = null,
?string $set_admin_javascript = null,
?string $set_page_width = null,
array $options,
?\CoreLibs\Admin\Backend $cms = null
): void {
// if we have cms data, check for array blocks and build
$smarty_data = [];
if ($cms !== null) {
$smarty_data = [
'HEADER' => $cms->HEADER,
'DATA' => $cms->DATA,
'DEBUG_DATA' => $cms->DEBUG_DATA
];
}
$this->setSmartyVars(
true,
$smarty_data,
$cms,
$compile_dir,
$cache_dir,
$set_js,
$set_css,
$set_font,
$set_g_title,
$set_default_encoding,
$set_admin_stylesheet,
$set_admin_javascript,
$set_page_width,
$options['compile_dir'] ?? null,
$options['cache_dir'] ?? null,
$options['js'] ?? null,
$options['css'] ?? null,
$options['font'] ?? null,
$options['g_title'] ?? null,
$options['default_encoding'] ?? null,
$options['admin_stylesheet'] ?? null,
$options['admin_javascript'] ?? null,
$options['page_width'] ?? null,
$options['content_path'] ?? null,
$options['user_name'] ?? null,
null,
null
);
@@ -552,6 +552,7 @@ class SmartyExtend extends \Smarty
*
* @param bool $admin_call default false
* will set admin only variables
* @param array<string,mixed> $smarty_data smarty data to merge
* @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for
* smarty variables merge
* @param string|null $compile_dir BASE . TEMPLATES_C
@@ -564,12 +565,15 @@ class SmartyExtend extends \Smarty
* @param string|null $set_admin_stylesheet ADMIN_STYLESHEET
* @param string|null $set_admin_javascript ADMIN_JAVASCRIPT
* @param string|null $set_page_width PAGE_WIDTH
* @param string|null $set_content_path CONTENT_PATH (only if $cms set and admin)
* @param string|null $set_user_name _SESSION['USER_NAME']
* @param string|null $set_stylesheet STYLESHEET
* @param string|null $set_javascript JAVASCRIPT
* @return void
*/
private function setSmartyVars(
bool $admin_call,
array $smarty_data = [],
?\CoreLibs\Admin\Backend $cms = null,
?string $compile_dir = null,
?string $cache_dir = null,
@@ -581,8 +585,10 @@ class SmartyExtend extends \Smarty
?string $set_admin_stylesheet = null,
?string $set_admin_javascript = null,
?string $set_page_width = null,
?string $set_content_path = null,
?string $set_user_name = null,
?string $set_stylesheet = null,
?string $set_javascript = null
?string $set_javascript = null,
): void {
// trigger deprecation
if (
@@ -597,7 +603,8 @@ class SmartyExtend extends \Smarty
$admin_call === true && (
$set_admin_stylesheet === null ||
$set_admin_javascript === null ||
$set_page_width === null
$set_page_width === null ||
$set_user_name === null
)
) ||
(
@@ -605,6 +612,9 @@ class SmartyExtend extends \Smarty
$set_stylesheet === null ||
$set_javascript === null
)
) ||
(
$admin_call === true && $cms !== null && $set_content_path === null
)
) {
/** @deprecated setSmartyVars call without parameters */
@@ -624,24 +634,12 @@ class SmartyExtend extends \Smarty
$set_admin_stylesheet = $set_admin_stylesheet ?? ADMIN_STYLESHEET;
$set_admin_javascript = $set_admin_javascript ?? ADMIN_JAVASCRIPT;
$set_page_width = $set_page_width ?? PAGE_WIDTH;
$set_content_path = $set_content_path ?? CONTENT_PATH;
$set_stylesheet = $set_stylesheet ?? STYLESHEET;
$set_javascript = $set_javascript ?? JAVASCRIPT;
// depreacte call globals cms on null 4mcs
if (
$cms === null &&
isset($GLOBALS['cms'])
) {
/** @deprecated setSmartyVars globals cms is deprecated */
trigger_error(
'Calling setSmartyVars without cms parameter when needed is deprecated',
E_USER_DEPRECATED
);
}
// this is ugly
$cms = $cms ?? $GLOBALS['cms'] ?? null;
if ($cms instanceof \CoreLibs\Admin\Backend) {
$this->mergeCmsSmartyVars($cms);
}
$set_user_name = $set_user_name ?? $_SESSION['USER_NAME'] ?? '';
// merge additional smarty data
$this->mergeCmsSmartyVars($smarty_data);
// trigger flags
$this->HEADER['USE_PROTOTYPE'] = $this->USE_PROTOTYPE;
@@ -683,12 +681,27 @@ class SmartyExtend extends \Smarty
$this->DATA['FORM_ACTION'] = $this->FORM_ACTION;
// special for admin
if ($admin_call === true) {
// depreacte call globals cms on null 4mcs
if (
$cms === null &&
isset($GLOBALS['cms'])
) {
/** @deprecated setSmartyVars globals cms is deprecated */
trigger_error(
'Calling setSmartyVars without cms parameter when needed is deprecated',
E_USER_DEPRECATED
);
}
// this is ugly
$cms = $cms ?? $GLOBALS['cms'] ?? null;
// set ACL extra show
if ($cms instanceof \CoreLibs\Admin\Backend) {
$this->DATA['show_ea_extra'] = $cms->acl['show_ea_extra'] ?? false;
$this->DATA['ADMIN'] = $cms->acl['admin'] ?? 0;
// top menu
$this->DATA['nav_menu'] = $cms->adbTopMenu();
$this->DATA['nav_menu'] = $cms->adbTopMenu(
$set_content_path
);
$this->DATA['nav_menu_count'] = count($this->DATA['nav_menu']);
// messages = ['msg' =>, 'class' => 'error/warning/...']
$this->DATA['messages'] = $cms->messages;
@@ -734,7 +747,7 @@ class SmartyExtend extends \Smarty
$this->DATA['JS_FLATPICKR'] = $this->JS_FLATPICKR;
$this->DATA['JS_FILE_UPLOADER'] = $this->JS_FILE_UPLOADER;
// user name
$this->DATA['USER_NAME'] = !empty($_SESSION['USER_NAME']) ? $_SESSION['USER_NAME'] : '';
$this->DATA['USER_NAME'] = $set_user_name;
// the template part to include into the body
$this->DATA['TEMPLATE_NAME'] = $this->TEMPLATE_NAME;
$this->DATA['CONTENT_INCLUDE'] = $this->CONTENT_INCLUDE;
@@ -748,18 +761,18 @@ class SmartyExtend extends \Smarty
/**
* merge outside object HEADER/DATA/DEBUG_DATA vars into the smarty class
*
* @param \CoreLibs\Admin\Backend $cms object that has header/data/debug_data
* @param array<string,mixed> $smarty_data array that has header/data/debug_data
* @return void
*/
public function mergeCmsSmartyVars(\CoreLibs\Admin\Backend $cms): void
public function mergeCmsSmartyVars(array $smarty_data): void
{
// array merge HEADER, DATA, DEBUG DATA
foreach (['HEADER', 'DATA', 'DEBUG_DATA'] as $ext_smarty) {
if (
isset($cms->{$ext_smarty}) &&
is_array($cms->{$ext_smarty})
isset($smarty_data[$ext_smarty]) &&
is_array($smarty_data[$ext_smarty])
) {
$this->{$ext_smarty} = array_merge($this->{$ext_smarty}, $cms->{$ext_smarty});
$this->{$ext_smarty} = array_merge($this->{$ext_smarty}, $smarty_data[$ext_smarty]);
}
}
}

View File

@@ -7,6 +7,14 @@ namespace tests;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
/*
Not yet covered tests:
- loginGetLocale
- loginGetHeaderColor
- loginGetPages
- loginGetEuid
*/
/**
* Test class for ACL\Login
* @coversDefaultClass \CoreLibs\ACL\Login
@@ -259,6 +267,8 @@ final class CoreLibsACLLoginTest extends TestCase
'GROUP_ACL_LEVEL' => -1,
'PAGES_ACL_LEVEL' => [],
'USER_ACL_LEVEL' => -1,
'USER_ADDITIONAL_ACL' => [],
'GROUP_ADDITIONAL_ACL' => [],
'UNIT_UID' => [
'AdminAccess' => 1,
],
@@ -272,6 +282,7 @@ final class CoreLibsACLLoginTest extends TestCase
'data' => [
'test' => 'value',
],
'additional_acl' => []
],
],
// 'UNIT_DEFAULT' => '',
@@ -1114,6 +1125,7 @@ final class CoreLibsACLLoginTest extends TestCase
'logout_target' => '',
'site_locale' => 'en_US.UTF-8',
'site_domain' => 'admin',
'site_encoding' => 'UTF-8',
'locale_path' => __DIR__ . DIRECTORY_SEPARATOR
. 'includes' . DIRECTORY_SEPARATOR
. 'locale' . DIRECTORY_SEPARATOR,
@@ -1796,6 +1808,7 @@ final class CoreLibsACLLoginTest extends TestCase
'logout_target' => '',
'site_locale' => 'en_US.UTF-8',
'site_domain' => 'admin',
'site_encoding' => 'UTF-8',
'locale_path' => __DIR__ . DIRECTORY_SEPARATOR
. 'includes' . DIRECTORY_SEPARATOR
. 'locale' . DIRECTORY_SEPARATOR,
@@ -1909,6 +1922,7 @@ final class CoreLibsACLLoginTest extends TestCase
'logout_target' => '',
'site_locale' => 'en_US.UTF-8',
'site_domain' => 'admin',
'site_encoding' => 'UTF-8',
'locale_path' => __DIR__ . DIRECTORY_SEPARATOR
. 'includes' . DIRECTORY_SEPARATOR
. 'locale' . DIRECTORY_SEPARATOR,
@@ -1996,6 +2010,7 @@ final class CoreLibsACLLoginTest extends TestCase
'logout_target' => '',
'site_locale' => 'en_US.UTF-8',
'site_domain' => 'admin',
'site_encoding' => 'UTF-8',
'locale_path' => __DIR__ . DIRECTORY_SEPARATOR
. 'includes' . DIRECTORY_SEPARATOR
. 'locale' . DIRECTORY_SEPARATOR,
@@ -2091,6 +2106,7 @@ final class CoreLibsACLLoginTest extends TestCase
'logout_target' => '',
'site_locale' => 'en_US.UTF-8',
'site_domain' => 'admin',
'site_encoding' => 'UTF-8',
'locale_path' => __DIR__ . DIRECTORY_SEPARATOR
. 'includes' . DIRECTORY_SEPARATOR
. 'locale' . DIRECTORY_SEPARATOR,

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,13 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsLanguageGetLocaleTest extends TestCase
{
public const SITE_ENCODING = 'UTF-8';
public const SITE_LOCALE = 'en_US.UTF-8';
public const SITE_DOMAIN = 'admin';
public const LOCALE_PATH = __DIR__ . DIRECTORY_SEPARATOR
. 'includes' . DIRECTORY_SEPARATOR
. 'locale' . DIRECTORY_SEPARATOR;
/**
* set all constant variables that must be set before call
*
@@ -22,7 +29,7 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
public static function setUpBeforeClass(): void
{
// default web page encoding setting
if (!defined('DEFAULT_ENCODING')) {
/* if (!defined('DEFAULT_ENCODING')) {
define('DEFAULT_ENCODING', 'UTF-8');
}
if (!defined('DEFAULT_LOCALE')) {
@@ -35,9 +42,9 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
}
if (!defined('SITE_LOCALE')) {
define('SITE_LOCALE', DEFAULT_LOCALE);
}
} */
// just set
if (!defined('BASE')) {
/* if (!defined('BASE')) {
define('BASE', str_replace('/configs', '', __DIR__) . DIRECTORY_SEPARATOR);
}
if (!defined('INCLUDES')) {
@@ -51,7 +58,7 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
}
if (!defined('CONTENT_PATH')) {
define('CONTENT_PATH', 'frontend' . DIRECTORY_SEPARATOR);
}
} */
// array session
$_SESSION = [];
global $_SESSION;
@@ -62,7 +69,7 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
*
* @return array<mixed>
*/
public function setLocaleProvider(): array
/* public function setLocaleProvider(): array
{
return [
// 0: locale
@@ -233,7 +240,7 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
// TODO invalid params (bad path) (no override)
// TODO param calls, but with override set
];
}
} */
/**
* Undocumented function
@@ -252,7 +259,7 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
* @param string|null $deprecation_message
* @return void
*/
public function testsetLocale(
/* public function testsetLocale(
?string $language,
?string $domain,
?string $encoding,
@@ -347,6 +354,214 @@ final class CoreLibsLanguageGetLocaleTest extends TestCase
// unset all vars
$_SESSION = [];
unset($GLOBALS['OVERRIDE_LANG']);
} */
/**
* all the test data
*
* @return array<mixed>
*/
public function setLocaleFromSessionProvider(): array
{
return [
// 0: locale
// 1: domain
// 2: encoding
// 3: path
// 4: SESSION: DEFAULT_LOCALE
// 5: SESSION: DEFAULT_CHARSET
// 5: SESSION: DEFAULT_DOMAIN
// 6: SESSION: LOCALE_PATH
// 6: expected array
// 7: deprecation message
'all session vars set' => [
// lang, domain, encoding, path
self::SITE_LOCALE, self::SITE_DOMAIN, self::SITE_ENCODING, self::LOCALE_PATH,
// SESSION SETTINGS: locale, charset, domain, path
'ja_JP.UTF-8', 'UTF-8', 'admin', __DIR__ . '/locale_other/',
// return array
[
'locale' => 'ja_JP.UTF-8',
'lang' => 'ja_JP',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?locale_other\/$/",
],
],
// param lang and domain (no override)
'no session set, only parameters' => [
// lang, domain, encoding, path
self::SITE_LOCALE, self::SITE_DOMAIN, self::SITE_ENCODING, self::LOCALE_PATH,
// SESSION SETTINGS: locale, charset, domain, path
null, null, null, null,
// return array
[
'locale' => 'en_US.UTF-8',
'lang' => 'en_US',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
],
// special parse session check for locales
'all session vars set, short lang' => [
// lang, domain, encoding, path
self::SITE_LOCALE, self::SITE_DOMAIN, self::SITE_ENCODING, self::LOCALE_PATH,
// SESSION SETTINGS: locale, charset, domain, path
'ja', 'UTF-8', 'admin', __DIR__ . '/locale_other/',
// return array
[
'locale' => 'ja',
'lang' => 'ja',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?locale_other\/$/",
],
],
// lang with modifier
// param lang and domain (no override)
'long locale, domain, encoding params, no sessions' => [
// lang, domain, encoding, path
self::SITE_LOCALE, self::SITE_DOMAIN, self::SITE_ENCODING, self::LOCALE_PATH,
// SESSION SETTINGS: locale, charset, domain, path
'de_CH.UTF-8@euro', 'admin', 'UTF-8', __DIR__ . '/includes/locale/',
// return array
[
'locale' => 'de_CH.UTF-8@euro',
'lang' => 'de_CH',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
],
// missing session values check
// special parse session check for locales
'session missing encoding, set from parameters' => [
// lang, domain, encoding, path
self::SITE_LOCALE, self::SITE_DOMAIN, self::SITE_ENCODING, self::LOCALE_PATH,
// SESSION SETTINGS: locale, charset, domain, path
'ja', null, 'admin', __DIR__ . '/locale_other/',
// return array
[
'locale' => 'ja',
'lang' => 'ja',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?locale_other\/$/",
],
],
// null return check for invalid entries
'no session set, only parameters, all invalid' => [
// lang, domain, encoding, path
'###', '&&&&', '$$$$', 'foo_bar_path',
// SESSION SETTINGS: locale, charset, domain, path
null, null, null, null,
// return array
[
'locale' => null,
'lang' => null,
'domain' => null,
'encoding' => null,
'path' => null,
],
],
// invalid session names, fall backup
'all session vars are invalid, fallback' => [
// lang, domain, encoding, path
self::SITE_LOCALE, self::SITE_DOMAIN, self::SITE_ENCODING, self::LOCALE_PATH,
// SESSION SETTINGS: locale, charset, domain, path
'###', '&&&&', '$$$$', 'foo_bar_path',
// return array
[
'locale' => 'en_US.UTF-8',
'lang' => 'en_US',
'domain' => 'admin',
'encoding' => 'UTF-8',
'path' => "/^\/(.*\/)?includes\/locale\/$/",
],
],
];
}
/**
* Undocumented function
*
* @covers ::setLocale
* @dataProvider setLocaleFromSessionProvider
* @testdox lang settings lang $language, domain $domain, encoding $encoding, path $path; session lang: $SESSION_DEFAULT_LOCALE, session char: $SESSION_DEFAULT_CHARSET [$_dataName]
*
* @param string| $language
* @param string| $domain
* @param string| $encoding
* @param string| $path
* @param string|null $SESSION_DEFAULT_LOCALE
* @param string|null $SESSION_DEFAULT_CHARSET
* @param string|null $SESSION_DEFAULT_DOMAIN
* @param string|null $SESSION_LOCALE_PATH
* @param array<mixed> $expected
* @return void
*/
public function testsetLocaleFromSession(
string $language,
string $domain,
string $encoding,
string $path,
?string $SESSION_DEFAULT_LOCALE,
?string $SESSION_DEFAULT_CHARSET,
?string $SESSION_DEFAULT_DOMAIN,
?string $SESSION_LOCALE_PATH,
array $expected,
): void {
$return_lang_settings = [];
global $_SESSION;
// set override
if ($SESSION_DEFAULT_LOCALE !== null) {
$_SESSION['DEFAULT_LOCALE'] = $SESSION_DEFAULT_LOCALE;
}
if ($SESSION_DEFAULT_CHARSET !== null) {
$_SESSION['DEFAULT_CHARSET'] = $SESSION_DEFAULT_CHARSET;
}
if ($SESSION_DEFAULT_DOMAIN !== null) {
$_SESSION['DEFAULT_DOMAIN'] = $SESSION_DEFAULT_DOMAIN;
}
if ($SESSION_LOCALE_PATH !== null) {
$_SESSION['LOCALE_PATH'] = $SESSION_LOCALE_PATH;
}
$return_lang_settings = \CoreLibs\Language\GetLocale::setLocaleFromSession(
$language,
$domain,
$encoding,
$path
);
// print "RETURN: " . print_r($return_lang_settings, true) . "\n";
foreach (
[
'locale', 'lang', 'domain', 'encoding', 'path'
] as $key
) {
$value = $expected[$key];
if (
!empty($value) &&
strpos($value, "/") === 0
) {
// this is regex
$this->assertMatchesRegularExpression(
$value,
$return_lang_settings[$key] ?? '',
'assert regex failed for ' . $key
);
} else {
// assert equal
$this->assertEquals(
$value,
$return_lang_settings[$key],
'assert equal failed for ' . $key
);
}
}
// unset all vars
$_SESSION = [];
unset($GLOBALS['OVERRIDE_LANG']);
}
}

View File

@@ -84,94 +84,141 @@ final class CoreLibsLanguageL10nTest extends TestCase
{
return [
// 0: locale
// 1: domain
// 2: encoding
// 1: encoding
// 2: domain
// 3: path
// 4: locale expected
// 5: locale set expected
// 6: lang expected
// 7: encoding expected
// 8: domain exepcted
// 9: context (null for none)
// 10: test string in
// 11: test translated
// 12: deprecation message (until removed)
// 7: lang short expected
// 8: encoding expected
// 9: domain exepcted
// 10: context (null for none)
// 11: test string in
// 12: test translated
// 13: deprecation message (until removed)
// new style load
'gettext load en' => [
'en_US.UTF-8',
'UTF-8',
'frontend',
__DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR,
//
// 4, 5, 6, 7, 8, 9
'en_US.UTF-8',
'en_US',
'en_US',
'en',
'UTF-8',
'frontend',
// 10
null,
// 11, 12
'Original',
'Translated frontend en_US',
// 13
null,
],
'gettext load en' => [
'en_US.UTF-8',
'UTF-8',
'frontend',
__DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR,
//
'en_US.UTF-8',
'en_US',
'en_US',
'en',
'UTF-8',
'frontend',
//
'context',
//
'Original',
'Original context frontend en_US',
//
null,
],
'gettext load ja' => [
'ja_JP.UTF-8',
'UTF-8',
'admin',
__DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR,
//
'ja_JP.UTF-8',
'ja_JP',
'ja_JP',
'ja',
'UTF-8',
'admin',
//
null,
//
'Original',
'Translated admin ja_JP',
//
null,
],
// load short locale with different encoding
'gettext load short ja no encoding' => [
'ja',
'SJIS',
'admin',
__DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR,
//
'ja',
'ja',
'ja',
'ja',
'SJIS',
'admin',
//
null,
//
'Original',
'Translated admin ja_JP',
//
null,
],
// mixed path and domain [DEPRECATED]
'mixed path and domain [DEPRECATED]' => [
'en_US.UTF-8',
'UTF-8',
__DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'locale' . DIRECTORY_SEPARATOR,
'frontend',
//
'en_US.UTF-8',
'en_US',
'en_US',
'en',
'UTF-8',
'frontend',
//
'context',
//
'Original',
'Original context frontend en_US',
//
'L10n constructor parameter switch is no longer supported. domain is 2nd, path is 3rd parameter'
],
// unset path
'unset path with locale and domain [DEPRECATED]' => [
'ja_JP.UTF-8',
'UTF-8',
'admin',
null,
//
'ja_JP.UTF-8',
'ja_JP',
'ja_JP',
'ja',
'UTF-8',
'admin',
//
null,
//
'Original',
'Translated admin ja_JP',
//
'Empty path parameter is no longer allowed if locale and domain are set',
],
// null set
@@ -179,15 +226,20 @@ final class CoreLibsLanguageL10nTest extends TestCase
'',
'',
'',
'',
//
'',
'',
'',
'',
'', // unset on empty call
'',
//
null,
//
'Original',
'Original',
//
null,
]
];
@@ -201,11 +253,13 @@ final class CoreLibsLanguageL10nTest extends TestCase
* @testdox check l10n init with Locale $locale, Path $path, Domain $domain, Legacy: $legacy with $context [$_dataName]
*
* @param string|null $locale
* @param string|null $encoding
* @param string|null $domain
* @param string|null $path
* @param string $locale_expected
* @param string $locale_set_expected
* @param string $lang_expected
* @param string $lang_short_expected
* @param string $encoding_expected
* @param string $domain_expected
* @param string|null $context
@@ -216,11 +270,13 @@ final class CoreLibsLanguageL10nTest extends TestCase
*/
public function testL10nObject(
?string $locale,
?string $encoding,
?string $domain,
?string $path,
string $locale_expected,
string $locale_set_expected,
string $lang_expected,
string $lang_short_expected,
string $encoding_expected,
string $domain_expected,
?string $context,
@@ -241,16 +297,18 @@ final class CoreLibsLanguageL10nTest extends TestCase
if ($locale === null) {
$l10n = new \CoreLibs\Language\L10n();
} elseif ($domain === null) {
// same as if locale is null
// deprecated, locale + domain must be set, handled like empty calls
$l10n = new \CoreLibs\Language\L10n($locale);
} elseif ($path === null) {
// deprecated, path must be set
// deprecated, path must be set, will thow DEPRECATION error, handled like empty
$l10n = new \CoreLibs\Language\L10n($locale, $domain);
} else {
} elseif ($encoding === null) {
// if encoding not found will be UTF-8
$l10n = new \CoreLibs\Language\L10n($locale, $domain, $path);
} else {
$l10n = new \CoreLibs\Language\L10n($locale, $domain, $path, $encoding);
}
restore_error_handler();
// print "LOC: " . $locale . ", " . $l10n->getLocale() . ", " . $locale_expected . "\n";
// print "MO: " . $l10n->getMoFile() . "\n";
$this->assertEquals(
$locale_expected,
@@ -286,6 +344,7 @@ final class CoreLibsLanguageL10nTest extends TestCase
[
'locale' => $locale_expected,
'lang' => $lang_expected,
'lang_short' => $lang_short_expected,
'domain' => $domain_expected,
'encoding' => $encoding_expected,
'path' => $path

View File

@@ -0,0 +1 @@
ja_JP