Compare commits

...

21 Commits

Author SHA1 Message Date
Clemens Schwaighofer
bcde36ac17 DB IO Cache reset should not be an error
If the query is not found, do not throw an error, just show a warning
2025-01-20 10:45:31 +09:00
Clemens Schwaighofer
8bde34ec7d Fix bug in DB IO prepared statement with INSERT and auto RETURNING add
INSERT will get a RETURNING added automatically if it has a primary key
This was not checked when query was compared for prepared statements.

Also added a prepared statement status checker
2025-01-17 17:52:41 +09:00
Clemens Schwaighofer
a345d71306 De-depricate the ACL Login loginCheckEditAccessId method
This is still used a lot, and there is no reason to deprecate it so early.

First all the other logic should be brought in to make this an easy
conversion.
2025-01-17 14:43:13 +09:00
Clemens Schwaighofer
0ff6294faa Fix ACL Login cuid <-> id pk lookups
Used the wrong SESSION var for lookup
2025-01-17 14:34:41 +09:00
Clemens Schwaighofer
757d7ae01d ACL Login fixes for legacy id lookups
add an edit access id lookup to cuid

Fix unit_cuid not initialized, only old unit_id
2025-01-17 12:48:46 +09:00
Clemens Schwaighofer
4e78b21c67 phpstan fix for fegetcsv param $length 2025-01-17 09:59:39 +09:00
Clemens Schwaighofer
d7e6434808 New DeprecatedHelper namespace
For temporary wrapper functions for deprecated calls that need this

PHP 8.4 fputcsv/fgetcsv/str_getcsv encoding default change deprecated warning

Note this does not cover the SqlFileInfo class as this is not used in our code
2025-01-17 09:58:02 +09:00
Clemens Schwaighofer
443cc2751d Update Logging file name change unit tests 2025-01-17 09:33:05 +09:00
Clemens Schwaighofer
cf6500b55a Logging class change to "." for block separator
Blocks for info are now separated with "." and not "_" to make it visual more easy to see
2025-01-17 09:08:13 +09:00
Clemens Schwaighofer
09c2ec653f ACL Login set deprecated edit user id too
We need that for some old calls in old projects
2025-01-16 14:49:15 +09:00
Clemens Schwaighofer
fc105f9295 Add ACL Login lookup edit access id to edit access cuid 2025-01-16 14:36:11 +09:00
Clemens Schwaighofer
053ab69330 Add edit access cuuid to the unit detail list 2025-01-16 14:04:30 +09:00
Clemens Schwaighofer
fd079316f5 ACL Login: Add edit_access_id to unit detail block
This is needed for a lot of legacy data lookup
2025-01-16 13:55:17 +09:00
Clemens Schwaighofer
08664e9834 Update log writing for login info
Fix the deprecated message in the Admin/Backend one with a full sample
Update the admin_header include sample page with the corret writeLog call
2025-01-16 10:40:41 +09:00
Clemens Schwaighofer
e063162161 Remove not needed use parts and ignore noop new for phan check 2025-01-15 12:53:02 +09:00
Clemens Schwaighofer
7fbc449a5c PHPunit test call script update
Fix for default PHP set via getting version from default PHP.
Add a verbose option and remove the fixed verbose setting from the phpunit config
Update the options call to add a usage info block
2025-01-15 11:57:25 +09:00
Clemens Schwaighofer
72912c8c90 Bad password check for PHP earlier than 8.4 2025-01-06 13:52:28 +09:00
Clemens Schwaighofer
de2ed8be3d EditBase SmartyExtended class call update 2024-12-27 17:07:44 +09:00
Clemens Schwaighofer
9d65f5d7c1 phpunit script update, SmartyExtended allow load of plugins
- phpunit has better options set for testdox/php version
- SmartyExtended has logger class as option (argument 2) and options
- SmartyExtneded can via option set html escape and load of plugins
	- plugin array is set of
		- file: path to plugin file
		- type: what type this is
		- tag: tag name
		- callable: the callable for the tag name
	- will throw exceptions on plugin load
	- for all other things will set warning only and skip read
- fix the Smarty call with the logger option
- fix password test for PHP 8.4 password hash change

*IMPORTANT*
SmartyExtended($l10n, $logger, $cache_id, $compile_id)
The second argument is now the Logger class, this MUST be updated for all calls
2024-12-27 14:00:12 +09:00
Clemens Schwaighofer
fbe827e989 Update Smarty Extended for Smarty-extended v5 upgrade 2024-12-27 11:30:55 +09:00
Clemens Schwaighofer
c778a4eb81 Add phive back in for static tools like phpunit instead of using the composer package 2024-12-27 09:32:54 +09:00
27 changed files with 715 additions and 110 deletions

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^9.6" installed="9.6.21" location="./tools/phpunit" copy="false"/>
<phar name="phpunit" version="^10.3.5" installed="10.3.5" location="./tools/phpunit" copy="false"/>
<phar name="phpcbf" version="^3.7.2" installed="3.10.3" location="./tools/phpcbf" copy="false"/>
<phar name="phpcs" version="^3.7.2" installed="3.10.3" location="./tools/phpcs" copy="false"/>
<phar name="phpstan" version="^1.10.37" installed="1.12.4" location="./tools/phpstan" copy="false"/>
<phar name="phan" version="^5.4.2" installed="5.4.3" location="./tools/phan" copy="false"/>
<phar name="phpcs" version="^3.10.3" installed="3.10.3" location="./tools/phpcs" copy="false"/>
<phar name="phpstan" version="^2.0" installed="2.0.4" location="./tools/phpstan" copy="false"/>
<phar name="phan" version="^5.4.3" installed="5.4.3" location="./tools/phan" copy="false"/>
<phar name="psalm" version="^5.15.0" installed="5.24.0" location="./tools/psalm" copy="false"/>
<phar name="phpdox" version="^0.12.0" installed="0.12.0" location="./tools/phpdox" copy="false"/>
<phar name="phpdocumentor" version="^3.4.2" installed="3.4.3" location="./tools/phpDocumentor" copy="false"/>

View File

@@ -1,5 +1,5 @@
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
# must be run in ${base}
cd $base;
cd $base || exit;
${base}tools/phan --progress-bar -C --analyze-twice;
cd ~;
cd ~ || exit;

View File

@@ -1,5 +1,5 @@
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
# must be run in ${base}
cd $base;
cd $base || exit;
${base}tools/phpstan;
cd ~;
cd ~ || exit;

View File

@@ -1,49 +1,96 @@
#!/bin/env bash
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
function error() {
if [ -t 1 ]; then echo "[MAK] ERROR: $*" >&2; fi; exit 0;
}
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-t] [-v] [-p VERSION]
Runs all the PHP unit tests.
If -p is not set, the default intalled PHP is used.
Available options:
-h, --help Print this help and exit
-t, --testdox Enable testdox output for phpunit
-v, --verbose Enable verbose output for PHPunit
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
EOF
exit
}
# set base variables
BASE_PATH="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
PHPUNIT_CONFIG="${BASE_PATH}phpunit.xml";
PHP_BIN_PATH=$(which php);
if [ -z "${PHP_BIN_PATH}" ]; then
echo "Cannot find php binary";
exit;
fi;
DEFAULT_PHP_VERSION=$(${PHP_BIN_PATH} -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;");
if [ -z "${DEFAULT_PHP_VERSION}" ]; then
echo "Cannot set default PHP version";
exit;
fi;
# -c phpunit.xml
# --testdox
# call with "t" to give verbose testdox output
# call with "-tt" to give verbose testdox output
# SUPPORTED: https://www.php.net/supported-versions.php
# call with php version number to force a certain php version
# call with -p <php version number> to force a certain php version
opt_testdox="";
if [ "${1}" = "t" ] || [ "${2}" = "t" ]; then
opt_testdox="--testdox";
fi;
php_bin="";
if [ -n "${1}" ]; then
opt_verbose="";
php_version="";
no_php_version=0;
while [ -n "${1-}" ]; do
case "${1}" in
# "7.3") php_bin="/usr/bin/php7.3 "; ;;
# "7.4") php_bin="/usr/bin/php7.4 "; ;;
# "8.0") php_bin="/usr/bin/php8.0 "; ;;
# "8.1") php_bin="/usr/bin/php8.1 "; ;;
"8.2") php_bin="/usr/bin/php8.2 "; ;;
"8.3") php_bin="/usr/bin/php8.4 "; ;;
*) echo "Not support PHP: ${1}"; exit; ;;
esac;
-t | --testdox)
opt_testdox="--testdox";
;;
-v | --verbose)
opt_verbose="--verbose";
;;
-p | --php)
php_version="${2-}";
shift
;;
-h | --help)
usage
;;
# invalid option
-?*)
error "[!] Unknown option: '$1'."
;;
esac
shift;
done;
if [ -z "${php_version}" ]; then
php_version="${DEFAULT_PHP_VERSION}";
no_php_version=1;
fi;
if [ -n "${2}" ] && [ -z "${php_bin}" ]; then
case "${2}" in
# "7.3") php_bin="/usr/bin/php7.3 "; ;;
# "7.4") php_bin="/usr/bin/php7.4 "; ;;
# "8.0") php_bin="/usr/bin/php8.0 "; ;;
# "8.1") php_bin="/usr/bin/php8.1 "; ;;
"8.2") php_bin="/usr/bin/php8.2 "; ;;
"8.3") php_bin="/usr/bin/php8.3 "; ;;
*) echo "Not support PHP: ${1}"; exit; ;;
esac;
php_bin="${PHP_BIN_PATH}${php_version}";
echo "Use PHP Version: ${php_version}";
if [ ! -f "${php_bin}" ]; then
echo "Set php ${php_bin} does not exist";
exit;
fi;
php_bin="${php_bin} ";
# Note 4dev/tests/bootstrap.php has to be set as bootstrap file in phpunit.xml
phpunit_call="${php_bin}${base}vendor/bin/phpunit ${opt_testdox} -c ${base}phpunit.xml ${base}4dev/tests/";
phpunit_call="${php_bin}${BASE_PATH}vendor/bin/phpunit ${opt_testdox} ${opt_verbose} -c ${PHPUNIT_CONFIG} ${BASE_PATH}4dev/tests/";
${phpunit_call};
if [ ! -z "${php_bin}" ]; then
echo "CALLED WITH PHP: ${php_bin}"$(${php_bin} --version);
echo -e "\nPHPUnit Config: ${PHPUNIT_CONFIG}";
if [ "${no_php_version}" -eq 0 ]; then
echo "CALLED WITH PHP: ${php_bin}$(${php_bin} --version)";
else
echo "Default PHP used: "$(php --version);
echo "Default PHP used: $(php --version)";
fi;
# __END__

View File

@@ -1531,6 +1531,12 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_uid']),
'Assert check access uid to cuid valid'
);
// - loginGetEditAccessCuidFromId
$this->assertEquals(
$expected['check_access_cuid'],
$login_mock->loginGetEditAccessCuidFromUid($mock_settings['edit_access_id']),
'Assert check access id to cuid valid'
);
// Deprecated
// - loginCheckEditAccess
$this->assertEquals(

View File

@@ -3692,7 +3692,7 @@ final class CoreLibsDBIOTest extends TestCase
*
* @return array
*/
public function preparedProviderValue(): array
public function providerDbGetPrepareCursorValue(): array
{
// 1: query (can be empty for do not set)
// 2: stm name
@@ -3736,7 +3736,7 @@ final class CoreLibsDBIOTest extends TestCase
* test return prepare cursor errors
*
* @covers ::dbGetPrepareCursorValue
* @dataProvider preparedProviderValue
* @dataProvider providerDbGetPrepareCursorValue
* @testdox prepared query $stm_name with $key expect error id $error_id [$_dataName]
*
* @param string $query
@@ -3769,6 +3769,94 @@ final class CoreLibsDBIOTest extends TestCase
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerDbPreparedCursorStatus(): array
{
return [
'empty statement pararm' => [
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
'stm_name' => 'test_stm_a',
'check_stm_name' => '',
'check_query' => '',
'expected' => false
],
'different stm_name' => [
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
'stm_name' => 'test_stm_b',
'check_stm_name' => 'other_name',
'check_query' => '',
'expected' => 0
],
'same stm_name' => [
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
'stm_name' => 'test_stm_c',
'check_stm_name' => 'test_stm_c',
'check_query' => '',
'expected' => 1
],
'same stm_name and query' => [
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
'stm_name' => 'test_stm_d',
'check_stm_name' => 'test_stm_d',
'check_query' => 'SELECT row_int, uid FROM table_with_primary_key',
'expected' => 2
],
'same stm_name and different query' => [
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
'stm_name' => 'test_stm_e',
'check_stm_name' => 'test_stm_e',
'check_query' => 'SELECT row_int, uid, row_int FROM table_with_primary_key',
'expected' => 1
],
'insert query test' => [
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
'stm_name' => 'test_stm_f',
'check_stm_name' => 'test_stm_f',
'check_query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES ($1, $2)',
'expected' => 2
]
];
}
/**
* test cursor status for prepared statement
*
* @covers ::dbPreparedCursorStatus
* @dataProvider providerDbPreparedCursorStatus
* @testdox Check prepared $stm_name ($check_stm_name) status is $expected [$_dataName]
*
* @param string $query
* @param string $stm_name
* @param string $check_stm_name
* @param string $check_query
* @param bool|int $expected
* @return void
*/
public function testDbPreparedCursorStatus(
string $query,
string $stm_name,
string $check_stm_name,
string $check_query,
bool|int $expected
): void {
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$db->dbPrepare($stm_name, $query);
// $db->dbExecute($stm_name);
$this->assertEquals(
$expected,
$db->dbPreparedCursorStatus($check_stm_name, $check_query),
'check prepared stement cursor status'
);
unset($db);
}
// - schema set/get tests
// dbGetSchema, dbSetSchema

View File

@@ -395,7 +395,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
}
$per_run_id = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
$per_run_id,
'assert per log run id 1st'
);
@@ -403,7 +403,7 @@ final class CoreLibsLoggingLoggingTest extends TestCase
$log->setLogUniqueId(true);
$per_run_id_2nd = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
"/^\d{4}-\d{2}-\d{2}_\d{6}\.U_[a-z0-9]{8}$/",
$per_run_id_2nd,
'assert per log run id 2nd'
);
@@ -824,13 +824,13 @@ final class CoreLibsLoggingLoggingTest extends TestCase
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_DEBUG.log'
$log->getLogFileId() . '.DEBUG.log'
);
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (info) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_INFO.log'
$log->getLogFileId() . '.INFO.log'
);
}

View File

@@ -13,6 +13,11 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsSecurityPasswordTest extends TestCase
{
/**
* Undocumented function
*
* @return array
*/
public function passwordProvider(): array
{
return [
@@ -21,6 +26,11 @@ final class CoreLibsSecurityPasswordTest extends TestCase
];
}
/**
* Note: we need different hash types for PHP versions
*
* @return array
*/
public function passwordRehashProvider(): array
{
return [
@@ -63,6 +73,10 @@ final class CoreLibsSecurityPasswordTest extends TestCase
*/
public function testPasswordRehashCheck(string $input, bool $expected): void
{
// in PHP 8.4 the length is $12
if (PHP_VERSION_ID > 80400) {
$input = str_replace('$2y$10$', '$2y$12$', $input);
}
$this->assertEquals(
$expected,
\CoreLibs\Security\Password::passwordRehashCheck($input)

View File

@@ -1,7 +1,7 @@
<phpunit
cacheResultFile="/tmp/phpunit-corelibs.result.cache"
colors="true"
verbose="true"
verbose="false"
convertDeprecationsToExceptions="true"
bootstrap="4dev/tests/bootstrap.php"
>

View File

@@ -273,8 +273,8 @@ $query_insert = <<<SQL
INSERT INTO
test_foo
(
test, some_bool, string_a, number_a, number_a_numeric,
some_time, some_timestamp, json_string
test, some_bool, string_a, number_a, numeric_a,
some_internval, some_timestamp, json_string
) VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8
@@ -283,8 +283,8 @@ RETURNING test
SQL;
$query_select = <<<SQL
SELECT
test, some_bool, string_a, number_a, number_a_numeric,
some_time, some_time, some_timestamp, json_string
test, some_bool, string_a, number_a, numeric_a,
some_time, some_internval, some_timestamp, json_string
FROM
test_foo
WHERE
@@ -554,7 +554,7 @@ print "<b>PREPARE QUERIES</b><br>";
// READ PREPARE
$q_prep = <<<SQL
SELECT test_foo_id, test, some_bool, string_a, number_a,
number_a_numeric, some_time
numeric_a, some_time
FROM test_foo
WHERE test = $1
ORDER BY test_foo_id DESC LIMIT 5
@@ -582,7 +582,7 @@ if ($db->dbPrepare('sel_test_foo', $q_prep) === false) {
// sel test with ANY () type
$q_prep = "SELECT test_foo_id, test, some_bool, string_a, number_a, "
. "number_a_numeric, some_time "
. "numeric_a, some_time "
. "FROM test_foo "
. "WHERE test = ANY($1) "
. "ORDER BY test_foo_id DESC LIMIT 5";
@@ -618,7 +618,7 @@ $test_bar = $db->dbEscapeLiteral('SOMETHING DIFFERENT');
$q = <<<SQL
SELECT test_foo_id, test, some_bool, string_a, number_a,
-- comment
number_a_numeric, some_time
numeric_a, some_time
FROM test_foo
WHERE test = $test_bar
ORDER BY test_foo_id DESC LIMIT 5
@@ -631,7 +631,7 @@ print "DB RETURN PARAMS<br>";
$q = <<<SQL
SELECT test_foo_id, test, some_bool, string_a, number_a,
-- comment
number_a_numeric, some_time
numeric_a, some_time
FROM test_foo
WHERE test = $1
ORDER BY test_foo_id DESC LIMIT 5
@@ -646,7 +646,7 @@ echo "<hr>";
print "DB RETURN PARAMS LIKE<br>";
$q = <<<SQL
SELECT
test_foo_id, test, some_bool, string_a, number_a, number_a_numeric
test_foo_id, test, some_bool, string_a, number_a, numeric_a
FROM test_foo
WHERE string_a LIKE $1;
SQL;
@@ -660,7 +660,7 @@ echo "<hr>";
print "DB RETURN PARAMS ANY<br>";
$q = <<<SQL
SELECT
test_foo_id, test, some_bool, string_a, number_a, number_a_numeric
test_foo_id, test, some_bool, string_a, number_a, numeric_a
FROM test_foo
WHERE string_a = ANY($1);
SQL;
@@ -707,6 +707,17 @@ if (
} else {
print "[PGB] [3] pgb_sel_test_foo prepare OK<br>";
}
$stm_status = $db->dbPreparedCursorStatus('');
print "[PGB] Empty statement name: " . $log->prAr($stm_status) . "<br>";
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foobar');
print "[PGB] Prepared name not match status: $stm_status<br>";
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo');
print "[PGB] Prepared name match status: $stm_status<br>";
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo', $q_prep);
print "[PGB] prepared exists and query match status: $stm_status<br>";
$stm_status = $db->dbPreparedCursorStatus('pgb_sel_test_foo', "SELECT * FROM test_foo");
print "[PGB] prepared exists and query not match status: $stm_status<br>";
$db_pgb->dbClose();
# db write class test

View File

@@ -57,6 +57,43 @@ if (($dbh = $db->dbGetDbh()) instanceof \PgSql\Connection) {
print "<b>TRUNCATE test_foo</b><br>";
$db->dbExec("TRUNCATE test_foo");
/*
BELOW IS THE FULL TABLE WITH ALL PostgreSQL Types
=> \d test_foo
Table "public.test_foo"
Column | Type | Nullable | Default
------------------+-----------------------------+----------+-----------------------------------------------
test | character varying | |
some_bool | boolean | |
string_a | character varying | |
number_a | integer | |
numeric_a | numeric | |
some_internval | interval | |
test_foo_id | integer | not null | generated always as identity
json_string | jsonb | |
some_timestamp | timestamp without time zone | |
some_binary | bytea | |
null_var | character varying | |
smallint_a | smallint | |
number_real | real | |
number_double | double precision | |
number_serial | integer | not null | nextval('test_foo_number_serial_seq'::regclass)
array_char_1 | character varying[] | |
array_char_2 | character varying[] | |
array_int_1 | integer[] | |
array_int_2 | integer[] | |
composite_item | inventory_item | |
array_composite | inventory_item[] | |
numeric_3 | numeric(3,0) | |
identity_always | bigint | not null | generated always as identity
identitiy_default | bigint | not null | generated by default as identity
uuid_var | uuid | | gen_random_uuid()
some_date | date | |
some_time | time without time zone | |
bigint_a | bigint | |
default_uuid | uuid | | gen_random_uuid()
*/
/* $q = <<<SQL
INSERT INTO test_foo (test, array_composite) VALUES ('C', '{"(a,1,1.5)","(b,2,2.5)"}')
SQL;
@@ -90,7 +127,7 @@ $query_params = [
$query_insert = <<<SQL
INSERT INTO test_foo (
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
test, some_bool, string_a, number_a, numeric_a, smallint_a,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_int_1,
array_composite,
@@ -106,7 +143,7 @@ INSERT INTO test_foo (
)
RETURNING
test_foo_id,
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
test, some_bool, string_a, number_a, numeric_a, smallint_a,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_int_1,
array_composite,
@@ -127,8 +164,8 @@ echo "<hr>";
$query_select = <<<SQL
SELECT
test_foo_id,
test, some_bool, string_a, number_a, number_a_numeric, smallint_a,
number_real, number_double, number_numeric_3, number_serial,
test, some_bool, string_a, number_a, numeric_a, smallint_a,
number_real, number_double, numeric_3, number_serial,
some_time, some_timestamp, json_string, null_var,
array_char_1, array_char_2, array_int_1, array_int_2, array_composite,
composite_item, (composite_item).*,

View File

@@ -0,0 +1,107 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
// basic class test file
define('USE_DATABASE', false);
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-phpv';
ob_end_flush();
$log = new CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
$_phpv = new CoreLibs\Check\PhpVersion();
$phpv_class = 'CoreLibs\Check\PhpVersion';
$PAGE_NAME = 'TEST CLASS: PHP VERSION';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
// fputcsv
print "<h3>\CoreLibs\DeprecatedHelper\Deprecated84::fputcsv()</h3>";
$test_csv = BASE . TMP . 'DeprecatedHelper.test.csv';
print "File: $test_csv<br>";
$fp = fopen($test_csv, "w");
if (!is_resource($fp)) {
die("Cannot open file: $test_csv");
}
\CoreLibs\DeprecatedHelper\Deprecated84::fputcsv($fp, ["A", "B", "C"]);
fclose($fp);
$fp = fopen($test_csv, "r");
if (!is_resource($fp)) {
die("Cannot open file: $test_csv");
}
while ($entry = \CoreLibs\DeprecatedHelper\Deprecated84::fgetcsv($fp)) {
print "fgetcsv: <pre>" . print_r($entry, true) . "</pre>";
}
fclose($fp);
$out = \CoreLibs\DeprecatedHelper\Deprecated84::str_getcsv("A,B,C");
print "str_getcsv: <pre>" . print_r($out, true) . "</pre>";
/**
* temporary different CSV function, because fgetcsv seems to be broken on some systems
* (does not read out japanese text)
*
* @param string $string full line for csv split
* @param string $encoding optional, if given, converts string to the internal encoding
* before we do anything
* @param string $delimiter sepperate character, default ','
* @param string $enclosure string line marker, default '"'
* @param string $flag INTERN | EXTERN. if INTERN uses the PHP function, else uses explode
* @return array<int,string|null> array with split data from input line
*/
function mtParseCSV(
string $string,
string $encoding = '',
string $delimiter = ',',
string $enclosure = '"',
string $flag = 'INTERN'
): array {
$lines = [];
if ($encoding) {
$string = \CoreLibs\Convert\Encoding::convertEncoding(
$string,
'UTF-8',
$encoding
);
}
if ($flag == 'INTERN') {
// split with PHP function
$lines = str_getcsv($string, $delimiter, $enclosure);
} else {
// split up with delimiter
$lines = explode(',', $string) ?: [];
}
// strip " from beginning and end of line
for ($i = 0; $i < count($lines); $i++) {
// remove line breaks
$lines[$i] = preg_replace("/\r\n?/", '', (string)$lines[$i]) ?? '';
// lingering " at the beginning and end of the line
$lines[$i] = preg_replace("/^\"/", '', (string)$lines[$i]) ?? '';
$lines[$i] = preg_replace("/\"$/", '', (string)$lines[$i]) ?? '';
}
return $lines;
}
print "</body></html>";
// __END__

View File

@@ -117,7 +117,7 @@ if (isset($login->loginGetAcl()['unit'])) {
if ($login->loginCheckEditAccessCuid($edit_access_cuid)) {
print "Set new:" . $edit_access_cuid . "<br>";
} else {
print "Load default unit id: " . $login->loginGetAcl()['unit_id'] . "<br>";
print "Load default unit id: " . $login->loginGetAcl()['unit_cuid'] . "<br>";
}
} else {
print "Something went wrong with the login<br>";
@@ -140,4 +140,17 @@ $login->writeLog(
write_type:'JSON'
);
echo "<hr>";
print "<h3>Legacy Lookups</h3>";
$edit_access_id = 1;
$edit_access_cuid = $login->loginGetEditAccessCuidFromId($edit_access_id);
$edit_access_id_rev = null;
if (is_string($edit_access_cuid)) {
$edit_access_id_rev = $login->loginGetEditAccessIdFromCuid($edit_access_cuid);
}
print "EA ID: " . $edit_access_id . "<br>";
print "EA CUID: " . $log->prAr($edit_access_cuid) . "<br>";
print "REV EA CUID: " . $log->prAr($edit_access_id_rev) . "<br>";
print "</body></html>";

View File

@@ -29,15 +29,17 @@ $table_arrays = [];
$table_arrays[\CoreLibs\Get\System::getPageName(1)] = [
// form fields mtaching up with db fields
'table_array' => [
'foo',
'bar'
],
// laod query
'load_query' => '',
'load_query' => 'SELECT uuid_nr, foo, bar FROM test',
// database table to load from
'table_name' => '',
'table_name' => 'test',
// for load dro pdown, format output
'show_fields' => [
[
'name' => 'name'
'name' => 'foo'
],
[
'name' => 'enabled',

View File

@@ -37,6 +37,8 @@ print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "PHP Version: " . PHP_VERSION . "<br>";
$password = 'something1234';
$enc_password = $_password->passwordSet($password);
print "PASSWORD: $password: " . $enc_password . "<br>";
@@ -51,6 +53,20 @@ print "PASSWORD REHASH: " . (string)$password_class::passwordRehashCheck($enc_pa
// direct static
print "S::PASSWORD VERFIY: " . (string)PwdChk::passwordVerify($password, $enc_password) . "<br>";
if (PHP_VERSION_ID < 80400) {
$rehash_test = '$2y$10$EgWJ2WE73DWi.hIyFRCdpejLXTvHbmTK3LEOclO1tAvXAXUNuUS4W';
$rehash_test_throw = '$2y$12$EgWJ2WE73DWi.hIyFRCdpejLXTvHbmTK3LEOclO1tAvXAXUNuUS4W';
} else {
$rehash_test = '$2y$12$EgWJ2WE73DWi.hIyFRCdpejLXTvHbmTK3LEOclO1tAvXAXUNuUS4W';
$rehash_test_throw = '$2y$10$EgWJ2WE73DWi.hIyFRCdpejLXTvHbmTK3LEOclO1tAvXAXUNuUS4W';
}
if (PwdChk::passwordRehashCheck($rehash_test)) {
print "Bad password [BAD]<br>";
}
if (PwdChk::passwordRehashCheck($rehash_test_throw)) {
print "Bad password [OK]<br>";
}
print "</body></html>";
// __END__

View File

@@ -141,6 +141,7 @@ $test_files = [
'class_test.error_msg.php' => 'Class Test: ERROR MSG',
'class_test.url-requests.curl.php' => 'Class Test: URL REQUESTS: CURL',
'subfolder/class_test.config.direct.php' => 'Class Test: CONFIG DIRECT SUB',
'class_test.deprecated.helper.php' => 'Class Test: DEPRECATED HELPERS',
];
asort($test_files);

View File

@@ -28,8 +28,6 @@ $log = new CoreLibs\Logging\Logging([
$_phpv = new CoreLibs\Check\PhpVersion();
$phpv_class = 'CoreLibs\Check\PhpVersion';
// define a list of from to color sets for conversion test
$PAGE_NAME = 'TEST CLASS: PHP VERSION';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";

View File

@@ -4,6 +4,8 @@
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
// FIXME: Smarty Class must be updated for PHP 8.4
declare(strict_types=1);
error_reporting(E_ALL | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
@@ -33,6 +35,7 @@ $l10n = new \CoreLibs\Language\L10n(
);
$smarty = new CoreLibs\Template\SmartyExtend(
$l10n,
$log,
CACHE_ID,
COMPILE_ID,
);
@@ -45,6 +48,7 @@ $adm = new CoreLibs\Admin\Backend(
);
$adm->DATA['adm_set'] = 'SET from admin class';
$PAGE_NAME = 'TEST CLASS: SMARTY';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";

View File

@@ -21,7 +21,7 @@
}
},
"require": {
"egrajp/smarty-extended": "^4.3",
"egrajp/smarty-extended": "^5.4",
"php": ">=8.1",
"gullevek/dotenv": "^2.0",
"psr/log": "^2.0 || ^3.0"

View File

@@ -91,7 +91,7 @@ $l10n = new \CoreLibs\Language\L10n(
);
// create smarty object
$smarty = new \CoreLibs\Template\SmartyExtend($l10n, CACHE_ID, COMPILE_ID);
$smarty = new \CoreLibs\Template\SmartyExtend($l10n, $log, CACHE_ID, COMPILE_ID);
// create new Backend class with db and loger attached
$cms = new \CoreLibs\Admin\Backend($db, $log, $session, $l10n, DEFAULT_ACL_LEVEL);
// the menu show flag (what menu to show)
@@ -116,7 +116,7 @@ $data = [
// log action
// no log if login
if (!$login->loginActionRun()) {
$login->writeLog('Submit', $data, $cms->adbGetActionSet(), 'BINARY');
$login->writeLog('Submit', $data, action_set:$cms->adbGetActionSet(), write_type:'BINARY');
}
//------------------------------ logging end

View File

@@ -1418,6 +1418,7 @@ class Login
'additional_acl' => Json::jsonConvertToArray($res['additional_acl']),
'data' => $ea_data
];
// LEGACY LOOKUP
$unit_access_eaid[$res['edit_access_id']] = [
'cuid' => $res['cuid'],
];
@@ -1477,6 +1478,8 @@ class Login
// username (login), group name
$this->acl['user_name'] = $_SESSION['LOGIN_USER_NAME'];
$this->acl['group_name'] = $_SESSION['LOGIN_GROUP_NAME'];
// DEPRECATED
$this->acl['euid'] = $_SESSION['LOGIN_EUID'];
// edit user cuid
$this->acl['eucuid'] = $_SESSION['LOGIN_EUCUID'];
$this->acl['eucuuid'] = $_SESSION['LOGIN_EUCUUID'];
@@ -1529,7 +1532,7 @@ class Login
$this->acl['page'] = $_SESSION['LOGIN_PAGES_ACL_LEVEL'][$this->page_name];
}
$this->acl['unit_id'] = null;
$this->acl['unit_cuid'] = null;
$this->acl['unit_name'] = null;
$this->acl['unit_uid'] = null;
$this->acl['unit'] = [];
@@ -1552,8 +1555,10 @@ class Login
$this->acl['unit_legacy'][$unit['id']] = $this->acl['unit'][$ea_cuid];
// detail name/level set
$this->acl['unit_detail'][$ea_cuid] = [
'id' => $unit['id'],
'name' => $unit['name'],
'uid' => $unit['uid'],
'cuuid' => $unit['cuuid'],
'level' => $this->default_acl_list[$this->acl['unit'][$ea_cuid]]['name'] ?? -1,
'default' => $unit['default'],
'data' => $unit['data'],
@@ -3212,7 +3217,7 @@ HTML;
* @return int|null same edit access id if ok
* or the default edit access id
* if given one is not valid
* @deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessValidCuid()
* @#deprecated Please switch to using edit access cuid check with ->loginCheckEditAccessValidCuid()
*/
public function loginCheckEditAccessId(?int $edit_access_id): ?int
{
@@ -3277,6 +3282,34 @@ HTML;
return (int)$_SESSION['LOGIN_UNIT_CUID'][$uid];
}
/**
* Legacy lookup for edit access id to cuid
*
* @param int $id edit access id PK
* @return string|false edit access cuid or false if not found
*/
public function loginGetEditAccessCuidFromId(int $id): string|false
{
if (!isset($_SESSION['LOGIN_UNIT_LEGACY'][$id])) {
return false;
}
return (string)$_SESSION['LOGIN_UNIT_LEGACY'][$id]['cuid'];
}
/**
* This is a Legacy lookup from the edit access id to cuid for further lookups in the normal list
*
* @param string $cuid edit access cuid
* @return int|false false on not found or edit access id PK
*/
public function loginGetEditAccessIdFromCuid(string $cuid): int|false
{
if (!isset($_SESSION['LOGIN_UNIT'][$cuid])) {
return false;
}
return $_SESSION['LOGIN_UNIT'][$cuid]['id'];
}
/**
* Check if admin flag is set
*

View File

@@ -289,7 +289,7 @@ class Backend
* JSON, STRING/SERIEAL, BINARY/BZIP or ZLIB
* @param string|null $db_schema [default=null] override target schema
* @return void
* @deprecated Use $login->writeLog() and set action_set from ->adbGetActionSet()
* @deprecated Use $login->writeLog($event, $data, action_set:$cms->adbGetActionSet(), write_type:$write_type)
*/
public function adbEditLog(
string $event = '',

View File

@@ -14,9 +14,6 @@ declare(strict_types=1);
namespace CoreLibs\Admin;
use Exception;
use SmartyException;
class EditBase
{
/** @var array<mixed> */
@@ -63,6 +60,7 @@ class EditBase
// smarty template engine (extended Translation version)
$this->smarty = new \CoreLibs\Template\SmartyExtend(
$l10n,
$log,
$options['cache_id'] ?? '',
$options['compile_id'] ?? '',
);
@@ -538,8 +536,7 @@ class EditBase
* builds the smarty content and runs smarty display output
*
* @return void
* @throws Exception
* @throws SmartyException
* @throws \Smarty\Exception
*/
public function editBaseRun(
?string $template_dir = null,

View File

@@ -2946,7 +2946,7 @@ class IO
$query_hash = $this->dbGetQueryHash($query, $params);
// clears cache for this query
if (empty($this->cursor_ext[$query_hash]['query'])) {
$this->__dbError(18, context: [
$this->__dbWarning(18, context: [
'query' => $query,
'params' => $params,
'hash' => $query_hash,
@@ -3141,6 +3141,7 @@ class IO
'pk_name' => '',
'count' => 0,
'query' => '',
'query_raw' => $query,
'result' => null,
'returning_id' => false,
'placeholder_converted' => [],
@@ -3237,11 +3238,12 @@ class IO
}
} else {
// if we try to use the same statement name for a differnt query, error abort
if ($this->prepare_cursor[$stm_name]['query'] != $query) {
if ($this->prepare_cursor[$stm_name]['query_raw'] != $query) {
// thrown error
$this->__dbError(26, false, context: [
'statement_name' => $stm_name,
'prepared_query' => $this->prepare_cursor[$stm_name]['query'],
'prepared_query_raw' => $this->prepare_cursor[$stm_name]['query_raw'],
'query' => $query,
'pk_name' => $pk_name,
]);
@@ -4364,6 +4366,37 @@ class IO
return $this->prepare_cursor[$stm_name][$key];
}
/**
* Checks if a prepared query eixsts
*
* @param string $stm_name Statement to check
* @param string $query [default=''] If set then query must also match
* @return false|int<0,2> False on missing stm_name
* 0: ok, 1: stm_name matchin, 2: stm_name and query matching
*/
public function dbPreparedCursorStatus(string $stm_name, string $query = ''): false|int
{
if (empty($stm_name)) {
$this->__dbError(
101,
false,
'No statement name given'
);
return false;
}
// does not exist
$return_value = 0;
if (!empty($this->prepare_cursor[$stm_name]['query_raw'])) {
// statement name eixts
$return_value = 1;
if ($this->prepare_cursor[$stm_name]['query_raw'] == $query) {
// query also matches
$return_value = 2;
}
}
return $return_value;
}
// ***************************
// ERROR AND WARNING DATA
// ***************************

View File

@@ -0,0 +1,95 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2025/1/17
* DESCRIPTION:
* Deprecated helper for fputcsv
*/
declare(strict_types=1);
namespace CoreLibs\DeprecatedHelper;
use InvalidArgumentException;
class Deprecated84
{
/**
* This is a wrapper for fputcsv to fix deprecated warning for $escape parameter
* See: https://www.php.net/manual/en/function.fputcsv.php
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
*
* @param mixed $stream
* @param array<mixed> $fields
* @param string $separator
* @param string $enclosure
* @param string $escape
* @param string $eol
* @return int|false
* @throws InvalidArgumentException
*/
public static function fputcsv(
mixed $stream,
array $fields,
string $separator = ",",
string $enclosure = '"',
string $escape = '', // set to empty for future compatible
string $eol = PHP_EOL
): int | false {
if (!is_resource($stream)) {
throw new \InvalidArgumentException("fputcsv stream parameter must be a resrouce");
}
return fputcsv($stream, $fields, $separator, $enclosure, $escape, $eol);
}
/**
* This is a wrapper for fgetcsv to fix deprecated warning for $escape parameter
* See: https://www.php.net/manual/en/function.fgetcsv.php
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
*
* @param mixed $stream
* @param null|int<0,max> $length
* @param string $separator
* @param string $enclosure
* @param string $escape
* @return array<mixed>|false
* @throws InvalidArgumentException
*/
public static function fgetcsv(
mixed $stream,
?int $length = null,
string $separator = ',',
string $enclosure = '"',
string $escape = '' // set to empty for future compatible
): array | false {
if (!is_resource($stream)) {
throw new \InvalidArgumentException("fgetcsv stream parameter must be a resrouce");
}
return fgetcsv($stream, $length, $separator, $enclosure, $escape);
}
/**
* This is a wrapper for str_getcsv to fix deprecated warning for $escape parameter
* See: https://www.php.net/manual/en/function.str-getcsv.php
* escape parameter deprecation and recommend to set to "" for compatible with PHP 9.0
*
* @param string $string
* @param string $separator
* @param string $enclosure
* @param string $escape
* @return array<mixed>
*/
// phpcs:disable PSR1.Methods.CamelCapsMethodName
public static function str_getcsv(
string $string,
string $separator = ",",
string $enclosure = '"',
string $escape = '' // set to empty for future compatible
): array {
return str_getcsv($string, $separator, $enclosure, $escape);
}
// phpcs:enable PSR1.Methods.CamelCapsMethodName
}
// __END__

View File

@@ -30,6 +30,10 @@ class Logging
{
/** @var int minimum size for a max file size, so we don't set 1 byte, 10kb */
public const MIN_LOG_MAX_FILESIZE = 10 * 1024;
/** @var string log file extension, not changeable */
private const LOG_FILE_NAME_EXT = "log";
/** @var string log file block separator, not changeable */
private const LOG_FILE_BLOCK_SEPARATOR = '.';
// NOTE: the second party array{} hs some errors
/** @var array<string,array<string,string|bool|Level>>|array{string:array{type:string,type_info?:string,mandatory:true,alias?:string,default:string|bool|Level,deprecated:bool,use?:string}} */
@@ -104,8 +108,6 @@ class Logging
private string $log_folder = '';
/** @var string a alphanumeric name that has to be set as global definition */
private string $log_file_id = '';
/** @var string log file name extension */
private string $log_file_name_ext = 'log';
/** @var string log file name with folder, for actual writing */
private string $log_file_name = '';
/** @var int set in bytes */
@@ -431,7 +433,7 @@ class Logging
private function buildLogFileName(Level $level, string $group_id = ''): string
{
// init base file path
$fn = $this->log_print_file . '.' . $this->log_file_name_ext;
$fn = $this->log_print_file . '.' . self::LOG_FILE_NAME_EXT;
// log ID prefix settings, if not valid, replace with empty
if (!empty($this->log_file_id)) {
$rpl_string = $this->log_file_id;
@@ -440,14 +442,15 @@ class Logging
}
$fn = str_replace('{LOGID}', $rpl_string, $fn); // log id (like a log file prefix)
$rpl_string = !$this->getLogFlag(Flag::per_level) ? '' :
'_' . $level->getName();
$rpl_string = $this->getLogFlag(Flag::per_level) ?
self::LOG_FILE_BLOCK_SEPARATOR . $level->getName() :
'';
$fn = str_replace('{LEVEL}', $rpl_string, $fn); // create output filename
// write per level
$rpl_string = !$this->getLogFlag(Flag::per_group) ? '' :
$rpl_string = $this->getLogFlag(Flag::per_group) ?
// normalize level, replace all non alphanumeric characters with -
'_' . (
self::LOG_FILE_BLOCK_SEPARATOR . (
// if return is only - then set error string
preg_match(
"/^-+$/",
@@ -455,25 +458,29 @@ class Logging
) ?
'INVALID-LEVEL-STRING' :
$level_string
);
) :
'';
$fn = str_replace('{GROUP}', $rpl_string, $fn); // create output filename
// set per class, but don't use get_class as we will only get self
$rpl_string = !$this->getLogFlag(Flag::per_class) ? '' : '_'
// set sub class settings
. str_replace('\\', '-', Support::getCallerTopLevelClass());
$rpl_string = $this->getLogFlag(Flag::per_class) ?
// set sub class settings
self::LOG_FILE_BLOCK_SEPARATOR . str_replace('\\', '-', Support::getCallerTopLevelClass()) :
'';
$fn = str_replace('{CLASS}', $rpl_string, $fn); // create output filename
// if request to write to one file
$rpl_string = !$this->getLogFlag(Flag::per_page) ?
'' :
'_' . System::getPageName(System::NO_EXTENSION);
$rpl_string = $this->getLogFlag(Flag::per_page) ?
self::LOG_FILE_BLOCK_SEPARATOR . System::getPageName(System::NO_EXTENSION) :
'';
$fn = str_replace('{PAGENAME}', $rpl_string, $fn); // create output filename
// if run id, we auto add ymd, so we ignore the log file date
if ($this->getLogFlag(Flag::per_run)) {
$rpl_string = '_' . $this->getLogUniqueId(); // add 8 char unique string
// add 8 char unique string and date block with time
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogUniqueId();
} elseif ($this->getLogFlag(Flag::per_date)) {
$rpl_string = '_' . $this->getLogDate(); // add date to file
// add date to file
$rpl_string = self::LOG_FILE_BLOCK_SEPARATOR . $this->getLogDate();
} else {
$rpl_string = '';
}
@@ -739,7 +746,10 @@ class Logging
{
if (empty($this->log_file_unique_id) || $override == true) {
$this->log_file_unique_id =
date('Y-m-d_His') . '_U_'
date('Y-m-d_His')
. self::LOG_FILE_BLOCK_SEPARATOR
. 'U_'
// this doesn't have to be unique for everything, just for this logging purpose
. substr(hash(
'sha1',
random_bytes(63)

View File

@@ -19,12 +19,13 @@ declare(strict_types=1);
namespace CoreLibs\Template;
// leading slash if this is in lib\Smarty
class SmartyExtend extends \Smarty
class SmartyExtend extends \Smarty\Smarty
{
// internal translation engine
/** @var \CoreLibs\Language\L10n */
/** @var \CoreLibs\Language\L10n language class */
public \CoreLibs\Language\L10n $l10n;
/** @var \CoreLibs\Logging\Logging $log logging class */
public \CoreLibs\Logging\Logging $log;
// lang & encoding
/** @var string */
@@ -157,14 +158,18 @@ class SmartyExtend extends \Smarty
* calls L10 for pass on internaly in smarty
* also registers the getvar caller plugin
*
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param string|null $cache_id
* @param string|null $compile_id
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param \CoreLibs\Logging\Logging $log Logger class
* @param string|null $cache_id [default=null]
* @param string|null $compile_id [default=null]
* @param array<string,mixed> $options [default=[]]
*/
public function __construct(
\CoreLibs\Language\L10n $l10n,
\CoreLibs\Logging\Logging $log,
?string $cache_id = null,
?string $compile_id = null
?string $compile_id = null,
array $options = []
) {
// trigger deprecation
if (
@@ -177,14 +182,33 @@ class SmartyExtend extends \Smarty
E_USER_DEPRECATED
);
}
// set variables (to be deprecated)
$cache_id = $cache_id ??
(defined('CACHE_ID') ? CACHE_ID : '');
$compile_id = $compile_id ??
(defined('COMPILE_ID') ? COMPILE_ID : '');
// set variables from global constants (deprecated)
if ($cache_id === null && defined('CACHE_ID')) {
trigger_error(
'SmartyExtended: No cache_id set and CACHE_ID constant set, this is deprecated',
E_USER_DEPRECATED
);
$cache_id = CACHE_ID;
}
if ($compile_id === null && defined('COMPILE_ID')) {
trigger_error(
'SmartyExtended: No compile_id set and COMPILE_ID constant set, this is deprecated',
E_USER_DEPRECATED
);
$compile_id = COMPILE_ID;
}
if (empty($cache_id)) {
throw new \BadMethodCallException('cache_id parameter is not set');
}
if (empty($compile_id)) {
throw new \BadMethodCallException('compile_id parameter is not set');
}
// call basic smarty
// or Smarty::__construct();
parent::__construct();
$this->log = $log;
// init lang
$this->l10n = $l10n;
// parse and read, legacy stuff
@@ -194,7 +218,6 @@ class SmartyExtend extends \Smarty
$this->lang_short = $locale['lang_short'];
$this->domain = $locale['domain'];
$this->lang_dir = $locale['path'];
// opt load functions so we can use legacy init for smarty run perhaps
\CoreLibs\Language\L10n::loadFunctions();
_setlocale(LC_MESSAGES, $locale['locale']);
@@ -203,7 +226,6 @@ class SmartyExtend extends \Smarty
_bind_textdomain_codeset($this->domain, $this->encoding);
// register smarty variable
// $this->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']);
$this->registerPlugin(self::PLUGIN_MODIFIER, 'getvar', [&$this, 'getTemplateVars']);
$this->page_name = \CoreLibs\Get\System::getPageName();
@@ -211,6 +233,77 @@ class SmartyExtend extends \Smarty
// set internal settings
$this->CACHE_ID = $cache_id;
$this->COMPILE_ID = $compile_id;
// set options
$this->setOptions($options);
}
/**
* set options
*
* @param array<string,mixed> $options
* @return void
*/
private function setOptions(array $options): void
{
// set escape html if option is set
if (!empty($options['escape_html'])) {
$this->setEscapeHtml(true);
}
// load plugins
// plugin array:
// 'file': string, path to plugin content to load
// 'type': a valid smarty type see Smarty PLUGIN_ constants for correct names
// 'tag': the smarty tag
// 'callback': the function to call in 'file'
if (!empty($options['plugins'])) {
foreach ($options['plugins'] as $plugin) {
// file is readable
if (
empty($plugin['file']) ||
!is_file($plugin['file']) ||
!is_readable($plugin['file'])
) {
$this->log->warning('SmartyExtended plugin load failed, file not accessable', [
'plugin' => $plugin,
]);
continue;
}
// tag is alphanumeric
if (!preg_match("/^\w+$/", $plugin['tag'] ?? '')) {
$this->log->warning('SmartyExtended plugin load failed, invalid tag', [
'plugin' => $plugin,
]);
continue;
}
// callback is alphanumeric
if (!preg_match("/^\w+$/", $plugin['callback'] ?? '')) {
$this->log->warning('SmartyExtended plugin load failed, invalid callback', [
'plugin' => $plugin,
]);
continue;
}
try {
/** @phan-suppress-next-line PhanNoopNew */
new \ReflectionClassConstant($this, $plugin['type']);
} catch (\ReflectionException $e) {
$this->log->error('SmartyExtended plugin load failed, type is not valid', [
'message' => $e->getMessage(),
'plugin' => $plugin,
]);
continue;
}
try {
require $plugin['file'];
$this->registerPlugin($plugin['type'], $plugin['tag'], $plugin['callback']);
} catch (\Smarty\Exception $e) {
$this->log->error('SmartyExtended plugin load failed with exception', [
'message' => $e->getMessage(),
'plugin' => $plugin,
]);
continue;
}
}
}
}
/**