Compare commits

..

75 Commits

Author SHA1 Message Date
90550746ab Bug fix in DB\ArrayIO for pk_id access 2023-09-27 09:43:32 +09:00
724031b944 Release: v9.7.5 2023-09-26 18:47:58 +09:00
027c35f9f0 Bug fix for Edit Order not loading 2023-09-26 18:47:02 +09:00
6ad844b519 Release: v9.7.4 2023-09-15 18:33:54 +09:00
e10987ce8b ErrorMsg update with target_style 2023-09-15 18:32:55 +09:00
b3617954eb Release: v9.7.3 2023-09-15 18:24:33 +09:00
67fd7b172a ErrorMessage: flag to also log 'error' level messages to log 2023-09-11 13:37:58 +09:00
fe729453ac Released: v9.7.2 2023-09-11 13:37:25 +09:00
b939edac3f Logging\ErrorMessage rename setError to setMessage 2023-09-08 21:01:05 +09:00
00528cb7d7 Release: v9.7.1 2023-09-08 18:50:35 +09:00
3b8583de61 Rename ErrorMessage method from setErrorMsgLevel to setError for backend 2023-09-08 18:49:58 +09:00
f6821b7c21 Release: v9.7.0 2023-09-08 18:39:11 +09:00
7b49394c5a Logging/ErrorMessage class 2023-09-08 18:38:19 +09:00
f624776397 Release: v9.6.2 2023-09-08 18:31:03 +09:00
5e31559c3e Release: v9.6.1: wrong 2023-09-05 15:14:04 +09:00
52a7f1197b Fixs in SetType class for string/int/numeric 2023-09-05 14:30:03 +09:00
e1466432a8 Release: v9.6.0 2023-09-01 18:34:22 +09:00
c57e798591 Add proper Exceptions to class instead of false return on critical problems 2023-09-01 18:32:57 +09:00
8126f08b8c Update publish script and zip file location
All zip files are now in published/package-download/
Move the download file name to the .env file in GITEA_UPLOAD_FILENAME
2023-09-01 09:57:05 +09:00
6f657f2890 Release: v9.5.1 2023-08-28 09:54:02 +09:00
92214ae136 DB\IO bug fixes for unset returns 2023-08-28 09:52:51 +09:00
71f9afd6c7 Release: v9.5.0 2023-08-22 13:36:08 +09:00
11de4f9915 Logging update for class/method trace 2023-08-22 13:34:43 +09:00
dfb46d4f4a Release: v9.4.0 2023-08-22 13:33:08 +09:00
53c7dda9a0 JSON Array to convert fix, bool/false return fix 2023-08-02 16:34:57 +09:00
daf2706e7e Release: v9.3.6 2023-07-26 12:13:59 +09:00
6d3a7b7b28 ACL\Login bug fix for acl:base must always be int 2023-07-26 11:52:53 +09:00
745340a7f5 Release: v9.3.5 2023-07-24 09:16:09 +09:00
419c578c46 ACL\Login get edit access id return value fix 2023-07-24 09:14:16 +09:00
6beff9c6ac Release: v9.3.4 2023-07-21 19:07:40 +09:00
79dbd053fa Remove per class loggingn from ACL\Login 2023-07-21 19:06:30 +09:00
74004e5221 Release: v9.3.3 2023-07-21 17:53:18 +09:00
0392187299 DB\IO init with null 2023-07-21 17:52:16 +09:00
edcc65df3e Release: v9.3.2 2023-07-21 17:49:09 +09:00
5dde52a309 DB\IO error/warning log prefix remove, Admin\Backend acl default value check 2023-07-14 15:10:03 +09:00
5f223fb50d Release: v9.3.1 2023-07-04 11:47:24 +09:00
2eaf80b1bd new Combined\DateTime method dateRangeHasWeekend 2023-07-04 11:46:34 +09:00
b5d601aec0 Release: v9.3.0 2023-06-28 15:34:31 +09:00
edcc77a6ab HtmlBuilder classes 2023-06-28 15:33:12 +09:00
0b1df7a901 Release: v9.2.0 2023-06-16 13:28:44 +09:00
e0f8bad2d9 DB\IO Call trace in context array for any debug calls 2023-06-16 13:24:34 +09:00
f5cd71cfbc Release: v9.1.0 2023-06-13 11:57:30 +09:00
1203164d7e DB\IO: add convert types to php type (TTD-606)
Convert DB types to PHP types
Current settings:
- off
- on: int/bool on;y
- json: json strings to array
- numeric: and real/float to php float (possible precision loss)
- bytea: convert PostgreSQL bytea strings to php data strings
2023-06-13 11:54:53 +09:00
b82e08ba05 Release: v9.0.8 2023-06-05 18:32:58 +09:00
6dfb68a6da Logging: internal fixes 2023-06-05 09:34:29 +09:00
5b944cd12d Release: v9.0.7 2023-06-05 09:32:12 +09:00
65cac4c6e2 Logging bug fixes for per_date/per_run flags 2023-06-02 17:45:21 +09:00
1c1ace58db Release: v9.0.6 2023-06-01 13:10:28 +09:00
16e12b5b8f Bug fixes 2023-06-01 13:08:24 +09:00
73063d28b2 Release: v9.0.5 2023-06-01 12:05:03 +09:00
e80d8006a2 print_r methods use mixed paramter 2023-06-01 12:03:38 +09:00
d648e4339a Must give go flag for publish 2023-06-01 11:09:37 +09:00
4b084f8785 Release: v9.0.4 2023-06-01 11:06:36 +09:00
d0d088b354 DB\IO bug fixes 2023-06-01 11:05:25 +09:00
e0d42af1d2 Release: v9.0.3 2023-06-01 09:17:33 +09:00
c1d26f122e phpunit tests updates 2023-06-01 09:16:06 +09:00
2c2826ac24 Bug fixes and clean ups 2023-06-01 08:47:40 +09:00
72f0810898 Release: v9.0.2 2023-05-31 18:49:58 +09:00
b69539b340 Merge branch 'development' 2023-05-31 18:48:58 +09:00
0b133133dd Release: v9.0.1 2023-05-31 18:48:55 +09:00
8fbe855fd4 Logger\Logger Excpetion calls update 2023-05-31 18:48:00 +09:00
d7410dfe78 Merge branch 'development' 2023-05-31 18:46:02 +09:00
5d36ac2f3e CoreLibs update v9.0.1 2023-05-31 18:45:47 +09:00
2e4ace1a39 Release: v9.0.0 2023-05-31 16:52:59 +09:00
aa5c3b9dcc composer add psr/log 2023-05-31 16:50:21 +09:00
24f7a903ef Composer json update for psr\log required 2023-05-31 16:42:55 +09:00
72993925f0 ACL\Login settings fix 2023-05-31 16:31:44 +09:00
29d5ef92d4 Updates for v9.0 release 2023-05-31 16:27:50 +09:00
f66f8f282e Release: v8.5.0 2023-05-24 16:02:05 +09:00
c010673705 CoreLibs add Security\SymmetricEncryption 2023-05-24 16:00:49 +09:00
b16ff4c613 Release: v8.4.0 2023-05-18 15:21:50 +09:00
e9c791c164 Add better error reporting to DB\IO for query with params
On error with query with params the query was sent to the server and
if ther query itself is ok but there is a problem with the parameters
a wrong error message ($1 not found) will be returned

Add pg_last_error reporting to catch this too.

Update both error reporting to return not string and prefix combined
but prefix + error string in array

In error return check that both strings are not equal, so we do not
return the same error string twice.

Also default set dbh variable in the PgSQL class to false so it will
skip last error report if there is no dbh set yet.

Bug fix for db query with params debug output. if there are more than 9
entries the $1 of eg $10 is replaced with $1 entry again. Changed to
'#' instead '$' to avoid this.

Other:
ACL\Login: replace EOM with HTML
config.master: replace list() with []
Add single DB tester where we can test single db calls without adding
more to the general test run
2023-05-18 15:20:36 +09:00
c7ec1300b7 Published: v8.3.1 2023-04-26 15:43:11 +09:00
064710324e Bug fix in arraySearchKey path reset 2023-04-26 15:41:56 +09:00
e0356dcadf Release: v8.3.0 2023-04-26 14:56:11 +09:00
93 changed files with 10049 additions and 2550 deletions

View File

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

View File

@@ -1,12 +1,25 @@
<?php <?php
/** /**
* This configuration file was automatically generated by 'phan --init --init-level=3'
*
* TODOs (added by 'phan --init'):
*
* - Go through this file and verify that there are no missing/unnecessary files/directories.
* (E.g. this only includes direct composer dependencies
* - You may have to manually add indirect composer dependencies to 'directory_list')
* - Look at 'plugins' and add or remove plugins if appropriate
* (see https://github.com/phan/phan/tree/v5/.phan/plugins#plugins)
* - Add global suppressions for pre-existing issues to suppress_issue_types
* (https://github.com/phan/phan/wiki/Tutorial-for-Analyzing-a-Large-Sloppy-Code-Base)
* - Consider setting up a baseline if there are a large number of pre-existing issues (see `phan --extended-help`)
*
* This configuration will be read and overlaid on top of the * This configuration will be read and overlaid on top of the
* default configuration. Command line arguments will be applied * default configuration. Command line arguments will be applied
* after this file is read. * after this file is read.
* *
* @see src/Phan/Config.php * @see https://github.com/phan/phan/wiki/Phan-Config-Settings for all configurable options
* See Config for all configurable options. * @see https://github.com/phan/phan/tree/v5/src/Phan/Config.php
* *
* A Note About Paths * A Note About Paths
* ================== * ==================
@@ -23,98 +36,343 @@
* '-d' flag. * '-d' flag.
*/ */
use Phan\Config; use Phan\Issue;
return [ return [
// "target_php_version" => "8.2",
// turn color on (-C) // The PHP version that the codebase will be checked for compatibility against.
"color_issue_messages_if_supported" => true, // For best results, the PHP binary used to run Phan should have the same PHP version.
// If true, missing properties will be created when // (Phan relies on Reflection for some types, param counts,
// and checks for undefined classes/methods/functions)
//
// Supported values: `'5.6'`, `'7.0'`, `'7.1'`, `'7.2'`, `'7.3'`, `'7.4'`,
// `'8.0'`, `'8.1'`, `null`.
// If this is set to `null`,
// then Phan assumes the PHP version which is closest to the minor version
// of the php executable used to execute Phan.
//
// Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist.
// (See `backward_compatibility_checks` for additional options)
// Automatically inferred from composer.json requirement for "php" of ">=8.2"
'target_php_version' => '8.1',
// If enabled, missing properties will be created when
// they are first seen. If false, we'll report an // they are first seen. If false, we'll report an
// error message. // error message if there is an attempt to write
"allow_missing_properties" => false, // to a class property that wasn't explicitly
// defined.
'allow_missing_properties' => false,
// Allow null to be cast as any type and for any // If enabled, null can be cast to any type and any
// type to be cast to null. // type can be cast to null. Setting this to true
"null_casts_as_any_type" => false, // will cut down on false positives.
'null_casts_as_any_type' => false,
// Backwards Compatibility Checking // If enabled, allow null to be cast as any array-like type.
'backward_compatibility_checks' => false, //
// This is an incremental step in migrating away from `null_casts_as_any_type`.
// If `null_casts_as_any_type` is true, this has no effect.
'null_casts_as_array' => true,
// Run a quick version of checks that takes less // If enabled, allow any array-like type to be cast to null.
// time // This is an incremental step in migrating away from `null_casts_as_any_type`.
"quick_mode" => false, // If `null_casts_as_any_type` is true, this has no effect.
'array_casts_as_null' => true,
// Only emit critical issues to start with // If enabled, scalars (int, float, bool, string, null)
// (0 is low severity, 5 is normal severity, 10 is critical) // are treated as if they can cast to each other.
"minimum_severity" => 0, // This does not affect checks of array keys. See `scalar_array_key_cast`.
'scalar_implicit_cast' => false,
// enable for dead code check // If enabled, any scalar array keys (int, string)
// this will spill out errors for all methods never called // are treated as if they can cast to each other.
// use after all is OK to try to find unused code blocks // E.g. `array<int,stdClass>` can cast to `array<string,stdClass>` and vice versa.
// ignore recommended: PhanUnreferencedPublicMethod // Normally, a scalar type such as int could only cast to/from int and mixed.
// "dead_code_detection" => true, 'scalar_array_key_cast' => true,
// default false for include path check // If this has entries, scalars (int, float, bool, string, null)
"enable_include_path_checks" => true, // are allowed to perform the casts listed.
"include_paths" => [ //
'.', // E.g. `['int' => ['float', 'string'], 'float' => ['int'], 'string' => ['int'], 'null' => ['string']]`
// '../test/configs/' // allows casting null to a string, but not vice versa.
], // (subset of `scalar_implicit_cast`)
'scalar_implicit_partial' => [],
// If enabled, Phan will warn if **any** type in a method invocation's object
// is definitely not an object,
// or if **any** type in an invoked expression is not a callable.
// Setting this to true will introduce numerous false positives
// (and reveal some bugs).
'strict_method_checking' => false,
// If enabled, Phan will warn if **any** type of the object expression for a property access
// does not contain that property.
'strict_object_checking' => false,
// If enabled, Phan will warn if **any** type in the argument's union type
// cannot be cast to a type in the parameter's expected union type.
// Setting this to true will introduce numerous false positives
// (and reveal some bugs).
'strict_param_checking' => false,
// If enabled, Phan will warn if **any** type in a property assignment's union type
// cannot be cast to a type in the property's declared union type.
// Setting this to true will introduce numerous false positives
// (and reveal some bugs).
'strict_property_checking' => false,
// If enabled, Phan will warn if **any** type in a returned value's union type
// cannot be cast to the declared return type.
// Setting this to true will introduce numerous false positives
// (and reveal some bugs).
'strict_return_checking' => false,
// If true, seemingly undeclared variables in the global
// scope will be ignored.
//
// This is useful for projects with complicated cross-file
// globals that you have no hope of fixing.
'ignore_undeclared_variables_in_global_scope' => true, 'ignore_undeclared_variables_in_global_scope' => true,
"file_list" => [ // Set this to false to emit `PhanUndeclaredFunction` issues for internal functions that Phan has signatures for,
"./test/configs/config.php", // but aren't available in the codebase, or from Reflection.
// "./test/configs/config.db.php", // (may lead to false positives if an extension isn't loaded)
// "./test/configs/config.host.php",
// "./test/configs/config.path.php",
"./test/configs/config.other.php",
"./test/configs/config.master.php",
],
// A list of directories that should be parsed for class and
// method information. After excluding the directories
// defined in exclude_analysis_directory_list, the remaining
// files will be statically analyzed for errors.
// //
// Thus, both first-party and third-party code being used by // If this is true(default), then Phan will not warn.
// your application should be included in this list. //
'directory_list' => [ // Even when this is false, Phan will still infer return values and check parameters of internal functions
// Change this to include the folders you wish to analyze // if Phan has the signatures.
// (and the folders of their dependencies) 'ignore_undeclared_functions_with_known_signatures' => true,
'src',
// To speed up analysis, we recommend going back later and
// limiting this to only the vendor/ subdirectories your
// project depends on.
// `phan --init` will generate a list of folders for you
'vendor/egrajp/smarty-extended',
],
// Backwards Compatibility Checking. This is slow
// and expensive, but you should consider running
// it before upgrading your version of PHP to a
// new version that has backward compatibility
// breaks.
//
// If you are migrating from PHP 5 to PHP 7,
// you should also look into using
// [php7cc (no longer maintained)](https://github.com/sstalle/php7cc)
// and [php7mar](https://github.com/Alexia/php7mar),
// which have different backwards compatibility checks.
//
// If you are still using versions of php older than 5.6,
// `PHP53CompatibilityPlugin` may be worth looking into if you are not running
// syntax checks for php 5.3 through another method such as
// `InvokePHPNativeSyntaxCheckPlugin` (see .phan/plugins/README.md).
'backward_compatibility_checks' => false,
// A list of directories holding code that we want // If true, check to make sure the return type declared
// to parse, but not analyze // in the doc-block (if any) matches the return type
"exclude_analysis_directory_list" => [ // declared in the method signature.
'vendor/egrajp/smarty-extended', 'check_docblock_signature_return_type_match' => false,
],
'exclude_file_list' => [
],
// what not to show as problem // This setting maps case-insensitive strings to union types.
'suppress_issue_types' => [ //
// 'PhanUndeclaredMethod', // This is useful if a project uses phpdoc that differs from the phpdoc2 standard.
'PhanEmptyFile', //
// ignore unreferences public methods, etc here (for dead code check) // If the corresponding value is the empty string,
'PhanUnreferencedPublicMethod', // then Phan will ignore that union type (E.g. can ignore 'the' in `@return the value`)
'PhanUnreferencedClass', //
'PhanWriteOnlyPublicProperty', // If the corresponding value is not empty,
'PhanUnreferencedConstant', // then Phan will act as though it saw the corresponding UnionTypes(s)
'PhanWriteOnlyPublicProperty', // when the keys show up in a UnionType of `@param`, `@return`, `@var`, `@property`, etc.
'PhanReadOnlyPublicProperty' //
], // This matches the **entire string**, not parts of the string.
// (E.g. `@return the|null` will still look for a class with the name `the`,
// but `@return the` will be ignored with the below setting)
//
// (These are not aliases, this setting is ignored outside of doc comments).
// (Phan does not check if classes with these names exist)
//
// Example setting: `['unknown' => '', 'number' => 'int|float', 'char' => 'string', 'long' => 'int', 'the' => '']`
'phpdoc_type_mapping' => [],
// Set to true in order to attempt to detect dead
// (unreferenced) code. Keep in mind that the
// results will only be a guess given that classes,
// properties, constants and methods can be referenced
// as variables (like `$class->$property` or
// `$class->$method()`) in ways that we're unable
// to make sense of.
//
// To more aggressively detect dead code,
// you may want to set `dead_code_detection_prefer_false_negative` to `false`.
'dead_code_detection' => false,
// Set to true in order to attempt to detect unused variables.
// `dead_code_detection` will also enable unused variable detection.
//
// This has a few known false positives, e.g. for loops or branches.
'unused_variable_detection' => false,
// Set to true in order to attempt to detect redundant and impossible conditions.
//
// This has some false positives involving loops,
// variables set in branches of loops, and global variables.
'redundant_condition_detection' => false,
// If enabled, Phan will act as though it's certain of real return types of a subset of internal functions,
// even if those return types aren't available in reflection
// (real types were taken from php 7.3 or 8.0-dev, depending on target_php_version).
//
// Note that with php 7 and earlier, php would return null or false for many internal functions
// if the argument types or counts were incorrect.
// As a result, enabling this setting with target_php_version 8.0 may result in false positives
// for `--redundant-condition-detection` when codebases also support php 7.x.
'assume_real_types_for_internal_functions' => false,
// If true, this runs a quick version of checks that takes less
// time at the cost of not running as thorough
// of an analysis. You should consider setting this
// to true only when you wish you had more **undiagnosed** issues
// to fix in your code base.
//
// In quick-mode the scanner doesn't rescan a function
// or a method's code block every time a call is seen.
// This means that the problem here won't be detected:
//
// ```php
// <?php
// function test($arg):int {
// return $arg;
// }
// test("abc");
// ```
//
// This would normally generate:
//
// ```
// test.php:3 PhanTypeMismatchReturn Returning type string but test() is declared to return int
// ```
//
// The initial scan of the function's code block has no
// type information for `$arg`. It isn't until we see
// the call and rescan `test()`'s code block that we can
// detect that it is actually returning the passed in
// `string` instead of an `int` as declared.
'quick_mode' => false,
// Override to hardcode existence and types of (non-builtin) globals in the global scope. // Override to hardcode existence and types of (non-builtin) globals in the global scope.
// Class names should be prefixed with `\`. // Class names should be prefixed with `\`.
// //
// (E.g. `['_FOO' => '\FooClass', 'page' => '\PageClass', 'userId' => 'int']`) // (E.g. `['_FOO' => '\FooClass', 'page' => '\PageClass', 'userId' => 'int']`)
'globals_type_map' => [], 'globals_type_map' => [],
// The minimum severity level to report on. This can be
// set to `Issue::SEVERITY_LOW`, `Issue::SEVERITY_NORMAL` or
// `Issue::SEVERITY_CRITICAL`. Setting it to only
// critical issues is a good place to start on a big
// sloppy mature code base.
'minimum_severity' => Issue::SEVERITY_LOW,
// Add any issue types (such as `'PhanUndeclaredMethod'`)
// to this list to inhibit them from being reported.
'suppress_issue_types' => [
// start ignore annotations
'PhanUnextractableAnnotationElementName',
'PhanUnextractableAnnotationSuffix',
// wrongly thinks Enum is class
'PhanCommentObjectInClassConstantType',
],
// A regular expression to match files to be excluded
// from parsing and analysis and will not be read at all.
//
// This is useful for excluding groups of test or example
// directories/files, unanalyzable files, or files that
// can't be removed for whatever reason.
// (e.g. `'@Test\.php$@'`, or `'@vendor/.*/(tests|Tests)/@'`)
'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
// A list of files that will be excluded from parsing and analysis
// and will not be read at all.
//
// This is useful for excluding hopelessly unanalyzable
// files that can't be removed for whatever reason.
'exclude_file_list' => [],
// A directory list that defines files that will be excluded
// from static analysis, but whose class and method
// information should be included.
//
// Generally, you'll want to include the directories for
// third-party code (such as "vendor/") in this list.
//
// n.b.: If you'd like to parse but not analyze 3rd
// party code, directories containing that code
// should be added to the `directory_list` as well as
// to `exclude_analysis_directory_list`.
'exclude_analysis_directory_list' => [
'vendor/',
],
// Enable this to enable checks of require/include statements referring to valid paths.
// The settings `include_paths` and `warn_about_relative_include_statement` affect the checks.
'enable_include_path_checks' => true,
"include_paths" => [
'.',
],
// The number of processes to fork off during the analysis
// phase.
'processes' => 1,
// List of case-insensitive file extensions supported by Phan.
// (e.g. `['php', 'html', 'htm']`)
'analyzed_file_extensions' => [
'php',
],
// You can put paths to stubs of internal extensions in this config option.
// If the corresponding extension is **not** loaded, then Phan will use the stubs instead.
// Phan will continue using its detailed type annotations,
// but load the constants, classes, functions, and classes (and their Reflection types)
// from these stub files (doubling as valid php files).
// Use a different extension from php to avoid accidentally loading these.
// The `tools/make_stubs` script can be used to generate your own stubs (compatible with php 7.0+ right now)
//
// (e.g. `['xdebug' => '.phan/internal_stubs/xdebug.phan_php']`)
'autoload_internal_extension_signatures' => [],
// A list of plugin files to execute.
//
// Plugins which are bundled with Phan can be added here by providing their name (e.g. `'AlwaysReturnPlugin'`)
//
// Documentation about available bundled plugins can be found
// [here](https://github.com/phan/phan/tree/v5/.phan/plugins).
//
// Alternately, you can pass in the full path to a PHP file with the plugin's implementation
// (e.g. `'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'`)
'plugins' => [
'AlwaysReturnPlugin',
'PregRegexCheckerPlugin',
'UnreachableCodePlugin',
],
// A list of directories that should be parsed for class and
// method information. After excluding the directories
// defined in `exclude_analysis_directory_list`, the remaining
// files will be statically analyzed for errors.
//
// Thus, both first-party and third-party code being used by
// your application should be included in this list.
'directory_list' => [
'src',
'vendor/egrajp/smarty-extended/src',
'vendor/phan/phan/src/Phan',
'vendor/phpunit/phpunit/src',
'vendor/psr/log/src',
'vendor/vimeo/psalm/src/Psalm',
'vendor/gullevek/dotenv',
],
// A list of individual files to include in analysis
// with a path relative to the root directory of the
// project.
'file_list' => [
"./test/configs/config.php",
"./test/configs/config.other.php",
"./test/configs/config.master.php",
],
]; ];

View File

@@ -16,14 +16,16 @@
], ],
"minimum-stability": "dev", "minimum-stability": "dev",
"require": { "require": {
"php": ">=8.1" "php": ">=8.2",
"psr/log": "^3.0@dev"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^1.10", "phpstan/phpstan": "^1.10",
"phan/phan": "v5.x-dev", "phan/phan": "v5.x-dev",
"phpunit/phpunit": "^9", "phpunit/phpunit": "^9",
"egrajp/smarty-extended": "^4.3", "egrajp/smarty-extended": "^4.3",
"vimeo/psalm": "^5.0@dev" "vimeo/psalm": "^5.0@dev",
"gullevek/dotenv": "dev-master"
}, },
"repositories": { "repositories": {
"git.egplusww.jp.Composer": { "git.egplusww.jp.Composer": {

1
publish/.gitignore vendored
View File

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

View File

@@ -1 +1 @@
8.2.2 9.7.5

1
publish/package-download/.gitignore vendored Normal file
View File

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

View File

@@ -1,8 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
BASE_FOLDER=$(dirname $(readlink -f $0))"/"; BASE_FOLDER=$(dirname $(readlink -f $0))"/";
PACKAGE_DOWNLOAD="${BASE_FOLDER}package-download/";
if [ ! -d "${PACKAGE_DOWNLOAD}" ]; then
mkdir "${PACKAGE_DOWNLOAD}";
fi;
VERSION=$(git tag --list | sort -V | tail -n1 | sed -e "s/^v//"); VERSION=$(git tag --list | sort -V | tail -n1 | sed -e "s/^v//");
file_last_published="${BASE_FOLDER}last.published"; file_last_published="${BASE_FOLDER}last.published";
go_flag="$1";
if [ -z "${VERSION}" ]; then if [ -z "${VERSION}" ]; then
echo "Version must be set in the form x.y.z without any leading characters"; echo "Version must be set in the form x.y.z without any leading characters";
@@ -18,6 +23,7 @@ if [ -f "${file_last_published}" ]; then
fi; fi;
# read in the .env.deploy file and we must have # read in the .env.deploy file and we must have
# GITEA_UPLOAD_FILENAME
# GITLAB_USER # GITLAB_USER
# GITLAB_TOKEN # GITLAB_TOKEN
# GITLAB_URL # GITLAB_URL
@@ -35,19 +41,27 @@ source .env.deploy;
cd -; cd -;
set +o allexport; set +o allexport;
if [ "${go_flag}" != "go" ]; then
echo "No go flag given";
echo "Would publish ${VERSION}";
echo "[END]";
exit;
fi;
echo "[START]"; echo "[START]";
# gitea # gitea
if [ ! -z "${GITEA_URL_DL}" ] && [ ! -z "${GITEA_URL_PUSH}" ] && if [ ! -z "${GITEA_UPLOAD_FILENAME}" ] &&
[ ! -z "${GITEA_URL_DL}" ] && [ ! -z "${GITEA_URL_PUSH}" ] &&
[ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then [ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then
curl -LJO \ curl -LJO \
--output-dir "${BASE_FOLDER}" \ --output-dir "${PACKAGE_DOWNLOAD}" \
${GITEA_URL_DL}/v${VERSION}.zip; ${GITEA_URL_DL}/v${VERSION}.zip;
curl --user ${GITEA_USER}:${GITEA_TOKEN} \ curl --user ${GITEA_USER}:${GITEA_TOKEN} \
--upload-file "${BASE_FOLDER}/CoreLibs-Composer-All-v${VERSION}.zip" \ --upload-file "${PACKAGE_DOWNLOAD}${GITEA_UPLOAD_FILENAME}-v${VERSION}.zip" \
${GITEA_URL_PUSH}?version=${VERSION}; ${GITEA_URL_PUSH}?version=${VERSION};
echo "${VERSION}" > "${file_last_published}"; echo "${VERSION}" > "${file_last_published}";
else else
echo "Missing either GITEA_USER or GITEA_TOKEN environment variable"; echo "Missing either GITEA_UPLOAD_FILENAME, GITEA_URL_DL, GITEA_URL_PUSH, GITEA_USER or GITEA_TOKEN environment variable";
fi; fi;
# gitlab # gitlab

View File

@@ -68,70 +68,70 @@ declare(strict_types=1);
namespace CoreLibs\ACL; namespace CoreLibs\ACL;
use CoreLibs\Check\Password; use CoreLibs\Security\Password;
use CoreLibs\Convert\Json; use CoreLibs\Convert\Json;
class Login class Login
{ {
/** @var string the user id var*/ /** @var ?int the user id var*/
private $euid; private ?int $euid;
/** @var string _GET/_POST loginUserId parameter for non password login */ /** @var string _GET/_POST loginUserId parameter for non password login */
private $login_user_id = ''; private string $login_user_id = '';
/** @var string source, either _GET or _POST or empty */ /** @var string source, either _GET or _POST or empty */
private $login_user_id_source = ''; private string $login_user_id_source = '';
/** @var bool set to true if illegal characters where found in the login user id string */ /** @var bool set to true if illegal characters where found in the login user id string */
private $login_user_id_unclear = false; private bool $login_user_id_unclear = false;
// is set to one if login okay, or EUID is set and user is okay to access this page // is set to one if login okay, or EUID is set and user is okay to access this page
/** @var bool */ /** @var bool */
private $permission_okay = false; private bool $permission_okay = false;
/** @var string pressed login */ /** @var string pressed login */
private $login = ''; private string $login = '';
/** @var string master action command */ /** @var string master action command */
private $action; private string $action;
/** @var string login name */ /** @var string login name */
private $username; private string $username;
/** @var string login password */ /** @var string login password */
private $password; private string $password;
/** @var string logout button */ /** @var string logout button */
private $logout; private string $logout;
/** @var bool if this is set to true, the user can change passwords */ /** @var bool if this is set to true, the user can change passwords */
private $password_change = false; private bool $password_change = false;
/** @var bool password change was successful */ /** @var bool password change was successful */
private $password_change_ok = false; private bool $password_change_ok = false;
// can we reset password and mail to user with new password set screen // can we reset password and mail to user with new password set screen
/** @var bool */ /** @var bool */
private $password_forgot = false; private bool $password_forgot = false;
/** @var bool password forgot mail send ok */ /** @var bool password forgot mail send ok */
// private $password_forgot_ok = false; // private $password_forgot_ok = false;
/** @var string */ /** @var string */
private $change_password; private string $change_password;
/** @var string */ /** @var string */
private $pw_username; private string $pw_username;
/** @var string */ /** @var string */
private $pw_old_password; private string $pw_old_password;
/** @var string */ /** @var string */
private $pw_new_password; private string $pw_new_password;
/** @var string */ /** @var string */
private $pw_new_password_confirm; private string $pw_new_password_confirm;
/** @var array<string> array of users for which the password change is forbidden */ /** @var array<string> array of users for which the password change is forbidden */
private $pw_change_deny_users = []; private array $pw_change_deny_users = [];
/** @var string */ /** @var string */
private $logout_target = ''; private string $logout_target = '';
/** @var int */ /** @var int */
private $max_login_error_count = -1; private int $max_login_error_count = -1;
/** @var array<string> */ /** @var array<string> */
private $lock_deny_users = []; private array $lock_deny_users = [];
/** @var string */ /** @var string */
private $page_name = ''; private string $page_name = '';
/** @var int if we have password change we need to define some rules */ /** @var int if we have password change we need to define some rules */
private $password_min_length = 9; private int $password_min_length = 9;
/** @var int an true maxium min, can never be set below this */ /** @var int an true maxium min, can never be set below this */
private $password_min_length_max = 9; private int $password_min_length_max = 9;
// max length is fixed as 255 (for input type max), if set highter // max length is fixed as 255 (for input type max), if set highter
// it will be set back to 255 // it will be set back to 255
/** @var int */ /** @var int */
private $password_max_length = 255; private int $password_max_length = 255;
/** @var int minum password length */ /** @var int minum password length */
public const PASSWORD_MIN_LENGTH = 9; public const PASSWORD_MIN_LENGTH = 9;
@@ -158,7 +158,7 @@ class Login
. "$/"; . "$/";
/** @var array<string> can have several regexes, if nothing set, all is ok */ /** @var array<string> can have several regexes, if nothing set, all is ok */
private $password_valid_chars = [ private array $password_valid_chars = [
// '^(?=.*\d)(?=.*[A-Za-z])[0-9A-Za-z!@#$%]{8,}$', // '^(?=.*\d)(?=.*[A-Za-z])[0-9A-Za-z!@#$%]{8,}$',
// '^(?.*(\pL)u)(?=.*(\pN)u)(?=.*([^\pL\pN])u).{8,}', // '^(?.*(\pL)u)(?=.*(\pN)u)(?=.*([^\pL\pN])u).{8,}',
]; ];
@@ -166,13 +166,13 @@ class Login
// login error code, can be matched to the array login_error_msg, // login error code, can be matched to the array login_error_msg,
// which holds the string // which holds the string
/** @var int */ /** @var int */
private $login_error = 0; private int $login_error = 0;
/** @var array<mixed> all possible login error conditions */ /** @var array<mixed> all possible login error conditions */
private $login_error_msg = []; private array $login_error_msg = [];
// this is an array holding all strings & templates passed // this is an array holding all strings & templates passed
// rom the outside (translation) // rom the outside (translation)
/** @var array<mixed> */ /** @var array<mixed> */
private $login_template = [ private array $login_template = [
'strings' => [], 'strings' => [],
'password_change' => '', 'password_change' => '',
'template' => '' 'template' => ''
@@ -180,59 +180,57 @@ class Login
// acl vars // acl vars
/** @var array<mixed> */ /** @var array<mixed> */
private $acl = []; private array $acl = [];
/** @var array<mixed> */ /** @var array<mixed> */
private $default_acl_list = []; private array $default_acl_list = [];
/** @var array<string,int> Reverse list to lookup level from type */ /** @var array<string,int> Reverse list to lookup level from type */
private $default_acl_list_type = []; private array $default_acl_list_type = [];
/** @var int default ACL level to be based on if nothing set */ /** @var int default ACL level to be based on if nothing set */
private $default_acl_level = 0; private int $default_acl_level = 0;
// login html, if we are on an ajax page // login html, if we are on an ajax page
/** @var string|null */ /** @var string|null */
private $login_html = ''; private ?string $login_html = '';
/** @var bool */ /** @var bool */
private $login_is_ajax_page = false; private bool $login_is_ajax_page = false;
// settings // settings
/** @var array<string,mixed> options */ /** @var array<string,mixed> options */
private $options = []; private array $options = [];
/** @var array<string,string> locale options: locale, domain, encoding (opt), path */ /** @var array<string,string> locale options: locale, domain, encoding (opt), path */
private $locale = [ private array $locale = [
'locale' => '', 'locale' => '',
'domain' => '', 'domain' => '',
'encoding' => '', 'encoding' => '',
'path' => '', 'path' => '',
]; ];
/** @var \CoreLibs\Debug\Logging logger */ /** @var \CoreLibs\Logging\Logging logger */
public $log; public \CoreLibs\Logging\Logging $log;
/** @var \CoreLibs\DB\IO database */ /** @var \CoreLibs\DB\IO database */
public $db; public \CoreLibs\DB\IO $db;
/** @var \CoreLibs\Language\L10n language */ /** @var \CoreLibs\Language\L10n language */
public $l; public \CoreLibs\Language\L10n $l;
/** @var \CoreLibs\Create\Session session class */ /** @var \CoreLibs\Create\Session session class */
public $session; public \CoreLibs\Create\Session $session;
/** /**
* constructor, does ALL, opens db, works through connection checks, * constructor, does ALL, opens db, works through connection checks,
* finishes itself * finishes itself
* *
* @param \CoreLibs\DB\IO $db Database connection class * @param \CoreLibs\DB\IO $db Database connection class
* @param \CoreLibs\Debug\Logging $log Logging class * @param \CoreLibs\Logging\Logging $log Logging class
* @param \CoreLibs\Create\Session $session Session interface class * @param \CoreLibs\Create\Session $session Session interface class
* @param array<string,mixed> $options Login ACL settings * @param array<string,mixed> $options Login ACL settings
* $auto_login [default true] DEPRECATED, moved into options * $auto_login [default true] DEPRECATED, moved into options
*/ */
public function __construct( public function __construct(
\CoreLibs\DB\IO $db, \CoreLibs\DB\IO $db,
\CoreLibs\Debug\Logging $log, \CoreLibs\Logging\Logging $log,
\CoreLibs\Create\Session $session, \CoreLibs\Create\Session $session,
array $options = [] array $options = []
) { ) {
// attach db class // attach db class
$this->db = $db; $this->db = $db;
// log login data for this class only
$log->setLogPer('class', true);
// attach logger // attach logger
$this->log = $log; $this->log = $log;
// attach session class // attach session class
@@ -242,7 +240,7 @@ class Login
if (false === $this->loginSetOptions($options)) { if (false === $this->loginSetOptions($options)) {
// on failure, exit // on failure, exit
echo "<b>Could not set options</b>"; echo "<b>Could not set options</b>";
$this->loginTerminate(4000); $this->loginTerminate('Could not set options', 3000);
} }
// string key, msg: string, flag: e (error), o (ok) // string key, msg: string, flag: e (error), o (ok)
@@ -394,11 +392,18 @@ class Login
/** /**
* Wrapper for exit calls * Wrapper for exit calls
* *
* @param int $code * @param string $message [='']
* @param int $code [=0]
* @return void * @return void
*/ */
protected function loginTerminate($code = 0): void protected function loginTerminate(string $message = '', int $code = 0): void
{ {
// all below 1000 are info end, all above 1000 are critical -> should throw exception?
if ($code < 1000) {
$this->log->info($message, ['code' => $code]);
} else {
$this->log->critical($message, ['code' => $code]);
}
exit($code); exit($code);
} }
@@ -883,7 +888,7 @@ class Login
} }
// normal user processing // normal user processing
// set class var and session var // set class var and session var
$_SESSION['EUID'] = $this->euid = $res['edit_user_id']; $_SESSION['EUID'] = $this->euid = (int)$res['edit_user_id'];
// check if user is okay // check if user is okay
$this->loginCheckPermissions(); $this->loginCheckPermissions();
if ($this->login_error == 0) { if ($this->login_error == 0) {
@@ -1048,7 +1053,7 @@ class Login
} }
// build master unit array // build master unit array
$unit_access[$res['edit_access_id']] = [ $unit_access[$res['edit_access_id']] = [
'id' => $res['edit_access_id'], 'id' => (int)$res['edit_access_id'],
'acl_level' => $res['level'], 'acl_level' => $res['level'],
'acl_type' => $res['type'], 'acl_type' => $res['type'],
'name' => $res['name'], 'name' => $res['name'],
@@ -1060,9 +1065,9 @@ class Login
]; ];
// set the default unit // set the default unit
if ($res['edit_default']) { if ($res['edit_default']) {
$_SESSION['UNIT_DEFAULT'] = $res['edit_access_id']; $_SESSION['UNIT_DEFAULT'] = (int)$res['edit_access_id'];
} }
$_SESSION['UNIT_UID'][$res['uid']] = $res['edit_access_id']; $_SESSION['UNIT_UID'][$res['uid']] = (int)$res['edit_access_id'];
// sub arrays for simple access // sub arrays for simple access
array_push($eauid, $res['edit_access_id']); array_push($eauid, $res['edit_access_id']);
$unit_acl[$res['edit_access_id']] = $res['level']; $unit_acl[$res['edit_access_id']] = $res['level'];
@@ -1148,18 +1153,18 @@ class Login
// user > page > group // user > page > group
// group ACL 0 // group ACL 0
if ($_SESSION['GROUP_ACL_LEVEL'] != -1) { if ($_SESSION['GROUP_ACL_LEVEL'] != -1) {
$this->acl['base'] = $_SESSION['GROUP_ACL_LEVEL']; $this->acl['base'] = (int)$_SESSION['GROUP_ACL_LEVEL'];
} }
// page ACL 1 // page ACL 1
if ( if (
isset($_SESSION['PAGES_ACL_LEVEL'][$this->page_name]) && isset($_SESSION['PAGES_ACL_LEVEL'][$this->page_name]) &&
$_SESSION['PAGES_ACL_LEVEL'][$this->page_name] != -1 $_SESSION['PAGES_ACL_LEVEL'][$this->page_name] != -1
) { ) {
$this->acl['base'] = $_SESSION['PAGES_ACL_LEVEL'][$this->page_name]; $this->acl['base'] = (int)$_SESSION['PAGES_ACL_LEVEL'][$this->page_name];
} }
// user ACL 2 // user ACL 2
if ($_SESSION['USER_ACL_LEVEL'] != -1) { if ($_SESSION['USER_ACL_LEVEL'] != -1) {
$this->acl['base'] = $_SESSION['USER_ACL_LEVEL']; $this->acl['base'] = (int)$_SESSION['USER_ACL_LEVEL'];
} }
} }
$_SESSION['BASE_ACL_LEVEL'] = $this->acl['base']; $_SESSION['BASE_ACL_LEVEL'] = $this->acl['base'];
@@ -1179,6 +1184,12 @@ class Login
$this->acl['page'] = $_SESSION['PAGES_ACL_LEVEL'][$this->page_name]; $this->acl['page'] = $_SESSION['PAGES_ACL_LEVEL'][$this->page_name];
} }
$this->acl['unit_id'] = null;
$this->acl['unit_name'] = null;
$this->acl['unit_uid'] = null;
$this->acl['unit'] = [];
$this->acl['unit_detail'] = [];
// PER ACCOUNT (UNIT/edit access)-> // PER ACCOUNT (UNIT/edit access)->
foreach ($_SESSION['UNIT'] as $ea_id => $unit) { foreach ($_SESSION['UNIT'] as $ea_id => $unit) {
// if admin flag is set, all units are set to 100 // if admin flag is set, all units are set to 100
@@ -1608,7 +1619,7 @@ class Login
// TODO: submit or JS to set target page as ajax call // TODO: submit or JS to set target page as ajax call
// NOTE: for the HTML block I ignore line lengths // NOTE: for the HTML block I ignore line lengths
// phpcs:disable // phpcs:disable
$this->login_template['password_change'] = <<<EOM $this->login_template['password_change'] = <<<HTML
<div id="pw_change_div" class="hidden" style="position: absolute; top: 30px; left: 50px; width: 400px; height: 220px; background-color: white; border: 1px solid black; padding: 25px;"> <div id="pw_change_div" class="hidden" style="position: absolute; top: 30px; left: 50px; width: 400px; height: 220px; background-color: white; border: 1px solid black; padding: 25px;">
<table> <table>
<tr><td class="norm" align="center" colspan="2"><h3>{TITLE_PASSWORD_CHANGE}</h3></td></tr> <tr><td class="norm" align="center" colspan="2"><h3>{TITLE_PASSWORD_CHANGE}</h3></td></tr>
@@ -1626,7 +1637,7 @@ class Login
</table> </table>
</div> </div>
{PASSWORD_CHANGE_SHOW} {PASSWORD_CHANGE_SHOW}
EOM; HTML;
// phpcs:enable // phpcs:enable
} }
if ($this->password_forgot) { if ($this->password_forgot) {
@@ -1650,7 +1661,7 @@ EOM;
// now check templates // now check templates
// TODO: submit or JS to set target page as ajax call // TODO: submit or JS to set target page as ajax call
if (!$this->login_template['template']) { if (!$this->login_template['template']) {
$this->login_template['template'] = <<<EOM $this->login_template['template'] = <<<HTML
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{LANGUAGE}"> <html lang="{LANGUAGE}">
<head> <head>
@@ -1712,7 +1723,7 @@ h3 { font-size: 18px; }
</form> </form>
</body> </body>
</html> </html>
EOM; HTML;
} }
} }
@@ -1806,14 +1817,14 @@ EOM;
$this->login_error = 1; $this->login_error = 1;
echo 'Could not connect to DB<br>'; echo 'Could not connect to DB<br>';
// if I can't connect to the DB to auth exit hard. No access allowed // if I can't connect to the DB to auth exit hard. No access allowed
$this->loginTerminate(1000); $this->loginTerminate('Could not connect to DB', 1000);
} }
// initial the session if there is no session running already // initial the session if there is no session running already
// check if session exists and could be created // check if session exists and could be created
if ($this->session->checkActiveSession() === false) { if ($this->session->checkActiveSession() === false) {
$this->login_error = 2; $this->login_error = 2;
echo '<b>No active session found</b>'; echo '<b>No active session found</b>';
$this->loginTerminate(2000); $this->loginTerminate('No active session found', 2000);
} }
// set internal page name // set internal page name
$this->page_name = $this->loginReadPageName(); $this->page_name = $this->loginReadPageName();
@@ -1849,13 +1860,13 @@ EOM;
if ($login_user_id_changed > 0) { if ($login_user_id_changed > 0) {
$this->login_user_id_unclear = true; $this->login_user_id_unclear = true;
// error for invalid user id? // error for invalid user id?
$this->log->debug('LOGIN USER ID', 'Invalid characters: ' $this->log->error('LOGIN USER ID: Invalid characters: '
. $login_user_id_changed . ' in loginUserId: ' . $login_user_id_changed . ' in loginUserId: '
. $this->login_user_id . ' (' . $this->login_user_id_source . ')'); . $this->login_user_id . ' (' . $this->login_user_id_source . ')');
} }
} }
// if there is none, there is none, saves me POST/GET check // if there is none, there is none, saves me POST/GET check
$this->euid = array_key_exists('EUID', $_SESSION) ? $_SESSION['EUID'] : 0; $this->euid = array_key_exists('EUID', $_SESSION) ? (int)$_SESSION['EUID'] : 0;
// get login vars, are so, can't be changed // get login vars, are so, can't be changed
// prepare // prepare
// pass on vars to Object vars // pass on vars to Object vars
@@ -1911,23 +1922,8 @@ EOM;
// echo $this->login_html; // echo $this->login_html;
$this->loginPrintLogin(); $this->loginPrintLogin();
} }
// do not go anywhere, quit processing here
// do something with possible debug data?
if (
in_array($this->options['target'], ['live', 'remove'])
) {
// login
$this->log->setLogLevelAll('debug', $this->options['debug']);
$this->log->setLogLevelAll('echo', false);
$this->log->setLogLevelAll('print', $this->options['debug']);
}
$status_msg = $this->log->printErrorMsg();
// if ($this->echo_output_all) {
if ($this->log->getLogLevelAll('echo')) {
echo $status_msg;
}
// exit so we don't process anything further, at all // exit so we don't process anything further, at all
$this->loginTerminate(3000); $this->loginTerminate('Exit after non ajax page load', 100);
} else { } else {
// if we are on an ajax page reset any POST/GET array data to avoid // if we are on an ajax page reset any POST/GET array data to avoid
// any accidentical processing going on // any accidentical processing going on
@@ -1935,7 +1931,7 @@ EOM;
$_GET = []; $_GET = [];
// set the action to login so we can trigger special login html return // set the action to login so we can trigger special login html return
$_POST['action'] = 'login'; $_POST['action'] = 'login';
$_POST['login_exit'] = 3000; $_POST['login_exit'] = 100;
$_POST['login_error'] = $this->loginGetLastErrorCode(); $_POST['login_error'] = $this->loginGetLastErrorCode();
$_POST['login_error_text'] = $this->loginGetErrorMsg( $_POST['login_error_text'] = $this->loginGetErrorMsg(
$this->loginGetLastErrorCode(), $this->loginGetLastErrorCode(),
@@ -2119,7 +2115,7 @@ EOM;
// unset session vars set/used in this login // unset session vars set/used in this login
$this->session->sessionDestroy(); $this->session->sessionDestroy();
// unset euid // unset euid
$this->euid = ''; $this->euid = null;
// then prints the login screen again // then prints the login screen again
$this->permission_okay = false; $this->permission_okay = false;
} }
@@ -2356,7 +2352,10 @@ EOM;
is_array($_SESSION['UNIT']) && is_array($_SESSION['UNIT']) &&
!array_key_exists($edit_access_id, $_SESSION['UNIT']) !array_key_exists($edit_access_id, $_SESSION['UNIT'])
) { ) {
return $_SESSION['UNIT_DEFAULT'] ?? null; $edit_access_id = null;
if (is_numeric($_SESSION['UNIT_DEFAULT'])) {
$edit_access_id = (int)$_SESSION['UNIT_DEFAULT'];
}
} }
return $edit_access_id; return $edit_access_id;
} }
@@ -2507,7 +2506,7 @@ EOM;
*/ */
public function loginGetEuid(): string public function loginGetEuid(): string
{ {
return $this->euid; return (string)$this->euid;
} }
} }

View File

@@ -35,97 +35,97 @@ class Backend
{ {
// page name // page name
/** @var array<mixed> */ /** @var array<mixed> */
public $menu = []; public array $menu = [];
/** @var int|string */ /** @var int|string */
public $menu_show_flag = 0; // top menu flag (mostly string) public int|string $menu_show_flag = 0; // top menu flag (mostly string)
// action ids // action ids
/** @var array<string> */ /** @var array<string> */
public $action_list = [ public array $action_list = [
'action', 'action_id', 'action_sub_id', 'action_yes', 'action_flag', 'action', 'action_id', 'action_sub_id', 'action_yes', 'action_flag',
'action_menu', 'action_value', 'action_error', 'action_loaded' 'action_menu', 'action_value', 'action_error', 'action_loaded'
]; ];
/** @var string */ /** @var string */
public $action; public string $action;
/** @var string|int */ /** @var string|int */
public $action_id; public string|int $action_id;
/** @var string|int */ /** @var string|int */
public $action_sub_id; public string|int $action_sub_id;
/** @var string|int|bool */ /** @var string|int|bool */
public $action_yes; public string|int|bool $action_yes;
/** @var string */ /** @var string */
public $action_flag; public string $action_flag;
/** @var string */ /** @var string */
public $action_menu; public string $action_menu;
/** @var string */ /** @var string */
public $action_loaded; public string $action_loaded;
/** @var string */ /** @var string */
public $action_value; public string $action_value;
/** @var string */ /** @var string */
public $action_error; public string $action_error;
// ACL array variable if we want to set acl data from outisde // ACL array variable if we want to set acl data from outisde
/** @var array<mixed> */ /** @var array<mixed> */
public $acl = []; public array $acl = [];
/** @var int */ /** @var int */
public $default_acl; public int $default_acl;
// queue key // queue key
/** @var string */ /** @var string */
public $queue_key; public string $queue_key;
// the current active edit access id // the current active edit access id
/** @var int */ /** @var int|null */
public $edit_access_id; public int|null $edit_access_id;
/** @var string */ /** @var string */
public $page_name; public string $page_name;
// error/warning/info messages // error/warning/info messages
/** @var array<mixed> */ /** @var array<mixed> */
public $messages = []; public array $messages = [];
/** @var bool */ /** @var bool */
public $error = false; public bool $error = false;
/** @var bool */ /** @var bool */
public $warning = false; public bool $warning = false;
/** @var bool */ /** @var bool */
public $info = false; public bool $info = false;
// internal lang & encoding vars // internal lang & encoding vars
/** @var string */ /** @var string */
public $lang_dir = ''; public string $lang_dir = '';
/** @var string */ /** @var string */
public $lang; public string $lang;
/** @var string */ /** @var string */
public $lang_short; public string $lang_short;
/** @var string */ /** @var string */
public $domain; public string $domain;
/** @var string */ /** @var string */
public $encoding; public string $encoding;
/** @var \CoreLibs\Debug\Logging logger */ /** @var \CoreLibs\Logging\Logging logger */
public $log; public \CoreLibs\Logging\Logging $log;
/** @var \CoreLibs\DB\IO database */ /** @var \CoreLibs\DB\IO database */
public $db; public \CoreLibs\DB\IO $db;
/** @var \CoreLibs\Language\L10n language */ /** @var \CoreLibs\Language\L10n language */
public $l; public \CoreLibs\Language\L10n $l;
/** @var \CoreLibs\Create\Session session class */ /** @var \CoreLibs\Create\Session session class */
public $session; public \CoreLibs\Create\Session $session;
// smarty publics [end processing in smarty class] // smarty publics [end processing in smarty class]
/** @var array<mixed> */ /** @var array<mixed> */
public $DATA; public array $DATA = [];
/** @var array<mixed> */ /** @var array<mixed> */
public $HEADER; public array $HEADER = [];
/** @var array<mixed> */ /** @var array<mixed> */
public $DEBUG_DATA; public array $DEBUG_DATA = [];
/** @var array<mixed> */ /** @var array<mixed> */
public $CONTENT_DATA; public array $CONTENT_DATA = [];
// CONSTRUCTOR / DECONSTRUCTOR |====================================> // CONSTRUCTOR / DECONSTRUCTOR |====================================>
/** /**
* main class constructor * main class constructor
* *
* @param \CoreLibs\DB\IO $db Database connection class * @param \CoreLibs\DB\IO $db Database connection class
* @param \CoreLibs\Debug\Logging $log Logging class * @param \CoreLibs\Logging\Logging $log Logging class
* @param \CoreLibs\Create\Session $session Session interface class * @param \CoreLibs\Create\Session $session Session interface class
* @param \CoreLibs\Language\L10n $l10n l10n language class * @param \CoreLibs\Language\L10n $l10n l10n language class
* @param int|null $set_default_acl_level Default ACL level * @param int|null $set_default_acl_level Default ACL level
*/ */
public function __construct( public function __construct(
\CoreLibs\DB\IO $db, \CoreLibs\DB\IO $db,
\CoreLibs\Debug\Logging $log, \CoreLibs\Logging\Logging $log,
\CoreLibs\Create\Session $session, \CoreLibs\Create\Session $session,
\CoreLibs\Language\L10n $l10n, \CoreLibs\Language\L10n $l10n,
?int $set_default_acl_level = null ?int $set_default_acl_level = null
@@ -133,7 +133,7 @@ class Backend
// attach db class // attach db class
$this->db = $db; $this->db = $db;
// set to log not per class // set to log not per class
$log->setLogPer('class', false); $log->unsetLogFlag(\CoreLibs\Logging\Logger\Flag::per_class);
// attach logger // attach logger
$this->log = $log; $this->log = $log;
// attach session class // attach session class
@@ -164,6 +164,10 @@ class Backend
); );
} }
$this->default_acl = $set_default_acl_level ?? DEFAULT_ACL_LEVEL; $this->default_acl = $set_default_acl_level ?? DEFAULT_ACL_LEVEL;
// if negative or larger than 100, reset to 0
if ($this->default_acl < 0 || $this->default_acl > 100) {
$this->default_acl = 0;
}
// queue key // queue key
if (preg_match("/^(add|save|delete|remove|move|up|down|push_live)$/", $this->action)) { if (preg_match("/^(add|save|delete|remove|move|up|down|push_live)$/", $this->action)) {
@@ -552,7 +556,7 @@ class Backend
string $suffix = '', string $suffix = '',
int $min_steps = 1, int $min_steps = 1,
bool $name_pos_back = false bool $name_pos_back = false
) { ): string {
// get the build layout // get the build layout
$html_time = \CoreLibs\Output\Form\Elements::printDateTime( $html_time = \CoreLibs\Output\Form\Elements::printDateTime(
$year, $year,

View File

@@ -20,42 +20,46 @@ use SmartyException;
class EditBase class EditBase
{ {
/** @var array<mixed> */ /** @var array<mixed> */
private $HEADER = []; private array $HEADER = [];
/** @var array<mixed> */ /** @var array<mixed> */
private $DATA = []; private array $DATA = [];
/** @var array<mixed> */ /** @var array<mixed> */
private $DEBUG_DATA = []; private array $DEBUG_DATA = [];
/** @var string the template name */ /** @var string the template name */
private $EDIT_TEMPLATE = ''; private string $EDIT_TEMPLATE = '';
/** @var \CoreLibs\Template\SmartyExtend smarty system */ /** @var \CoreLibs\Template\SmartyExtend smarty system */
private $smarty; private \CoreLibs\Template\SmartyExtend $smarty;
/** @var \CoreLibs\Output\Form\Generate form generate system */ /** @var \CoreLibs\Output\Form\Generate form generate system */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** @var \CoreLibs\Debug\Logging */ /** @var \CoreLibs\Logging\Logging */
public $log; public \CoreLibs\Logging\Logging $log;
/** @var \CoreLibs\Language\L10n */
public \CoreLibs\Language\L10n $l;
/** @var \CoreLibs\ACL\Login */ /** @var \CoreLibs\ACL\Login */
public $login; public \CoreLibs\ACL\Login $login;
/** /**
* construct form generator * construct form generator
* *
* @param array<mixed> $db_config db config array, mandatory * phpcs:ignore
* @param \CoreLibs\Debug\Logging $log Logging class, null auto set * @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class, null auto set
* @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set * @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set
* @param \CoreLibs\ACL\Login $login login class for ACL settings * @param \CoreLibs\ACL\Login $login login class for ACL settings
* @param array<string,mixed> $options Various settings options * @param array<string,mixed> $options Various settings options
*/ */
public function __construct( public function __construct(
array $db_config, array $db_config,
\CoreLibs\Debug\Logging $log, \CoreLibs\Logging\Logging $log,
\CoreLibs\Language\L10n $l10n, \CoreLibs\Language\L10n $l10n,
\CoreLibs\ACL\Login $login, \CoreLibs\ACL\Login $login,
array $options array $options
) { ) {
$this->log = $log; $this->log = $log;
$this->login = $login; $this->login = $login;
$this->l = $l10n;
// smarty template engine (extended Translation version) // smarty template engine (extended Translation version)
$this->smarty = new \CoreLibs\Template\SmartyExtend( $this->smarty = new \CoreLibs\Template\SmartyExtend(
$l10n, $l10n,
@@ -63,7 +67,7 @@ class EditBase
$options['compile_id'] ?? '', $options['compile_id'] ?? '',
); );
// turn off set log per class // turn off set log per class
$log->setLogPer('class', false); $log->unsetLogFlag(\CoreLibs\Logging\Logger\Flag::per_class);
// create form class // create form class
$this->form = new \CoreLibs\Output\Form\Generate( $this->form = new \CoreLibs\Output\Form\Generate(
@@ -76,7 +80,7 @@ class EditBase
echo "I am sorry, but this page cannot be viewed by a mobile phone"; echo "I am sorry, but this page cannot be viewed by a mobile phone";
exit; exit;
} }
// $this->form->log->debug('POST', $this->form->log->prAr($_POST)); // $this->log->debug('POST', $this->log->prAr($_POST));
} }
/** /**
@@ -178,7 +182,7 @@ class EditBase
} // while read data ... } // while read data ...
// html title // html title
$this->HEADER['HTML_TITLE'] = $this->form->l->__('Edit Order'); $this->HEADER['HTML_TITLE'] = $this->l->__('Edit Order');
$messages = []; $messages = [];
$error = $_POST['error'] ?? 0; $error = $_POST['error'] ?? 0;
@@ -631,7 +635,7 @@ class EditBase
'editAdmin_' . $this->smarty->lang 'editAdmin_' . $this->smarty->lang
); );
$this->form->log->debug('DEBUGEND', '==================================== [Form END]'); $this->log->debug('DEBUGEND', '==================================== [Form END]');
} }
} }

View File

@@ -58,39 +58,39 @@ class Basic
{ {
// page and host name // page and host name
/** @var string */ /** @var string */
public $page_name; public string $page_name;
/** @var string */ /** @var string */
public $host_name; public string $host_name;
/** @var int */ /** @var int */
public $host_port; public int $host_port;
// logging interface, Debug\Logging class // logging interface, Debug\Logging class
/** @var \CoreLibs\Debug\Logging */ /** @var \CoreLibs\Logging\Logging */
public $log; public \CoreLibs\Logging\Logging $log;
/** @var \CoreLibs\Create\Session */ /** @var \CoreLibs\Create\Session */
public $session; public \CoreLibs\Create\Session $session;
// email valid checks // email valid checks
/** @var array<mixed> */ /** @var array<mixed> */
public $email_regex_check = []; public array $email_regex_check = [];
/** @var string */ /** @var string */
public $email_regex; // regex var for email check public string $email_regex; // regex var for email check
// data path for files // data path for files
/** @var array<mixed> */ /** @var array<mixed> */
public $data_path = []; public array $data_path = [];
// ajax flag // ajax flag
/** @var bool */ /** @var bool */
protected $ajax_page_flag = false; protected bool $ajax_page_flag = false;
/** /**
* main Basic constructor to init and check base settings * main Basic constructor to init and check base settings
* @param \CoreLibs\Debug\Logging|null $log Logging class * @param \CoreLibs\Logging\Logging|null $log Logging class
* @param string|null $session_name Set session name * @param string|null $session_name Set session name
* @deprecated DO NOT USE Class\Basic anymore. Use dedicated logger and sub classes * @deprecated DO NOT USE Class\Basic anymore. Use dedicated logger and sub classes
*/ */
public function __construct( public function __construct(
\CoreLibs\Debug\Logging $log = null, \CoreLibs\Logging\Logging $log = null,
?string $session_name = null ?string $session_name = null
) { ) {
trigger_error('Class \CoreLibs\Basic is deprected', E_USER_DEPRECATED); trigger_error('Class \CoreLibs\Basic is deprected', E_USER_DEPRECATED);
@@ -120,7 +120,10 @@ class Basic
} }
// logging interface moved here (->debug is now ->log->debug) // logging interface moved here (->debug is now ->log->debug)
$this->log = $log ?? new \CoreLibs\Debug\Logging(); $this->log = $log ?? new \CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => 'ClassBasic-DEPRECATED',
]);
// set ajax page flag based on the AJAX_PAGE varaibles // set ajax page flag based on the AJAX_PAGE varaibles
// convert to true/false so if AJAX_PAGE is 0 or false it is // convert to true/false so if AJAX_PAGE is 0 or false it is
@@ -176,8 +179,9 @@ class Basic
*/ */
public function basicSetLogId(string $string): string public function basicSetLogId(string $string): string
{ {
trigger_error('Method ' . __METHOD__ . ' is deprecated, use $basic->log->basicSetLogId() or use \CoreLibs\Debug\Logging() class', E_USER_DEPRECATED); trigger_error('Method ' . __METHOD__ . ' is deprecated, use log->setLogId() or use \CoreLibs\Logging\Logging() class', E_USER_DEPRECATED);
return $this->log->setLogId($string); $this->log->setLogFileId($string);
return $this->log->getLogFileId();
} }
// ****** DEBUG/ERROR FUNCTIONS ****** // ****** DEBUG/ERROR FUNCTIONS ******
@@ -252,7 +256,7 @@ class Basic
} }
// ****** DEBUG LOGGING FUNCTIONS ****** // ****** DEBUG LOGGING FUNCTIONS ******
// Moved to \CoreLibs\Debug\Logging // Moved to \CoreLibs\Logging\Logging
/** /**
* passes list of level names, to turn on debug * passes list of level names, to turn on debug
@@ -265,68 +269,9 @@ class Basic
*/ */
public function debugFor(string $type, string $flag): void public function debugFor(string $type, string $flag): void
{ {
trigger_error('Method ' . __METHOD__ . ' is deprecated, use $basic->log->debugFor() or use \CoreLibs\Debug\Logging() class', E_USER_DEPRECATED); trigger_error('Method ' . __METHOD__ . ' functionaility is fully deprecated', E_USER_DEPRECATED);
/** @phan-suppress-next-line PhanTypeMismatchArgumentReal, PhanParamTooFew @phpstan-ignore-next-line */
$this->log->setLogLevel(...[func_get_args()]);
} }
/**
* checks if we have a need to work on certain debug output
* Needs debug/echo/print ad target for which of the debug flag groups we check
* also needs level string to check in the per level output flag check.
* In case we have invalid target it will return false
* @param string $target target group to check debug/echo/print
* @param string $level level to check in detailed level flag
* @return bool true on access allowed or false on no access
*/
/* private function doDebugTrigger(string $target, string $level): bool
{
$access = false;
// check if we do debug, echo or print
switch ($target) {
case 'debug':
if ((
(isset($this->debug_output[$level]) && $this->debug_output[$level]) ||
$this->debug_output_all
) &&
(!isset($this->debug_output_not[$level]) ||
(isset($this->debug_output_not[$level]) && !$this->debug_output_not[$level])
)
) {
$access = true;
}
break;
case 'echo':
if ((
(isset($this->echo_output[$level]) && $this->echo_output[$level]) ||
$this->echo_output_all
) &&
(!isset($this->echo_output_not[$level]) ||
(isset($this->echo_output_not[$level]) && !$this->echo_output_not[$level])
)
) {
$access = true;
}
break;
case 'print':
if ((
(isset($this->print_output[$level]) && $this->print_output[$level]) ||
$this->print_output_all
) &&
(!isset($this->print_output_not[$level]) ||
(isset($this->print_output_not[$level]) && !$this->print_output_not[$level])
)
) {
$access = true;
}
break;
default:
// fall through with access false
break;
}
return $access;
} */
/** /**
* write debug data to error_msg array * write debug data to error_msg array
* @param string $level id for error message, groups messages together * @param string $level id for error message, groups messages together
@@ -335,11 +280,12 @@ class Basic
* all html tags will be stripped and <br> changed to \n * all html tags will be stripped and <br> changed to \n
* this is only used for debug output * this is only used for debug output
* @return void has no return * @return void has no return
* @deprecated Use $basic->log->debug() instead * @deprecated Use Logger\Logger->debug() instead
*/ */
public function debug(string $level, string $string, bool $strip = false): void public function debug(string $level, string $string, bool $strip = false): void
{ {
$this->log->debug($level, $string, $strip); trigger_error('Method ' . __METHOD__ . ' has moved to Logger\Logger->debug()', E_USER_DEPRECATED);
$this->log->debug($level, $string);
} }
/** /**
@@ -351,8 +297,7 @@ class Basic
*/ */
public function mergeErrors(array $error_msg = []): void public function mergeErrors(array $error_msg = []): void
{ {
trigger_error('Method ' . __METHOD__ . ' is deprecated, use $basic->log->mergeErrors() or use \CoreLibs\Debug\Logging() class', E_USER_DEPRECATED); trigger_error('Method ' . __METHOD__ . ' is fully deprecated', E_USER_DEPRECATED);
$this->log->mergeErrors($error_msg);
} }
/** /**
@@ -363,7 +308,8 @@ class Basic
*/ */
public function printErrorMsg(string $string = ''): string public function printErrorMsg(string $string = ''): string
{ {
return $this->log->printErrorMsg($string); trigger_error('Method ' . __METHOD__ . ' is fully deprecated', E_USER_DEPRECATED);
return '';
} }
/** /**
@@ -376,8 +322,7 @@ class Basic
*/ */
public function resetErrorMsg(string $level = ''): void public function resetErrorMsg(string $level = ''): void
{ {
trigger_error('Method ' . __METHOD__ . ' is deprecated, use $basic->log->resetErrorMsg() or use \CoreLibs\Debug\Logging() class', E_USER_DEPRECATED); trigger_error('Method ' . __METHOD__ . ' is fully deprecated', E_USER_DEPRECATED);
$this->log->resetErrorMsg($level);
} }
// ****** DEBUG SUPPORT FUNCTIONS ****** // ****** DEBUG SUPPORT FUNCTIONS ******
@@ -388,11 +333,11 @@ class Basic
* prints a html formatted (pre) array * prints a html formatted (pre) array
* @param array<mixed> $array any array * @param array<mixed> $array any array
* @return string formatted array for output with <pre> tag added * @return string formatted array for output with <pre> tag added
* @deprecated Use $this->log->prAr() instead * @deprecated Use \CoreLibs\Debug\Support::prAr() instead
*/ */
public function printAr(array $array): string public function printAr(array $array): string
{ {
return $this->log->prAr($array); return \CoreLibs\Debug\Support::prAr($array);
} }
/** /**
@@ -1164,7 +1109,7 @@ class Basic
public function passwordSet(string $password): string public function passwordSet(string $password): string
{ {
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordSet()', E_USER_DEPRECATED); trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordSet()', E_USER_DEPRECATED);
return \CoreLibs\Check\Password::passwordSet($password); return \CoreLibs\Security\Password::passwordSet($password);
} }
/** /**
@@ -1177,7 +1122,7 @@ class Basic
public function passwordVerify(string $password, string $hash): bool public function passwordVerify(string $password, string $hash): bool
{ {
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordVerify()', E_USER_DEPRECATED); trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordVerify()', E_USER_DEPRECATED);
return \CoreLibs\Check\Password::passwordVerify($password, $hash); return \CoreLibs\Security\Password::passwordVerify($password, $hash);
} }
/** /**
@@ -1189,7 +1134,7 @@ class Basic
public function passwordRehashCheck(string $hash): bool public function passwordRehashCheck(string $hash): bool
{ {
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordRehashCheck()', E_USER_DEPRECATED); trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordRehashCheck()', E_USER_DEPRECATED);
return \CoreLibs\Check\Password::passwordRehashCheck($hash); return \CoreLibs\Security\Password::passwordRehashCheck($hash);
} }
// *** BETTER PASSWORD OPTIONS END *** // *** BETTER PASSWORD OPTIONS END ***

View File

@@ -14,8 +14,6 @@ declare(strict_types=1);
namespace CoreLibs\Check; namespace CoreLibs\Check;
use Exception;
class Colors class Colors
{ {
/** @var int 1 for HEX rgb */ /** @var int 1 for HEX rgb */
@@ -41,6 +39,7 @@ class Colors
* @param int|false $rgb_flag flag to check for rgb * @param int|false $rgb_flag flag to check for rgb
* @param int|false $hsl_flag flag to check for hsl type * @param int|false $hsl_flag flag to check for hsl type
* @return bool True if no error, False if error * @return bool True if no error, False if error
* @throws \UnexpectedValueException 1: cannot extract color from string
*/ */
private static function rgbHslContentCheck( private static function rgbHslContentCheck(
string $color, string $color,
@@ -52,7 +51,7 @@ class Colors
if ( if (
!is_array($color_list = preg_split("/,\s*/", $matches[1] ?? '')) !is_array($color_list = preg_split("/,\s*/", $matches[1] ?? ''))
) { ) {
throw new \Exception("Could not extract color list from rgg/hsl", 3); throw new \UnexpectedValueException("Could not extract color list from rgg/hsl", 1);
} }
// based on rgb/hsl settings check that entries are valid // based on rgb/hsl settings check that entries are valid
// rgb: either 0-255 OR 0-100% // rgb: either 0-255 OR 0-100%
@@ -124,7 +123,8 @@ class Colors
* @param int $flags defaults to ALL, else use | to combined from * @param int $flags defaults to ALL, else use | to combined from
* HEX_RGB, HEX_RGBA, RGB, RGBA, HSL, HSLA * HEX_RGB, HEX_RGBA, RGB, RGBA, HSL, HSLA
* @return bool True if valid, False if not * @return bool True if valid, False if not
* @throws Exception 1: no valid flag set * @throws \UnexpectedValueException 1: no valid flag set
* @throws \InvalidArgumentException 2: no regex block set
*/ */
public static function validateColor(string $color, int $flags = self::ALL): bool public static function validateColor(string $color, int $flags = self::ALL): bool
{ {
@@ -152,10 +152,10 @@ class Colors
} }
// wrong flag set // wrong flag set
if ($flags > self::ALL) { if ($flags > self::ALL) {
throw new \Exception("Invalid flags parameter: $flags", 1); throw new \UnexpectedValueException("Invalid flags parameter: $flags", 1);
} }
if (!count($regex_blocks)) { if (!count($regex_blocks)) {
throw new \Exception("No regex blocks set: $flags", 2); throw new \InvalidArgumentException("No regex blocks set: $flags", 2);
} }
// build regex // build regex

View File

@@ -8,7 +8,7 @@ class Email
{ {
// this is for error check parts in where the email regex failed // this is for error check parts in where the email regex failed
/** @var array<int,string> */ /** @var array<int,string> */
private static $email_regex_check = [ private static array $email_regex_check = [
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@" 0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER . "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
1 => "@(.*)@(.*)", // double @ 1 => "@(.*)@(.*)", // double @
@@ -21,7 +21,7 @@ class Email
]; ];
// for above position, description string below // for above position, description string below
/** @var array<int,string> */ /** @var array<int,string> */
private static $email_regex_check_message = [ private static array $email_regex_check_message = [
0 => 'Invalid email address', 0 => 'Invalid email address',
1 => 'Double @ mark in email address', 1 => 'Double @ mark in email address',
2 => 'Invalid email part before @ sign', 2 => 'Invalid email part before @ sign',
@@ -33,7 +33,7 @@ class Email
]; ];
// the array with the mobile types that are valid // the array with the mobile types that are valid
/** @var array<string,string> */ /** @var array<string,string> */
private static $mobile_email_type = [ private static array $mobile_email_type = [
'.*@docomo\.ne\.jp$' => 'keitai_docomo', '.*@docomo\.ne\.jp$' => 'keitai_docomo',
// correct are a[2-4], b2, c[1-9], e[2-9], h[2-4], t[1-9] // correct are a[2-4], b2, c[1-9], e[2-9], h[2-4], t[1-9]
'.*@([a-z0-9]{2}\.)?ezweb\.ne\.jp$' => 'keitai_kddi_ezweb', '.*@([a-z0-9]{2}\.)?ezweb\.ne\.jp$' => 'keitai_kddi_ezweb',
@@ -72,7 +72,7 @@ class Email
]; ];
// short list for mobile email types // short list for mobile email types
/** @var array<string,string> */ /** @var array<string,string> */
private static $mobile_email_type_short = [ private static array $mobile_email_type_short = [
'keitai_docomo' => 'docomo', 'keitai_docomo' => 'docomo',
'keitai_kddi_ezweb' => 'kddi', 'keitai_kddi_ezweb' => 'kddi',
'keitai_kddi' => 'kddi', 'keitai_kddi' => 'kddi',
@@ -169,10 +169,10 @@ class Email
* @param string $email email string * @param string $email email string
* @param bool $short default false, if true, * @param bool $short default false, if true,
* returns only short type (pc instead of pc_html) * returns only short type (pc instead of pc_html)
* @return string|bool email type, eg "pc", "docomo", etc, * @return string|false email type, eg "pc", "docomo", etc,
* false for invalid short type * false for invalid short type
*/ */
public static function getEmailType(string $email, bool $short = false) public static function getEmailType(string $email, bool $short = false): string|false
{ {
// trip if there is no email address // trip if there is no email address
if (!$email) { if (!$email) {
@@ -200,9 +200,9 @@ class Email
* gets the short email type from a long email type * gets the short email type from a long email type
* *
* @param string $email_type email string * @param string $email_type email string
* @return string|bool short string or false for invalid * @return string|false short string or false for invalid
*/ */
public static function getShortEmailType(string $email_type) public static function getShortEmailType(string $email_type): string|false
{ {
// check if the short email type exists // check if the short email type exists
if (isset(self::$mobile_email_type_short[$email_type])) { if (isset(self::$mobile_email_type_short[$email_type])) {

View File

@@ -11,7 +11,7 @@ namespace CoreLibs\Check;
class Encoding class Encoding
{ {
/** @var int<min, -1>|int<1, max>|string */ /** @var int<min, -1>|int<1, max>|string */
private static $mb_error_char = ''; private static int|string $mb_error_char = '';
/** /**
* set error char * set error char
@@ -90,27 +90,26 @@ class Encoding
$temp = mb_convert_encoding($string, $to_encoding, $from_encoding); $temp = mb_convert_encoding($string, $to_encoding, $from_encoding);
$compare = mb_convert_encoding($temp, $from_encoding, $to_encoding); $compare = mb_convert_encoding($temp, $from_encoding, $to_encoding);
// if string does not match anymore we have a convert problem // if string does not match anymore we have a convert problem
if ($string != $compare) { if ($string == $compare) {
$failed = [];
// go through each character and find the ones that do not match
for ($i = 0, $iMax = mb_strlen($string, $from_encoding); $i < $iMax; $i++) {
$char = mb_substr($string, $i, 1, $from_encoding);
$r_char = mb_substr($compare, $i, 1, $from_encoding);
// the ord 194 is a hack to fix the IE7/IE8
// bug with line break and illegal character
if (
(($char != $r_char && (!self::$mb_error_char ||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
ord($char) != 194
) {
$failed[] = $char;
}
}
return $failed;
} else {
return false; return false;
} }
$failed = [];
// go through each character and find the ones that do not match
for ($i = 0, $iMax = mb_strlen($string, $from_encoding); $i < $iMax; $i++) {
$char = mb_substr($string, $i, 1, $from_encoding);
$r_char = mb_substr($compare, $i, 1, $from_encoding);
// the ord 194 is a hack to fix the IE7/IE8
// bug with line break and illegal character
if (
(($char != $r_char && (!self::$mb_error_char ||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
ord($char) != 194
) {
$failed[] = $char;
}
}
return $failed;
} }
} }

View File

@@ -1,6 +1,8 @@
<?php <?php
/* /*
* NOTE: this is deprecated and all moved \CoreLibs\Security\Password
*
* core password set, check and rehash check wrapper functions * core password set, check and rehash check wrapper functions
*/ */
@@ -8,6 +10,8 @@ declare(strict_types=1);
namespace CoreLibs\Check; namespace CoreLibs\Check;
use CoreLibs\Security\Password as PasswordNew;
class Password class Password
{ {
/** /**
@@ -15,13 +19,16 @@ class Password
* *
* @param string $password password * @param string $password password
* @return string hashed password * @return string hashed password
* @deprecated v9.0 Moved to \CoreLibs\Security\Password::passwordSet
*/ */
public static function passwordSet(string $password): string public static function passwordSet(string $password): string
{ {
// always use the PHP default for the password trigger_error(
// password options ca be set in the password init, 'Method ' . __METHOD__ . ' is deprecated, use '
// but should be kept as default . '\CoreLibs\Security\Password::passwordSet',
return password_hash($password, PASSWORD_DEFAULT); E_USER_DEPRECATED
);
return PasswordNew::passwordSet($password);
} }
/** /**
@@ -30,14 +37,16 @@ class Password
* @param string $password password * @param string $password password
* @param string $hash password hash * @param string $hash password hash
* @return bool true or false * @return bool true or false
* @deprecated v9.0 Moved to \CoreLibs\Security\Password::passwordVerify
*/ */
public static function passwordVerify(string $password, string $hash): bool public static function passwordVerify(string $password, string $hash): bool
{ {
if (password_verify($password, $hash)) { trigger_error(
return true; 'Method ' . __METHOD__ . ' is deprecated, use '
} else { . '\CoreLibs\Security\Password::passwordVerify',
return false; E_USER_DEPRECATED
} );
return PasswordNew::passwordVerify($password, $hash);
} }
/** /**
@@ -45,14 +54,16 @@ class Password
* *
* @param string $hash password hash * @param string $hash password hash
* @return bool true or false * @return bool true or false
* @deprecated v9.0 Moved to \CoreLibs\Security\Password::passwordRehashCheck
*/ */
public static function passwordRehashCheck(string $hash): bool public static function passwordRehashCheck(string $hash): bool
{ {
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) { trigger_error(
return true; 'Method ' . __METHOD__ . ' is deprecated, use '
} else { . '\CoreLibs\Security\Password::passwordRehashCheck',
return false; E_USER_DEPRECATED
} );
return PasswordNew::passwordRehashCheck($hash);
} }
} }

View File

@@ -208,7 +208,9 @@ class ArrayHandler
$prev_depth = 0; $prev_depth = 0;
foreach ($recursive as $key => $value) { foreach ($recursive as $key => $value) {
if ($prev_depth > $recursive->getDepth()) { if ($prev_depth > $recursive->getDepth()) {
$key_path = []; // remove all trailing to ne depth
$diff = $prev_depth - $recursive->getDepth();
array_splice($key_path, -$diff, $diff);
} }
$prev_depth = $recursive->getDepth(); $prev_depth = $recursive->getDepth();
if ($flat === false) { if ($flat === false) {
@@ -243,14 +245,13 @@ class ArrayHandler
* bool key flag: true: handle keys as string or int * bool key flag: true: handle keys as string or int
* default false: all keys are string * default false: all keys are string
* *
* @return array<mixed>|false merged array * @return array<mixed> merged array
*/ */
public static function arrayMergeRecursive(): array|false public static function arrayMergeRecursive(): array
{ {
// croak on not enough arguemnts (we need at least two) // croak on not enough arguemnts (we need at least two)
if (func_num_args() < 2) { if (func_num_args() < 2) {
trigger_error(__FUNCTION__ . ' needs two or more array arguments', E_USER_WARNING); throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments');
return false;
} }
// default key is not string // default key is not string
$key_is_string = false; $key_is_string = false;
@@ -263,15 +264,13 @@ class ArrayHandler
} }
// check that arrays count is at least two, else we don't have enough to do anything // check that arrays count is at least two, else we don't have enough to do anything
if (count($arrays) < 2) { if (count($arrays) < 2) {
trigger_error(__FUNCTION__ . ' needs two or more array arguments', E_USER_WARNING); throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments');
return false;
} }
$merged = []; $merged = [];
while ($arrays) { while ($arrays) {
$array = array_shift($arrays); $array = array_shift($arrays);
if (!is_array($array)) { if (!is_array($array)) {
trigger_error(__FUNCTION__ . ' encountered a non array argument', E_USER_WARNING); throw new \TypeError(__FUNCTION__ . ' encountered a non array argument');
return false;
} }
if (!$array) { if (!$array) {
continue; continue;

View File

@@ -105,49 +105,48 @@ class DateTime
bool $show_micro = true bool $show_micro = true
): string { ): string {
// check if the timestamp has any h/m/s/ms inside, if yes skip // check if the timestamp has any h/m/s/ms inside, if yes skip
if (!preg_match("/(h|m|s|ms)/", (string)$timestamp)) { if (preg_match("/(h|m|s|ms)/", (string)$timestamp)) {
list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 4)), 2, null); return (string)$timestamp;
// if negative remember }
$negative = false; list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 4)), 2, null);
if ((int)$timestamp < 0) { // if negative remember
$negative = true; $negative = false;
} if ((int)$timestamp < 0) {
$timestamp = abs((float)$timestamp); $negative = true;
$timegroups = [86400, 3600, 60, 1]; }
$labels = ['d', 'h', 'm', 's']; $timestamp = abs((float)$timestamp);
$time_string = ''; $timegroups = [86400, 3600, 60, 1];
// if timestamp is zero, return zero string $labels = ['d', 'h', 'm', 's'];
if ($timestamp == 0) { $time_string = '';
$time_string = '0s'; // if timestamp is zero, return zero string
} else { if ($timestamp == 0) {
for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) { $time_string = '0s';
$output = floor((float)$timestamp / $timegroups[$i]);
$timestamp = (float)$timestamp % $timegroups[$i];
// output has days|hours|min|sec
if ($output || $time_string) {
$time_string .= $output . $labels[$i] . (($i + 1) != count($timegroups) ? ' ' : '');
}
}
}
// only add ms if we have an ms value
if ($ms !== null) {
// if we have ms and it has leading zeros, remove them, but only if it is nut just 0
$ms = preg_replace("/^0+(\d+)$/", '${1}', $ms);
if (!is_string($ms) || empty($ms)) {
$ms = '0';
}
// add ms if there
if ($show_micro) {
$time_string .= ' ' . $ms . 'ms';
} elseif (!$time_string) {
$time_string .= $ms . 'ms';
}
}
if ($negative) {
$time_string = '-' . $time_string;
}
} else { } else {
$time_string = $timestamp; for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) {
$output = floor((float)$timestamp / $timegroups[$i]);
$timestamp = (float)$timestamp % $timegroups[$i];
// output has days|hours|min|sec
if ($output || $time_string) {
$time_string .= $output . $labels[$i] . (($i + 1) != count($timegroups) ? ' ' : '');
}
}
}
// only add ms if we have an ms value
if ($ms !== null) {
// if we have ms and it has leading zeros, remove them, but only if it is nut just 0
$ms = preg_replace("/^0+(\d+)$/", '${1}', $ms);
if (!is_string($ms) || empty($ms)) {
$ms = '0';
}
// add ms if there
if ($show_micro) {
$time_string .= ' ' . $ms . 'ms';
} elseif (!$time_string) {
$time_string .= $ms . 'ms';
}
}
if ($negative) {
$time_string = '-' . $time_string;
} }
return (string)$time_string; return (string)$time_string;
} }
@@ -162,37 +161,36 @@ class DateTime
public static function stringToTime(string|int|float $timestring): string|int|float public static function stringToTime(string|int|float $timestring): string|int|float
{ {
$timestamp = 0; $timestamp = 0;
if (preg_match("/(d|h|m|s|ms)/", (string)$timestring)) { if (!preg_match("/(d|h|m|s|ms)/", (string)$timestring)) {
$timestring = (string)$timestring;
// pos for preg match read + multiply factor
$timegroups = [2 => 86400, 4 => 3600, 6 => 60, 8 => 1];
$matches = [];
// if start with -, strip and set negative
$negative = false;
if (preg_match("/^-/", $timestring)) {
$negative = true;
$timestring = substr($timestring, 1);
}
// preg match: 0: full string
// 2, 4, 6, 8 are the to need values
preg_match("/^((\d+)d ?)?((\d+)h ?)?((\d+)m ?)?((\d+)s ?)?((\d+)ms)?$/", $timestring, $matches);
// multiply the returned matches and sum them up. the last one (ms) is added with .
foreach ($timegroups as $i => $time_multiply) {
if (isset($matches[$i]) && is_numeric($matches[$i])) {
$timestamp += (float)$matches[$i] * $time_multiply;
}
}
if (isset($matches[10]) && is_numeric($matches[10])) {
$timestamp .= '.' . $matches[10];
}
if ($negative) {
// cast to flaot so we can do a negative multiplication
$timestamp = (float)$timestamp * -1;
}
return $timestamp;
} else {
return $timestring; return $timestring;
} }
$timestring = (string)$timestring;
// pos for preg match read + multiply factor
$timegroups = [2 => 86400, 4 => 3600, 6 => 60, 8 => 1];
$matches = [];
// if start with -, strip and set negative
$negative = false;
if (preg_match("/^-/", $timestring)) {
$negative = true;
$timestring = substr($timestring, 1);
}
// preg match: 0: full string
// 2, 4, 6, 8 are the to need values
preg_match("/^((\d+)d ?)?((\d+)h ?)?((\d+)m ?)?((\d+)s ?)?((\d+)ms)?$/", $timestring, $matches);
// multiply the returned matches and sum them up. the last one (ms) is added with .
foreach ($timegroups as $i => $time_multiply) {
if (isset($matches[$i]) && is_numeric($matches[$i])) {
$timestamp += (float)$matches[$i] * $time_multiply;
}
}
if (isset($matches[10]) && is_numeric($matches[10])) {
$timestamp .= '.' . $matches[10];
}
if ($negative) {
// cast to flaot so we can do a negative multiplication
$timestamp = (float)$timestamp * -1;
}
return $timestamp;
} }
/** /**
@@ -323,36 +321,36 @@ class DateTime
* *
* @param string $start_date start date string in YYYY-MM-DD * @param string $start_date start date string in YYYY-MM-DD
* @param string $end_date end date string in YYYY-MM-DD * @param string $end_date end date string in YYYY-MM-DD
* @return int|bool false on error * @return int int -1 (s<e)/0 (s=e)/1 (s>e) as difference
* or int -1 (s<e)/0 (s=e)/1 (s>e) as difference * @throws \UnexpectedValueException On empty start/end values
*/ */
public static function compareDate(string $start_date, string $end_date): int|bool public static function compareDate(string $start_date, string $end_date): int
{ {
// pre check for empty or wrong // pre check for empty or wrong
if ($start_date == '--' || $end_date == '--' || !$start_date || !$end_date) { if ($start_date == '--' || $end_date == '--' || empty($start_date) || empty($end_date)) {
return false; throw new \UnexpectedValueException('Start or End date not set or are just "--"', 1);
} }
// if invalid, quit // if invalid, quit
if (($start_timestamp = strtotime($start_date)) === false) { if (($start_timestamp = strtotime($start_date)) === false) {
return false; throw new \UnexpectedValueException("Error parsing start date through strtotime()", 2);
} }
if (($end_timestamp = strtotime($end_date)) === false) { if (($end_timestamp = strtotime($end_date)) === false) {
return false; throw new \UnexpectedValueException("Error parsing end date through strtotime()", 3);
} }
$comp = 0;
// convert anything to Y-m-d and then to timestamp // convert anything to Y-m-d and then to timestamp
// this is to remove any time parts // this is to remove any time parts
$start_timestamp = strtotime(date('Y-m-d', $start_timestamp)); $start_timestamp = strtotime(date('Y-m-d', $start_timestamp));
$end_timestamp = strtotime(date('Y-m-d', $end_timestamp)); $end_timestamp = strtotime(date('Y-m-d', $end_timestamp));
// compare, or end with false // compare, or end with false
if ($start_timestamp < $end_timestamp) { if ($start_timestamp < $end_timestamp) {
return -1; $comp = -1;
} elseif ($start_timestamp == $end_timestamp) { } elseif ($start_timestamp == $end_timestamp) {
return 0; $comp = 0;
} elseif ($start_timestamp > $end_timestamp) { } elseif ($start_timestamp > $end_timestamp) {
return 1; $comp = 1;
} else {
return false;
} }
return $comp;
} }
/** /**
@@ -366,32 +364,32 @@ class DateTime
* *
* @param string $start_datetime start date/time in YYYY-MM-DD HH:mm:ss * @param string $start_datetime start date/time in YYYY-MM-DD HH:mm:ss
* @param string $end_datetime end date/time in YYYY-MM-DD HH:mm:ss * @param string $end_datetime end date/time in YYYY-MM-DD HH:mm:ss
* @return int|bool false for error * @return int -1 (s<e)/0 (s=e)/1 (s>e) as difference
* or -1 (s<e)/0 (s=e)/1 (s>e) as difference * @throws \UnexpectedValueException On empty start/end values
*/ */
public static function compareDateTime(string $start_datetime, string $end_datetime): int|bool public static function compareDateTime(string $start_datetime, string $end_datetime): int
{ {
// pre check for empty or wrong // pre check for empty or wrong
if ($start_datetime == '--' || $end_datetime == '--' || !$start_datetime || !$end_datetime) { if ($start_datetime == '--' || $end_datetime == '--' || empty($start_datetime) || empty($end_datetime)) {
return false; throw new \UnexpectedValueException('Start or end timestamp not set or are just "--"', 1);
} }
// quit if invalid timestamp // quit if invalid timestamp
if (($start_timestamp = strtotime($start_datetime)) === false) { if (($start_timestamp = strtotime($start_datetime)) === false) {
return false; throw new \UnexpectedValueException("Error parsing start timestamp through strtotime()", 2);
} }
if (($end_timestamp = strtotime($end_datetime)) === false) { if (($end_timestamp = strtotime($end_datetime)) === false) {
return false; throw new \UnexpectedValueException("Error parsing end timestamp through strtotime()", 3);
} }
$comp = 0;
// compare, or return false // compare, or return false
if ($start_timestamp < $end_timestamp) { if ($start_timestamp < $end_timestamp) {
return -1; $comp = -1;
} elseif ($start_timestamp == $end_timestamp) { } elseif ($start_timestamp == $end_timestamp) {
return 0; $comp = 0;
} elseif ($start_timestamp > $end_timestamp) { } elseif ($start_timestamp > $end_timestamp) {
return 1; $comp = 1;
} else {
return false;
} }
return $comp;
} }
/** /**
@@ -452,6 +450,31 @@ class DateTime
return $days; return $days;
} }
} }
/**
* check if a weekend day (sat/sun) is in the given date range
* Can have time too, but is not needed
*
* @param string $start_date Y-m-d
* @param string $end_date Y-m-d
* @return bool True for has weekend, False for has not
*/
public static function dateRangeHasWeekend(
string $start_date,
string $end_date,
): bool {
$dd_start = new \DateTime($start_date);
$dd_end = new \DateTime($end_date);
if (
// starts with a weekend
$dd_start->format('N') >= 6 ||
// start day plus diff will be 6 and so fall into a weekend
((int)$dd_start->format('w') + $dd_start->diff($dd_end)->days) >= 6
) {
return true;
}
return false;
}
} }
// __END__ // __END__

View File

@@ -28,15 +28,15 @@ class Colors
* @param int $green green 0-255 * @param int $green green 0-255
* @param int $blue blue 0-255 * @param int $blue blue 0-255
* @param bool $hex_prefix default true, prefix with "#" * @param bool $hex_prefix default true, prefix with "#"
* @return string|bool rgb in hex values with leading # if set, * @return string rgb in hex values with leading # if set,
* false for invalid color * @throws \LengthException If any argument is not in the range of 0~255
*/ */
public static function rgb2hex( public static function rgb2hex(
int $red, int $red,
int $green, int $green,
int $blue, int $blue,
bool $hex_prefix = true bool $hex_prefix = true
): string|bool { ): string {
$hex_color = ''; $hex_color = '';
if ($hex_prefix === true) { if ($hex_prefix === true) {
$hex_color = '#'; $hex_color = '#';
@@ -44,7 +44,8 @@ class Colors
foreach (['red', 'green', 'blue'] as $color) { foreach (['red', 'green', 'blue'] as $color) {
// if not valid, abort // if not valid, abort
if ($$color < 0 || $$color > 255) { if ($$color < 0 || $$color > 255) {
return false; throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
. ' is not in the range of 0 to 255', 1);
} }
// pad left with 0 // pad left with 0
$hex_color .= str_pad(dechex($$color), 2, '0', STR_PAD_LEFT); $hex_color .= str_pad(dechex($$color), 2, '0', STR_PAD_LEFT);
@@ -55,37 +56,39 @@ class Colors
/** /**
* converts a hex RGB color to the int numbers * converts a hex RGB color to the int numbers
* *
* @param string $hexStr RGB hexstring * @param string $hex_string RGB hexstring
* @param bool $return_as_string flag to return as string * @param bool $return_as_string flag to return as string
* @param string $seperator string seperator: default: "," * @param string $seperator string seperator: default: ","
* @return string|array<string,float|int>|bool false on error or array with RGB * @return string|array<string,float|int> array with RGB
* or a string with the seperator * or a string with the seperator
* @throws \InvalidArgumentException if hex string is empty
* @throws \UnexpectedValueException if the hex string value is not valid
*/ */
public static function hex2rgb( public static function hex2rgb(
string $hexStr, string $hex_string,
bool $return_as_string = false, bool $return_as_string = false,
string $seperator = ',' string $seperator = ','
): string|array|bool { ): string|array {
$hexStr = preg_replace("/[^0-9A-Fa-f]/", '', $hexStr); // Gets a proper hex string $hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $hex_string); // Gets a proper hex string
if (!is_string($hexStr)) { if (!is_string($hex_string)) {
return false; throw new \InvalidArgumentException('hex_string argument cannot be empty', 1);
} }
$rgbArray = []; $rgbArray = [];
if (strlen($hexStr) == 6) { if (strlen($hex_string) == 6) {
// If a proper hex code, convert using bitwise operation. // If a proper hex code, convert using bitwise operation.
// No overhead... faster // No overhead... faster
$colorVal = hexdec($hexStr); $colorVal = hexdec($hex_string);
$rgbArray['r'] = 0xFF & ($colorVal >> 0x10); $rgbArray['r'] = 0xFF & ($colorVal >> 0x10);
$rgbArray['g'] = 0xFF & ($colorVal >> 0x8); $rgbArray['g'] = 0xFF & ($colorVal >> 0x8);
$rgbArray['b'] = 0xFF & $colorVal; $rgbArray['b'] = 0xFF & $colorVal;
} elseif (strlen($hexStr) == 3) { } elseif (strlen($hex_string) == 3) {
// If shorthand notation, need some string manipulations // If shorthand notation, need some string manipulations
$rgbArray['r'] = hexdec(str_repeat(substr($hexStr, 0, 1), 2)); $rgbArray['r'] = hexdec(str_repeat(substr($hex_string, 0, 1), 2));
$rgbArray['g'] = hexdec(str_repeat(substr($hexStr, 1, 1), 2)); $rgbArray['g'] = hexdec(str_repeat(substr($hex_string, 1, 1), 2));
$rgbArray['b'] = hexdec(str_repeat(substr($hexStr, 2, 1), 2)); $rgbArray['b'] = hexdec(str_repeat(substr($hex_string, 2, 1), 2));
} else { } else {
// Invalid hex color code // Invalid hex color code
return false; throw new \UnexpectedValueException('Invalid hex_string: ' . $hex_string, 2);
} }
// returns the rgb string or the associative array // returns the rgb string or the associative array
return $return_as_string ? implode($seperator, $rgbArray) : $rgbArray; return $return_as_string ? implode($seperator, $rgbArray) : $rgbArray;
@@ -97,20 +100,21 @@ class Colors
* returns: * returns:
* array with hue (0-360), sat (0-100%), brightness/value (0-100%) * array with hue (0-360), sat (0-100%), brightness/value (0-100%)
* *
* @param int $red red 0-255 * @param int $red red 0-255
* @param int $green green 0-255 * @param int $green green 0-255
* @param int $blue blue 0-255 * @param int $blue blue 0-255
* @return array<int|float>|bool Hue, Sat, Brightness/Value * @return array<int|float> Hue, Sat, Brightness/Value
* false for input value error * @throws \LengthException If any argument is not in the range of 0~255
*/ */
public static function rgb2hsb(int $red, int $green, int $blue): array|bool public static function rgb2hsb(int $red, int $green, int $blue): array
{ {
// check that rgb is from 0 to 255 // check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) { foreach (['red', 'green', 'blue'] as $color) {
if ($$c < 0 || $$c > 255) { if ($$color < 0 || $$color > 255) {
return false; throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
. ' is not in the range of 0 to 255', 1);
} }
$$c = $$c / 255; $$color = $$color / 255;
} }
$MAX = max($red, $green, $blue); $MAX = max($red, $green, $blue);
@@ -144,13 +148,13 @@ class Colors
* converts HSB/V to RGB values RGB is full INT * converts HSB/V to RGB values RGB is full INT
* if HSB/V value is invalid, sets this value to 0 * if HSB/V value is invalid, sets this value to 0
* *
* @param float $H hue 0-360 (int) * @param float $H hue 0-360 (int)
* @param float $S saturation 0-100 (int) * @param float $S saturation 0-100 (int)
* @param float $V brightness/value 0-100 (int) * @param float $V brightness/value 0-100 (int)
* @return array<int>|bool 0 red/1 green/2 blue array as 0-255 * @return array<int> 0 red/1 green/2 blue array as 0-255
* false for input value error * @throws \LengthException If any argument is not in the valid range
*/ */
public static function hsb2rgb(float $H, float $S, float $V): array|bool public static function hsb2rgb(float $H, float $S, float $V): array
{ {
// check that H is 0 to 359, 360 = 0 // check that H is 0 to 359, 360 = 0
// and S and V are 0 to 1 // and S and V are 0 to 1
@@ -158,13 +162,13 @@ class Colors
$H = 0; $H = 0;
} }
if ($H < 0 || $H > 359) { if ($H < 0 || $H > 359) {
return false; throw new \LengthException('Argument value ' . $H . ' for hue is not in the range of 0 to 359', 1);
} }
if ($S < 0 || $S > 100) { if ($S < 0 || $S > 100) {
return false; throw new \LengthException('Argument value ' . $S . ' for saturation is not in the range of 0 to 100', 2);
} }
if ($V < 0 || $V > 100) { if ($V < 0 || $V > 100) {
return false; throw new \LengthException('Argument value ' . $V . ' for brightness is not in the range of 0 to 100', 3);
} }
// convert to internal 0-1 format // convert to internal 0-1 format
$S /= 100; $S /= 100;
@@ -230,20 +234,21 @@ class Colors
* return: * return:
* array with hue (0-360), saturation (0-100%) and luminance (0-100%) * array with hue (0-360), saturation (0-100%) and luminance (0-100%)
* *
* @param int $red red 0-255 * @param int $red red 0-255
* @param int $green green 0-255 * @param int $green green 0-255
* @param int $blue blue 0-255 * @param int $blue blue 0-255
* @return array<float>|bool hue/sat/luminance * @return array<float> hue/sat/luminance
* false for input value error * @throws \LengthException If any argument is not in the range of 0~255
*/ */
public static function rgb2hsl(int $red, int $green, int $blue): array|bool public static function rgb2hsl(int $red, int $green, int $blue): array
{ {
// check that rgb is from 0 to 255 // check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) { foreach (['red', 'green', 'blue'] as $color) {
if ($$c < 0 || $$c > 255) { if ($$color < 0 || $$color > 255) {
return false; throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
. ' is not in the range of 0 to 255', 1);
} }
$$c = $$c / 255; $$color = $$color / 255;
} }
$min = min($red, $green, $blue); $min = min($red, $green, $blue);
@@ -284,24 +289,25 @@ class Colors
* converts an HSL to RGB * converts an HSL to RGB
* if HSL value is invalid, set this value to 0 * if HSL value is invalid, set this value to 0
* *
* @param float $hue hue: 0-360 (degrees) * @param float $hue hue: 0-360 (degrees)
* @param float $sat saturation: 0-100 * @param float $sat saturation: 0-100
* @param float $lum luminance: 0-100 * @param float $lum luminance: 0-100
* @return array<int,float|int>|bool red/blue/green 0-255 each * @return array<int,float|int> red/blue/green 0-255 each
* @throws \LengthException If any argument is not in the valid range
*/ */
public static function hsl2rgb(float $hue, float $sat, float $lum): array|bool public static function hsl2rgb(float $hue, float $sat, float $lum): array
{ {
if ($hue == 360) { if ($hue == 360) {
$hue = 0; $hue = 0;
} }
if ($hue < 0 || $hue > 359) { if ($hue < 0 || $hue > 359) {
return false; throw new \LengthException('Argument value ' . $hue . ' for hue is not in the range of 0 to 359', 1);
} }
if ($sat < 0 || $sat > 100) { if ($sat < 0 || $sat > 100) {
return false; throw new \LengthException('Argument value ' . $sat . ' for saturation is not in the range of 0 to 100', 2);
} }
if ($lum < 0 || $lum > 100) { if ($lum < 0 || $lum > 100) {
return false; throw new \LengthException('Argument value ' . $lum . ' for luminance is not in the range of 0 to 100', 3);
} }
// calc to internal convert value for hue // calc to internal convert value for hue
$hue = (1 / 360) * $hue; $hue = (1 / 360) * $hue;

View File

@@ -26,8 +26,12 @@ class SetVarTypeMain
?string $default = null, ?string $default = null,
bool $to_null = false bool $to_null = false
): ?string { ): ?string {
if (is_string($val)) { if (
return $val; $val === null ||
is_scalar($val) ||
$val instanceof \Stringable
) {
return (string)$val;
} }
if ($to_null === false) { if ($to_null === false) {
return (string)$default; return (string)$default;
@@ -39,6 +43,7 @@ class SetVarTypeMain
* Will convert input data to string if possible. * Will convert input data to string if possible.
* Runs for string/int/float/bool/null * Runs for string/int/float/bool/null
* Will skip array/object/resource/callable/etc and use default for that * Will skip array/object/resource/callable/etc and use default for that
* Note: this is pretty much the same as setStrMain because string is easy
* *
* @param mixed $val Input variable * @param mixed $val Input variable
* @param string|null $default Default value * @param string|null $default Default value
@@ -71,6 +76,7 @@ class SetVarTypeMain
/** /**
* If input variable is int, return it, else return default value. If to_null * If input variable is int, return it, else return default value. If to_null
* is true then null as return is allowed, else only int is returned * is true then null as return is allowed, else only int is returned
* Note, if float is sent in, int is returned
* *
* @param mixed $val Input variable * @param mixed $val Input variable
* @param int|null $default Default value * @param int|null $default Default value
@@ -82,8 +88,8 @@ class SetVarTypeMain
?int $default = null, ?int $default = null,
bool $to_null = false bool $to_null = false
): ?int { ): ?int {
if (is_int($val)) { if (is_numeric($val)) {
return $val; return (int)$val;
} }
if ($to_null === false) { if ($to_null === false) {
return (int)$default; return (int)$default;
@@ -129,6 +135,7 @@ class SetVarTypeMain
/** /**
* If input is float return it, else set to default value. If to_null is set * If input is float return it, else set to default value. If to_null is set
* to true, allow null return * to true, allow null return
* Note if an int is sent in, float is returned
* *
* @param mixed $val Input variable * @param mixed $val Input variable
* @param float|null $default Default value * @param float|null $default Default value
@@ -140,8 +147,8 @@ class SetVarTypeMain
?float $default = null, ?float $default = null,
bool $to_null = false bool $to_null = false
): ?float { ): ?float {
if (is_float($val)) { if (is_numeric($val)) {
return $val; return (float)$val;
} }
if ($to_null === false) { if ($to_null === false) {
return (float)$default; return (float)$default;

View File

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

View File

@@ -12,7 +12,7 @@ namespace CoreLibs\Convert;
class MimeAppName class MimeAppName
{ {
/** @var array<string,string> */ /** @var array<string,string> */
private static $mime_apps = []; private static array $mime_apps = [];
/** /**
* constructor: init mime list * constructor: init mime list

View File

@@ -14,7 +14,7 @@ namespace CoreLibs\Create;
class Email class Email
{ {
/** @var array<string> allowed list for encodings that can do KV folding */ /** @var array<string> allowed list for encodings that can do KV folding */
private static $encoding_kv_allowed = [ private static array $encoding_kv_allowed = [
'UTF-8', 'UTF-8',
'EUC-JP', 'EUC-JP',
'SJIS', 'SJIS',
@@ -25,7 +25,7 @@ class Email
'JIS-ms', 'JIS-ms',
]; ];
/** @var string normaly this does not need to be changed */ /** @var string normaly this does not need to be changed */
private static $mb_convert_kana_mode = 'KV'; private static string $mb_convert_kana_mode = 'KV';
/** /**
* create mime encoded email part for to/from emails. * create mime encoded email part for to/from emails.
@@ -137,7 +137,7 @@ class Email
* @param bool $kv_folding If set to true and a valid encoding, * @param bool $kv_folding If set to true and a valid encoding,
* do KV folding * do KV folding
* @param bool $test test flag, default off * @param bool $test test flag, default off
* @param \CoreLibs\Debug\Logging|null $log Logging class, * @param \CoreLibs\Logging\Logging|null $log Logging class,
* only used if test flag is true * only used if test flag is true
* @return int 2 test only, no sent * @return int 2 test only, no sent
* 1 for ok, * 1 for ok,
@@ -156,7 +156,7 @@ class Email
string $encoding = 'UTF-8', string $encoding = 'UTF-8',
bool $kv_folding = false, bool $kv_folding = false,
bool $test = false, bool $test = false,
?\CoreLibs\Debug\Logging $log = null ?\CoreLibs\Logging\Logging $log = null
): int { ): int {
/** @var array<string> */ /** @var array<string> */
$to_emails = []; $to_emails = [];
@@ -259,11 +259,11 @@ class Email
$mail_delivery_status = 2; $mail_delivery_status = 2;
} }
// log if an log instance exists // log if an log instance exists
if ($log instanceof \CoreLibs\Debug\Logging) { if ($log instanceof \CoreLibs\Logging\Logging) {
// build debug strings: convert to UTF-8 if not utf-8 // build debug strings: convert to UTF-8 if not utf-8
$log->debug('SEND EMAIL', 'HEADERS: ' . $log->prAr($headers) . ', ' $log->debug('SEND EMAIL', 'HEADERS: ' . \CoreLibs\Debug\Support::prAr($headers) . ', '
. 'ENCODING: ' . $encoding . ', ' . 'ENCODING: ' . $encoding . ', '
. 'KV FOLDING: ' . $log->prBl($kv_folding) . ', ' . 'KV FOLDING: ' . \CoreLibs\Debug\Support::prBl($kv_folding) . ', '
. 'TO: ' . $to_email . ', ' . 'TO: ' . $to_email . ', '
. 'SUBJECT: ' . $out_subject . ', ' . 'SUBJECT: ' . $out_subject . ', '
. 'BODY: ' . ($encoding == 'UTF-8' ? . 'BODY: ' . ($encoding == 'UTF-8' ?

View File

@@ -10,6 +10,7 @@ namespace CoreLibs\Create;
class Hash class Hash
{ {
public const DEFAULT_HASH = 'adler32';
public const STANDARD_HASH_LONG = 'ripemd160'; public const STANDARD_HASH_LONG = 'ripemd160';
public const STANDARD_HASH_SHORT = 'adler32'; public const STANDARD_HASH_SHORT = 'adler32';
@@ -58,7 +59,7 @@ class Hash
/** /**
* replacemend for __crc32b call (alternate) * replacemend for __crc32b call (alternate)
* defaults to adler 32 * defaults to adler 32
* allowed crc32b, adler32, fnv132, fnv1a32, joaat * allowed: any in hash algos list, default to adler 32
* all that create 8 char long hashes * all that create 8 char long hashes
* *
* @param string $string string to hash * @param string $string string to hash
@@ -67,15 +68,15 @@ class Hash
*/ */
public static function __hash( public static function __hash(
string $string, string $string,
string $hash_type = self::STANDARD_HASH_SHORT string $hash_type = self::DEFAULT_HASH
): string { ): string {
// if not empty, check if in valid list
if ( if (
!in_array( empty($hash_type) ||
$hash_type, !in_array($hash_type, hash_algos())
['crc32b', 'adler32', 'fnv132', 'fnv1a32', 'joaat']
)
) { ) {
$hash_type = 'adler32'; // fallback to default hash type if none set or invalid
$hash_type = self::DEFAULT_HASH;
} }
return hash($hash_type, $string); return hash($hash_type, $string);
} }

View File

@@ -12,13 +12,13 @@ class RandomKey
{ {
// key generation // key generation
/** @var string */ /** @var string */
private static $key_range = ''; private static string $key_range = '';
/** @var int */ /** @var int */
private static $one_key_length; private static int $one_key_length;
/** @var int */ /** @var int */
private static $key_length = 4; // default key length private static int $key_length = 4; // default key length
/** @var int */ /** @var int */
private static $max_key_length = 256; // max allowed length private static int $max_key_length = 256; // max allowed length
/** /**
* if launched as class, init random key data first * if launched as class, init random key data first
@@ -100,7 +100,9 @@ class RandomKey
public static function randomKeyGen(int $key_length = -1): string public static function randomKeyGen(int $key_length = -1): string
{ {
// init random key strings if not set // init random key strings if not set
if (!is_numeric(self::$one_key_length)) { if (
!isset(self::$one_key_length)
) {
self::initRandomKeyData(); self::initRandomKeyData();
} }
$use_key_length = 0; $use_key_length = 0;

View File

@@ -15,9 +15,6 @@ namespace CoreLibs\Create;
class Session class Session
{ {
/** @var string list for errors */
private $session_intern_error_str = '';
/** /**
* init a session, if array is empty or array does not have session_name set * init a session, if array is empty or array does not have session_name set
* then no auto init is run * then no auto init is run
@@ -71,17 +68,6 @@ class Session
return true; return true;
} }
/**
* Return set error string, empty if none set
* Error strings are only set in the startSession method
*
* @return string Last error string
*/
public function getErrorStr(): string
{
return $this->session_intern_error_str;
}
/** /**
* check if session name is valid * check if session name is valid
* *
@@ -120,13 +106,11 @@ class Session
{ {
// we can't start sessions on command line // we can't start sessions on command line
if ($this->checkCliStatus()) { if ($this->checkCliStatus()) {
$this->session_intern_error_str = '[SESSION] No sessions in php cli'; throw new \RuntimeException('[SESSION] No sessions in php cli', 1);
return false;
} }
// if session are OFF // if session are OFF
if ($this->getSessionStatus() === PHP_SESSION_DISABLED) { if ($this->getSessionStatus() === PHP_SESSION_DISABLED) {
$this->session_intern_error_str = '[SESSION] Sessions are disabled'; throw new \RuntimeException('[SESSION] Sessions are disabled', 2);
return false;
} }
// session_status // session_status
// initial the session if there is no session running already // initial the session if there is no session running already
@@ -139,8 +123,7 @@ class Session
if (!empty($session_name)) { if (!empty($session_name)) {
// invalid session name, abort // invalid session name, abort
if (!$this->checkValidSessionName($session_name)) { if (!$this->checkValidSessionName($session_name)) {
$this->session_intern_error_str = '[SESSION] Invalid session name: ' . $session_name; throw new \UnexpectedValueException('[SESSION] Invalid session name: ' . $session_name, 3);
return false;
} }
$this->setSessionName($session_name); $this->setSessionName($session_name);
} }
@@ -149,11 +132,10 @@ class Session
} }
// if we still have no active session // if we still have no active session
if (!$this->checkActiveSession()) { if (!$this->checkActiveSession()) {
$this->session_intern_error_str = '[SESSION] Failed to activate session'; throw new \RuntimeException('[SESSION] Failed to activate session', 4);
return false;
} }
if (false === ($session_id = $this->getSessionId())) { if (false === ($session_id = $this->getSessionId())) {
$this->session_intern_error_str = '[SESSION] getSessionId did not return a session id'; throw new \UnexpectedValueException('[SESSION] getSessionId did not return a session id', 5);
} }
return $session_id; return $session_id;
} }

View File

@@ -1,5 +1,12 @@
<?php <?php
/**
* Create uniqIds
*
* If convert ID to hash:
* https://github.com/vinkla/hashids
*/
declare(strict_types=1); declare(strict_types=1);
namespace CoreLibs\Create; namespace CoreLibs\Create;
@@ -7,10 +14,39 @@ namespace CoreLibs\Create;
class Uids class Uids
{ {
// what to use as a default hash if non ise set and no DEFAULT_HASH is defined // what to use as a default hash if non ise set and no DEFAULT_HASH is defined
public const DEFAULT_HASH = 'sha256';
/** @var int */
public const DEFAULT_UNNIQ_ID_LENGTH = 64;
/** @var string */
public const STANDARD_HASH_LONG = 'ripemd160'; public const STANDARD_HASH_LONG = 'ripemd160';
/** @var string */
public const STANDARD_HASH_SHORT = 'adler32'; public const STANDARD_HASH_SHORT = 'adler32';
/**
* Create unique id, lower length is for
*
* @param int $length Length for uniq id, min is 4 characters
* Uneven lengths will return lower bound (9 -> 8)
* @param bool $force_length [default=false] if set to true and we have
* uneven length, then we shorten to this length
* @return string Uniq id
*/
private static function uniqIdL(int $length = 64, bool $force_length = false): string
{
$uniqid_length = ($length < 4) ? 4 : $length;
if ($force_length) {
$uniqid_length++;
}
/** @var int<1,max> make sure that internal this is correct */
$random_bytes_length = ($uniqid_length - ($uniqid_length % 2)) / 2;
$uniqid = bin2hex(random_bytes($random_bytes_length));
// if not forced shorten return next lower length
if (!$force_length) {
return $uniqid;
}
return substr($uniqid, 0, $length);
}
/** /**
* creates psuedo random uuid v4 * creates psuedo random uuid v4
* Code take from class here: * Code take from class here:
@@ -20,7 +56,7 @@ class Uids
*/ */
public static function uuidv4(): string public static function uuidv4(): string
{ {
return sprintf( /* return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low" // 32 bits for "time_low"
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
@@ -38,49 +74,62 @@ class Uids
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff) mt_rand(0, 0xffff)
); ); */
$data = random_bytes(16);
assert(strlen($data) == 16);
// 0-1: 32 bits for "time_low"
// 2: 16 bits for "time_mid"
// 3: 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
// 4: 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
// 5-7: 48 bits for "node"
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
} }
/** /**
* TODO: make this a proper uniq ID creation * creates a uniq id based on lengths
* add uuidv4 subcall to the uuid function too
* creates a uniq id
* *
* @param string $type uniq id type, currently md5 or sha256 allowed * @param int|string $length Either length in int, or fallback type for length
* if not set will use DEFAULT_HASH if set * for string type md5 (32), sha256 (64)
* @return string uniq id * STANDARD_HASH_LONG: ripemd160 (40)
* STANDARD_HASH_SHORT: adler32 (8)
* It is recommended to use the integer
* @param bool $force_length [default=false] if set to true and we have
* uneven length, then we shorten to this length
* @return string Uniq id
*/ */
public static function uniqId(string $type = ''): string public static function uniqId(
{ int|string $length = self::DEFAULT_UNNIQ_ID_LENGTH,
$uniq_id = ''; bool $force_length = false
switch ($type) { ): string {
if (is_int($length)) {
return self::uniqIdL($length, $force_length);
}
switch ($length) {
case 'md5': case 'md5':
$uniq_id = md5(uniqid((string)rand(), true)); $length = 32;
break; break;
case self::DEFAULT_HASH: case 'sha256':
$uniq_id = hash(self::DEFAULT_HASH, uniqid((string)rand(), true)); $length = 64;
break; break;
case self::STANDARD_HASH_LONG: case self::STANDARD_HASH_LONG:
$uniq_id = hash(self::STANDARD_HASH_LONG, uniqid((string)rand(), true)); $length = 40;
break; break;
case self::STANDARD_HASH_SHORT: case self::STANDARD_HASH_SHORT:
$uniq_id = hash(self::STANDARD_HASH_SHORT, uniqid((string)rand(), true)); $length = 8;
break; break;
default: default:
// if not empty, check if in valid list $length = 64;
if (
!empty($type) &&
in_array($type, hash_algos())
) {
$hash = $type;
} else {
// fallback to default hash type if none set or invalid
$hash = self::DEFAULT_HASH;
}
$uniq_id = hash($hash, uniqid((string)rand(), true));
break; break;
} }
return $uniq_id; return self::uniqIdL($length);
} }
/** /**

View File

@@ -39,33 +39,35 @@ class ArrayIO extends \CoreLibs\DB\IO
{ {
// main calss variables // main calss variables
/** @var array<mixed> */ /** @var array<mixed> */
public $table_array; // the array from the table to work on public array $table_array; // the array from the table to work on
/** @var string */ /** @var string */
public $table_name; // the table_name public string $table_name; // the table_name
/** @var string */ /** @var string */
public $pk_name = ''; // the primary key from this table public string $pk_name = ''; // the primary key from this table
/** @var int|string|null */ /** @var int|string|null */
public $pk_id; // the PK id public int|string|null $pk_id; // the PK id
// security values // security values
/** @var int base acl for current page */ /** @var int base acl for current page */
private $base_acl_level = 0; private int $base_acl_level = 0;
/** /**
* constructor for the array io class, set the * constructor for the array io class, set the
* primary key name automatically (from array) * primary key name automatically (from array)
* *
* @param array<mixed> $db_config db connection config * phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db connection config
* @param array<mixed> $table_array table array config * @param array<mixed> $table_array table array config
* @param string $table_name table name string * @param string $table_name table name string
* @param \CoreLibs\Debug\Logging $log Logging class * @param \CoreLibs\Logging\Logging $log Logging class
* @param int $base_acl_level Set base acl level, if needed * @param int $base_acl_level Set base acl level, if needed
* @param int $acl_admin Flag if this is an admin ACL access level * @param int $acl_admin Flag if this is an admin ACL access level
* @throws \RuntimeException Missing table array or table name entry
*/ */
public function __construct( public function __construct(
array $db_config, array $db_config,
array $table_array, array $table_array,
string $table_name, string $table_name,
\CoreLibs\Debug\Logging $log, \CoreLibs\Logging\Logging $log,
int $base_acl_level = 0, int $base_acl_level = 0,
int $acl_admin = 0 int $acl_admin = 0
) { ) {
@@ -82,6 +84,7 @@ class ArrayIO extends \CoreLibs\DB\IO
// error abort if no table array or no table name // error abort if no table array or no table name
if (empty($table_array) || empty($table_name)) { if (empty($table_array) || empty($table_name)) {
$this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing'); $this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing');
throw new \RuntimeException('MAJOR ERROR: Core settings missing', 1999);
} }
// set primary key for given table_array // set primary key for given table_array
@@ -195,7 +198,7 @@ class ArrayIO extends \CoreLibs\DB\IO
public function dbCheckPkSet(): bool public function dbCheckPkSet(): bool
{ {
// if pk_id is set, overrule ... // if pk_id is set, overrule ...
if ($this->pk_id) { if (!empty($this->pk_id)) {
$this->table_array[$this->pk_name]['value'] = $this->pk_id; $this->table_array[$this->pk_name]['value'] = $this->pk_id;
} }
// if not set ... produce error // if not set ... produce error
@@ -243,7 +246,7 @@ class ArrayIO extends \CoreLibs\DB\IO
return $this->table_array; return $this->table_array;
} }
if ($acl_limit === true && $this->base_acl_level < 100) { if ($acl_limit === true && $this->base_acl_level < 100) {
$this->log->debug('DB DELETE ERROR', 'ACL Limit on, Delete, ' $this->log->error('DB DELETE ERROR: ACL Limit on, Delete, '
. 'but base ACL level of 100 not met: ' . $this->base_acl_level); . 'but base ACL level of 100 not met: ' . $this->base_acl_level);
return $this->table_array; return $this->table_array;
} }
@@ -406,7 +409,7 @@ class ArrayIO extends \CoreLibs\DB\IO
} }
// early abort for new write with not enough ACL level // early abort for new write with not enough ACL level
if ($insert && $acl_limit === true && $this->base_acl_level < 100) { if ($insert && $acl_limit === true && $this->base_acl_level < 100) {
$this->log->debug('DB WRITE ERROR', 'ACL Limit on, Insert, ' $this->log->error('DB WRITE ERROR: ACL Limit on, Insert, '
. 'but base ACL level of 100 not met: ' . $this->base_acl_level); . 'but base ACL level of 100 not met: ' . $this->base_acl_level);
return $this->table_array; return $this->table_array;
} }
@@ -579,7 +582,7 @@ class ArrayIO extends \CoreLibs\DB\IO
} // while ... } // while ...
if (empty($q_data)) { if (empty($q_data)) {
$this->log->debug('DB WRITE ERROR', 'No data to write, possible through ACL'); $this->log->error('DB WRITE ERROR: No data to write, possible through ACL');
return $this->table_array; return $this->table_array;
} }
@@ -587,7 +590,7 @@ class ArrayIO extends \CoreLibs\DB\IO
// get it at the end, cause now we can be more sure of no double IDs, etc // get it at the end, cause now we can be more sure of no double IDs, etc
reset($this->table_array); reset($this->table_array);
// create select part & addition FK part // create select part & addition FK part
foreach ($this->table_array as $column => $data_array) { foreach (array_keys($this->table_array) as $column) {
// check FK ... // check FK ...
if ( if (
isset($this->table_array[$column]['fk']) && isset($this->table_array[$column]['fk']) &&

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -209,10 +209,17 @@ interface SqlFunctions
/** /**
* Undocumented function * Undocumented function
* *
* @param \PgSql\Result|false $cursor * @return array{0:string,1:string}
* @return string
*/ */
public function __dbPrintError(\PgSql\Result|false $cursor = false): string; public function __dbPrintLastError(): array;
/**
* Undocumented function
*
* @param \PgSql\Result|false $cursor
* @return array{0:string,1:string}
*/
public function __dbPrintError(\PgSql\Result|false $cursor = false): array;
/** /**
* Undocumented function * Undocumented function

View File

@@ -59,9 +59,9 @@ namespace CoreLibs\DB\SQL;
class PgSQL implements Interface\SqlFunctions class PgSQL implements Interface\SqlFunctions
{ {
/** @var string */ /** @var string */
private $last_error_query; private string $last_error_query;
/** @var \PgSql\Connection|false */ /** @var \PgSql\Connection|false */
private $dbh; private \PgSql\Connection|false $dbh = false;
/** /**
* queries last error query and returns true or false if error was set * queries last error query and returns true or false if error was set
@@ -532,18 +532,37 @@ class PgSQL implements Interface\SqlFunctions
return $this->dbh; return $this->dbh;
} }
/**
* Returns last error for active cursor
*
* @return array{0:string,1:string} prefix, error string
*/
public function __dbPrintLastError(): array
{
if (is_bool($this->dbh)) {
return ['', ''];
}
if (!empty($error_message = pg_last_error($this->dbh))) {
return [
'-PostgreSQL-Error-Last-',
$error_message
];
}
return ['', ''];
}
/** /**
* reads the last error for this cursor and returns * reads the last error for this cursor and returns
* html formatted string with error name * html formatted string with error name
* *
* @param \PgSql\Result|false $cursor cursor * @param \PgSql\Result|false $cursor cursor
* or null * or null
* @return string error string * @return array{0:string,1:string} prefix, error string
*/ */
public function __dbPrintError(\PgSql\Result|false $cursor = false): string public function __dbPrintError(\PgSql\Result|false $cursor = false): array
{ {
if (is_bool($this->dbh)) { if (is_bool($this->dbh)) {
return ''; return ['', ''];
} }
// run the query again for the error result here // run the query again for the error result here
if ((is_bool($cursor)) && $this->last_error_query) { if ((is_bool($cursor)) && $this->last_error_query) {
@@ -552,10 +571,12 @@ class PgSQL implements Interface\SqlFunctions
$cursor = pg_get_result($this->dbh); $cursor = pg_get_result($this->dbh);
} }
if ($cursor && $error_str = pg_result_error($cursor)) { if ($cursor && $error_str = pg_result_error($cursor)) {
return '-PostgreSQL-Error- ' return [
. $error_str; '-PostgreSQL-Error-',
$error_str
];
} else { } else {
return ''; return ['', ''];
} }
} }

View File

@@ -12,9 +12,9 @@ namespace CoreLibs\Debug;
class FileWriter class FileWriter
{ {
/** @var string */ /** @var string */
private static $debug_filename = 'debug_file.log'; // where to write output private static string $debug_filename = 'debug_file.log'; // where to write output
/** @var string */ /** @var string */
private static $debug_folder; private static string $debug_folder;
/** /**
* Set a debug log folder, if not set BASE+LOG folders are set * Set a debug log folder, if not set BASE+LOG folders are set
@@ -63,7 +63,7 @@ class FileWriter
* *
* @param string $string string to write to the file * @param string $string string to write to the file
* @param boolean $enter default true, if set adds a linebreak \n at the end * @param boolean $enter default true, if set adds a linebreak \n at the end
* @return bool True for log written, false for not wirrten * @return bool True for log written, false for not written
*/ */
public static function fdebug(string $string, bool $enter = true): bool public static function fdebug(string $string, bool $enter = true): bool
{ {
@@ -75,9 +75,9 @@ class FileWriter
empty(self::$debug_folder) && empty(self::$debug_folder) &&
defined('BASE') && defined('LOG') defined('BASE') && defined('LOG')
) { ) {
/** @deprecated Do not use this anymore, define path with fsetFolder */ /** @deprecated Do not use this anymore, define path with festFolder */
trigger_error( trigger_error(
'fsetFolder must be set first. Setting via LOG_FILE_ID and LOg constants is deprecated', 'fsetFolder must be set first. Setting via LOG_FILE_ID and LOG constants is deprecated',
E_USER_DEPRECATED E_USER_DEPRECATED
); );
self::$debug_folder = BASE . LOG; self::$debug_folder = BASE . LOG;

View File

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

911
src/Debug/LoggingLegacy.php Normal file
View File

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

View File

@@ -13,13 +13,13 @@ use CoreLibs\Convert\Byte;
class MemoryUsage class MemoryUsage
{ {
/** @var int */ /** @var int */
private static $start_memory = 0; private static int $start_memory = 0;
/** @var int */ /** @var int */
private static $set_memory = 0; private static int $set_memory = 0;
/** @var int */ /** @var int */
private static $previous_memory = 0; private static int $previous_memory = 0;
/** @var bool */ /** @var bool */
private static $debug_memory = false; private static bool $debug_memory = false;
/** /**
* set memory flag, or return set memory flag * set memory flag, or return set memory flag

View File

@@ -12,18 +12,18 @@ class RunningTime
{ {
// hr // hr
/** @var float */ /** @var float */
private static $hr_start_time; private static float $hr_start_time;
/** @var float */ /** @var float */
private static $hr_end_time; private static float $hr_end_time;
/** @var float */ /** @var float */
private static $hr_last_time; private static float $hr_last_time;
// normal // normal
/** @var float */ /** @var float */
private static $start_time; private static float $start_time;
/** @var float */ /** @var float */
private static $end_time; private static float $end_time;
/** @var string */ /** @var string */
private static $running_time_string; private static string $running_time_string;
/** /**
* sub calculation for running time based on out time. * sub calculation for running time based on out time.
@@ -79,7 +79,7 @@ class RunningTime
public static function hrRunningTime(string $out_time = 'ms'): float public static function hrRunningTime(string $out_time = 'ms'): float
{ {
// if start time not set, set start time // if start time not set, set start time
if (!self::$hr_start_time) { if (empty(self::$hr_start_time)) {
self::$hr_start_time = hrtime(true); self::$hr_start_time = hrtime(true);
self::$hr_last_time = self::$hr_start_time; self::$hr_last_time = self::$hr_start_time;
$run_time = 0; $run_time = 0;
@@ -137,7 +137,7 @@ class RunningTime
list($micro, $timestamp) = explode(' ', microtime()); list($micro, $timestamp) = explode(' ', microtime());
$running_time = 0; $running_time = 0;
// set start & end time // set start & end time
if (!self::$start_time) { if (empty(self::$start_time)) {
// always reset running time string on first call // always reset running time string on first call
self::$running_time_string = ''; self::$running_time_string = '';
self::$start_time = ((float)$micro + (float)$timestamp); self::$start_time = ((float)$micro + (float)$timestamp);
@@ -149,7 +149,7 @@ class RunningTime
self::$running_time_string .= date('Y-m-d H:i:s', (int)$timestamp); self::$running_time_string .= date('Y-m-d H:i:s', (int)$timestamp);
self::$running_time_string .= ' ' . $micro . ($simple ? ', ' : '<br>'); self::$running_time_string .= ' ' . $micro . ($simple ? ', ' : '<br>');
// if both are set // if both are set
if (self::$start_time && self::$end_time) { if (!empty(self::$start_time) && !empty(self::$end_time)) {
$running_time = self::$end_time - self::$start_time; $running_time = self::$end_time - self::$start_time;
self::$running_time_string .= ($simple ? 'Run: ' : "<b>Script running time</b>: ") . $running_time . " s"; self::$running_time_string .= ($simple ? 'Run: ' : "<b>Script running time</b>: ") . $running_time . " s";
// reset start & end time after run // reset start & end time after run

View File

@@ -21,7 +21,7 @@ class Support
*/ */
public static function printTime(int $set_microtime = -1): string public static function printTime(int $set_microtime = -1): string
{ {
list($microtime, $timestamp) = explode(' ', microtime()); [$microtime, $timestamp] = explode(' ', microtime());
$string = date("Y-m-d H:i:s", (int)$timestamp); $string = date("Y-m-d H:i:s", (int)$timestamp);
// if microtime flag is -1 no round, if 0, no microtime, if >= 1, round that size // if microtime flag is -1 no round, if 0, no microtime, if >= 1, round that size
if ($set_microtime == -1) { if ($set_microtime == -1) {
@@ -34,31 +34,43 @@ class Support
} }
/** /**
* prints a html formatted (pre) array * prints a html formatted (pre) data
* *
* @param array<mixed> $array any array * @param mixed $data any data
* @param bool $no_html set to true to use ##HTMLPRE## * @param bool $no_html default add <pre>
* @return string formatted array for output with <pre> tag added * @return string formatted array for output with <pre> tag added
*/ */
public static function printAr(array $array, bool $no_html = false): string public static function printAr(mixed $data, bool $no_html = false): string
{ {
if ($no_html === false) { return $no_html ?
return "<pre>" . print_r($array, true) . "</pre>"; print_r($data, true) :
} else { '<pre>' . print_r($data, true) . '</pre>';
return '##HTMLPRE##' . print_r($array, true) . '##/HTMLPRE##';
}
} }
/** /**
* alternate name for printAr function * alternate name for printAr function
* *
* @param array<mixed> $array any array * @param mixed $data any array
* @param bool $no_html set to true to use ##HTMLPRE## * @param bool $no_html default add <pre>
* @return string formatted array for output with <pre> tag added * @return string formatted array for output with <pre> tag added
*/ */
public static function printArray(array $array, bool $no_html = false): string public static function printArray(mixed $data, bool $no_html = false): string
{ {
return self::printAr($array, $no_html); return self::printAr($data, $no_html);
}
/**
* A replacement for the \CoreLibs\Debug\Support::printAr
* But this does not wrap it in <pre></pre>
* Do not use this without using it in a string in debug function
* Note: for full data debug dumps use Support::dumpVar()
*
* @param mixed $data Data to print
* @return string print_r formated
*/
public static function prAr(mixed $data): string
{
return self::printAr($data, true);
} }
/** /**
@@ -66,21 +78,42 @@ class Support
* if $name is set prefix with nae * if $name is set prefix with nae
* default true: true, false: false * default true: true, false: false
* *
* @param bool $bool Variable to convert * @param bool $bool Variable to convert
* @param string $name [default: ''] Prefix name * @param string $name [=''] Prefix name
* @param string $true [default: true] True string * @param string $true [='true'] True string
* @param string $false [default: false] False string * @param string $false [='false'] False string
* @return string String with converted bool text for debug * @param bool $no_html [=false] if true do not print html
* @return string String with converted bool text for debug
*/ */
public static function printBool( public static function printBool(
bool $bool, bool $bool,
string $name = '', string $name = '',
string $true = 'true', string $true = 'true',
string $false = 'false',
bool $no_html = false,
): string {
return
(!empty($name) ?
($no_html ?
$name : '<b>' . $name . '</b>') . ': '
: '')
. ($bool ? $true : $false);
}
/**
* Convert bool value to string value. Short name alias for printBool
*
* @param bool $bool Bool value to be transformed
* @param string $true [='true'] Override default string 'true'
* @param string $false [=false'] Override default string 'false'
* @return string $true or $false string for true/false bool
*/
public static function prBl(
bool $bool,
string $true = 'true',
string $false = 'false' string $false = 'false'
): string { ): string {
$string = (!empty($name) ? '<b>' . $name . '</b>: ' : '') return self::printBool($bool, '', $true, $false, true);
. ($bool ? $true : $false);
return $string;
} }
/** /**
@@ -89,9 +122,10 @@ class Support
* if object return get_class * if object return get_class
* for array use printAr function, can be controlled with no_html for * for array use printAr function, can be controlled with no_html for
* Debug\Logging compatible output * Debug\Logging compatible output
* Recommended to use Support::dumpVar()
* *
* @param mixed $mixed * @param mixed $mixed
* @param bool $no_html set to true to use ##HTMLPRE##or html escape * @param bool $no_html set to true to strip <pre> tags
* @return string * @return string
*/ */
public static function printToString(mixed $mixed, bool $no_html = false): string public static function printToString(mixed $mixed, bool $no_html = false): string
@@ -120,26 +154,141 @@ class Support
} }
/** /**
* if there is a need to find out which parent method called a child method, * Dumps var data and returns it as string
* eg for debugging, this function does this * var_dump based
* Recommended debug output
* *
* call this method in the child method and you get the parent function that called * @param mixed $data Anything
* @param int $level debug level, default 1 * @param bool $no_html [=false] If true strip all html tags
* @return ?string null or the function that called the function * (for text print)
* where this method is called * @return string A text string
*/ */
public static function getCallerMethod(int $level = 1): ?string public static function dumpVar(
mixed $data,
bool $no_html = false,
): string {
// dump data
ob_start();
var_dump($data);
$debug_dump = ob_get_clean() ?: '[FAILED TO GET var_dump() data]';
// check if the original caller is dV, if yes, up the caller level for
// the file line get by 1, so we get file + pos from the dV call and
// not this call
$caller_level = 1;
$caller_list = self::getCallerMethodList();
if ($caller_list[0] == 'dV') {
$caller_level = 2;
}
// we need to strip the string in <small></small that is
// "path ... CoreLibs/Debug/Support.php:<number>:
// and replace it with the caller methods and location
$caller_file_number = self::getCallerFileLine($caller_level);
$debug_dump = preg_replace(
'|<small>(/.*:\d+:)</small>|',
'<small>' . $caller_file_number . ':</small>',
$debug_dump
) ?? $debug_dump; // in case of failure keep original
// if strip is ture, remove all HTML tags and convert any html entities back
return $no_html ?
str_replace(
// things to replace in the string if set
['&gt;', '&lt;', '&#13;', '&#10;'],
['>', '<', "\r", "\n"],
strip_tags($debug_dump)
) :
$debug_dump;
}
/**
* exports (dumps) var, in more printable design, but without detail info
*
* @param mixed $data Anything
* @param bool $no_html [=false] If true true do not add <pre> tags
* @return string A text string
*/
public static function exportVar(mixed $data, bool $no_html = false): string
{
return $no_html ?
var_export($data, true) :
'<pre>' . var_export($data, true) . '</pre>';
}
/**
* Return file name and line number where this was called
* One level up
*
* @param int $level [=1] trace level
* @return string|null null or file name:line number
*/
public static function getCallerFileLine(int $level = 1): ?string
{ {
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print \CoreLibs\Debug\Support::printAr($traces); // print \CoreLibs\Debug\Support::printAr($traces);
// We should check from top down if unset? // We should check from top down if unset?
// sets the start point here, and in level two (the sub call) we find this // sets the start point here, and in level two (the sub call) we find this
if (isset($traces[$level])) {
return ($traces[$level]['file'] ?? $traces[$level]['function'])
. ':' . ($traces[$level]['line'] ?? '-');
}
return null;
}
/**
* if there is a need to find out which parent method called a child method,
* eg for debugging, this function does this
*
* call this method in the child method and you get the parent function that called
* @param int $level [=1] trace level
* @return string|null null or the function that called the function
* where this method is called
*/
public static function getCallerMethod(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "getCallerMethod:<br>" . \CoreLibs\Debug\Support::printAr($traces);
// We should check from top down if unset?
// sets the start point here, and in level two (the sub call) we find this
if (isset($traces[$level])) { if (isset($traces[$level])) {
return $traces[$level]['function']; return $traces[$level]['function'];
} }
return null; return null;
} }
/**
* get the class that first called it and skip the base class
* Companion method to getCallerMethod
*
* @param int $level [=1] trace level
* @return ?string null if class not found
*/
public static function getCallerClass(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "getCallerClass:<br>" . \CoreLibs\Debug\Support::printAr($traces);
if (isset($traces[$level])) {
return $traces[$level]['class'] ?? null;
}
return null;
}
/**
* Returns class and method together
*
* @param int $level [=1] travel level
* @return string|null null if trace level not found, else namespace class and method
*/
public static function getCallerClassMethod(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print "getCallerClass:<br>" . \CoreLibs\Debug\Support::printAr($traces);
if (isset($traces[$level])) {
return ($traces[$level]['class'] ?? '-')
. ($traces[$level]['type'] ?? '')
. $traces[$level]['function'];
}
return null;
}
/** /**
* Returns array with all methods in the call stack in the order so that last * Returns array with all methods in the call stack in the order so that last
* called is last in order * called is last in order
@@ -169,25 +318,21 @@ class Support
* Is mostly used in debug log statements to get the class where the debug * Is mostly used in debug log statements to get the class where the debug
* was called * was called
* gets top level class * gets top level class
* loops over the debug backtrace until if finds the first class (from the end) * loops over the debug backtrace until if finds the first class (from the end)
* *
* @return string Class name with namespace * @return string Class name with namespace
*/ */
public static function getCallerClass(): string public static function getCallerTopLevelClass(): string
{ {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// ?? [['class' => get_called_class()]]; // print "getCallerClass:<br>" . \CoreLibs\Debug\Support::printAr($traces);
// TODO make sure that this doesn't loop forver
$class = null; $class = null;
while ($class === null && count($backtrace) > 0) { // reverse and stop at first set class, this is the top level one
// if current is foreach (array_reverse($traces) as $trace) {
// [function] => debug $class = $trace['class'] ?? null;
// [class] => CoreLibs\Debug\Logging if (!empty($class)) {
// then return break;
// (OUTSIDE) because it was not called from a class method }
// or return file name
$get_class = array_pop($backtrace);
$class = $get_class['class'] ?? null;
} }
// on null or empty return empty string // on null or empty return empty string
return empty($class) ? '' : $class; return empty($class) ? '' : $class;

View File

@@ -54,14 +54,15 @@ class System
/** /**
* get the host name without the port as given by the SELF var * get the host name without the port as given by the SELF var
* if no host name found will set to NOHOST:0
* *
* @return array<mixed> host name/port name * @return array{string,int} host name/port number
*/ */
public static function getHostName(): array public static function getHostName(): array
{ {
$host = $_SERVER['HTTP_HOST'] ?? 'NOHOST:NOPORT'; $host = $_SERVER['HTTP_HOST'] ?? 'NOHOST:0';
list($host_name, $port) = array_pad(explode(':', $host), 2, self::DEFAULT_PORT); [$host_name, $port] = array_pad(explode(':', $host), 2, self::DEFAULT_PORT);
return [$host_name, $port]; return [$host_name, (int)$port];
} }
/** /**

View File

@@ -29,9 +29,9 @@ namespace CoreLibs\Language\Core;
class CachedFileReader extends \CoreLibs\Language\Core\StringReader class CachedFileReader extends \CoreLibs\Language\Core\StringReader
{ {
/** @var int */ /** @var int */
public $error = 0; public int $error = 0;
/** @var string */ /** @var string */
public $fd_str = ''; public string $fd_str = '';
/** /**
* Undocumented function * Undocumented function

View File

@@ -27,13 +27,13 @@ namespace CoreLibs\Language\Core;
class FileReader class FileReader
{ {
/** @var int */ /** @var int */
public $fr_pos; public int $fr_pos;
/** @var resource|bool */ /** @var resource|bool */
public $fr_fd; public mixed $fr_fd; // no resource type yet
/** @var int */ /** @var int */
public $fr_length; public int $fr_length;
/** @var int */ /** @var int */
public $error = 0; public int $error = 0;
/** /**
* file read constructor * file read constructor

View File

@@ -41,31 +41,31 @@ class GetTextReader
{ {
// public: // public:
/** @var int */ /** @var int */
public $error = 0; // public variable that holds error code (0 if no error) public int $error = 0; // public variable that holds error code (0 if no error)
// private: // private:
/** @var int */ /** @var int */
private $BYTEORDER = 0; // 0: low endian, 1: big endian private int $BYTEORDER = 0; // 0: low endian, 1: big endian
/** @var FileReader */ /** @var FileReader */
private $STREAM; private FileReader $STREAM;
/** @var bool */ /** @var bool */
private $short_circuit = false; private bool $short_circuit = false;
/** @var bool */ /** @var bool */
private $enable_cache = false; private bool $enable_cache = false;
/** @var int */ /** @var int */
private $originals = 0; // offset of original table private int $originals = 0; // offset of original table
/** @var int */ /** @var int */
private $translations = 0; // offset of translation table private int $translations = 0; // offset of translation table
/** @var string */ /** @var string */
private $pluralheader = ''; // cache header field for plural forms private string $pluralheader = ''; // cache header field for plural forms
/** @var int */ /** @var int */
private $total = 0; // total string count private int $total = 0; // total string count
/** @var array<mixed>|null */ /** @var array<mixed>|null */
private $table_originals = null; // table for original strings (offsets) private array|null $table_originals = null; // table for original strings (offsets)
/** @var array<mixed>|null */ /** @var array<mixed>|null */
private $table_translations = null; // table for translated strings (offsets) private array|null $table_translations = null; // table for translated strings (offsets)
/** @var array<mixed> */ /** @var array<mixed> */
private $cache_translations = []; // original -> translation mapping private array $cache_translations = []; // original -> translation mapping
/* Methods */ /* Methods */

View File

@@ -27,9 +27,9 @@ namespace CoreLibs\Language\Core;
class StringReader class StringReader
{ {
/** @var int */ /** @var int */
public $sr_pos; public int $sr_pos;
/** @var string */ /** @var string */
public $sr_str; public string $sr_str;
/** /**
* constructor for string reader * constructor for string reader

View File

@@ -35,42 +35,42 @@ class L10n
/** @var string the default fallback encoding if nothing is set */ /** @var string the default fallback encoding if nothing is set */
public const DEFAULT_CHARSET = 'UTF-8'; public const DEFAULT_CHARSET = 'UTF-8';
/** @var string the current locale */ /** @var string the current locale */
private $locale = ''; private string $locale = '';
/** @var string the SET locale as WHERE the domain file is */ /** @var string the SET locale as WHERE the domain file is */
private $locale_set = ''; private string $locale_set = '';
/** @var string the default selected/active domain */ /** @var string the default selected/active domain */
private $domain = ''; private string $domain = '';
/** @var string encoding, as from locale or set from outside */ /** @var string encoding, as from locale or set from outside */
private $override_encoding = self::DEFAULT_CHARSET; private string $override_encoding = self::DEFAULT_CHARSET;
/** @var string encoding set during the parse Locale */ /** @var string encoding set during the parse Locale */
private $encoding = ''; private string $encoding = '';
/** @var array<string,array<string,GetTextReader>> locale > domain = translator */ /** @var array<string,array<string,GetTextReader>> locale > domain = translator */
private $domains = []; private array $domains = [];
/** @var array<string,string> bound paths for domains */ /** @var array<string,string> bound paths for domains */
private $paths = ['' => './']; private array $paths = ['' => './'];
// files // files
/** @var string the full path to the mo file to loaded */ /** @var string the full path to the mo file to loaded */
private $mofile = ''; private string $mofile = '';
/** @var string base path to search level */ /** @var string base path to search level */
private $base_locale_path = ''; private string $base_locale_path = '';
/** @var string dynamic set path to where the mo file is actually */ /** @var string dynamic set path to where the mo file is actually */
private $base_content_path = ''; private string $base_content_path = '';
// errors // errors
/** @var bool if load of mo file was unsuccessful */ /** @var bool if load of mo file was unsuccessful */
private $load_failure = false; private bool $load_failure = false;
// object holders // object holders
/** @var FileReader|bool reader class for file reading, false for short circuit */ /** @var FileReader|bool reader class for file reading, false for short circuit */
private $input = false; private FileReader|bool $input = false;
/** @var GetTextReader reader class for MO data */ /** @var GetTextReader reader class for MO data */
private $l10n; private GetTextReader|null $l10n = null;
/** /**
* @static * @static
* @var L10n self class * @var L10n self class
*/ */
private static $instance; private static L10n $instance;
/** /**
* class constructor call for language getstring * class constructor call for language getstring
@@ -124,7 +124,6 @@ class L10n
*/ */
public static function getInstance(): L10n public static function getInstance(): L10n
{ {
/** @phpstan-ignore-next-line */
if (empty(self::$instance)) { if (empty(self::$instance)) {
self::$instance = new self(); self::$instance = new self();
} }
@@ -253,6 +252,13 @@ class L10n
// dummy // dummy
$this->l10n = new GetTextReader($this->input); $this->l10n = new GetTextReader($this->input);
} }
// if this is still null here, we abort
if ($this->l10n === null) {
throw new \RuntimeException(
"Could not create CoreLibs\Language\Core\GetTextReader object",
E_USER_ERROR
);
}
return $this->l10n; return $this->l10n;
} }
@@ -673,6 +679,7 @@ class L10n
// fallback passthrough // fallback passthrough
if ($this->l10n === null) { if ($this->l10n === null) {
echo $text; echo $text;
return;
} }
echo $this->l10n->translate($text); echo $this->l10n->translate($text);
} }

View File

@@ -0,0 +1,239 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/9/7
* DESCRIPTION:
* General error collection class for output to frontend or to log
*/
declare(strict_types=1);
namespace CoreLibs\Logging;
use CoreLibs\Logging\Logger\MessageLevel;
class ErrorMessage
{
/** @var array<int,array{id:string,level:string,str:string,target:string,target_style:string,highlight:string[]}> */
private array $error_str = [];
/** @var \CoreLibs\Logging\Logging $log */
public \CoreLibs\Logging\Logging $log;
/** @var bool $log_error global flag to log error level message */
private bool $log_error = false;
/**
* init ErrorMessage
*
* @param \CoreLibs\Logging\Logging $log
* @param bool $log_error [=false]
*/
public function __construct(
\CoreLibs\Logging\Logging $log,
bool $log_error = false
) {
$this->log = $log;
$this->log_error = $log_error;
}
/**
* pushes new error message into the error_str array
* error_id: internal Error ID (should be unique)
* level: error level, can only be ok, info, warn, error, abort, crash
* ok and info are positive response: success
* warn: success, but there might be some things that are not 100% ok
* error: input error or error in executing request
* abort: an internal error happened as mandatory information that normally is
* there is missing, or the ACL level that should normally match does not
* will be logged to "critical"
* crash: system failure or critical system problems (db connection failure)
* will be logged as "alert"
* not set: unkown, will be logged as "emergency"
* target/highlight: id target name for frontend where to attach this message
* highlight is a list of other target points to highlight
* for highlight targets css names are $level without a prefix and should be
* nested in the target element "input .error { ... }"
* target_style: if not set uses 'error-' $level as css style. applies to targets or main only
*
* @param string $error_id Any internal error ID for this error
* @param string $level Error level in ok/info/warn/error
* @param string $str Error message (out)
* @param string $target alternate attachment point for this error message
* @param string $target_style Alternate color style for the error message
* @param array<string> $highlight Any additional error data as error OR
* highlight points for field highlights
* @param string|null $message If abort/crash, non localized $str
* @param array<mixed> $context Additionl info for abort/crash messages
* @param bool|null $log_error [=null] log level 'error' to error, if null use global,
* else set for this call only
*/
public function setErrorMsg(
string $error_id,
string $level,
string $str,
string $target = '',
string $target_style = '',
array $highlight = [],
?string $message = null,
array $context = [],
?bool $log_error = null,
): void {
if ($log_error === null) {
$log_error = $this->log_error;
}
$original_level = $level;
$level = MessageLevel::fromName($level)->name;
// if not string set, write message string if set, else level/error id
if (empty($str)) {
$str = $message ?? 'L:' . $level . '|E:' . $error_id;
}
$this->error_str[] = [
'id' => $error_id,
'level' => $level,
'str' => $str,
'target' => $target,
'target_style' => $target_style,
'highlight' => $highlight,
];
// write to log for abort/crash
switch ($level) {
case 'error':
if ($log_error) {
$this->log->error($message ?? $str, array_merge([
'id' => $error_id,
'level' => $original_level,
], $context));
}
break;
case 'abort':
$this->log->critical($message ?? $str, array_merge([
'id' => $error_id,
'level' => $original_level,
], $context));
break;
case 'crash':
$this->log->alert($message ?? $str, array_merge([
'id' => $error_id,
'level' => $original_level,
], $context));
break;
case 'unknown':
$this->log->emergency($message ?? $str, array_merge([
'id' => $error_id,
'level' => $original_level,
], $context));
break;
}
}
/**
* pushes new error message into the error_str array
* Note, the parameter order is different and does not need an error id
* This is for backend alerts
*
* @param string $level error level (ok/warn/info/error)
* @param string $str error string
* @param string|null $error_id optional error id for precise error lookup
* @param string $target Alternate id name for output target on frontend
* @param string $target_style Alternate color style for the error message
* @param array<string> $highlight Any additional error data as error OR
* highlight points for field highlights
* @param string|null $message If abort/crash, non localized $str
* @param array<mixed> $context Additionl info for abort/crash messages
* @param bool|null $log_error [=null] log level 'error' to error, if null use global,
* else set for this call only
*/
public function setMessage(
string $level,
string $str,
?string $error_id = null,
string $target = '',
string $target_style = '',
array $highlight = [],
?string $message = null,
array $context = [],
?bool $log_error = null,
): void {
$this->setErrorMsg(
$error_id ?? '',
$level,
$str,
$target,
$target_style,
$highlight,
$message,
$context,
$log_error
);
}
// *********************************************************************
// GETTERS
// *********************************************************************
/**
* Returns the current set error content from setErrorMsg method
*
* @return array<int,array{id:string,level:string,str:string,target:string,highlight:string[]}> Error messages array
*/
public function getErrorMsg(): array
{
return $this->error_str;
}
/**
* Current set error ids
*
* @return array<string>
*/
public function getErrorIds(): array
{
return array_column($this->error_str, 'id');
}
/**
* Gets the LAST entry in the array list.
* If nothing found returns empty array set
*
* @return array{id:string,level:string,str:string,target:string,target:string,highlight:string[]} Error block
*/
public function getLastErrorMsg(): array
{
return $this->error_str[array_key_last($this->error_str)] ?? [
'level' => '',
'str' => '',
'id' => '',
'target' => '',
'target_string' => '',
'highlight' => [],
];
}
// *********************************************************************
// FLAG SETTERS
// *********************************************************************
/**
* Set the log error flag
*
* @param bool $flag True to log level error too, False for do not (Default)
* @return void
*/
public function setFlagLogError(bool $flag): void
{
$this->log_error = $flag;
}
/**
* Get the current log error flag
*
* @return bool
*/
public function getFlagLogError(): bool
{
return $this->log_error;
}
}
// __END__

107
src/Logging/Logger/Flag.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/5/29
* DESCRIPTION:
* Logging options flags for output file name building
*
* per_run: and timestamp + uid will be added
* per_date: ymd will be added (per_run > per_date, cannot be used at the same time)
* per_group: for debug level, group per group id (old level)
* per_page: per file name logging
* per_class: log per class
* per_level: per logging level file split
*/
declare(strict_types=1);
namespace CoreLibs\Logging\Logger;
enum Flag: int
{
/** all off flag */
case all_off = 0;
/** write per run */
case per_run = 1;
/** write per date */
case per_date = 2;
/** was PER_LEVEL, write per group id (debug) */
case per_group = 4;
/** write per page (filename) */
case per_page = 8;
/** write per class */
case per_class = 16;
/** write per log level name */
case per_level = 32;
/**
* get internal name from string value
*
* @param non-empty-string $name
* @return self
*/
public static function fromName(string $name): self
{
return match ($name) {
'Run', 'run', 'per_run', 'PER_RUN' => self::per_run,
'Date', 'date', 'per_date', 'PER_DATE' => self::per_date,
'Group', 'group', 'per_group', 'PER_GROUP' => self::per_group,
'Page', 'page', 'per_page', 'PER_PAGE' => self::per_page,
'Class', 'class', 'per_class', 'PER_CLASS' => self::per_class,
'Level', 'level', 'per_level', 'PER_LEVEL' => self::per_level,
default => self::all_off,
};
}
/**
* Get internal name from int value
*
* @param int $value
* @return self
*/
public static function fromValue(int $value): self
{
return self::from($value);
}
/**
* convert current set level to name (upper case)
*
* @return string
*/
public function getName(): string
{
return strtoupper($this->name);
}
/** @var int[] */
public const VALUES = [
0,
1,
2,
4,
8,
16,
32,
];
/** @var string[] */
public const NAMES = [
'ALL_OFF',
'PER_RUN',
'PER_DATE',
'PER_GROUP',
'PER_PAGE',
'PER_CLASS',
'PER_LEVEL',
];
}
// __END__

View File

@@ -0,0 +1,231 @@
<?php // phpcs:disable Generic.Files.LineLength
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023-05-25
* DESCRIPTION:
* Debug levels
*
* They are based on the Mono log ones
* FC 5424 {@see https://datatracker.ietf.org/doc/html/rfc5424}
*/
declare(strict_types=1);
namespace CoreLibs\Logging\Logger;
use Psr\Log\LogLevel;
/**
* Represents the log levels
*
* Monolog supports the logging levels described by RFC 5424 {@see https://datatracker.ietf.org/doc/html/rfc5424}
* but due to BC the severity values used internally are not 0-7.
*
* To get the level name/value out of a Level there are several options:
*
* - Use ->getName() to get the standard Monolog name which is full uppercased (e.g. "DEBUG")
* - Use ->toPsrLogLevel() to get the standard PSR-3 name which is full lowercased (e.g. "debug")
* - Use ->toRFC5424Level() to get the standard RFC 5424 value (e.g. 7 for debug, 0 for emergency)
* - Use ->name to get the enum case's name which is capitalized (e.g. "Debug")
*
* To get the internal value for filtering, if the includes/isLowerThan/isHigherThan methods are
* not enough, you can use ->value to get the enum case's integer value.
*/
enum Level: int
{
/**
* Detailed debug information
*/
case Debug = 100;
/**
* Interesting events
*
* Examples: User logs in, SQL logs.
*/
case Info = 200;
/**
* Uncommon events
*/
case Notice = 250;
/**
* Exceptional occurrences that are not errors
*
* Examples: Use of deprecated APIs, poor use of an API,
* undesirable things that are not necessarily wrong.
*/
case Warning = 300;
/**
* Runtime errors
*/
case Error = 400;
/**
* Critical conditions
*
* Example: Application component unavailable, unexpected exception.
*/
case Critical = 500;
/**
* Action must be taken immediately
*
* Example: Entire website down, database unavailable, etc.
* This should trigger the SMS alerts and wake you up.
*/
case Alert = 550;
/**
* Urgent alert.
*/
case Emergency = 600;
/**
* @param value-of<self::NAMES>|LogLevel::*|'Debug'|'Info'|'Notice'|'Warning'|'Error'|'Critical'|'Alert'|'Emergency' $name
* @return static
*/
public static function fromName(string $name): self
{
return match ($name) {
'debug', 'Debug', 'DEBUG' => self::Debug,
'info', 'Info', 'INFO' => self::Info,
'notice', 'Notice', 'NOTICE' => self::Notice,
'warning', 'Warning', 'WARNING' => self::Warning,
'error', 'Error', 'ERROR' => self::Error,
'critical', 'Critical', 'CRITICAL' => self::Critical,
'alert', 'Alert', 'ALERT' => self::Alert,
'emergency', 'Emergency', 'EMERGENCY' => self::Emergency,
};
}
/**
* @param value-of<self::VALUES> $value
* @return static
*/
public static function fromValue(int $value): self
{
return self::from($value);
}
/**
* Returns true if the passed $level is higher or equal to $this
*
* @param Level $level
* @return bool
*/
public function includes(Level $level): bool
{
return $this->value <= $level->value;
}
/**
* If level is higher than set one
*
* @param Level $level
* @return bool
*/
public function isHigherThan(Level $level): bool
{
return $this->value > $level->value;
}
/**
* if level is lower than set one
*
* @param Level $level
* @return bool
*/
public function isLowerThan(Level $level): bool
{
return $this->value < $level->value;
}
/**
* Returns the monolog standardized all-capitals name of the level
*
* Use this instead of $level->name which returns the enum case name (e.g. Debug vs DEBUG if you use getName())
*
* @phan-suppress-next-line PhanTypeMismatchDeclaredReturn
* @return value-of<self::NAMES>
*/
public function getName(): string
{
return match ($this) {
self::Debug => 'DEBUG',
self::Info => 'INFO',
self::Notice => 'NOTICE',
self::Warning => 'WARNING',
self::Error => 'ERROR',
self::Critical => 'CRITICAL',
self::Alert => 'ALERT',
self::Emergency => 'EMERGENCY',
};
}
/**
* Returns the PSR-3 level matching this instance
*
* @phpstan-return \Psr\Log\LogLevel::*
*/
public function toPsrLogLevel(): string
{
return match ($this) {
self::Debug => LogLevel::DEBUG,
self::Info => LogLevel::INFO,
self::Notice => LogLevel::NOTICE,
self::Warning => LogLevel::WARNING,
self::Error => LogLevel::ERROR,
self::Critical => LogLevel::CRITICAL,
self::Alert => LogLevel::ALERT,
self::Emergency => LogLevel::EMERGENCY,
};
}
/**
* Returns the RFC 5424 level matching this instance
*
* @phpstan-return int<0, 7>
*/
public function toRFC5424Level(): int
{
return match ($this) {
self::Debug => 7,
self::Info => 6,
self::Notice => 5,
self::Warning => 4,
self::Error => 3,
self::Critical => 2,
self::Alert => 1,
self::Emergency => 0,
};
}
public const VALUES = [
100,
200,
250,
300,
400,
500,
550,
600,
];
public const NAMES = [
'DEBUG',
'INFO',
'NOTICE',
'WARNING',
'ERROR',
'CRITICAL',
'ALERT',
'EMERGENCY',
];
}
// __END__

View File

@@ -0,0 +1,84 @@
<?php // phpcs:disable Generic.Files.LineLength
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023-09-08
* DESCRIPTION:
* Error message return levels
*/
declare(strict_types=1);
namespace CoreLibs\Logging\Logger;
enum MessageLevel: int
{
case ok = 100;
case info = 200;
case warn = 300;
case error = 400;
case abort = 500;
case crash = 550;
case unknown = 600;
/**
* @param string $name any string name, if not matching use unkown
* @return static
*/
public static function fromName(string $name): self
{
return match (strtolower($name)) {
'ok' => self::ok,
'info' => self::info,
'warn', 'warning' => self::warn,
'error' => self::error,
'abort' => self::abort,
'crash' => self::crash,
default => self::unknown,
};
}
/**
* @param int $value
* @return static
*/
public static function fromValue(int $value): self
{
return self::tryFrom($value) ?? self::unknown;
}
/**
* Returns true if the passed $level is higher or equal to $this
*
* @param MessageLevel $level
* @return bool
*/
public function includes(MessageLevel $level): bool
{
return $this->value <= $level->value;
}
/**
* If level is higher than set one
*
* @param MessageLevel $level
* @return bool
*/
public function isHigherThan(MessageLevel $level): bool
{
return $this->value > $level->value;
}
/**
* if level is lower than set one
*
* @param MessageLevel $level
* @return bool
*/
public function isLowerThan(MessageLevel $level): bool
{
return $this->value < $level->value;
}
}
// __END__

1395
src/Logging/Logging.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -219,96 +219,99 @@ declare(strict_types=1);
namespace CoreLibs\Output\Form; namespace CoreLibs\Output\Form;
use CoreLibs\Get\System; use CoreLibs\Get\System;
use CoreLibs\Debug\Support;
class Generate extends \CoreLibs\DB\Extended\ArrayIO class Generate extends \CoreLibs\DB\Extended\ArrayIO
{ {
// for the load statetment describes which elements from // for the load statetment describes which elements from
// the load query should be shown and i which format // the load query should be shown and i which format
/** @var array<mixed> */ /** @var array<mixed> */
public $field_array = []; public array $field_array = [];
/** @var string */ /** @var string */
public $load_query; // the query needed for loading a data set (one row in the table) public string $load_query; // the query needed for loading a data set (one row in the table)
/** @var string */ /** @var string */
public $col_name; // the name of the columen (before _<type>) [used for order button] public string $col_name; // the name of the columen (before _<type>) [used for order button]
/** @var int */ /** @var int */
public $yes; // the yes flag that triggers the template to show ALL and not only new/load public int $yes = 0; // the yes flag that triggers the template to show ALL and not only new/load
/** @var string */ /** @var string */
public $msg; // the error msg public string $msg = ''; // the error msg
/** @var int */ /** @var int */
public $error; // the error flag set for printing red error msg public int $error = 0; // the error flag set for printing red error msg
/** @var int */ /** @var int */
public $warning; // warning flag, for information (saved, loaded, etc) public int $warning = 0; // warning flag, for information (saved, loaded, etc)
/** @var string */ /** @var string */
public $archive_pk_name; // the pk name for the load select form public string $archive_pk_name; // the pk name for the load select form
/** @var string */ /** @var string */
private $int_pk_name; // primary key, only internal usage private string $int_pk_name; // primary key, only internal usage
/** @var array<mixed> */ /** @var array<mixed> */
public $reference_array = []; // reference arrays -> stored in $this->reference_array[$table_name] => []; public array $reference_array = []; // reference arrays -> stored in $this->reference_array[$table_name] => [];
// NOTE: should be changed to this @var mixed[] // NOTE: should be changed to this @var mixed[]
/** @var array<mixed> */ /** @var array<mixed> */
public $element_list; // element list for elements next to each other as a special sub group public array $element_list; // element list for elements next to each other as a special sub group
/** @var array<mixed> */ /** @var array<mixed> */
public $table_array = []; public array $table_array = [];
/** @var string */ /** @var string */
public $my_page_name; // the name of the page without .php extension public string $my_page_name; // the name of the page without .php extension
/** @var bool */ /** @var bool */
public $mobile_phone = false; public bool $mobile_phone = false;
/** @var string */ /** @var string */
public $email_regex; public string $email_regex;
// buttons and checkboxes // buttons and checkboxes
/** @var string */ /** @var string */
public $archive; public string $archive;
/** @var string */ /** @var string */
public $new; public string $new;
/** @var string */ /** @var string */
public $really_new; public string $really_new;
/** @var string */ /** @var string */
public $delete; public string $delete;
/** @var string */ /** @var string */
public $really_delete; public string $really_delete;
/** @var string */ /** @var string */
public $save; public string $save;
/** @var string */ /** @var string */
public $remove_button; public string $remove_button;
// security values // security values
/** @var int base acl for current page */ /** @var int base acl for current page */
private $base_acl_level = 0; private int $base_acl_level = 0;
/** @var int admin master flag (1/0) */ /** @var int admin master flag (1/0) */
private $acl_admin = 0; private int $acl_admin = 0;
/** @var array<mixed> */ /** @var array<mixed> */
public $security_level; public array $security_level;
/** @var array<string,mixed> Login ACL */ /** @var array<string,mixed> Login ACL */
public $login_acl = []; public array $login_acl = [];
// layout publics // layout publics
/** @var int */ /** @var int */
public $table_width; public int $table_width = 0;
// internal lang & encoding vars // internal lang & encoding vars
/** @var string */ /** @var string */
public $lang_dir = ''; public string $lang_dir = '';
/** @var string */ /** @var string */
public $lang; public string $lang;
/** @var string */ /** @var string */
public $lang_short; public string $lang_short;
/** @var string */ /** @var string */
public $domain; public string $domain;
/** @var string */ /** @var string */
public $encoding; public string $encoding;
// language // language
/** @var \CoreLibs\Language\L10n */ /** @var \CoreLibs\Language\L10n */
public $l; public \CoreLibs\Language\L10n $l;
// log // log
/** @var \CoreLibs\Debug\Logging */ /** @var \CoreLibs\Logging\Logging */
public $log; public \CoreLibs\Logging\Logging $log;
/** @var \CoreLibs\DB\Extended\ArrayIO */
public \CoreLibs\DB\Extended\ArrayIO $dba;
// now some default error msgs (english) // now some default error msgs (english)
/** @var array<mixed> */ /** @var array<mixed> */
public $language_array = []; public array $language_array = [];
/** /**
* construct form generator * construct form generator
* *
* @param array<mixed> $db_config db config array, mandatory * phpcs:ignore
* @param \CoreLibs\Debug\Logging $log Logging class * @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class
* @param \CoreLibs\Language\L10n $l10n l10n language class * @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,mixed> $login_acl Login ACL array, * @param array<string,mixed> $login_acl Login ACL array,
* at least base/admin should be set * at least base/admin should be set
@@ -319,7 +322,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
*/ */
public function __construct( public function __construct(
array $db_config, array $db_config,
\CoreLibs\Debug\Logging $log, \CoreLibs\Logging\Logging $log,
\CoreLibs\Language\L10n $l10n, \CoreLibs\Language\L10n $l10n,
array $login_acl, array $login_acl,
?array $table_arrays = null, ?array $table_arrays = null,
@@ -327,7 +330,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
// init logger if not set // init logger if not set
$this->log = $log; $this->log = $log;
// don't log per class // don't log per class
$this->log->setLogPer('class', false); $this->log->unsetLogFlag(\CoreLibs\Logging\Logger\Flag::per_class);
// init the language class // init the language class
$this->l = $l10n; $this->l = $l10n;
// parse and read, legacy stuff // parse and read, legacy stuff
@@ -380,6 +383,15 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
$this->base_acl_level, $this->base_acl_level,
$this->acl_admin $this->acl_admin
); );
$this->dba = new \CoreLibs\DB\Extended\ArrayIO(
$db_config,
$config_array['table_array'],
$config_array['table_name'],
$this->log,
// set the ACL
$this->base_acl_level,
$this->acl_admin
);
// here should be a check if the config_array is correct ... // here should be a check if the config_array is correct ...
if (isset($config_array['show_fields']) && is_array($config_array['show_fields'])) { if (isset($config_array['show_fields']) && is_array($config_array['show_fields'])) {
$this->field_array = $config_array['show_fields']; $this->field_array = $config_array['show_fields'];
@@ -388,7 +400,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
$this->load_query = $config_array['load_query']; $this->load_query = $config_array['load_query'];
} }
if (empty($this->load_query)) { if (empty($this->load_query)) {
$this->log->debug('INIT ERROR', 'Missing Load Query for: ' . $this->my_page_name); $this->log->error('Missing Load Query for: ' . $this->my_page_name);
} }
$this->archive_pk_name = 'a_' . $this->pk_name; $this->archive_pk_name = 'a_' . $this->pk_name;
$this->col_name = str_replace('_id', '', $this->pk_name); $this->col_name = str_replace('_id', '', $this->pk_name);
@@ -485,7 +497,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
/** @var TableArrays\Interface\TableArraysInterface|false $class */ /** @var TableArrays\Interface\TableArraysInterface|false $class */
$class = new $class_string($this); $class = new $class_string($this);
} catch (\Throwable $t) { } catch (\Throwable $t) {
$this->log->debug('CLASS LOAD', 'Failed loading: ' . $class_string . ' => ' . $t->getMessage()); $this->log->critical('CLASS LOADING: Failed loading: ' . $class_string . ' => ' . $t->getMessage());
return false; return false;
} }
if (is_object($class)) { if (is_object($class)) {
@@ -827,7 +839,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
return $return_array; return $return_array;
} }
if (empty($this->load_query)) { if (empty($this->load_query)) {
$this->log->debug('LOAD LIST ERROR', 'Missing load list query'); $this->log->error('Missing load list query');
return $return_array; return $return_array;
} }
@@ -1954,7 +1966,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
if ($this->table_array[$key]['value']) { if ($this->table_array[$key]['value']) {
// use the better new passwordSet instead of crypt based // use the better new passwordSet instead of crypt based
$this->table_array[$key]['value'] = $this->table_array[$key]['value'] =
\CoreLibs\Check\Password::passwordSet($this->table_array[$key]['value']); \CoreLibs\Security\Password::passwordSet($this->table_array[$key]['value']);
$this->table_array[$key]['HIDDEN_value'] = $this->table_array[$key]['value']; $this->table_array[$key]['HIDDEN_value'] = $this->table_array[$key]['value'];
} else { } else {
// $this->table_array[$key]['HIDDEN_value'] = // $this->table_array[$key]['HIDDEN_value'] =
@@ -2617,8 +2629,8 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
} }
} }
// add lost error ones // add lost error ones
$this->log->debug('ERROR', 'P: ' . $data['prefix'] . ', ' $this->log->error('P: ' . $data['prefix'] . ', '
. $this->log->prAr($_POST['ERROR'][$data['prefix']] ?? [])); . Support::prAr($_POST['ERROR'][$data['prefix']] ?? []));
if ($this->error && !empty($_POST['ERROR'][$data['prefix']])) { if ($this->error && !empty($_POST['ERROR'][$data['prefix']])) {
$prfx = $data['prefix']; // short $prfx = $data['prefix']; // short
$_post_data = []; $_post_data = [];

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditAccess implements Interface\TableArraysInterface class EditAccess implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditGroups implements Interface\TableArraysInterface class EditGroups implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditLanguages implements Interface\TableArraysInterface class EditLanguages implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditMenuGroup implements Interface\TableArraysInterface class EditMenuGroup implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditOrder implements Interface\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private \CoreLibs\Output\Form\Generate $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* NOTE: this is a dummy array to just init the Form\Generate class and is not used for anything else
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'-'
],
'table_name' => '-',
'load_query' => '',
'show_fields' => [],
];
}
}
// __END__

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditPages implements Interface\TableArraysInterface class EditPages implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditSchemas implements Interface\TableArraysInterface class EditSchemas implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditUsers implements Interface\TableArraysInterface class EditUsers implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -7,7 +7,7 @@ namespace CoreLibs\Output\Form\TableArrays;
class EditVisibleGroup implements Interface\TableArraysInterface class EditVisibleGroup implements Interface\TableArraysInterface
{ {
/** @var \CoreLibs\Output\Form\Generate */ /** @var \CoreLibs\Output\Form\Generate */
private $form; private \CoreLibs\Output\Form\Generate $form;
/** /**
* constructor * constructor

View File

@@ -23,7 +23,8 @@ class Image
* if empty ROOT is choosen * if empty ROOT is choosen
* @param string $cache_source cache path, if not given TMP is used * @param string $cache_source cache path, if not given TMP is used
* @param bool $clear_cache if set to true, will create thumb all the tame * @param bool $clear_cache if set to true, will create thumb all the tame
* @return string|false thumbnail name, or false for error * @return string thumbnail name
* @throws \RuntimeException no ImageMagick convert command found
*/ */
public static function createThumbnail( public static function createThumbnail(
string $pic, string $pic,
@@ -33,14 +34,15 @@ class Image
string $path = '', string $path = '',
string $cache_source = '', string $cache_source = '',
bool $clear_cache = false bool $clear_cache = false
): string|false { ): string {
// get image type flags // get image type flags
$image_types = [ $image_types = [
0 => 'UNKOWN-IMAGE',
1 => 'gif', 1 => 'gif',
2 => 'jpg', 2 => 'jpg',
3 => 'png' 3 => 'png'
]; ];
$return_data = false; $return_data = '';
$CONVERT = ''; $CONVERT = '';
// if CONVERT is not defined, abort // if CONVERT is not defined, abort
/** @phan-suppress-next-line PhanUndeclaredConstant */ /** @phan-suppress-next-line PhanUndeclaredConstant */
@@ -48,7 +50,7 @@ class Image
/** @phan-suppress-next-line PhanUndeclaredConstant */ /** @phan-suppress-next-line PhanUndeclaredConstant */
$CONVERT = CONVERT; $CONVERT = CONVERT;
} else { } else {
return $return_data; throw new \RuntimeException('CONVERT set binary is not executable or CONVERT is not defined');
} }
if (!empty($cache_source)) { if (!empty($cache_source)) {
$tmp_src = $cache_source; $tmp_src = $cache_source;
@@ -68,72 +70,7 @@ class Image
$pic = $tmp[(count($tmp) - 1)]; $pic = $tmp[(count($tmp) - 1)];
} }
// does this picture exist and is it a picture // does this picture exist and is it a picture
if (file_exists($filename) && is_file($filename)) { if (!file_exists($filename) || !is_file($filename)) {
[$width, $height, $type] = getimagesize($filename) ?: [];
$convert_prefix = '';
$create_file = false;
$delete_filename = '';
// check if we can skip the PDF creation: if we have size, if do not have type, we assume type png
if (!$type) {
$check_thumb = $tmp_src . 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[3];
if (!is_file($check_thumb)) {
$create_file = true;
} else {
$type = 3;
}
}
// if type is not in the list, but returns as PDF, we need to convert to JPEG before
if (!$type) {
$output = [];
$return = null;
// is this a PDF, if no, return from here with nothing
$convert_prefix = 'png:';
# TEMP convert to PNG, we then override the file name
$convert_string = $CONVERT . ' ' . $filename . ' ' . $convert_prefix . $filename . '_TEMP';
$status = exec($convert_string, $output, $return);
$filename .= '_TEMP';
// for delete, in case we need to glob
$delete_filename = $filename;
// find file, if we can't find base name, use -0 as the first one (ignore other pages in multiple ones)
if (!is_file($filename)) {
$filename .= '-0';
}
[$width, $height, $type] = getimagesize($filename) ?: [];
}
// if no size given, set size to original
if (!$size_x || $size_x < 1) {
$size_x = $width;
}
if (!$size_y || $size_y < 1) {
$size_y = $height;
}
$thumb = 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[$type];
$thumbnail = $tmp_src . $thumb;
// check if we already have this picture converted
if (!is_file($thumbnail) || $clear_cache == true) {
// convert the picture
if ($width > $size_x) {
$convert_string = $CONVERT . ' -geometry ' . $size_x . 'x ' . $filename . ' ' . $thumbnail;
$status = exec($convert_string, $output, $return);
// get the size of the converted data, if converted
if (is_file($thumbnail)) {
[$width, $height, $type] = getimagesize($thumbnail) ?: [];
}
}
if ($height > $size_y) {
$convert_string = $CONVERT . ' -geometry x' . $size_y . ' ' . $filename . ' ' . $thumbnail;
$status = exec($convert_string, $output, $return);
}
}
if (!is_file($thumbnail)) {
copy($filename, $thumbnail);
}
$return_data = $thumb;
// if we have a delete filename, delete here with glob
if ($delete_filename) {
array_map('unlink', glob($delete_filename . '*') ?: []);
}
} else {
if (!empty($dummy) && strstr($dummy, '/') === false) { if (!empty($dummy) && strstr($dummy, '/') === false) {
// check if we have the "dummy" image flag set // check if we have the "dummy" image flag set
$filename = PICTURES . ICONS . strtoupper($dummy) . ".png"; $filename = PICTURES . ICONS . strtoupper($dummy) . ".png";
@@ -141,11 +78,77 @@ class Image
if (!empty($dummy) && file_exists($filename) && is_file($filename)) { if (!empty($dummy) && file_exists($filename) && is_file($filename)) {
$return_data = $filename; $return_data = $filename;
} else { } else {
$return_data = false; throw new \Exception('Could not set dummy return file: ' . $dummy . ' in ' . $filename);
} }
} else { } else {
$filename = $dummy; $return_data = $dummy;
} }
return $return_data;
}
// resize image
[$width, $height, $type] = getimagesize($filename) ?: [0, 0, 0];
$convert_prefix = '';
$create_file = false;
$delete_filename = '';
// check if we can skip the PDF creation: if we have size, if do not have type, we assume type png
if (!$type) {
$check_thumb = $tmp_src . 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[3];
if (!is_file($check_thumb)) {
$create_file = true;
} else {
$type = 3;
}
}
// if type is not in the list, but returns as PDF, we need to convert to JPEG before
if (!$type) {
$output = [];
$return = null;
// is this a PDF, if no, return from here with nothing
$convert_prefix = 'png:';
# TEMP convert to PNG, we then override the file name
$convert_string = $CONVERT . ' ' . $filename . ' ' . $convert_prefix . $filename . '_TEMP';
$status = exec($convert_string, $output, $return);
$filename .= '_TEMP';
// for delete, in case we need to glob
$delete_filename = $filename;
// find file, if we can't find base name, use -0 as the first one (ignore other pages in multiple ones)
if (!is_file($filename)) {
$filename .= '-0';
}
[$width, $height, $type] = getimagesize($filename) ?: [0, 0, 0];
}
// if no size given, set size to original
if (!$size_x || $size_x < 1) {
$size_x = $width;
}
if (!$size_y || $size_y < 1) {
$size_y = $height;
}
$thumb = 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[$type];
$thumbnail = $tmp_src . $thumb;
// check if we already have this picture converted
if (!is_file($thumbnail) || $clear_cache == true) {
// convert the picture
if ($width > $size_x) {
$convert_string = $CONVERT . ' -geometry ' . $size_x . 'x ' . $filename . ' ' . $thumbnail;
$status = exec($convert_string, $output, $return);
// get the size of the converted data, if converted
if (is_file($thumbnail)) {
[$width, $height, $type] = getimagesize($thumbnail) ?: [0, 0, 0];
}
}
if ($height > $size_y) {
$convert_string = $CONVERT . ' -geometry x' . $size_y . ' ' . $filename . ' ' . $thumbnail;
$status = exec($convert_string, $output, $return);
}
}
if (!is_file($thumbnail)) {
copy($filename, $thumbnail);
}
$return_data = $thumb;
// if we have a delete filename, delete here with glob
if ($delete_filename) {
array_map('unlink', glob($delete_filename . '*') ?: []);
} }
return $return_data; return $return_data;
} }
@@ -172,7 +175,9 @@ class Image
* set to false to not use (default true) * set to false to not use (default true)
* to use quick but less nice version * to use quick but less nice version
* @param int $jpeg_quality default 80, set image quality for jpeg only * @param int $jpeg_quality default 80, set image quality for jpeg only
* @return string|false thumbnail with path * @return string thumbnail with path
* @throws \UnexpectedValueException input values for filename or cache_folder are wrong
* @throws \RuntimeException convert (gd) failed
*/ */
public static function createThumbnailSimple( public static function createThumbnailSimple(
string $filename, string $filename,
@@ -184,8 +189,9 @@ class Image
bool $use_cache = true, bool $use_cache = true,
bool $high_quality = true, bool $high_quality = true,
int $jpeg_quality = 80 int $jpeg_quality = 80
): string|false { ): string {
$thumbnail = false; $thumbnail = false;
$exception_message = 'Could not create thumbnail';
// $this->debug('IMAGE PREPARE', "FILE: $filename (exists " // $this->debug('IMAGE PREPARE', "FILE: $filename (exists "
// .(string)file_exists($filename)."), WIDTH: $thumb_width, HEIGHT: $thumb_height"); // .(string)file_exists($filename)."), WIDTH: $thumb_width, HEIGHT: $thumb_height");
if ( if (
@@ -209,15 +215,20 @@ class Image
} }
// check that input image exists and is either jpeg or png // check that input image exists and is either jpeg or png
// also fail if the basic CACHE folder does not exist at all // also fail if the basic CACHE folder does not exist at all
if ( if (!file_exists($filename)) {
!file_exists($filename) || // return $thumbnail;
!is_dir($cache_folder) || throw new \UnexpectedValueException('Missing image file: ' . $filename);
!is_writable($cache_folder) }
) { if (!is_dir($cache_folder)) {
return $thumbnail; // return $thumbnail;
throw new \UnexpectedValueException('Cache folder is not a directory: ' . $cache_folder);
}
if (!is_writable($cache_folder)) {
// return $thumbnail;
throw new \UnexpectedValueException('Cache folder is not writeable: ' . $cache_folder);
} }
// $this->debug('IMAGE PREPARE', "FILENAME OK, THUMB WIDTH/HEIGHT OK"); // $this->debug('IMAGE PREPARE', "FILENAME OK, THUMB WIDTH/HEIGHT OK");
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: []; [$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null];
$thumbnail_write_path = null; $thumbnail_write_path = null;
$thumbnail_web_path = null; $thumbnail_web_path = null;
// path set first // path set first
@@ -279,12 +290,18 @@ class Image
// image, copy source image, offset in image, source x/y, new size, source image size // image, copy source image, offset in image, source x/y, new size, source image size
$thumb = imagecreatetruecolor($thumb_width_r, $thumb_height_r); $thumb = imagecreatetruecolor($thumb_width_r, $thumb_height_r);
if ($thumb === false) { if ($thumb === false) {
return false; throw new \RuntimeException(
'imagecreatetruecolor failed: ' . $thumbnail . ', ' . $filename,
1
);
} }
if ($img_type == IMAGETYPE_PNG) { if ($img_type == IMAGETYPE_PNG) {
$imagecolorallocatealpha = imagecolorallocatealpha($thumb, 0, 0, 0, 127); $imagecolorallocatealpha = imagecolorallocatealpha($thumb, 0, 0, 0, 127);
if ($imagecolorallocatealpha === false) { if ($imagecolorallocatealpha === false) {
return false; throw new \RuntimeException(
'imagecolorallocatealpha failed: ' . $thumbnail . ', ' . $filename,
2
);
} }
// preservere transaprency // preservere transaprency
imagecolortransparent( imagecolortransparent(
@@ -346,7 +363,10 @@ class Image
imagedestroy($source); imagedestroy($source);
imagedestroy($thumb); imagedestroy($thumb);
} else { } else {
$thumbnail = false; throw new \RuntimeException(
'Invalid source image file. Only JPEG/PNG are allowed: ' . $filename,
3
);
} }
} }
} else { } else {
@@ -387,14 +407,20 @@ class Image
} }
$thumb = imagecreatetruecolor($thumb_width, $thumb_height); $thumb = imagecreatetruecolor($thumb_width, $thumb_height);
if ($thumb === false) { if ($thumb === false) {
return false; throw new \RuntimeException(
'imagecreatetruecolor dummy failed: ' . $thumbnail . ', ' . $filename,
3
);
} }
// add outside border px = 5% (rounded up) // add outside border px = 5% (rounded up)
// eg 50px -> 2.5px // eg 50px -> 2.5px
$gray = imagecolorallocate($thumb, 200, 200, 200); $gray = imagecolorallocate($thumb, 200, 200, 200);
$white = imagecolorallocate($thumb, 255, 255, 255); $white = imagecolorallocate($thumb, 255, 255, 255);
if ($gray === false || $white === false) { if ($gray === false || $white === false) {
return false; throw new \RuntimeException(
'imagecolorallocate/imagecolorallocate dummy failed: ' . $thumbnail . ', ' . $filename,
2
);
} }
// fill gray background // fill gray background
imagefill($thumb, 0, 0, $gray); imagefill($thumb, 0, 0, $gray);
@@ -429,7 +455,11 @@ class Image
// add web path // add web path
$thumbnail = $thumbnail_web_path . $thumbnail; $thumbnail = $thumbnail_web_path . $thumbnail;
} }
// either return false or the thumbnail name + output path web // if still false -> throw exception
if ($thumbnail === false) {
throw new \RuntimeException($exception_message);
}
// else return the thumbnail name + output path web
return $thumbnail; return $thumbnail;
} }
@@ -447,7 +477,7 @@ class Image
if (!function_exists('exif_read_data') || !is_writeable($filename)) { if (!function_exists('exif_read_data') || !is_writeable($filename)) {
return; return;
} }
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: []; [$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [0, 0, null];
// add @ to avoid "file not supported error" // add @ to avoid "file not supported error"
$exif = @exif_read_data($filename); $exif = @exif_read_data($filename);
$orientation = null; $orientation = null;

View File

@@ -23,13 +23,13 @@ class ProgressBar
// private vars // private vars
/** @var string */ /** @var string */
public $code; // unique code public string $code; // unique code
/** @var string */ /** @var string */
public $status = 'new'; // current status (new,show,hide) public string $status = 'new'; // current status (new,show,hide)
/** @var float|int */ /** @var float|int */
public $step = 0; // current step public float|int $step = 0; // current step
/** @var array<string,null|int|float> */ /** @var array<string,null|int|float> */
public $position = [ // current bar position public array $position = [ // current bar position
'left' => null, 'left' => null,
'top' => null, 'top' => null,
'width' => null, 'width' => null,
@@ -37,43 +37,43 @@ class ProgressBar
]; ];
/** @var int */ /** @var int */
public $clear_buffer_size = 1; // we need to send this before the lfush to get browser output public int $clear_buffer_size = 1; // we need to send this before the lfush to get browser output
/** @var int */ /** @var int */
public $clear_buffer_size_init = 1024 * 1024; // if I don't send that junk, it won't send anything public int $clear_buffer_size_init = 1024 * 1024; // if I don't send that junk, it won't send anything
// public vars // public vars
/** @var int */ /** @var int */
public $min = 0; // minimal steps public int $min = 0; // minimal steps
/** @var int */ /** @var int */
public $max = 100; // maximal steps public int $max = 100; // maximal steps
/** @var int */ /** @var int */
public $left = 5; // bar position from left public int $left = 5; // bar position from left
/** @var int */ /** @var int */
public $top = 5; // bar position from top public int $top = 5; // bar position from top
/** @var int */ /** @var int */
public $width = 300; // bar width public int $width = 300; // bar width
/** @var int */ /** @var int */
public $height = 25; // bar height public int $height = 25; // bar height
/** @var int */ /** @var int */
public $pedding = 0; // bar pedding public int $pedding = 0; // bar pedding
/** @var string */ /** @var string */
public $color = '#0033ff'; // bar color public string $color = '#0033ff'; // bar color
/** @var string */ /** @var string */
public $bgr_color = '#c0c0c0'; // bar background color public string $bgr_color = '#c0c0c0'; // bar background color
/** @var string */ /** @var string */
public $bgr_color_master = '#ffffff'; // master div background color public string $bgr_color_master = '#ffffff'; // master div background color
/** @var int */ /** @var int */
public $border = 1; // bar border width public int $border = 1; // bar border width
/** @var string */ /** @var string */
public $brd_color = '#000000'; // bar border color public string $brd_color = '#000000'; // bar border color
/** @var string */ /** @var string */
public $direction = 'right'; // direction of motion (right,left,up,down) public string $direction = 'right'; // direction of motion (right,left,up,down)
/** @var array<string,string|bool|int> */ /** @var array<string,string|bool|int> */
public $frame = ['show' => false]; // ProgressBar Frame public array $frame = ['show' => false]; // ProgressBar Frame
/* 'show' => false, # frame show (true/false) /* 'show' => false, # frame show (true/false)
'left' => 200, # frame position from left 'left' => 200, # frame position from left
'top' => 100, # frame position from top 'top' => 100, # frame position from top
@@ -86,7 +86,7 @@ class ProgressBar
/** @#var array{string}{string: string|int} */ /** @#var array{string}{string: string|int} */
/** @var mixed[][] */ /** @var mixed[][] */
public $label = []; // ProgressBar Labels public array $label = []; // ProgressBar Labels
/* 'name' => [ # label name /* 'name' => [ # label name
'type' => 'text', # label type (text,button,step,percent,crossbar) 'type' => 'text', # label type (text,button,step,percent,crossbar)
'value' => 'Please wait ...', # label value 'value' => 'Please wait ...', # label value
@@ -105,7 +105,7 @@ class ProgressBar
/** @var string */ /** @var string */
// output strings // output strings
public $prefix_message = ''; public string $prefix_message = '';
/** /**
* progress bar constructor * progress bar constructor

View File

@@ -0,0 +1,61 @@
<?php
/**
* very simple symmetric encryption
* better use: https://paragonie.com/project/halite
*
* this is for creating secret keys for
* Security\SymmetricEncryption
*/
declare(strict_types=1);
namespace CoreLibs\Security;
class CreateKey
{
/**
* Create a random key that is a hex string
*
* @return string Hex string key for encrypting
*/
public static function generateRandomKey(): string
{
return self::bin2hex(self::randomKey());
}
/**
* create a random string as binary to encrypt data
* to store it in clear text in some .env file use bin2hex
*
* @return string Binary string for encryption
*/
public static function randomKey(): string
{
return random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
}
/**
* convert binary key to hex string
*
* @param string $hex_key Convert binary key string to hex
* @return string
*/
public static function bin2hex(string $hex_key): string
{
return sodium_bin2hex($hex_key);
}
/**
* convert hex string to binary key
*
* @param string $string_key Convery hex key string to binary
* @return string
*/
public static function hex2bin(string $string_key): string
{
return sodium_hex2bin($string_key);
}
}
// __END__

59
src/Security/Password.php Normal file
View File

@@ -0,0 +1,59 @@
<?php
/*
* core password set, check and rehash check wrapper functions
*/
declare(strict_types=1);
namespace CoreLibs\Security;
class Password
{
/**
* creates the password hash
*
* @param string $password password
* @return string hashed password
*/
public static function passwordSet(string $password): string
{
// always use the PHP default for the password
// password options ca be set in the password init,
// but should be kept as default
return password_hash($password, PASSWORD_DEFAULT);
}
/**
* checks if the entered password matches the hash
*
* @param string $password password
* @param string $hash password hash
* @return bool true or false
*/
public static function passwordVerify(string $password, string $hash): bool
{
if (password_verify($password, $hash)) {
return true;
} else {
return false;
}
}
/**
* checks if the password needs to be rehashed
*
* @param string $hash password hash
* @return bool true or false
*/
public static function passwordRehashCheck(string $hash): bool
{
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
return true;
} else {
return false;
}
}
}
// __END__

View File

@@ -0,0 +1,99 @@
<?php
/**
* very simple symmetric encryption
* Better use:
* https://paragonie.com/project/halite
* https://github.com/paragonie/halite
*
* current code is just to encrypt and decrypt
*
* must use a valid encryption key created with
* Secruty\CreateKey class
*/
declare(strict_types=1);
namespace CoreLibs\Security;
use CoreLibs\Security\CreateKey;
use SodiumException;
class SymmetricEncryption
{
/**
* Encrypt a message
*
* @param string $message Message to encrypt
* @param string $key Encryption key (as hex string)
* @return string
* @throws \Exception
* @throws \RangeException
*/
public static function encrypt(string $message, string $key): string
{
try {
$key = CreateKey::hex2bin($key);
} catch (SodiumException $e) {
throw new \UnexpectedValueException('Invalid hex key');
}
if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
throw new \RangeException(
'Key is not the correct size (must be '
. 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).'
);
}
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$cipher = base64_encode(
$nonce
. sodium_crypto_secretbox(
$message,
$nonce,
$key
)
);
sodium_memzero($message);
sodium_memzero($key);
return $cipher;
}
/**
* Decrypt a message
*
* @param string $encrypted Message encrypted with safeEncrypt()
* @param string $key Encryption key (as hex string)
* @return string
* @throws \Exception
*/
public static function decrypt(string $encrypted, string $key): string
{
try {
$key = CreateKey::hex2bin($key);
} catch (SodiumException $e) {
throw new \Exception('Invalid hex key');
}
$decoded = base64_decode($encrypted);
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plain = false;
try {
$plain = sodium_crypto_secretbox_open(
$ciphertext,
$nonce,
$key
);
} catch (SodiumException $e) {
throw new \UnexpectedValueException('Invalid ciphertext (too short)');
}
if (!is_string($plain)) {
throw new \UnexpectedValueException('Invalid Key');
}
sodium_memzero($ciphertext);
sodium_memzero($key);
return $plain;
}
}
// __END__

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,134 +24,132 @@ class SmartyExtend extends \Smarty
{ {
// internal translation engine // internal translation engine
/** @var \CoreLibs\Language\L10n */ /** @var \CoreLibs\Language\L10n */
public $l10n; public \CoreLibs\Language\L10n $l10n;
// lang & encoding // lang & encoding
/** @var string */ /** @var string */
public $lang_dir = ''; public string $lang_dir = '';
/** @var string */ /** @var string */
public $lang; public string $lang;
/** @var string */ /** @var string */
public $locale_set; public string $lang_short;
/** @var string */ /** @var string */
public $lang_short; public string $domain;
/** @var string */ /** @var string */
public $domain; public string $encoding;
/** @var string */
public $encoding;
// page name // page name
/** @var string */ /** @var string */
public $page_name; public string $page_name;
// array for data parsing // array for data parsing
/** @var array<mixed> */ /** @var array<mixed> */
public $HEADER = []; public array $HEADER = [];
/** @var array<mixed> */ /** @var array<mixed> */
public $DATA = []; public array $DATA = [];
/** @var array<mixed> */ /** @var array<mixed> */
public $DEBUG_DATA = []; public array $DEBUG_DATA = [];
/** @var array<mixed> */ /** @var array<mixed> */
private $CONTENT_DATA = []; private array $CONTENT_DATA = [];
// control vars // control vars
/** @var bool */ /** @var bool */
public $USE_PROTOTYPE = USE_PROTOTYPE; public bool $USE_PROTOTYPE = USE_PROTOTYPE;
/** @var bool */ /** @var bool */
public $USE_JQUERY = USE_JQUERY; public bool $USE_JQUERY = USE_JQUERY;
/** @var bool */ /** @var bool */
public $USE_SCRIPTACULOUS = USE_SCRIPTACULOUS; public bool $USE_SCRIPTACULOUS = USE_SCRIPTACULOUS;
// sub content input vars // sub content input vars
/** @var bool */ /** @var bool */
public $USE_TINY_MCE = false; public bool $USE_TINY_MCE = false;
/** @var bool */ /** @var bool */
public $JS_DATEPICKR = false; public bool $JS_DATEPICKR = false;
/** @var bool */ /** @var bool */
public $JS_FLATPICKR = false; public bool $JS_FLATPICKR = false;
/** @var bool */ /** @var bool */
public $JS_FILE_UPLOADER = false; public bool $JS_FILE_UPLOADER = false;
/** @var bool */ /** @var bool */
public $DEBUG_TMPL = false; public bool $DEBUG_TMPL = false;
/** @var bool */ /** @var bool */
public $USE_INCLUDE_TEMPLATE = false; public bool $USE_INCLUDE_TEMPLATE = false;
// cache & compile // cache & compile
/** @var string */ /** @var string */
public $CACHE_ID = ''; public string $CACHE_ID = '';
/** @var string */ /** @var string */
public $COMPILE_ID = ''; public string $COMPILE_ID = '';
// template vars // template vars
/** @var string */ /** @var string */
public $MASTER_TEMPLATE_NAME; public string $MASTER_TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $PAGE_FILE_NAME; public string $PAGE_FILE_NAME = '';
/** @var string */ /** @var string */
public $CONTENT_INCLUDE; public string $CONTENT_INCLUDE = '';
/** @var string */ /** @var string */
public $FORM_NAME; public string $FORM_NAME = '';
/** @var string */ /** @var string */
public $FORM_ACTION; public string $FORM_ACTION = '';
/** @var string */ /** @var string */
public $L_TITLE; public string $L_TITLE = '';
/** @var string|int */ /** @var string|int */
public $PAGE_WIDTH; public string|int $PAGE_WIDTH;
// smarty include/set var // smarty include/set var
/** @var string */ /** @var string */
public $TEMPLATE_PATH; public string $TEMPLATE_PATH = '';
/** @var string */ /** @var string */
public $TEMPLATE_NAME; public string $TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $INC_TEMPLATE_NAME; public string $INC_TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $JS_TEMPLATE_NAME; public string $JS_TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $CSS_TEMPLATE_NAME; public string $CSS_TEMPLATE_NAME = '';
/** @var string|null */ /** @var string|null */
public $TEMPLATE_TRANSLATE; public string|null $TEMPLATE_TRANSLATE;
/** @var string|null */ /** @var string|null */
public $JS_TRANSLATE; public string|null $JS_TRANSLATE;
// core group // core group
/** @var string */ /** @var string */
public $JS_CORE_TEMPLATE_NAME; public string $JS_CORE_TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $CSS_CORE_TEMPLATE_NAME; public string $CSS_CORE_TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $JS_CORE_INCLUDE; public string $JS_CORE_INCLUDE = '';
/** @var string */ /** @var string */
public $CSS_CORE_INCLUDE; public string $CSS_CORE_INCLUDE = '';
// local names // local names
/** @var string */ /** @var string */
public $JS_SPECIAL_TEMPLATE_NAME = ''; public string $JS_SPECIAL_TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $CSS_SPECIAL_TEMPLATE_NAME = ''; public string $CSS_SPECIAL_TEMPLATE_NAME = '';
/** @var string */ /** @var string */
public $JS_INCLUDE; public string $JS_INCLUDE = '';
/** @var string */ /** @var string */
public $CSS_INCLUDE; public string $CSS_INCLUDE = '';
/** @var string */ /** @var string */
public $JS_SPECIAL_INCLUDE; public string $JS_SPECIAL_INCLUDE = '';
/** @var string */ /** @var string */
public $CSS_SPECIAL_INCLUDE; public string $CSS_SPECIAL_INCLUDE = '';
/** @var string */ /** @var string */
public $ADMIN_JAVASCRIPT; public string $ADMIN_JAVASCRIPT = '';
/** @var string */ /** @var string */
public $ADMIN_STYLESHEET; public string $ADMIN_STYLESHEET = '';
/** @var string */ /** @var string */
public $FRONTEND_JAVASCRIPT; public string $FRONTEND_JAVASCRIPT = '';
/** @var string */ /** @var string */
public $FRONTEND_STYLESHEET; public string $FRONTEND_STYLESHEET = '';
// other smarty folder vars // other smarty folder vars
/** @var string */ /** @var string */
public $INCLUDES; public string $INCLUDES = '';
/** @var string */ /** @var string */
public $JAVASCRIPT; public string $JAVASCRIPT = '';
/** @var string */ /** @var string */
public $CSS; public string $CSS = '';
/** @var string */ /** @var string */
public $FONT; public string $FONT = '';
/** @var string */ /** @var string */
public $PICTURES; public string $PICTURES = '';
/** @var string */ /** @var string */
public $CACHE_PICTURES; public string $CACHE_PICTURES = '';
/** @var string */ /** @var string */
public $CACHE_PICTURES_ROOT; public string $CACHE_PICTURES_ROOT = '';
// constructor class, just sets the language stuff // constructor class, just sets the language stuff
/** /**
@@ -222,6 +220,7 @@ class SmartyExtend extends \Smarty
// core CS // core CS
$this->CSS_CORE_INCLUDE = ''; $this->CSS_CORE_INCLUDE = '';
if ( if (
!empty($this->CSS_CORE_TEMPLATE_NAME) &&
file_exists($this->CSS . $this->CSS_CORE_TEMPLATE_NAME) && file_exists($this->CSS . $this->CSS_CORE_TEMPLATE_NAME) &&
is_file($this->CSS . $this->CSS_CORE_TEMPLATE_NAME) is_file($this->CSS . $this->CSS_CORE_TEMPLATE_NAME)
) { ) {
@@ -230,6 +229,7 @@ class SmartyExtend extends \Smarty
// core JS // core JS
$this->JS_CORE_INCLUDE = ''; $this->JS_CORE_INCLUDE = '';
if ( if (
!empty($this->JS_CORE_TEMPLATE_NAME) &&
file_exists($this->JAVASCRIPT . $this->JS_CORE_TEMPLATE_NAME) && file_exists($this->JAVASCRIPT . $this->JS_CORE_TEMPLATE_NAME) &&
is_file($this->JAVASCRIPT . $this->JS_CORE_TEMPLATE_NAME) is_file($this->JAVASCRIPT . $this->JS_CORE_TEMPLATE_NAME)
) { ) {
@@ -373,7 +373,7 @@ class SmartyExtend extends \Smarty
// check for template include // check for template include
if ( if (
$this->USE_INCLUDE_TEMPLATE === true && $this->USE_INCLUDE_TEMPLATE === true &&
!$this->TEMPLATE_NAME empty($this->TEMPLATE_NAME)
) { ) {
$this->TEMPLATE_NAME = $this->CONTENT_INCLUDE; $this->TEMPLATE_NAME = $this->CONTENT_INCLUDE;
// add to cache & compile id // add to cache & compile id
@@ -390,7 +390,7 @@ class SmartyExtend extends \Smarty
exit('MASTER TEMPLATE: ' . $this->MASTER_TEMPLATE_NAME . ' could not be found'); exit('MASTER TEMPLATE: ' . $this->MASTER_TEMPLATE_NAME . ' could not be found');
} }
if ( if (
$this->TEMPLATE_NAME && !empty($this->TEMPLATE_NAME) &&
!file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->TEMPLATE_NAME) !file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->TEMPLATE_NAME)
) { ) {
exit('INCLUDE TEMPLATE: ' . $this->TEMPLATE_NAME . ' could not be found'); exit('INCLUDE TEMPLATE: ' . $this->TEMPLATE_NAME . ' could not be found');
@@ -398,7 +398,7 @@ class SmartyExtend extends \Smarty
// javascript translate data as template for auto translate // javascript translate data as template for auto translate
if (empty($this->TEMPLATE_TRANSLATE)) { if (empty($this->TEMPLATE_TRANSLATE)) {
$this->TEMPLATE_TRANSLATE = 'jsTranslate-' $this->TEMPLATE_TRANSLATE = 'jsTranslate-'
. $this->locale_set . '.' . $this->encoding . $this->l10n->getLocaleSet() . '.' . $this->encoding
. '.tpl'; . '.tpl';
} else { } else {
// we assume we have some fixed set // we assume we have some fixed set
@@ -408,22 +408,27 @@ class SmartyExtend extends \Smarty
if (strpos($this->TEMPLATE_TRANSLATE, '.tpl')) { if (strpos($this->TEMPLATE_TRANSLATE, '.tpl')) {
$this->TEMPLATE_TRANSLATE = str_replace( $this->TEMPLATE_TRANSLATE = str_replace(
'.tpl', '.tpl',
'-' . $this->locale_set . '.' . $this->encoding . '.tpl', '-' . $this->l10n->getLocaleSet() . '.' . $this->encoding . '.tpl',
$this->TEMPLATE_TRANSLATE $this->TEMPLATE_TRANSLATE
); );
} else { } else {
$this->TEMPLATE_TRANSLATE .= '-' $this->TEMPLATE_TRANSLATE .= '-'
. $this->locale_set . '.' . $this->encoding . $this->l10n->getLocaleSet() . '.' . $this->encoding
. '.tpl'; . '.tpl';
} }
} }
// if we can't find it, dump it // if we can't find it, dump it
if (!file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->TEMPLATE_TRANSLATE)) { if (
!file_exists(
$this->getTemplateDir()[0] . DIRECTORY_SEPARATOR
. $this->TEMPLATE_TRANSLATE
)
) {
$this->TEMPLATE_TRANSLATE = null; $this->TEMPLATE_TRANSLATE = null;
} }
if (empty($this->JS_TRANSLATE)) { if (empty($this->JS_TRANSLATE)) {
$this->JS_TRANSLATE = 'translate-' $this->JS_TRANSLATE = 'translate-'
. $this->locale_set . '.' . $this->encoding . '.js'; . $this->l10n->getLocaleSet() . '.' . $this->encoding . '.js';
} else { } else {
// we assume we have some fixed set // we assume we have some fixed set
// we must add _<locale>.<encoding> // we must add _<locale>.<encoding>
@@ -432,12 +437,12 @@ class SmartyExtend extends \Smarty
if (strpos($this->JS_TRANSLATE, '.js')) { if (strpos($this->JS_TRANSLATE, '.js')) {
$this->JS_TRANSLATE = str_replace( $this->JS_TRANSLATE = str_replace(
'.js', '.js',
'-' . $this->locale_set . '.' . $this->encoding . '.js', '-' . $this->l10n->getLocaleSet() . '.' . $this->encoding . '.js',
$this->JS_TRANSLATE $this->JS_TRANSLATE
); );
} else { } else {
$this->JS_TRANSLATE .= '-' $this->JS_TRANSLATE .= '-'
. $this->locale_set . '.' . $this->encoding . $this->l10n->getLocaleSet() . '.' . $this->encoding
. '.js'; . '.js';
} }
} }
@@ -675,10 +680,12 @@ class SmartyExtend extends \Smarty
$this->HEADER['DEFAULT_ENCODING'] = $set_default_encoding; $this->HEADER['DEFAULT_ENCODING'] = $set_default_encoding;
// form name // form name
$this->DATA['FORM_NAME'] = !$this->FORM_NAME ? $this->DATA['FORM_NAME'] = empty($this->FORM_NAME) ?
str_replace('.php', '', $this->page_name) : str_replace('.php', '', $this->page_name) :
$this->FORM_NAME; $this->FORM_NAME;
$this->DATA['FORM_ACTION'] = $this->FORM_ACTION; $this->DATA['FORM_ACTION'] = empty($this->FORM_ACTION) ?
'' :
$this->FORM_ACTION;
// special for admin // special for admin
if ($admin_call === true) { if ($admin_call === true) {
// depreacte call globals cms on null 4mcs // depreacte call globals cms on null 4mcs
@@ -735,7 +742,7 @@ class SmartyExtend extends \Smarty
} }
// html title // html title
// set local page title // set local page title
$this->HEADER['HTML_TITLE'] = !$this->L_TITLE ? $this->HEADER['HTML_TITLE'] = empty($this->L_TITLE) ?
ucfirst(str_replace('_', ' ', \CoreLibs\Get\System::getPageName(1))) ucfirst(str_replace('_', ' ', \CoreLibs\Get\System::getPageName(1)))
. (!empty($set_g_title) ? '-' . $this->l10n->__($set_g_title) : '') : . (!empty($set_g_title) ? '-' . $this->l10n->__($set_g_title) : '') :
$this->l10n->__($this->L_TITLE); $this->l10n->__($this->L_TITLE);

View File

@@ -68,13 +68,10 @@ final class CoreLibsACLLoginTest extends TestCase
// logger is always needed // logger is always needed
// define basic connection set valid and one invalid // define basic connection set valid and one invalid
self::$log = new \CoreLibs\Debug\Logging([ self::$log = new \CoreLibs\Logging\Logging([
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log', // 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
'log_folder' => DIRECTORY_SEPARATOR . 'tmp', 'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-ACL-Login-Test', 'log_file_id' => 'CoreLibs-ACL-Login-Test',
'debug_all' => true,
'echo_all' => false,
'print_all' => true,
]); ]);
// test database we need to connect do, if not possible this test is skipped // test database we need to connect do, if not possible this test is skipped
self::$db = new \CoreLibs\DB\IO( self::$db = new \CoreLibs\DB\IO(
@@ -170,8 +167,10 @@ final class CoreLibsACLLoginTest extends TestCase
// change_password, pw_username, pw_old_password, pw_new_password, // change_password, pw_username, pw_old_password, pw_new_password,
// pw_new_password_confirm // pw_new_password_confirm
// 3[session]: override session set // 3[session]: override session set
// 4[error] : expected error code, 0 for all ok, 3000 for login page view // 4[error] : expected error code, 0 for all ok, 100 for login page view
// note that 1000 (no db), 2000 (no session) must be tested too // note that 1000 (no db), 2000 (no session), 3000 (options set error)
// must be tested too
// <1000 info, >=1000 critical error
// 5[return] : expected return array, eg login_error code, // 5[return] : expected return array, eg login_error code,
// or other info data to match // or other info data to match
$tests = [ $tests = [
@@ -183,7 +182,7 @@ final class CoreLibsACLLoginTest extends TestCase
[], [],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 0, 'login_error' => 0,
'error_string' => 'Success: <b>No error</b>', 'error_string' => 'Success: <b>No error</b>',
@@ -201,7 +200,7 @@ final class CoreLibsACLLoginTest extends TestCase
[], [],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 0, 'login_error' => 0,
'error_string' => 'Success: <b>No error</b>', 'error_string' => 'Success: <b>No error</b>',
@@ -224,7 +223,7 @@ final class CoreLibsACLLoginTest extends TestCase
[], [],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 0, 'login_error' => 0,
'error_string' => 'Success: <b>No error</b>', 'error_string' => 'Success: <b>No error</b>',
@@ -311,7 +310,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => '', 'login_password' => '',
], ],
[], [],
3000, 100,
[ [
'login_error' => 102, 'login_error' => 102,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -332,7 +331,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'abc', 'login_password' => 'abc',
], ],
[], [],
3000, 100,
[ [
'login_error' => 102, 'login_error' => 102,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -353,7 +352,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => '', 'login_password' => '',
], ],
[], [],
3000, 100,
[ [
'login_error' => 102, 'login_error' => 102,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -374,7 +373,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'abc', 'login_password' => 'abc',
], ],
[], [],
3000, 100,
[ [
'login_error' => 1010, 'login_error' => 1010,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -398,7 +397,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'abc', 'login_password' => 'abc',
], ],
[], [],
3000, 100,
[ [
// default password is plain text // default password is plain text
'login_error' => 1012, 'login_error' => 1012,
@@ -424,7 +423,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin', 'login_password' => 'admin',
], ],
[], [],
3000, 100,
[ [
'login_error' => 106, 'login_error' => 106,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -449,7 +448,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin', 'login_password' => 'admin',
], ],
[], [],
3000, 100,
[ [
'login_error' => 104, 'login_error' => 104,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -474,7 +473,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin', 'login_password' => 'admin',
], ],
[], [],
3000, 100,
[ [
'login_error' => 105, 'login_error' => 105,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -523,7 +522,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin', 'login_password' => 'admin',
], ],
[], [],
3000, 100,
[ [
'login_error' => 107, 'login_error' => 107,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -577,7 +576,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin', 'login_password' => 'admin',
], ],
[], [],
3000, 100,
[ [
'login_error' => 107, 'login_error' => 107,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -603,7 +602,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin', 'login_password' => 'admin',
], ],
[], [],
3000, 100,
[ [
'login_error' => 107, 'login_error' => 107,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -628,7 +627,7 @@ final class CoreLibsACLLoginTest extends TestCase
'login_password' => 'admin', 'login_password' => 'admin',
], ],
[], [],
3000, 100,
[ [
'login_error' => 108, 'login_error' => 108,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -764,7 +763,7 @@ final class CoreLibsACLLoginTest extends TestCase
], ],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 1010, 'login_error' => 1010,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -856,7 +855,7 @@ final class CoreLibsACLLoginTest extends TestCase
], ],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 1101, 'login_error' => 1101,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -912,7 +911,7 @@ final class CoreLibsACLLoginTest extends TestCase
], ],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 1102, 'login_error' => 1102,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -968,7 +967,7 @@ final class CoreLibsACLLoginTest extends TestCase
], ],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 1102, 'login_error' => 1102,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -995,7 +994,7 @@ final class CoreLibsACLLoginTest extends TestCase
], ],
[], [],
[], [],
3000, 100,
[ [
'login_error' => 1102, 'login_error' => 1102,
'error_string' => '<span style="color: red;">Fatal Error:</span> ' 'error_string' => '<span style="color: red;">Fatal Error:</span> '
@@ -1136,7 +1135,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any()) $login_mock->expects($this->any())
->method('loginTerminate') ->method('loginTerminate')
->will( ->will(
$this->returnCallback(function ($code) { $this->returnCallback(function ($message, $code) {
throw new \Exception('', $code); throw new \Exception('', $code);
}) })
); );
@@ -1230,7 +1229,11 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->loginSetMaxLoginErrorCount($mock_settings['max_login_error_count']); $login_mock->loginSetMaxLoginErrorCount($mock_settings['max_login_error_count']);
// temporary wrong password // temporary wrong password
$_POST['login_password'] = 'wrong'; $_POST['login_password'] = 'wrong';
for ($run = 1, $max_run = $login_mock->loginGetMaxLoginErrorCount(); $run <= $max_run; $run++) { for (
$run = 1, $max_run = $login_mock->loginGetMaxLoginErrorCount();
$run <= $max_run;
$run++
) {
try { try {
$login_mock->loginMainCall(); $login_mock->loginMainCall();
} catch (\Exception $e) { } catch (\Exception $e) {
@@ -1478,10 +1481,10 @@ final class CoreLibsACLLoginTest extends TestCase
// print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n"; // print "AJAX: " . $login_mock->loginGetAjaxFlag() . "\n";
// print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n"; // print "AJAX GLOBAL: " . ($GLOBALS['AJAX_PAGE'] ?? '{f}') . "\n";
// print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n"; // print "Login error expext: " . ($expected['login_error'] ?? '{0}') . "\n";
// if this is 3000, then we do further error checks // if this is 100, then we do further error checks
if ( if (
$e->getCode() == 3000 || $e->getCode() == 100 ||
!empty($_POST['login_exit']) && $_POST['login_exit'] == 3000 !empty($_POST['login_exit']) && $_POST['login_exit'] == 100
) { ) {
$this->assertEquals( $this->assertEquals(
$expected['login_error'], $expected['login_error'],
@@ -1819,7 +1822,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any()) $login_mock->expects($this->any())
->method('loginTerminate') ->method('loginTerminate')
->will( ->will(
$this->returnCallback(function ($code) { $this->returnCallback(function ($message, $code) {
throw new \Exception('', $code); throw new \Exception('', $code);
}) })
); );
@@ -1933,7 +1936,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any()) $login_mock->expects($this->any())
->method('loginTerminate') ->method('loginTerminate')
->will( ->will(
$this->returnCallback(function ($code) { $this->returnCallback(function ($message, $code) {
throw new \Exception('', $code); throw new \Exception('', $code);
}) })
); );
@@ -2021,7 +2024,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any()) $login_mock->expects($this->any())
->method('loginTerminate') ->method('loginTerminate')
->will( ->will(
$this->returnCallback(function ($code) { $this->returnCallback(function ($message, $code) {
throw new \Exception('', $code); throw new \Exception('', $code);
}) })
); );
@@ -2117,7 +2120,7 @@ final class CoreLibsACLLoginTest extends TestCase
$login_mock->expects($this->any()) $login_mock->expects($this->any())
->method('loginTerminate') ->method('loginTerminate')
->will( ->will(
$this->returnCallback(function ($code) { $this->returnCallback(function ($message, $code) {
throw new \Exception('', $code); throw new \Exception('', $code);
}) })
); );

View File

@@ -13,6 +13,11 @@ use PHPUnit\Framework\TestCase;
*/ */
final class CoreLibsCheckColorsTest extends TestCase final class CoreLibsCheckColorsTest extends TestCase
{ {
/**
* Undocumented function
*
* @return array<mixed>
*/
public function validateColorProvider(): array public function validateColorProvider(): array
{ {
/* /*
@@ -321,7 +326,7 @@ final class CoreLibsCheckColorsTest extends TestCase
*/ */
public function testValidateColorException(int $flag): void public function testValidateColorException(int $flag): void
{ {
$this->expectException(\Exception::class); $this->expectException(\UnexpectedValueException::class);
\CoreLibs\Check\Colors::validateColor('#ffffff', $flag); \CoreLibs\Check\Colors::validateColor('#ffffff', $flag);
} }
} }

View File

@@ -518,17 +518,20 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
return [ return [
// error <2 arguments // error <2 arguments
'too view arguments' => [ 'too view arguments' => [
'ArgumentCountError',
'arrayMergeRecursive needs two or more array arguments', 'arrayMergeRecursive needs two or more array arguments',
[1] [1]
], ],
// error <2 arrays // error <2 arrays
'only one array' => [ 'only one array' => [
'ArgumentCountError',
'arrayMergeRecursive needs two or more array arguments', 'arrayMergeRecursive needs two or more array arguments',
[1], [1],
true, true,
], ],
// error element is not array // error element is not array
'non array between array' => [ 'non array between array' => [
'TypeError',
'arrayMergeRecursive encountered a non array argument', 'arrayMergeRecursive encountered a non array argument',
[1], [1],
'string', 'string',
@@ -947,18 +950,20 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
*/ */
public function testArrayMergeRecursiveWarningA(): void public function testArrayMergeRecursiveWarningA(): void
{ {
set_error_handler( // set_error_handler(
static function (int $errno, string $errstr): never { // static function (int $errno, string $errstr): never {
throw new Exception($errstr, $errno); // throw new Exception($errstr, $errno);
}, // },
E_USER_WARNING // E_USER_WARNING
); // );
$arrays = func_get_args(); $arrays = func_get_args();
// first is expected warning // first is expected warning
$exception = array_shift($arrays);
$warning = array_shift($arrays); $warning = array_shift($arrays);
// phpunit 10.0 compatible // phpunit 10.0 compatible
$this->expectException($exception);
$this->expectExceptionMessage($warning); $this->expectExceptionMessage($warning);
\CoreLibs\Combined\ArrayHandler::arrayMergeRecursive(...$arrays); \CoreLibs\Combined\ArrayHandler::arrayMergeRecursive(...$arrays);

View File

@@ -309,45 +309,73 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'2020-12-12', '2020-12-12',
'2021-12-12', '2021-12-12',
-1, -1,
null,
null,
], ],
'dates equal' => [ 'dates equal' => [
'2020-12-12', '2020-12-12',
'2020-12-12', '2020-12-12',
0, 0,
null,
null,
], ],
'second date smaller' => [ 'second date smaller' => [
'2021-12-12', '2021-12-12',
'2020-12-12', '2020-12-12',
1 1,
null,
null,
], ],
'dates equal with different time' => [ 'dates equal with different time' => [
'2020-12-12 12:12:12', '2020-12-12 12:12:12',
'2020-12-12 13:13:13', '2020-12-12 13:13:13',
0, 0,
null,
null,
], ],
'invalid dates --' => [ 'invalid dates --' => [
'--', '--',
'--', '--',
false false,
'UnexpectedValueException',
1,
], ],
'empty dates' => [ 'empty dates' => [
'', '',
'', '',
false false,
'UnexpectedValueException',
1
], ],
'invalid dates' => [ 'invalid dates' => [
'not a date', 'not a date',
'not a date either', 'not a date either',
false, false,
'UnexpectedValueException',
2
],
'invalid end date' => [
'1990-01-01',
'not a date either',
false,
'UnexpectedValueException',
3
], ],
'out of bound dates' => [ 'out of bound dates' => [
'1900-1-1', '1900-1-1',
'9999-12-31', '9999-12-31',
-1 -1,
null,
null,
] ]
]; ];
} }
/**
* Undocumented function
*
* @return array<mixed>
*/
public function dateTimeCompareProvider(): array public function dateTimeCompareProvider(): array
{ {
return [ return [
@@ -355,51 +383,85 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'2020-12-12', '2020-12-12',
'2021-12-12', '2021-12-12',
-1, -1,
null,
null,
], ],
'dates equal no timestamp' => [ 'dates equal no timestamp' => [
'2020-12-12', '2020-12-12',
'2020-12-12', '2020-12-12',
0, 0,
null,
null,
], ],
'second date smaller no timestamp' => [ 'second date smaller no timestamp' => [
'2021-12-12', '2021-12-12',
'2020-12-12', '2020-12-12',
1 1,
null,
null,
], ],
'date equal first time smaller' => [ 'date equal first time smaller' => [
'2020-12-12 12:12:12', '2020-12-12 12:12:12',
'2020-12-12 13:13:13', '2020-12-12 13:13:13',
-1, -1,
null,
null,
], ],
'date equal time equal' => [ 'date equal time equal' => [
'2020-12-12 12:12:12', '2020-12-12 12:12:12',
'2020-12-12 12:12:12', '2020-12-12 12:12:12',
0, 0,
null,
null,
], ],
'date equal second time smaller' => [ 'date equal second time smaller' => [
'2020-12-12 13:13:13', '2020-12-12 13:13:13',
'2020-12-12 12:12:12', '2020-12-12 12:12:12',
1, 1,
null,
null,
], ],
'valid date invalid time' => [ 'valid date invalid time' => [
'2020-12-12 13:99:13', '2020-12-12 13:99:13',
'2020-12-12 12:12:99', '2020-12-12 12:12:99',
false, false,
'UnexpectedValueException',
2
],
'valid date invalid end time' => [
'2020-12-12 13:12:13',
'2020-12-12 12:12:99',
false,
'UnexpectedValueException',
3
], ],
'invalid datetimes --' => [ 'invalid datetimes --' => [
'--', '--',
'--', '--',
false, false,
'UnexpectedValueException',
1
], ],
'empty datetimess' => [ 'empty datetimess' => [
'', '',
'', '',
false, false,
'UnexpectedValueException',
1
], ],
'invalid datetimes' => [ 'invalid date times' => [
'not a date', 'not a date',
'not a date either', 'not a date either',
false, false,
'UnexpectedValueException',
2
],
'invalid end date time' => [
'1990-01-01 12:12:12',
'not a date either',
false,
'UnexpectedValueException',
3
], ],
]; ];
} }
@@ -458,6 +520,47 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @return array
*/
public function dateRangeHasWeekendProvider(): array
{
return [
'no weekend' => [
'2023-07-03',
'2023-07-04',
false
],
'start weekend sat' => [
'2023-07-01',
'2023-07-04',
true
],
'start weekend sun' => [
'2023-07-02',
'2023-07-04',
true
],
'end weekend sat' => [
'2023-07-03',
'2023-07-08',
true
],
'end weekend sun' => [
'2023-07-03',
'2023-07-09',
true
],
'long period > 6 days' => [
'2023-07-03',
'2023-07-27',
true
]
];
}
/** /**
* date string convert test * date string convert test
* *
@@ -573,10 +676,21 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
* @param string $input_a * @param string $input_a
* @param string $input_b * @param string $input_b
* @param int|bool $expected * @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void * @return void
*/ */
public function testCompareDate(string $input_a, string $input_b, $expected): void public function testCompareDate(
{ string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Combined\DateTime::compareDate($input_a, $input_b) \CoreLibs\Combined\DateTime::compareDate($input_a, $input_b)
@@ -593,10 +707,21 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
* @param string $input_a * @param string $input_a
* @param string $input_b * @param string $input_b
* @param int|bool $expected * @param int|bool $expected
* @param string|null $exception
* @param int|null $exception_code
* @return void * @return void
*/ */
public function testCompareDateTime(string $input_a, string $input_b, $expected): void public function testCompareDateTime(
{ string $input_a,
string $input_b,
int|bool $expected,
?string $exception,
?int $exception_code
): void {
if ($expected === false) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Combined\DateTime::compareDateTime($input_a, $input_b) \CoreLibs\Combined\DateTime::compareDateTime($input_a, $input_b)
@@ -780,6 +905,29 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
$output $output
); );
} }
/**
* Undocumented function
*
* @covers ::dateRangeHasWeekend
* @dataProvider dateRangeHasWeekendProvider
* @testdox dateRangeHasWeekend $start_date and $end_date are expected weekend $expected [$_dataName]
*
* @param string $start_date
* @param string $end_date
* @param bool $expected
* @return void
*/
public function testDateRangeHasWeekend(
string $start_date,
string $end_date,
bool $expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::dateRangeHasWeekend($start_date, $end_date)
);
}
} }
// __END__ // __END__

View File

@@ -59,6 +59,27 @@ final class CoreLibsConvertColorsTest extends TestCase
3 => false, 3 => false,
4 => false 4 => false
], ],
'invalid color red ' => [
0 => -12,
1 => 12,
2 => 12,
3 => false,
4 => false
],
'invalid color green ' => [
0 => 12,
1 => -12,
2 => 12,
3 => false,
4 => false
],
'invalid color blue ' => [
0 => 12,
1 => 12,
2 => -12,
3 => false,
4 => false
],
]; ];
} }
@@ -150,10 +171,40 @@ final class CoreLibsConvertColorsTest extends TestCase
'valid' => true, 'valid' => true,
], ],
// invalid values // invalid values
'invalid color' => [ 'invalid color r/h/h low' => [
'rgb' => [-12, 300, 12], 'rgb' => [-1, 12, 12],
'hsb' => [-12, 300, 12], 'hsb' => [-1, 50, 50],
'hsl' => [-12, 300, 12], 'hsl' => [-1, 50, 50],
'valid' => false,
],
'invalid color r/h/h high' => [
'rgb' => [256, 12, 12],
'hsb' => [361, 50, 50],
'hsl' => [361, 50, 50],
'valid' => false,
],
'invalid color g/s/s low' => [
'rgb' => [12, -1, 12],
'hsb' => [1, -1, 50],
'hsl' => [1, -1, 50],
'valid' => false,
],
'invalid color g/s/s high' => [
'rgb' => [12, 256, 12],
'hsb' => [1, 101, 50],
'hsl' => [1, 101, 50],
'valid' => false,
],
'invalid color b/b/l low' => [
'rgb' => [12, 12, -1],
'hsb' => [1, 50, -1],
'hsl' => [1, 50, -1],
'valid' => false,
],
'invalid color b/b/l high' => [
'rgb' => [12, 12, 256],
'hsb' => [1, 50, 101],
'hsl' => [1, 50, 101],
'valid' => false, 'valid' => false,
], ],
]; ];
@@ -246,11 +297,22 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param int $input_r * @param int $input_r
* @param int $input_g * @param int $input_g
* @param int $input_b * @param int $input_b
* @param string|bool $expected_hash
* @param string|bool $expected * @param string|bool $expected
* @return void * @return void
*/ */
public function testRgb2hex(int $input_r, int $input_g, int $input_b, $expected_hash, $expected) public function testRgb2hex(
{ int $input_r,
int $input_g,
int $input_b,
string|bool $expected_hash,
string|bool $expected
) {
// if expected hash is or expected is false, we need to check for
// LengthException
if ($expected_hash === false || $expected === false) {
$this->expectException(\LengthException::class);
}
// with # // with #
$this->assertEquals( $this->assertEquals(
$expected_hash, $expected_hash,
@@ -292,11 +354,19 @@ final class CoreLibsConvertColorsTest extends TestCase
*/ */
public function testHex2rgb( public function testHex2rgb(
string $input, string $input,
$expected, array|bool $expected,
$expected_str, string|bool $expected_str,
string $separator, string $separator,
$expected_str_sep string|bool $expected_str_sep
): void { ): void {
if ($expected === false || $expected_str === false || $expected_str_sep === false) {
$hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $input);
if (!is_string($hex_string)) {
$this->expectException(\InvalidArgumentException::class);
} else {
$this->expectException(\UnexpectedValueException::class);
}
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Convert\Colors::hex2rgb($input) \CoreLibs\Convert\Colors::hex2rgb($input)
@@ -324,8 +394,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected * @param array|bool $expected
* @return void * @return void
*/ */
public function testRgb2hsb(int $input_r, int $input_g, int $input_b, $expected): void public function testRgb2hsb(int $input_r, int $input_g, int $input_b, array|bool $expected): void
{ {
if ($expected === false) {
$this->expectException(\LengthException::class);
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Convert\Colors::rgb2hsb($input_r, $input_g, $input_b) \CoreLibs\Convert\Colors::rgb2hsb($input_r, $input_g, $input_b)
@@ -345,8 +418,12 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected * @param array|bool $expected
* @return void * @return void
*/ */
public function testHsb2rgb(float $input_h, float $input_s, float $input_b, $expected): void public function testHsb2rgb(float $input_h, float $input_s, float $input_b, array|bool $expected): void
{ {
if ($expected === false) {
$this->expectException(\LengthException::class);
$expected = [];
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Convert\Colors::hsb2rgb($input_h, $input_s, $input_b) \CoreLibs\Convert\Colors::hsb2rgb($input_h, $input_s, $input_b)
@@ -366,8 +443,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected * @param array|bool $expected
* @return void * @return void
*/ */
public function testRgb2hsl(int $input_r, int $input_g, int $input_b, $expected): void public function testRgb2hsl(int $input_r, int $input_g, int $input_b, array|bool $expected): void
{ {
if ($expected === false) {
$this->expectException(\LengthException::class);
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Convert\Colors::rgb2hsl($input_r, $input_g, $input_b) \CoreLibs\Convert\Colors::rgb2hsl($input_r, $input_g, $input_b)
@@ -387,8 +467,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected * @param array|bool $expected
* @return void * @return void
*/ */
public function testHsl2rgb($input_h, float $input_s, float $input_l, $expected): void public function testHsl2rgb(int|float $input_h, float $input_s, float $input_l, array|bool $expected): void
{ {
if ($expected === false) {
$this->expectException(\LengthException::class);
}
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Convert\Colors::hsl2rgb($input_h, $input_s, $input_l) \CoreLibs\Convert\Colors::hsl2rgb($input_h, $input_s, $input_l)
@@ -406,11 +489,11 @@ final class CoreLibsConvertColorsTest extends TestCase
*/ */
public function testHslHsb360hue(): void public function testHslHsb360hue(): void
{ {
$this->assertNotFalse( $this->assertIsArray(
\CoreLibs\Convert\Colors::hsl2rgb(360.0, 90.5, 41.2), \CoreLibs\Convert\Colors::hsl2rgb(360.0, 90.5, 41.2),
'HSL to RGB with 360 hue' 'HSL to RGB with 360 hue'
); );
$this->assertNotFalse( $this->assertIsArray(
\CoreLibs\Convert\Colors::hsb2rgb(360, 95, 78.0), \CoreLibs\Convert\Colors::hsb2rgb(360, 95, 78.0),
'HSB to RGB with 360 hue' 'HSB to RGB with 360 hue'
); );

View File

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

View File

@@ -43,12 +43,17 @@ final class CoreLibsConvertSetVarTypeNullTest extends TestCase
'int, no override' => [ 'int, no override' => [
1, 1,
null, null,
null '1'
], ],
'int, override set' => [ 'int, override set' => [
1, 1,
'not int', 'not int',
'not int' '1'
],
'array, override set' => [
[1, 2],
null,
null
] ]
]; ];
} }
@@ -201,7 +206,7 @@ final class CoreLibsConvertSetVarTypeNullTest extends TestCase
'float' => [ 'float' => [
1.5, 1.5,
null, null,
null 1
] ]
]; ];
} }
@@ -349,7 +354,7 @@ final class CoreLibsConvertSetVarTypeNullTest extends TestCase
'int' => [ 'int' => [
1, 1,
null, null,
null 1.0
] ]
]; ];
} }

View File

@@ -43,11 +43,16 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
'int, no override' => [ 'int, no override' => [
1, 1,
null, null,
'' '1'
], ],
'int, override set' => [ 'int, override set' => [
1, 1,
'not int', 'not int',
'1'
],
'array, override set' => [
[1, 2],
'not int',
'not int' 'not int'
] ]
]; ];
@@ -189,7 +194,7 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
'float' => [ 'float' => [
1.5, 1.5,
null, null,
0 1
] ]
]; ];
} }
@@ -330,7 +335,7 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
'int' => [ 'int' => [
1, 1,
null, null,
0.0 1.0
] ]
]; ];
} }
@@ -341,7 +346,7 @@ final class CoreLibsConvertSetVarTypeTest extends TestCase
* @dataProvider varSetTypeFloatProvider * @dataProvider varSetTypeFloatProvider
* @testdox setFloat $input with override $default will be $expected [$_dataName] * @testdox setFloat $input with override $default will be $expected [$_dataName]
* *
* @param mixed $input * @param mixed $input
* @param float|null $default * @param float|null $default
* @param float $expected * @param float $expected
* @return void * @return void

View File

@@ -22,12 +22,9 @@ final class CoreLibsCreateEmailTest extends TestCase
*/ */
public static function setUpBeforeClass(): void public static function setUpBeforeClass(): void
{ {
self::$log = new \CoreLibs\Debug\Logging([ self::$log = new \CoreLibs\Logging\Logging([
'log_folder' => DIRECTORY_SEPARATOR . 'tmp', 'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-Create-Email-Test', 'log_file_id' => 'CoreLibs-Create-Email-Test',
'debug_all' => true,
'echo_all' => false,
'print_all' => true,
]); ]);
} }
@@ -624,7 +621,7 @@ final class CoreLibsCreateEmailTest extends TestCase
// force new set for each run // force new set for each run
self::$log->setLogUniqueId(true); self::$log->setLogUniqueId(true);
// set on of unique log id // set on of unique log id
self::$log->setLogPer('run', true); self::$log->setLogFlag(\CoreLibs\Logging\Logger\Flag::per_run);
// init logger // init logger
$status = \CoreLibs\Create\Email::sendEmail( $status = \CoreLibs\Create\Email::sendEmail(
$subject, $subject,
@@ -646,7 +643,9 @@ final class CoreLibsCreateEmailTest extends TestCase
// assert content: must load JSON from log file // assert content: must load JSON from log file
if ($status == 2) { if ($status == 2) {
// open file, get last entry with 'SEND EMAIL JSON' key // open file, get last entry with 'SEND EMAIL JSON' key
$file = file_get_contents(self::$log->getLogFileName()); $file = file_get_contents(
self::$log->getLogFolder() . self::$log->getLogFile()
);
if ($file !== false) { if ($file !== false) {
// extract SEND EMAIL JSON line // extract SEND EMAIL JSON line
$found = preg_match_all("/^.* <SEND EMAIL JSON> - (.*)$/m", $file, $matches); $found = preg_match_all("/^.* <SEND EMAIL JSON> - (.*)$/m", $file, $matches);

View File

@@ -30,8 +30,10 @@ final class CoreLibsCreateSessionTest extends TestCase
// setSessionName: true/false, // setSessionName: true/false,
// checkActiveSession: true/false, [1st call, 2nd call] // checkActiveSession: true/false, [1st call, 2nd call]
// getSessionId: string or false // getSessionId: string or false
// 3: exepcted name (session) // 3: exepcted name (session)]
// 4: expected error string // 4: Exception thrown on error
// 5: exception code, null for none
// 6: expected error string
return [ return [
'session parameter' => [ 'session parameter' => [
'sessionNameParameter', 'sessionNameParameter',
@@ -44,7 +46,9 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'sessionNameParameter', 'sessionNameParameter',
'' null,
null,
'',
], ],
'session globals' => [ 'session globals' => [
'sessionNameGlobals', 'sessionNameGlobals',
@@ -57,7 +61,9 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'sessionNameGlobals', 'sessionNameGlobals',
'' null,
null,
'',
], ],
'session name default' => [ 'session name default' => [
'', '',
@@ -70,7 +76,9 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'', '',
'' null,
null,
'',
], ],
// error checks // error checks
// 1: we are in cli // 1: we are in cli
@@ -85,6 +93,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'', '',
'RuntimeException',
1,
'[SESSION] No sessions in php cli' '[SESSION] No sessions in php cli'
], ],
// 2: session disabled // 2: session disabled
@@ -99,6 +109,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'', '',
'RuntimeException',
2,
'[SESSION] Sessions are disabled' '[SESSION] Sessions are disabled'
], ],
// 3: invalid session name: string // 3: invalid session name: string
@@ -113,6 +125,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'', '',
'UnexpectedValueException',
3,
'[SESSION] Invalid session name: 1invalid$session#;' '[SESSION] Invalid session name: 1invalid$session#;'
], ],
// 3: invalid session name: only numbers // 3: invalid session name: only numbers
@@ -127,6 +141,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'', '',
'UnexpectedValueException',
3,
'[SESSION] Invalid session name: 123' '[SESSION] Invalid session name: 123'
], ],
// 3: invalid session name: invalid name short // 3: invalid session name: invalid name short
@@ -143,6 +159,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => '1234abcd4567' 'getSessionId' => '1234abcd4567'
], ],
'', '',
'RuntimeException',
4,
'[SESSION] Failed to activate session' '[SESSION] Failed to activate session'
], ],
// 5: get session id return false // 5: get session id return false
@@ -157,6 +175,8 @@ final class CoreLibsCreateSessionTest extends TestCase
'getSessionId' => false 'getSessionId' => false
], ],
'', '',
'UnexpectedValueException',
5,
'[SESSION] getSessionId did not return a session id' '[SESSION] getSessionId did not return a session id'
], ],
]; ];
@@ -173,6 +193,7 @@ final class CoreLibsCreateSessionTest extends TestCase
* @param string $type * @param string $type
* @param array<mixed> $mock_data * @param array<mixed> $mock_data
* @param string $expected * @param string $expected
* @param string|null $exception
* @param string $expected_error * @param string $expected_error
* @return void * @return void
*/ */
@@ -181,6 +202,8 @@ final class CoreLibsCreateSessionTest extends TestCase
string $type, string $type,
array $mock_data, array $mock_data,
string $expected, string $expected,
?string $exception,
?int $exception_code,
string $expected_error string $expected_error
): void { ): void {
// override expected // override expected
@@ -224,6 +247,11 @@ final class CoreLibsCreateSessionTest extends TestCase
// regex for session id // regex for session id
$ression_id_regex = "/^\w+$/"; $ression_id_regex = "/^\w+$/";
if ($exception !== null) {
$this->expectException($exception);
$this->expectExceptionCode($exception_code);
}
unset($GLOBALS['SET_SESSION_NAME']); unset($GLOBALS['SET_SESSION_NAME']);
$session_id = ''; $session_id = '';
switch ($type) { switch ($type) {
@@ -253,13 +281,6 @@ final class CoreLibsCreateSessionTest extends TestCase
$expected, $expected,
$session_mock->getSessionName() $session_mock->getSessionName()
); );
} else {
// false checks
$this->assertEquals(
$expected_error,
$session_mock->getErrorStr(),
'error assert'
);
} }
} }

View File

@@ -21,45 +21,83 @@ final class CoreLibsCreateUidsTest extends TestCase
public function uniqIdProvider(): array public function uniqIdProvider(): array
{ {
return [ return [
// number length
'too short' => [
0 => 1,
1 => 4,
2 => null
],
'valid length: 10' => [
0 => 10,
1 => 10,
2 => null
],
'valid length: 9, auto length' => [
0 => 9,
1 => 8,
2 => null
],
'valid length: 9, force length' => [
0 => 9,
1 => 9,
2 => true,
],
'very long: 512' => [
0 => 512,
1 => 512,
2 => null
],
// below is all legacy
'md5 hash' => [ 'md5 hash' => [
0 => 'md5', 0 => 'md5',
1 => 32, 1 => 32,
2 => null
], ],
'sha256 hash' => [ 'sha256 hash' => [
0 => 'sha256', 0 => 'sha256',
1 => 64 1 => 64,
2 => null
], ],
'ripemd160 hash' => [ 'ripemd160 hash' => [
0 => 'ripemd160', 0 => 'ripemd160',
1 => 40 1 => 40,
2 => null
], ],
'adler32 hash' => [ 'adler32 hash' => [
0 => 'adler32', 0 => 'adler32',
1 => 8 1 => 8,
2 => null
], ],
'not in list hash but valid' => [ 'not in list, set default length' => [
0 => 'sha3-512', 0 => 'sha3-512',
1 => strlen(hash('sha3-512', 'A')) 1 => 64,
2 => null
], ],
'default hash not set' => [ 'default hash not set' => [
0 => null, 0 => null,
1 => 64, 1 => 64,
2 => null
], ],
'invalid name' => [ 'invalid name' => [
0 => 'iamnotavalidhash', 0 => 'iamnotavalidhash',
1 => 64, 1 => 64,
2 => null
], ],
'auto: ' . \CoreLibs\Create\Uids::DEFAULT_HASH => [ // auto calls
0 => \CoreLibs\Create\Uids::DEFAULT_HASH, 'auto: ' . \CoreLibs\Create\Uids::DEFAULT_UNNIQ_ID_LENGTH => [
1 => strlen(hash(\CoreLibs\Create\Uids::DEFAULT_HASH, 'A')) 0 => \CoreLibs\Create\Uids::DEFAULT_UNNIQ_ID_LENGTH,
1 => 64,
2 => null
], ],
'auto: ' . \CoreLibs\Create\Uids::STANDARD_HASH_LONG => [ 'auto: ' . \CoreLibs\Create\Uids::STANDARD_HASH_LONG => [
0 => \CoreLibs\Create\Uids::STANDARD_HASH_LONG, 0 => \CoreLibs\Create\Uids::STANDARD_HASH_LONG,
1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_LONG, 'A')) 1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_LONG, 'A')),
2 => null
], ],
'auto: ' . \CoreLibs\Create\Uids::STANDARD_HASH_SHORT => [ 'auto: ' . \CoreLibs\Create\Uids::STANDARD_HASH_SHORT => [
0 => \CoreLibs\Create\Uids::STANDARD_HASH_SHORT, 0 => \CoreLibs\Create\Uids::STANDARD_HASH_SHORT,
1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_SHORT, 'A')) 1 => strlen(hash(\CoreLibs\Create\Uids::STANDARD_HASH_SHORT, 'A')),
2 => null
], ],
]; ];
} }
@@ -105,25 +143,26 @@ final class CoreLibsCreateUidsTest extends TestCase
* *
* @covers ::uniqId * @covers ::uniqId
* @dataProvider uniqIdProvider * @dataProvider uniqIdProvider
* @testdox uniqId $input will be length $expected [$_dataName] * @testdox uniqId $input will be length $expected (Force $flag) [$_dataName]
* *
* @param string|null $input * @param int|string|null $input
* @param string $expected * @param string $expected
* @param bool|null $flag
* @return void * @return void
*/ */
public function testUniqId(?string $input, int $expected): void public function testUniqId(int|string|null $input, int $expected, ?bool $flag): void
{ {
if ($input === null) { if ($input === null) {
$this->assertEquals( $uniq_id_length = strlen(\CoreLibs\Create\Uids::uniqId());
$expected, } elseif ($flag === null) {
strlen(\CoreLibs\Create\Uids::uniqId()) $uniq_id_length = strlen(\CoreLibs\Create\Uids::uniqId($input));
);
} else { } else {
$this->assertEquals( $uniq_id_length = strlen(\CoreLibs\Create\Uids::uniqId($input, $flag));
$expected,
strlen(\CoreLibs\Create\Uids::uniqId($input))
);
} }
$this->assertEquals(
$expected,
$uniq_id_length
);
} }
/** /**

View File

@@ -37,6 +37,8 @@ namespace tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\MockObject\MockObject;
use CoreLibs\Logging\Logger\Level;
use CoreLibs\DB\Options\Convert;
/** /**
* Test class for DB\IO + DB\SQL\PgSQL * Test class for DB\IO + DB\SQL\PgSQL
@@ -59,20 +61,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql', 'db_type' => 'pgsql',
'db_encoding' => '', 'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer 'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
],
// same as valid, but db debug is off
'valid_debug_false' => [
'db_name' => 'corelibs_db_io_test',
'db_user' => 'corelibs_db_io_test',
'db_pass' => 'corelibs_db_io_test',
'db_host' => 'localhost',
'db_port' => 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => false,
], ],
// same as valid, but encoding is set // same as valid, but encoding is set
'valid_with_encoding_utf8' => [ 'valid_with_encoding_utf8' => [
@@ -85,7 +73,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql', 'db_type' => 'pgsql',
'db_encoding' => 'UTF-8', 'db_encoding' => 'UTF-8',
'db_ssl' => 'allow', // allow, disable, require, prefer 'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
], ],
// valid with no schema set // valid with no schema set
'valid_no_schema' => [ 'valid_no_schema' => [
@@ -98,7 +85,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql', 'db_type' => 'pgsql',
'db_encoding' => '', 'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer 'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
], ],
// invalid (missing db name) // invalid (missing db name)
'invalid' => [ 'invalid' => [
@@ -111,7 +97,6 @@ final class CoreLibsDBIOTest extends TestCase
'db_type' => 'pgsql', 'db_type' => 'pgsql',
'db_encoding' => '', 'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer 'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true,
], ],
]; ];
private static $log; private static $log;
@@ -132,14 +117,12 @@ final class CoreLibsDBIOTest extends TestCase
); );
} }
// define basic connection set valid and one invalid // define basic connection set valid and one invalid
self::$log = new \CoreLibs\Debug\Logging([ self::$log = new \CoreLibs\Logging\Logging([
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log', // 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
'log_folder' => DIRECTORY_SEPARATOR . 'tmp', 'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-DB-IO-Test', 'log_file_id' => 'CoreLibs-DB-IO-Test',
'debug_all' => false,
'echo_all' => false,
'print_all' => false,
]); ]);
// will be true, default logging is true
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -156,7 +139,7 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbExec("DROP TABLE test_meta"); $db->dbExec("DROP TABLE test_meta");
} }
// uid is for internal reference tests // uid is for internal reference tests
$base_table = <<<EOM $base_table = <<<SQL
uid VARCHAR, uid VARCHAR,
row_int INT, row_int INT,
row_numeric NUMERIC, row_numeric NUMERIC,
@@ -172,36 +155,36 @@ final class CoreLibsDBIOTest extends TestCase
row_array_varchar VARCHAR ARRAY row_array_varchar VARCHAR ARRAY
) )
WITHOUT OIDS WITHOUT OIDS
EOM; SQL;
// create the tables // create the tables
$db->dbExec( $db->dbExec(
// primary key name is table + '_id' // primary key name is table + '_id'
<<<EOM <<<SQL
CREATE TABLE table_with_primary_key ( CREATE TABLE table_with_primary_key (
table_with_primary_key_id SERIAL PRIMARY KEY, table_with_primary_key_id SERIAL PRIMARY KEY,
$base_table $base_table
EOM SQL
/* "CREATE TABLE table_with_primary_key (" /* "CREATE TABLE table_with_primary_key ("
// primary key name is table + '_id' // primary key name is table + '_id'
. "table_with_primary_key_id SERIAL PRIMARY KEY, " . "table_with_primary_key_id SERIAL PRIMARY KEY, "
. $base_table */ . $base_table */
); );
$db->dbExec( $db->dbExec(
<<<EOM <<<SQL
CREATE TABLE table_without_primary_key ( CREATE TABLE table_without_primary_key (
$base_table $base_table
EOM SQL
/* "CREATE TABLE table_without_primary_key (" /* "CREATE TABLE table_without_primary_key ("
. $base_table */ . $base_table */
); );
// create simple table for meta test // create simple table for meta test
$db->dbExec( $db->dbExec(
<<<EOM <<<SQL
CREATE TABLE test_meta ( CREATE TABLE test_meta (
row_1 VARCHAR, row_1 VARCHAR,
row_2 INT row_2 INT
) WITHOUT OIDS ) WITHOUT OIDS
EOM SQL
/* "CREATE TABLE test_meta (" /* "CREATE TABLE test_meta ("
. "row_1 VARCHAR, " . "row_1 VARCHAR, "
. "row_2 INT" . "row_2 INT"
@@ -462,12 +445,14 @@ final class CoreLibsDBIOTest extends TestCase
{ {
// 0: connection array // 0: connection array
// 1: status after connection // 1: status after connection
// 2: exception name
// 2: info string // 2: info string
// 3: ??? // 3: ???
return [ return [
'invalid connection' => [ 'invalid connection' => [
self::$db_config['invalid'], self::$db_config['invalid'],
false, false,
'RuntimeException',
"-DB-info-> Connected to db '' with schema 'public' as user " "-DB-info-> Connected to db '' with schema 'public' as user "
. "'' at host '' on port '5432' with ssl mode 'allow' **** " . "'' at host '' on port '5432' with ssl mode 'allow' **** "
. "-DB-info-> DB IO Class debug output: Yes **** ", . "-DB-info-> DB IO Class debug output: Yes **** ",
@@ -476,6 +461,7 @@ final class CoreLibsDBIOTest extends TestCase
'valid connection' => [ 'valid connection' => [
self::$db_config['valid'], self::$db_config['valid'],
true, true,
'',
"-DB-info-> Connected to db 'corelibs_db_io_test' with " "-DB-info-> Connected to db 'corelibs_db_io_test' with "
. "schema 'public' as user 'corelibs_db_io_test' at host " . "schema 'public' as user 'corelibs_db_io_test' at host "
. "'localhost' on port '5432' with ssl mode 'allow' **** " . "'localhost' on port '5432' with ssl mode 'allow' **** "
@@ -492,13 +478,21 @@ final class CoreLibsDBIOTest extends TestCase
* @dataProvider connectionProvider * @dataProvider connectionProvider
* @testdox Connection will be $expected [$_dataName] * @testdox Connection will be $expected [$_dataName]
* *
* @param array $connection
* @param bool $expected_status
* @param string $exception
* @param string $expected_string
* @return void * @return void
*/ */
public function testConnection( public function testConnection(
array $connection, array $connection,
bool $expected_status, bool $expected_status,
string $exception,
string $expected_string string $expected_string
): void { ): void {
if ($expected_status === false) {
$this->expectException($exception);
}
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
$connection, $connection,
self::$log self::$log
@@ -537,6 +531,9 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function debugSetProvider(): array public function debugSetProvider(): array
{ {
// 0: db connecdtion
// 1: override log flag, null for default
// 2: set flag
return [ return [
'default debug set' => [ 'default debug set' => [
// what base connection // what base connection
@@ -544,11 +541,6 @@ final class CoreLibsDBIOTest extends TestCase
// actions (set) // actions (set)
null, null,
// set exepected // set exepected
self::$db_config['valid']['db_debug'],
],
'set debug to true' => [
'valid_debug_false',
true,
true, true,
], ],
'set debug to false' => [ 'set debug to false' => [
@@ -559,99 +551,46 @@ final class CoreLibsDBIOTest extends TestCase
]; ];
} }
/**
* test set for toggleDEbug
*
* @return array
*/
public function debugToggleProvider(): array
{
return [
'default debug set' => [
// what base connection
'valid',
// actions
null,
// toggle is inverse
self::$db_config['valid']['db_debug'] ? false : true,
],
'toggle debug to true' => [
'valid_debug_false',
true,
true,
],
'toggle debug to false' => [
'valid',
false,
false,
]
];
}
/** /**
* Test dbSetDbug, dbGetDebug * Test dbSetDbug, dbGetDebug
* *
* @covers ::dbGetDbug * @covers ::dbGetDbug
* @covers ::dbSetDebug * @covers ::dbSetDebug
* @dataProvider debugSetProvider * @testdox Set and Get Debug flag
* @testdox Setting debug $set will be $expected [$_dataName]
* *
* @return void * @return void
*/ */
public function testDbSetDebug( public function testDbSetDebug(): void
string $connection, {
?bool $set, $connection = 'valid';
bool $expected // default set, expect true
): void {
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
); );
$this->assertEquals( $this->assertTrue(
$expected, $db->dbGetDebug()
$set === null ?
$db->dbSetDebug() :
$db->dbSetDebug($set)
); );
// must always match // switch off
$this->assertEquals( $db->dbSetDebug(false);
$expected, $this->assertFalse(
$db->dbGetDebug() $db->dbGetDebug()
); );
$db->dbClose(); $db->dbClose();
} // second conenction with log set NOT debug
$log = new \CoreLibs\Logging\Logging([
/** // 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
* Test dbToggleDebug, dbGetDebug 'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
* 'log_file_id' => 'CoreLibs-DB-IO-Test',
* @covers ::dbGetDbug 'log_level' => \CoreLibs\Logging\Logger\Level::Notice,
* @covers ::dbSetDebug ]);
* @dataProvider debugToggleProvider
* @testdox Toggle debug $toggle will be $expected [$_dataName]
*
* @return void
*/
public function testDbToggleDebug(
string $connection,
?bool $toggle,
bool $expected
): void {
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log $log
); );
$this->assertEquals( $this->assertFalse(
$expected,
$toggle === null ?
$db->dbToggleDebug() :
$db->dbToggleDebug($toggle)
);
// must always match
$this->assertEquals(
$expected,
$db->dbGetDebug() $db->dbGetDebug()
); );
$db->dbClose();
} }
// - set max query call sets // - set max query call sets
@@ -794,6 +733,10 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testGetSetting(string $connection, array $settings): void public function testGetSetting(string $connection, array $settings): void
{ {
// if settings are all empty -> assume exception
if (empty($settings['db_name']) && empty($settings['db_user'])) {
$this->expectException('RuntimeException');
}
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
@@ -809,7 +752,6 @@ final class CoreLibsDBIOTest extends TestCase
'host' => 'db_host', 'host' => 'db_host',
'port' => 'db_port', 'port' => 'db_port',
'ssl' => 'db_ssl', 'ssl' => 'db_ssl',
'debug' => 'db_debug',
'password' => '***', 'password' => '***',
] as $read => $compare ] as $read => $compare
) { ) {
@@ -1342,10 +1284,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
], ],
'row_2' => [ 'row_2' => [
'num' => 2, 'num' => 2,
@@ -1355,10 +1297,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
] ]
] ]
], ],
@@ -1374,10 +1316,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
], ],
'row_2' => [ 'row_2' => [
'num' => 2, 'num' => 2,
@@ -1387,10 +1329,10 @@ final class CoreLibsDBIOTest extends TestCase
'has default' => false, 'has default' => false,
'array dims' => 0, 'array dims' => 0,
'is enum' => false, 'is enum' => false,
'is base' => 1, 'is base' => true,
'is composite' => false, 'is composite' => false,
'is pesudo' => false,
'description' => '', 'description' => '',
'is pseudo' => false
] ]
] ]
], ],
@@ -4425,16 +4367,16 @@ final class CoreLibsDBIOTest extends TestCase
] ]
] ]
], ],
// same but as EOM // same but as heredoc
'single insert (PK), EOM string' => [ 'single insert (PK), heredoc string' => [
<<<EOM <<<SQL
INSERT INTO table_with_primary_key ( INSERT INTO table_with_primary_key (
row_varchar, row_varchar_literal, row_int, row_date row_varchar, row_varchar_literal, row_int, row_date
) VALUES ( ) VALUES (
'Text', 'Other', 123, '2022-03-01' 'Text', 'Other', 123, '2022-03-01'
) )
RETURNING row_varchar, row_varchar_literal, row_int, row_date RETURNING row_varchar, row_varchar_literal, row_int, row_date
EOM, SQL,
null, null,
null, null,
null, null,
@@ -4529,16 +4471,16 @@ final class CoreLibsDBIOTest extends TestCase
] ]
] ]
], ],
// same as above but as EOM string // same as above but as heredoc string
'single insert (No PK), EOM string' => [ 'single insert (No PK), heredoc string' => [
<<<EOM <<<SQL
INSERT INTO table_without_primary_key ( INSERT INTO table_without_primary_key (
row_varchar, row_varchar_literal, row_int, row_date row_varchar, row_varchar_literal, row_int, row_date
) VALUES ( ) VALUES (
'Text', 'Other', 123, '2022-03-01' 'Text', 'Other', 123, '2022-03-01'
) )
RETURNING row_varchar, row_varchar_literal, row_int, row_date RETURNING row_varchar, row_varchar_literal, row_int, row_date
EOM, SQL,
null, null,
null, null,
null, null,
@@ -4631,6 +4573,176 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbClose(); $db->dbClose();
} }
// testing auto convert
/**
* Undocumented function
*
* @covers ::dbSetConvertFlag
* @testdox Check convert type works
*
* @return void
*/
public function testConvertType(): void
{
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$bytea_data = $db->dbEscapeBytea(
file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'CoreLibsDBIOTest.php') ?: ''
);
$query_insert = <<<SQL
INSERT INTO table_with_primary_key (
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
) VALUES (
$1,
$2, $3, $4, $5,
$6, $7, $8, $9,
$10, $11, $12, $13
)
SQL;
$db->dbExecParams(
$query_insert,
[
'CONVERT_TYPE_TEST',
1, 1.5, 'varchar', 'varchar literla',
json_encode(['json', 'a', 1, true, 'sub' => ['b', 'c']]),
json_encode(['jsonb', 'a', 1, true, 'sub' => ['b', 'c']]),
$bytea_data, date('Y-m-d H:i:s'), date('Y-m-d'), date('H:m:s'),
'{1,2,3}', '{"a","b","c"}'
]
);
$type_layout = [
'uid' => 'string',
'row_int' => 'int',
'row_numeric' => 'float',
'row_varchar' => 'string',
'row_varchar_literal' => 'string',
'row_json' => 'json',
'row_jsonb' => 'json',
'row_bytea' => 'bytea',
'row_timestamp' => 'string',
'row_date' => 'string',
'row_interval' => 'string',
'row_array_int' => 'string',
'row_array_varchar' => 'string'
];
$query_select = <<<SQL
SELECT
uid,
row_int, row_numeric, row_varchar, row_varchar_literal,
row_json, row_jsonb, row_bytea, row_timestamp,
row_date, row_interval, row_array_int, row_array_varchar
FROM
table_with_primary_key
WHERE
uid = $1
SQL;
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
// all hast to be string
foreach ($res as $key => $value) {
$this->assertIsString($value, 'Aseert string for column: ' . $key);
}
// convert base only
$db->dbSetConvertFlag(Convert::on);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::numeric);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::json);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
$db->dbSetConvertFlag(Convert::bytea);
$res = $db->dbReturnRowParams($query_select, ['CONVERT_TYPE_TEST']);
foreach ($res as $key => $value) {
if (is_numeric($key)) {
$name = $db->dbGetFieldName($key);
} else {
$name = $key;
}
switch ($type_layout[$name]) {
case 'int':
$this->assertIsInt($value, 'Aseert int for column: ' . $key . '/' . $name);
break;
case 'float':
$this->assertIsFloat($value, 'Aseert float for column: ' . $key . '/' . $name);
break;
case 'json':
case 'jsonb':
$this->assertIsArray($value, 'Aseert array for column: ' . $key . '/' . $name);
break;
case 'bytea':
// for hex types it must not start with \x
$this->assertStringStartsNotWith(
'\x',
$value,
'Aseert bytes not starts with \x for column: ' . $key . '/' . $name
);
break;
default:
$this->assertIsString($value, 'Aseert string for column: ' . $key . '/' . $name);
break;
}
}
}
// - internal read data (post exec) // - internal read data (post exec)
// dbGetNumRows, dbGetNumFields, dbGetFieldNames, // dbGetNumRows, dbGetNumFields, dbGetFieldNames,
// dbGetQuery, dbGetQueryHash, dbGetDbh // dbGetQuery, dbGetQueryHash, dbGetDbh
@@ -4662,7 +4774,7 @@ final class CoreLibsDBIOTest extends TestCase
. "('Foxtrott', 'Tango', 789, '1982-10-15') ", . "('Foxtrott', 'Tango', 789, '1982-10-15') ",
null, null,
// //
2, 3,
4, 4,
['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'], ['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'],
['varchar', 'varchar', 'int4', 'date'], ['varchar', 'varchar', 'int4', 'date'],
@@ -4779,9 +4891,16 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbExecParams($query, $params); $db->dbExecParams($query, $params);
} }
$this->assertInstanceOf(
'PgSql\Result',
$db->dbGetCursor(),
'Failed assert dbGetCursor'
);
$this->assertEquals( $this->assertEquals(
$compare_query ?? $query, $compare_query ?? $query,
$db->dbGetQuery() $db->dbGetQuery(),
'Failed assert dbGetQuery'
); );
$this->assertEquals( $this->assertEquals(
// perhaps move that somewhere else? // perhaps move that somewhere else?
@@ -4794,7 +4913,8 @@ final class CoreLibsDBIOTest extends TestCase
($params === null ? ($params === null ?
$db->dbGetQueryHash($query) : $db->dbGetQueryHash($query) :
$db->dbGetQueryHash($query, $params) $db->dbGetQueryHash($query, $params)
) ),
'Failed assertdbGetQueryHash '
); );
$this->assertEquals( $this->assertEquals(
$expected_rows, $expected_rows,
@@ -4816,6 +4936,35 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbGetFieldTypes(), $db->dbGetFieldTypes(),
'Failed assert dbGetFieldTypes' 'Failed assert dbGetFieldTypes'
); );
// check FieldNameTypes matches
$this->assertEquals(
array_combine(
$expected_col_names,
$expected_col_types
),
$db->dbGetFieldNameTypes(),
'Failed assert dbGetFieldNameTypes'
);
// check pos matches name
// name matches type
// pos matches type
foreach ($expected_col_names as $pos => $name) {
$this->assertEquals(
$name,
$db->dbGetFieldName($pos),
'Failed assert dbGetFieldName: ' . $pos . ' => ' . $name
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($name),
'Failed assert dbGetFieldType: ' . $name . ' => ' . $expected_col_types[$pos]
);
$this->assertEquals(
$expected_col_types[$pos],
$db->dbGetFieldType($pos),
'Failed assert dbGetFieldType: ' . $pos . ' => ' . $expected_col_types[$pos]
);
}
$dbh = $db->dbGetDbh(); $dbh = $db->dbGetDbh();
$this->assertIsObject( $this->assertIsObject(
$dbh $dbh

View File

@@ -10,12 +10,13 @@ use PHPUnit\Framework\TestCase;
/** /**
* Test class for Debug\Logging * Test class for Debug\Logging
* @coversDefaultClass \CoreLibs\Debug\Logging * @coversDefaultClass \CoreLibs\Debug\LoggingLegacy
* @testdox \CoreLibs\Debug\Logging method tests * @testdox \CoreLibs\Debug\LoggingLegacy method tests
*/ */
final class CoreLibsDebugLoggingTest extends TestCase final class CoreLibsDebugLoggingLegacyTest extends TestCase
{ {
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR; private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
/** /**
* test set for options BASIC * test set for options BASIC
* *
@@ -24,7 +25,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
* 1: expected * 1: expected
* 2: override * 2: override
* override: * override:
* - constant for COSNTANTS * - constant for CONSTANTS
* - global for _GLOBALS * - global for _GLOBALS
* *
* @return array * @return array
@@ -138,7 +139,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
// catch this with the message // catch this with the message
$this->expectExceptionMessage($deprecation_message); $this->expectExceptionMessage($deprecation_message);
} }
$log = new \CoreLibs\Debug\Logging($options); $log = new \CoreLibs\Debug\LoggingLegacy($options);
// reset error handler // reset error handler
restore_error_handler(); restore_error_handler();
// check that settings match // check that settings match
@@ -308,7 +309,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
// catch this with the message // catch this with the message
$this->expectExceptionMessage($deprecation_message); $this->expectExceptionMessage($deprecation_message);
} }
$log = new \CoreLibs\Debug\Logging($options); $log = new \CoreLibs\Debug\LoggingLegacy($options);
// reset error handler // reset error handler
restore_error_handler(); restore_error_handler();
// check current // check current
@@ -385,7 +386,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
bool $expected_get bool $expected_get
): void { ): void {
// neutral start with default // neutral start with default
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogLevelAll', 'file_id' => 'testSetGetLogLevelAll',
'log_folder' => self::LOG_FOLDER 'log_folder' => self::LOG_FOLDER
]); ]);
@@ -510,7 +511,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
$expected_get $expected_get
): void { ): void {
// neutral start with default // neutral start with default
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogLevel', 'file_id' => 'testSetGetLogLevel',
'log_folder' => self::LOG_FOLDER 'log_folder' => self::LOG_FOLDER
]); ]);
@@ -592,7 +593,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
bool $expected_get bool $expected_get
): void { ): void {
// neutral start with default // neutral start with default
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogPer', 'file_id' => 'testSetGetLogPer',
'log_folder' => self::LOG_FOLDER 'log_folder' => self::LOG_FOLDER
]); ]);
@@ -624,7 +625,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
public function testSetGetLogPrintFileDate(bool $input, bool $expected_set, bool $expected_get): void public function testSetGetLogPrintFileDate(bool $input, bool $expected_set, bool $expected_get): void
{ {
// neutral start with default // neutral start with default
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testSetGetLogPrintFileDate', 'file_id' => 'testSetGetLogPrintFileDate',
'log_folder' => self::LOG_FOLDER 'log_folder' => self::LOG_FOLDER
]); ]);
@@ -693,7 +694,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/ */
public function testPrAr(array $input, string $expected): void public function testPrAr(array $input, string $expected): void
{ {
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testPrAr', 'file_id' => 'testPrAr',
'log_folder' => self::LOG_FOLDER 'log_folder' => self::LOG_FOLDER
]); ]);
@@ -757,7 +758,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/ */
public function testPrBl(bool $input, ?string $true, ?string $false, string $expected): void public function testPrBl(bool $input, ?string $true, ?string $false, string $expected): void
{ {
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testPrBl', 'file_id' => 'testPrBl',
'log_folder' => self::LOG_FOLDER 'log_folder' => self::LOG_FOLDER
]); ]);
@@ -932,7 +933,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
// remove any files named /tmp/error_log_TestDebug*.log // remove any files named /tmp/error_log_TestDebug*.log
array_map('unlink', glob($options['log_folder'] . 'error_msg_' . $options['file_id'] . '*.log')); array_map('unlink', glob($options['log_folder'] . 'error_msg_' . $options['file_id'] . '*.log'));
// init logger // init logger
$log = new \CoreLibs\Debug\Logging($options); $log = new \CoreLibs\Debug\LoggingLegacy($options);
// * debug (A/B) // * debug (A/B)
// NULL check for strip/prefix // NULL check for strip/prefix
$this->assertEquals( $this->assertEquals(
@@ -1046,13 +1047,13 @@ final class CoreLibsDebugLoggingTest extends TestCase
public function testLogUniqueId(bool $option, bool $override): void public function testLogUniqueId(bool $option, bool $override): void
{ {
if ($option === true) { if ($option === true) {
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testLogUniqueId', 'file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER, 'log_folder' => self::LOG_FOLDER,
'per_run' => $option 'per_run' => $option
]); ]);
} else { } else {
$log = new \CoreLibs\Debug\Logging([ $log = new \CoreLibs\Debug\LoggingLegacy([
'file_id' => 'testLogUniqueId', 'file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER 'log_folder' => self::LOG_FOLDER
]); ]);

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace tests; namespace tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use CoreLibs\Debug\Support;
/** /**
* Test class for Debug\Support * Test class for Debug\Support
@@ -40,6 +41,32 @@ final class CoreLibsDebugSupportTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @cover ::printTime
* @dataProvider printTimeProvider
* @testdox printTime test with $microtime and match to regex [$_dataName]
*
* @param int|null $mircrotime
* @param string $expected
* @return void
*/
public function testPrintTime(?int $microtime, string $regex): void
{
if ($microtime === null) {
$this->assertMatchesRegularExpression(
$regex,
Support::printTime()
);
} else {
$this->assertMatchesRegularExpression(
$regex,
Support::printTime($microtime)
);
}
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -50,18 +77,55 @@ final class CoreLibsDebugSupportTest extends TestCase
return [ return [
'empty array' => [ 'empty array' => [
0 => [], 0 => [],
1 => "<pre>Array\n(\n)\n</pre>" 1 => "<pre>Array\n(\n)\n</pre>",
2 => "Array\n(\n)\n",
], ],
'simple array' => [ 'simple array' => [
0 => ['a', 'b'], 0 => ['a', 'b'],
1 => "<pre>Array\n(\n" 1 => "<pre>Array\n(\n"
. " [0] => a\n" . " [0] => a\n"
. " [1] => b\n" . " [1] => b\n"
. ")\n</pre>" . ")\n</pre>",
2 => "Array\n(\n"
. " [0] => a\n"
. " [1] => b\n"
. ")\n",
], ],
]; ];
} }
/**
* Undocumented function
*
* @cover ::printAr
* @cover ::printArray
* @dataProvider printArrayProvider
* @testdox printAr/printArray $input will be $expected [$_dataName]
*
* @param array $input
* @param string $expected
* @param string $expected_strip
* @return void
*/
public function testPrintAr(array $input, string $expected, string $expected_strip): void
{
$this->assertEquals(
$expected,
Support::printAr($input),
'assert printAr'
);
$this->assertEquals(
$expected,
Support::printArray($input),
'assert printArray'
);
$this->assertEquals(
$expected_strip,
Support::prAr($input),
'assert prAr'
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -73,27 +137,31 @@ final class CoreLibsDebugSupportTest extends TestCase
'true input default' => [ 'true input default' => [
0 => true, 0 => true,
1 => [], 1 => [],
2 => 'true' 2 => 'true',
3 => 'true',
], ],
'false input default' => [ 'false input default' => [
0 => false, 0 => false,
1 => [], 1 => [],
2 => 'false' 2 => 'false',
3 => 'false'
], ],
'false input param name' => [ 'false input param name' => [
0 => false, 0 => false,
1 => [ 1 => [
'name' => 'param test' 'name' => 'param test'
], ],
2 => '<b>param test</b>: false' 2 => '<b>param test</b>: false',
3 => 'false'
], ],
'true input param name, true override' => [ 'true input param name, true override' => [
0 => true, 0 => true,
1 => [ 1 => [
'name' => 'param test', 'name' => 'param test',
'true' => 'ok' 'true' => 'ok',
], ],
2 => '<b>param test</b>: ok' 2 => '<b>param test</b>: ok',
3 => 'ok',
], ],
'false input param name, true override, false override' => [ 'false input param name, true override, false override' => [
0 => false, 0 => false,
@@ -102,11 +170,77 @@ final class CoreLibsDebugSupportTest extends TestCase
'true' => 'ok', 'true' => 'ok',
'false' => 'not', 'false' => 'not',
], ],
2 => '<b>param test</b>: not' 2 => '<b>param test</b>: not',
3 => 'not'
], ],
]; ];
} }
/**
* Undocumented function
*
* @cover ::printBool
* @dataProvider printBoolProvider
* @testdox printBool $input will be $expected [$_dataName]
*
* @param bool $input
* @param array $params
* @param string $expected
* @param string $expected_strip
* @return void
*/
public function testPrintBool(bool $input, array $params, string $expected, string $expected_strip): void
{
if (
isset($params['name']) &&
isset($params['true']) &&
isset($params['false'])
) {
$string = Support::printBool(
$input,
$params['name'],
$params['true'],
$params['false']
);
$string_strip = Support::prBl(
$input,
$params['true'],
$params['false']
);
} elseif (isset($params['name']) && isset($params['true'])) {
$string = Support::printBool(
$input,
$params['name'],
$params['true']
);
$string_strip = Support::prBl(
$input,
$params['true'],
);
} elseif (isset($params['name'])) {
$string = Support::printBool(
$input,
$params['name']
);
$string_strip = Support::prBl(
$input
);
} else {
$string = Support::printBool($input);
$string_strip = Support::prBl($input);
}
$this->assertEquals(
$expected,
$string,
'assert printBool'
);
$this->assertEquals(
$expected_strip,
$string_strip,
'assert prBl'
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -169,12 +303,10 @@ final class CoreLibsDebugSupportTest extends TestCase
'an array, no html' => [ 'an array, no html' => [
['a', 'b'], ['a', 'b'],
true, true,
"##HTMLPRE##" "Array\n(\n"
. "Array\n(\n"
. " [0] => a\n" . " [0] => a\n"
. " [1] => b\n" . " [1] => b\n"
. ")\n" . ")\n",
. "##/HTMLPRE##",
], ],
// resource // resource
'a resource' => [ 'a resource' => [
@@ -191,6 +323,295 @@ final class CoreLibsDebugSupportTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @cover ::printToString
* @dataProvider printToStringProvider
* @testdox printToString $input with $flag will be $expected [$_dataName]
*
* @param mixed $input anything
* @param boolean|null $flag html flag, only for string and array
* @param string $expected always string
* @return void
*/
public function testPrintToString(mixed $input, ?bool $flag, string $expected): void
{
if ($flag === null) {
// if expected starts with / and ends with / then this is a regex compare
if (
substr($expected, 0, 1) == '/' &&
substr($expected, -1, 1) == '/'
) {
$this->assertMatchesRegularExpression(
$expected,
Support::printToString($input)
);
} else {
$this->assertEquals(
$expected,
Support::printToString($input)
);
}
} else {
$this->assertEquals(
$expected,
Support::printToString($input, $flag)
);
}
}
/**
* Undocumented function
*
* @return array
*/
public function providerDumpExportVar(): array
{
return [
'string' => [
'input' => 'string',
'flag' => null,
'expected_dump' => 'string(6) "string"' . "\n",
'expected_export' => "<pre>'string'</pre>",
],
'string, no html' => [
'input' => 'string',
'flag' => true,
'expected_dump' => 'string(6) "string"' . "\n",
'expected_export' => "'string'",
],
// int
'int' => [
'input' => 6,
'flag' => null,
'expected_dump' => 'int(6)' . "\n",
'expected_export' => "<pre>6</pre>",
],
// float
'float' => [
'input' => 1.6,
'flag' => null,
'expected_dump' => 'float(1.6)' . "\n",
'expected_export' => "<pre>1.6</pre>",
],
// bool
'bool' => [
'input' => true,
'flag' => null,
'expected_dump' => 'bool(true)' . "\n",
'expected_export' => "<pre>true</pre>",
],
// array
'array' => [
'input' => ['string', true],
'flag' => null,
'expected_dump' => "array(2) {\n"
. " [0]=>\n"
. " string(6) \"string\"\n"
. " [1]=>\n"
. " bool(true)\n"
. "}\n",
'expected_export' => "<pre>array (\n"
. " 0 => 'string',\n"
. " 1 => true,\n"
. ")</pre>",
],
// more
];
}
/**
* Undocumented function
*
* @cover ::dumpVar
* @cover ::exportVar
* @dataProvider providerDumpExportVar
* @testdox dump/exportVar $input with $flag will be $expected_dump / $expected_export [$_dataName]
*
* @param mixed $input
* @param bool|null $flag
* @param string $expected_dump
* @param string $expected_export
* @return void
*/
public function testDumpExportVar(mixed $input, ?bool $flag, string $expected_dump, string $expected_export): void
{
if ($flag === null) {
$dump = Support::dumpVar($input);
$export = Support::exportVar($input);
} else {
$dump = Support::dumpVar($input, $flag);
$export = Support::exportVar($input, $flag);
}
$this->assertEquals(
$expected_dump,
$dump,
'assert dumpVar'
);
$this->assertEquals(
$expected_export,
$export,
'assert dumpVar'
);
}
/**
* Undocumented function
*
* @cover ::getCallerFileLine
* @testWith ["vendor/phpunit/phpunit/src/Framework/TestCase.php:"]
* @testdox getCallerFileLine check based on regex /[\w\-\/]/vendor/phpunit/phpunit/src/Framework/TestCase.php:\d+ [$_dataName]
*
* @param string $expected
* @return void
*/
public function testGetCallerFileLine(): void
{
// regex prefix with path "/../" and then fixed vendor + \d+
$regex = "/^\/[\w\-\/]+\/vendor\/phpunit\/phpunit\/src\/Framework\/TestCase.php:\d+$/";
$this->assertMatchesRegularExpression(
$regex,
Support::getCallerFileLine()
);
}
/**
* Undocumented function
*
* @cover ::getCallerMethod
* @testWith ["testGetCallerMethod"]
* @testdox getCallerMethod check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerMethod(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerMethod()
);
}
/**
* Undocumented function
*
* @cover ::getCallerMethodList
* @testWith [["main", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"]]
* @testdox getCallerMethodList check if it returns $expected [$_dataName]
*
* @param array $expected
* @return void
*/
public function testGetCallerMethodList(array $expected): void
{
$compare = Support::getCallerMethodList();
// 10: legact
// 11: direct
// 12: full call
switch (count($compare)) {
case 10:
// add nothing
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
'assert expected 10'
);
break;
case 11:
// add one "run" before "runBare"
// array_splice(
// $expected,
// 7,
// 0,
// ['run']
// );
array_splice(
$expected,
0,
0,
['include']
);
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
'assert expected 11'
);
break;
case 12:
// add two "run" before "runBare"
array_splice(
$expected,
7,
0,
['run']
);
array_splice(
$expected,
0,
0,
['include']
);
$this->assertEquals(
$expected,
Support::getCallerMethodList(),
'assert expected 12'
);
break;
}
}
/**
* test the lowest one (one above base)
*
* @cover ::getCallerClass
* @testWith ["tests\\CoreLibsDebugSupportTest"]
* @testdox getCallerClass check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerClass(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerClass()
);
}
/**
* test highest return (top level)
*
* @cover ::getCallerTopLevelClass
* @testWith ["PHPUnit\\TextUI\\Command"]
* @testdox getCallerTopLevelClass check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerTopLevelClass(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerTopLevelClass()
);
}
/**
* test highest return (top level)
*
* @cover ::getCallerClassMethod
* @testWith ["tests\\CoreLibsDebugSupportTest->testGetCallerClassMethod"]
* @testdox getCallerClassMethod check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerClassMethod(string $expected): void
{
$this->assertEquals(
$expected,
Support::getCallerClassMethod()
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -236,205 +657,6 @@ final class CoreLibsDebugSupportTest extends TestCase
]; ];
} }
/**
* Undocumented function
*
* @cover ::printTime
* @dataProvider printTimeProvider
* @testdox printTime test with $microtime and match to regex [$_dataName]
*
* @param int|null $mircrotime
* @param string $expected
* @return void
*/
public function testPrintTime(?int $microtime, string $regex): void
{
if ($microtime === null) {
$this->assertMatchesRegularExpression(
$regex,
\CoreLibs\Debug\Support::printTime()
);
} else {
$this->assertMatchesRegularExpression(
$regex,
\CoreLibs\Debug\Support::printTime($microtime)
);
}
}
/**
* Undocumented function
*
* @cover ::printAr
* @cover ::printArray
* @dataProvider printArrayProvider
* @testdox printAr/printArray $input will be $expected [$_dataName]
*
* @param array $input
* @param string $expected
* @return void
*/
public function testPrintAr(array $input, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printAr($input),
'assert printAr'
);
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printArray($input),
'assert printArray'
);
}
/**
* Undocumented function
*
* @cover ::printBool
* @dataProvider printBoolProvider
* @testdox printBool $input will be $expected [$_dataName]
*
* @param bool $input
* @param array $params
* @param string $expected
* @return void
*/
public function testPrintBool(bool $input, array $params, string $expected): void
{
if (
isset($params['name']) &&
isset($params['true']) &&
isset($params['false'])
) {
$string = \CoreLibs\Debug\Support::printBool(
$input,
$params['name'],
$params['true'],
$params['false']
);
} elseif (isset($params['name']) && isset($params['true'])) {
$string = \CoreLibs\Debug\Support::printBool(
$input,
$params['name'],
$params['true']
);
} elseif (isset($params['name'])) {
$string = \CoreLibs\Debug\Support::printBool(
$input,
$params['name']
);
} else {
$string = \CoreLibs\Debug\Support::printBool($input);
}
$this->assertEquals(
$expected,
$string,
'assert printBool'
);
}
/**
* Undocumented function
*
* @cover ::printToString
* @dataProvider printToStringProvider
* @testdox printToString $input with $flag will be $expected [$_dataName]
*
* @param mixed $input anything
* @param boolean|null $flag html flag, only for string and array
* @param string $expected always string
* @return void
*/
public function testPrintToString(mixed $input, ?bool $flag, string $expected): void
{
if ($flag === null) {
// if expected starts with / and ends with / then this is a regex compare
if (
substr($expected, 0, 1) == '/' &&
substr($expected, -1, 1) == '/'
) {
$this->assertMatchesRegularExpression(
$expected,
\CoreLibs\Debug\Support::printToString($input)
);
} else {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printToString($input)
);
}
} else {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::printToString($input, $flag)
);
}
}
/**
* Undocumented function
*
* @cover ::getCallerMethod
* @testWith ["testGetCallerMethod"]
* @testdox getCallerMethod check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerMethod(string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerMethod()
);
}
/**
* Undocumented function
*
* @cover ::getCallerMethodList
* @testWith [["main", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"],["include", "main", "run", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"]]
* @testdox getCallerMethodList check if it returns $expected [$_dataName]
*
* @param array $expected
* @return void
*/
public function testGetCallerMethodList(array $expected, array $expected_group): void
{
$compare = \CoreLibs\Debug\Support::getCallerMethodList();
// if we direct call we have 10, if we call as folder we get 11
if (count($compare) == 10) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected 10'
);
} else {
$this->assertEquals(
$expected_group,
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected group'
);
}
}
/**
* Undocumented function
*
* @cover ::getCallerClass
* @testWith ["PHPUnit\\TextUI\\Command"]
* @testdox getCallerClass check if it returns $expected [$_dataName]
*
* @return void
*/
public function testGetCallerClass(string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerClass()
);
}
/** /**
* Undocumented function * Undocumented function
* *
@@ -453,19 +675,19 @@ final class CoreLibsDebugSupportTest extends TestCase
if ($replace === null && $flag === null) { if ($replace === null && $flag === null) {
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Debug\Support::debugString($input), Support::debugString($input),
'assert all default' 'assert all default'
); );
} elseif ($flag === null) { } elseif ($flag === null) {
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Debug\Support::debugString($input, $replace), Support::debugString($input, $replace),
'assert flag default' 'assert flag default'
); );
} else { } else {
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Debug\Support::debugString($input, $replace, $flag), Support::debugString($input, $replace, $flag),
'assert all set' 'assert all set'
); );
} }

View File

@@ -67,17 +67,17 @@ final class CoreLibsGetSystemTest extends TestCase
'original set' => [ 'original set' => [
0 => null, 0 => null,
1 => 'NOHOST', 1 => 'NOHOST',
2 => 'NOPORT', 2 => 0,
], ],
'override set no port' => [ 'override set no port' => [
0 => 'foo.org', 0 => 'foo.org',
1 => 'foo.org', 1 => 'foo.org',
2 => '80' 2 => 80
], ],
'override set with port' => [ 'override set with port' => [
0 => 'foo.org:443', 0 => 'foo.org:443',
1 => 'foo.org', 1 => 'foo.org',
2 => '443' 2 => 443
] ]
]; ];
} }
@@ -138,10 +138,10 @@ final class CoreLibsGetSystemTest extends TestCase
* *
* @param string|null $input * @param string|null $input
* @param string $expected_host * @param string $expected_host
* @param string $expected_port * @param int $expected_port
* @return void * @return void
*/ */
public function testGetHostNanme(?string $input, string $expected_host, string $expected_port): void public function testGetHostNanme(?string $input, string $expected_host, int $expected_port): void
{ {
// print "HOSTNAME: " . $_SERVER['HTTP_HOST'] . "<br>"; // print "HOSTNAME: " . $_SERVER['HTTP_HOST'] . "<br>";
// print "SERVER: " . print_r($_SERVER, true) . "\n"; // print "SERVER: " . print_r($_SERVER, true) . "\n";

View File

@@ -0,0 +1,349 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Logging\Logger\Level;
/**
* Test class for Logging
* @coversDefaultClass \CoreLibs\Logging\ErrorMessages
* @testdox \CoreLibs\Logging\ErrorMEssages method tests
*/
final class CoreLibsLoggingErrorMessagesTest extends TestCase
{
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
/**
* tear down and remove log data
*
* @return void
*/
public static function tearDownAfterClass(): void
{
array_map('unlink', glob(self::LOG_FOLDER . '*.log'));
}
/**
* for checking level only
*
* @return array
*/
public function providerErrorMessageLevel(): array
{
return [
'ok' => [
'level' => 'ok',
'str' => 'OK',
'expected' => 'ok',
],
'info' => [
'level' => 'info',
'str' => 'INFO',
'expected' => 'info',
],
'warn' => [
'level' => 'warn',
'str' => 'WARN',
'expected' => 'warn'
],
'warning' => [
'level' => 'warning',
'str' => 'WARN',
'expected' => 'warn'
],
'error' => [
'level' => 'error',
'str' => 'ERROR',
'expected' => 'error'
],
'abort' => [
'level' => 'abort',
'str' => 'ABORT',
'expected' => 'abort'
],
'crash' => [
'level' => 'crash',
'str' => 'CRASH',
'expected' => 'crash'
],
'wrong level' => [
'level' => 'wrong',
'str' => 'WRONG',
'expected' => 'unknown'
]
];
}
/**
* Undocumented function
*
* @dataProvider providerErrorMessageLevel
* @testdox error message level: $level will be $expected [$_dataName]
*
* @param string $level
* @param string $str
* @param string $expected
* @return void
*/
public function testErrorMessageLevelOk(string $level, string $str, string $expected): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug,
]);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setMessage(
$level,
$str
);
$this->assertEquals(
[
'level' => $expected,
'str' => $str,
'id' => '',
'target' => '',
'target_style' => '',
'highlight' => [],
],
$em->getLastErrorMsg()
);
}
/**
* Undocumented function
*
* @testdox Test of all methods for n messages [$_dataName]
*
* @return void
*/
public function testErrorMessageOk(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug
]);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setErrorMsg(
'100',
'info',
'INFO MESSAGE'
);
$this->assertEquals(
[
'id' => '100',
'level' => 'info',
'str' => 'INFO MESSAGE',
'target' => '',
'target_style' => '',
'highlight' => [],
],
$em->getLastErrorMsg()
);
$this->assertEquals(
['100'],
$em->getErrorIds()
);
$this->assertEquals(
[
[
'id' => '100',
'level' => 'info',
'str' => 'INFO MESSAGE',
'target' => '',
'target_style' => '',
'highlight' => [],
]
],
$em->getErrorMsg()
);
$em->setErrorMsg(
'200',
'error',
'ERROR MESSAGE'
);
$this->assertEquals(
[
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'target' => '',
'target_style' => '',
'highlight' => [],
],
$em->getLastErrorMsg()
);
$this->assertEquals(
['100', '200'],
$em->getErrorIds()
);
$this->assertEquals(
[
[
'id' => '100',
'level' => 'info',
'str' => 'INFO MESSAGE',
'target' => '',
'target_style' => '',
'highlight' => [],
],
[
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'target' => '',
'target_style' => '',
'highlight' => [],
]
],
$em->getErrorMsg()
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerErrorMessageLog(): array
{
return [
'error, not logged' => [
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'message' => null,
'log_error' => null,
'expected' => '<ERROR> ERROR MESSAGE',
],
'error, logged' => [
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'message' => null,
'log_error' => true,
'expected' => '<ERROR> ERROR MESSAGE',
],
'error, logged, message' => [
'id' => '200',
'level' => 'error',
'str' => 'ERROR MESSAGE',
'message' => 'OTHER ERROR MESSAGE',
'log_error' => true,
'expected' => '<ERROR> OTHER ERROR MESSAGE',
],
'crash' => [
'id' => '300',
'level' => 'crash',
'str' => 'CRASH MESSAGE',
'message' => null,
'log_error' => null,
'expected' => '<ALERT> CRASH MESSAGE',
],
'crash, message' => [
'id' => '300',
'level' => 'crash',
'str' => 'CRASH MESSAGE',
'message' => 'OTHER CRASH MESSAGE',
'log_error' => null,
'expected' => '<ALERT> OTHER CRASH MESSAGE',
],
'abort' => [
'id' => '200',
'level' => 'abort',
'str' => 'ABORT MESSAGE',
'message' => null,
'log_error' => null,
'expected' => '<CRITICAL> ABORT MESSAGE',
],
'abort, message' => [
'id' => '200',
'level' => 'abort',
'str' => 'ABORT MESSAGE',
'message' => 'OTHER ABORT MESSAGE',
'log_error' => null,
'expected' => '<CRITICAL> OTHER ABORT MESSAGE',
],
'unknown' => [
'id' => '400',
'level' => 'wrong level',
'str' => 'WRONG LEVEL MESSAGE',
'message' => null,
'log_error' => null,
'expected' => '<EMERGENCY> WRONG LEVEL MESSAGE',
],
'unknown, message' => [
'id' => '400',
'level' => 'wrong level',
'str' => 'WRONG LEVEL MESSAGE',
'message' => 'OTHER WRONG LEVEL MESSAGE',
'log_error' => null,
'expected' => '<EMERGENCY> OTHER WRONG LEVEL MESSAGE',
],
];
}
/**
* Undocumented function
*
* @dataProvider providerErrorMessageLog
* @testdox Test Log writing [$_dataName]
*
* @param string $id
* @param string $level
* @param string $str
* @param string|null $message
* @param bool|null $log_error
* @param string $expected
* @return void
*/
public function testErrorMessageLog(
string $id,
string $level,
string $str,
?string $message,
?bool $log_error,
string $expected
): void {
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testErrorMessages',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Debug,
'log_per_run' => true
]);
$em = new \CoreLibs\Logging\ErrorMessage($log);
$em->setErrorMsg(
$id,
$level,
$str,
message: $message,
log_error: $log_error
);
$file_content = '';
if (is_file($log->getLogFolder() . $log->getLogFile())) {
$file_content = file_get_contents(
$log->getLogFolder() . $log->getLogFile()
) ?: '';
}
// if n
if ($level == 'error' && ($log_error === null || $log_error === false)) {
$this->assertStringNotContainsString(
$expected,
$file_content
);
} else {
$this->assertStringContainsString(
$expected,
$file_content
);
}
}
}
// __END__

View File

@@ -0,0 +1,847 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Logging\Logger\Level;
use CoreLibs\Logging\Logger\Flag;
// TODO: setLogPer test log file written matches pattern
/**
* Test class for Logging
* @coversDefaultClass \CoreLibs\Logging\Logging
* @testdox \CoreLibs\Logging\Logging method tests
*/
final class CoreLibsLoggingLoggingTest extends TestCase
{
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
. "\[[\w\-\.\/]+:\d+\]\s{1}" // folder/file
. "\[\w+\]\s{1}" // run id
. "{[\w\\\\]+((::|->)\w+)?}\s{1}"; // class
public static function tearDownAfterClass(): void
{
array_map('unlink', glob(self::LOG_FOLDER . '*.log'));
}
/**
* test set for options BASIC
*
* 0: options
* - null for NOT set
* 1: expected
* 2: override
* override:
* - constant for COSNTANTS
* - global for _GLOBALS
*
* @return array
*/
public function optionsProvider(): array
{
return [
'log folder set' => [
'options' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'log_file_id' => 'testClassInit',
],
'expected' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => [],
],
// -> deprecation warning, log_folder must be set
'no log folder set' => [
'options' => [
'log_file_id' => 'testClassInit'
],
'expected' => [
'log_folder' => getcwd() . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => []
],
// -> upcoming deprecated
'file_id set but not log_file_id' => [
'options' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'file_id' => 'testClassInit',
],
'expected' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => [],
],
// both file_id and log_file_id set -> WARNING
'file_id and log_file_id set' => [
'options' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'file_id' => 'testClassInit',
'log_file_id' => 'testClassInit',
],
'expected' => [
'log_folder' => DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'testClassInit',
],
'override' => [],
],
// no log file id set -> error,
'nothing set' => [
'options' => [],
'expected' => [
'log_folder' => getcwd() . DIRECTORY_SEPARATOR,
'log_level' => Level::Debug,
'log_file_id' => 'NOHOST-0_PHPUnit-TextUI-Command',
],
'override' => []
]
];
}
/**
* init logging class
*
* @dataProvider optionsProvider
* @testdox init test [$_dataName]
*
* @param array $options
* @param array $expected
* @param array $override
* @return void
*/
public function testClassInit(array $options, array $expected, array $override): void
{
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
if (!defined($var)) {
define($var, $value);
}
}
// for deprecated no log_folder set
// if base is defined and it does have AAASetupData set
// change the log_folder "Debug" to "AAASetupData"
if (
defined('BASE') &&
strpos(BASE, DIRECTORY_SEPARATOR . 'AAASetupData') !== false
) {
$expected['log_folder'] = str_replace(
DIRECTORY_SEPARATOR . 'Debug',
DIRECTORY_SEPARATOR . 'AAASetupData',
$expected['log_folder']
);
}
}
// if not log folder and constant set -> expect E_USER_DEPRECATION
if (!empty($override['constant']) && empty($options['log_folder'])) {
// the deprecation message
$deprecation_message = 'options: log_folder must be set. '
. 'Setting via BASE and LOG constants is deprecated';
// convert E_USER_DEPRECATED to a exception
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \Exception($errstr, $errno);
},
E_USER_DEPRECATED
);
// catch this with the message
$this->expectExceptionMessage($deprecation_message);
}
// alert for log file id with globals
if (!empty($override['constant']) && empty($options['log_file_id'])) {
//
}
// alert for log file id and file id set
if (
!empty($options['log_file_id']) &&
!empty($options['file_id'])
) {
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \InvalidArgumentException($errstr, $errno);
},
E_USER_WARNING
);
$error_message = 'options: "file_id" is deprecated use: "log_file_id".';
$this->expectExceptionMessage($error_message);
$this->expectException(\InvalidArgumentException::class);
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \Exception($errstr, $errno);
},
E_USER_DEPRECATED
);
$this->expectException(\Exception::class);
// $error_message = 'options: both log_file_id and log_id are set at the same time, will use log_file_id';
// $this->expectExceptionMessage($error_message);
}
// empty log folder
if (empty($override['constant']) && empty($options['log_folder'])) {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessageMatches("/^Missing mandatory option: \"/");
} elseif (empty($options['log_file_id']) && !empty($options['file_id'])) {
// the deprecation message
$deprecation_message = 'options: "file_id" is deprecated use: "log_file_id".';
// convert E_USER_DEPRECATED to a exception
set_error_handler(
static function (int $errno, string $errstr): never {
throw new \Exception($errstr, $errno);
},
E_USER_DEPRECATED
);
// catch this with the message
$this->expectExceptionMessage($deprecation_message);
}
$log = new \CoreLibs\Logging\Logging($options);
// reset error handler
restore_error_handler();
// check that settings match
$this->assertEquals(
$expected['log_folder'],
$log->getLogFolder(),
'log folder not matching'
);
$this->assertEquals(
$expected['log_file_id'],
$log->getLogFileId(),
'log file id not matching'
);
}
// test all setters/getters
// setLoggingLevel
// getLoggingLevel
// loggingLevelIsDebug
/**
* Undocumented function
*
* @covers ::setLoggingLevel
* @covers ::getLoggingLevel
* @covers ::loggingLevelIsDebug
* @testdox setLoggingLevel set/get checks
*
* @return void
*/
public function testSetLoggingLevel(): void
{
// valid that is not Debug
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Info
]);
$this->assertEquals(
Level::Info,
$log->getLoggingLevel()
);
$this->assertFalse(
$log->loggingLevelIsDebug()
);
// not set, should be debug]
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
Level::Debug,
$log->getLoggingLevel()
);
$this->assertTrue(
$log->loggingLevelIsDebug()
);
// invalid, should be debug, will throw excpetion too
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Option: "log_level" is not of instance \CoreLibs\Logging\Logger\Level');
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
'log_level' => 'I'
]);
$this->assertEquals(
Level::Debug,
$log->getLoggingLevel()
);
$this->assertTrue(
$log->loggingLevelIsDebug()
);
// set valid, then change
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLoggingLevel',
'log_folder' => self::LOG_FOLDER,
'log_level' => Level::Info
]);
$this->assertEquals(
Level::Info,
$log->getLoggingLevel()
);
$log->setLoggingLevel(Level::Notice);
$this->assertEquals(
Level::Notice,
$log->getLoggingLevel()
);
// illegal logging level
$this->expectException(\Psr\Log\InvalidArgumentException::class);
$this->expectExceptionMessageMatches("/^Level \"NotGood\" is not defined, use one of: /");
$log->setLoggingLevel('NotGood');
}
// setLogFileId
// getLogFileId
/**
* Undocumented function
*
* @covers ::setLogFileId
* @covers ::getLogFileId
* @testdox setLogFileId set/get checks
*
* @return void
*/
public function testLogFileId(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogFileId',
'log_folder' => self::LOG_FOLDER
]);
// bad, keep same
$log->setLogFileId('$$##$%#$%&');
$this->assertEquals(
'testLogFileId',
$log->getLogFileId()
);
// good, change
$log->setLogFileId('validID');
$this->assertEquals(
'validID',
$log->getLogFileId()
);
// invalid on init
$this->expectException(\Psr\Log\InvalidArgumentException::class);
$this->expectExceptionMessage('LogFileId: no log file id set');
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => '$$$"#"#$"#$',
'log_folder' => self::LOG_FOLDER
]);
}
// setLogUniqueId
// getLogUniqueId
/**
* Undocumented function
*
* @return array
*/
public function logUniqueIdProvider(): array
{
return [
'option set' => [
'option' => true,
'override' => false,
],
'direct set' => [
'option' => false,
'override' => false,
],
'override set' => [
'option' => false,
'override' => true,
],
'option and override set' => [
'option' => false,
'override' => true,
],
];
}
/**
* Undocumented function
*
* @covers ::setLogUniqueId
* @covers ::getLogUniqueId
* @dataProvider logUniqueIdProvider
* @testdox per run log id set test: option: $option, override: $override [$_dataName]
*
* @param bool $option
* @param bool $override
* @return void
*/
public function testLogUniqueId(bool $option, bool $override): void
{
if ($option === true) {
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER,
'log_per_run' => $option
]);
} else {
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogUniqueId',
'log_folder' => self::LOG_FOLDER
]);
$log->setLogUniqueId();
}
$per_run_id = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id,
'assert per log run id 1st'
);
if ($override === true) {
$log->setLogUniqueId(true);
$per_run_id_2nd = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id_2nd,
'assert per log run id 2nd'
);
$this->assertNotEquals(
$per_run_id,
$per_run_id_2nd,
'1st and 2nd don\'t match'
);
}
}
// setLogDate
// getLogDate
/**
* Undocumented function
*
* @covers ::setLogDate
* @covers ::getLogDate
* @testdox setLogDate set/get checks
*
* @return void
*/
public function testSetLogDate(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLogFileId',
'log_folder' => self::LOG_FOLDER,
'log_per_date' => true,
]);
$this->assertEquals(
date('Y-m-d'),
$log->getLogDate()
);
}
// setLogFlag
// getLogFlag
// unsetLogFlag
// getLogFlags
// Logger\Flag
/**
* Undocumented function
*
* @covers Logger\Flag
* @testdox Logger\Flag enum test
*
* @return void
*/
public function testLoggerFlag(): void
{
// logger flags to check that they exist
$flags = [
'all_off' => 0,
'per_run' => 1,
'per_date' => 2,
'per_group' => 4,
'per_page' => 8,
'per_class' => 16,
'per_level' => 32,
];
// from int -> return value
foreach ($flags as $name => $value) {
$this->assertEquals(
Flag::fromName($name),
Flag::fromValue($value)
);
}
}
/**
* Undocumented function
*
* @covers ::setLogFlag
* @covers ::getLogFlag
* @covers ::unsetLogFlag
* @covers ::getLogFlags
* @testdox setLogDate set/get checks
*
* @return void
*/
public function testSetLogFlag(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogFlag',
'log_folder' => self::LOG_FOLDER,
]);
// set valid log flag
$log->setLogFlag(Flag::per_run);
$this->assertTrue(
$log->getLogFlag(Flag::per_run)
);
// flags seum
$this->assertEquals(
Flag::per_run->value,
$log->getLogFlags(),
);
// unset valid log flag
$log->unsetLogFlag(Flag::per_run);
$this->assertFalse(
$log->getLogFlag(Flag::per_run)
);
// illegal Flags cannot be set, they will throw eerros onrun
// test multi set and sum is equals set
$log->setLogFlag(Flag::per_date);
$log->setLogFlag(Flag::per_group);
$this->assertEquals(
Flag::per_date->value + Flag::per_group->value,
$log->getLogFlags()
);
}
// setLogFolder
// getLogFolder
/**
* Undocumented function
*
* @covers ::setLogFolder
* @covers ::getLogFolder
* @testdox setLogFolder set/get checks, init check
*
* @return void
*/
public function testSetLogFolder(): void
{
// set to good folder
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogFolder',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
self::LOG_FOLDER,
$log->getLogFolder()
);
// set to a good other folder
$log->setLogFolder(DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR);
$this->assertEquals(
DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
$log->getLogFolder()
);
// good other folder with missing trailing slash
$log->setLogFolder(DIRECTORY_SEPARATOR . 'tmp');
$this->assertEquals(
DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
$log->getLogFolder()
);
// a bad folder -> last good folder
$log->setLogFolder('I-am-not existing');
$this->assertEquals(
DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR,
$log->getLogFolder()
);
// init with a bad folder
$this->expectException(\Psr\Log\InvalidArgumentException::class);
$this->expectExceptionMessage('Folder: "I-am-bad" is not writeable for logging');
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogFolderInvalid',
'log_folder' => 'I-am-bad',
]);
}
// getLogFile (no set, only correct after log run)
// setLogMaxFileSize
// getLogMaxFileSize
/**
* Undocumented function
*
* @covers ::setLogMaxFileSize
* @covers ::getLogMaxFileSize
* @testdox setLogMaxFileSize set/get checks, init check
*
* @return void
*/
public function testSetLogMaxFileSize(): void
{
// init set to 0
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testSetLogMaxFileSize',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
0,
$log->getLogMaxFileSize()
);
// set to new, valid size
$file_size = 200 * 1024;
$valid = $log->setLogMaxFileSize($file_size);
$this->assertTrue($valid);
$this->assertEquals(
$file_size,
$log->getLogMaxFileSize()
);
// invalid size, < 0, will be last and return false
$valid = $log->setLogMaxFileSize(-1);
$this->assertFalse($valid);
$this->assertEquals(
$file_size,
$log->getLogMaxFileSize()
);
// too small (< MIN_LOG_MAX_FILESIZE)
$valid = $log->setLogMaxFileSize($log::MIN_LOG_MAX_FILESIZE - 1);
$this->assertFalse($valid);
$this->assertEquals(
$file_size,
$log->getLogMaxFileSize()
);
}
// getOption (option params)
/**
* Undocumented function
*
* @covers ::getOption
* @testdox getOption checks
*
* @return void
*/
public function testGetOption(): void
{
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testGetOption',
'log_folder' => self::LOG_FOLDER,
]);
$this->assertEquals(
self::LOG_FOLDER,
$log->getOption('log_folder')
);
// not found
$this->assertNull(
$log->getOption('I_do not exist')
);
}
// test all logger functions
// debug (special)
// info
// notice
// warning
// error
// critical
// alert
// emergency
/**
* Undocumented function
*
* @covers ::debug
* @testdox debug checks
*
* @return void
*/
public function testDebug(): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testDebug',
'log_folder' => self::LOG_FOLDER,
]);
// clean all data in folder first
array_map('unlink', glob($log->getLogFolder() . $log->getLogFileId() . '*.log'));
$group_id = 'G';
$message = 'D';
$log_status = $log->debug($group_id, $message);
$this->assertTrue($log_status, 'debug write successful');
$file_content = file_get_contents(
$log->getLogFolder() . $log->getLogFile()
) ?: '';
$log_level = $log->getLoggingLevel()->getName();
// [2023-05-30 15:51:39.36128800] [NOHOST:0]
// [www/vendor/bin/phpunit] [7b9d0747] {PHPUnit\TextUI\Command}
// <DEBUG:G> D
$this->assertMatchesRegularExpression(
"/" . self::REGEX_BASE
. "<$log_level:$group_id>\s{1}" // log level / group id
. "$message" // message
. "/",
$file_content
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerLoggingLevelWrite(): array
{
return [
'info' => [
'message' => 'I',
'file_id' => Level::Info->name,
'level' => Level::Info
],
'notice' => [
'message' => 'N',
'file_id' => Level::Notice->name,
'level' => Level::Notice
],
'warning' => [
'message' => 'W',
'file_id' => Level::Warning->name,
'level' => Level::Warning
],
'error' => [
'message' => 'E',
'file_id' => Level::Error->name,
'level' => Level::Error
],
'crticial' => [
'message' => 'C',
'file_id' => Level::Critical->name,
'level' => Level::Critical
],
'alert' => [
'message' => 'A',
'file_id' => Level::Alert->name,
'level' => Level::Alert
],
'emergency' => [
'message' => 'Em',
'file_id' => Level::Emergency->name,
'level' => Level::Emergency
],
];
}
/**
* Undocumented function
*
* @covers ::info
* @covers ::notice
* @covers ::warning
* @covers ::error
* @covers ::critical
* @covers ::alert
* @covers ::emergency
* @dataProvider providerLoggingLevelWrite
* @testdox logging level write checks for $level [$_dataName]
*
* @return void
*/
public function testLoggingLevelWrite(string $message, string $file_id, Level $level): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'test' . $file_id,
'log_folder' => self::LOG_FOLDER,
'log_level' => $level,
]);
// clean all data in folder first
array_map('unlink', glob($log->getLogFolder() . $log->getLogFileId() . '*.log'));
switch ($level->value) {
case 200:
$log_status = $log->info($message);
break;
case 250:
$log_status = $log->notice($message);
break;
case 300:
$log_status = $log->warning($message);
break;
case 400:
$log_status = $log->error($message);
break;
case 500:
$log_status = $log->critical($message);
break;
case 550:
$log_status = $log->alert($message);
break;
case 600:
$log_status = $log->emergency($message);
break;
}
$this->assertTrue($log_status, 'log write successful');
$file_content = file_get_contents(
$log->getLogFolder() . $log->getLogFile()
) ?: '';
$log_level = $log->getLoggingLevel()->getName();
$this->assertMatchesRegularExpression(
"/" . self::REGEX_BASE
. "<$log_level>\s{1}" // log level / group id
. "$message" // message
. "/",
$file_content,
'log message regex'
);
}
// check log level that writer writes in correct level
// also that non debug ignores prefix/group
/**
* Undocumented function
*
* @covers ::log
* @testdox log() general call test
*
* @return void
*/
public function testLoggingLog(): void
{
// init logger
$log = new \CoreLibs\Logging\Logging([
'log_file_id' => 'testLoggingLog',
'log_folder' => self::LOG_FOLDER,
'log_per_level' => true,
]);
$log_ok = $log->log(Level::Debug, 'DEBUG', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (debug) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_DEBUG.log'
);
$log_ok = $log->log(Level::Info, 'INFO', group_id: 'GROUP_ID', prefix: 'PREFIX:');
$this->assertTrue($log_ok, 'assert ::log (info) OK');
$this->assertEquals(
$log->getLogFile(),
$log->getLogFileId() . '_INFO.log'
);
}
// must test flow:
// init normal
// log -> check file name
// set per date
// log -> check file name
// and same for per_run
// deprecated calls check?
}
// __END__

3
test/phpunit/Logging/log/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*log
*LOG
!.gitignore

View File

@@ -7,11 +7,11 @@ namespace tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/** /**
* Test class for Check\Password * Test class for Security\Password
* @coversDefaultClass \CoreLibs\Check\Password * @coversDefaultClass \CoreLibs\Security\Password
* @testdox \CoreLibs\Check\Password method tests * @testdox \CoreLibs\Security\Password method tests
*/ */
final class CoreLibsCheckPasswordTest extends TestCase final class CoreLibsSecurityPasswordTest extends TestCase
{ {
public function passwordProvider(): array public function passwordProvider(): array
{ {
@@ -46,7 +46,7 @@ final class CoreLibsCheckPasswordTest extends TestCase
{ {
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Check\Password::passwordVerify($input, \CoreLibs\Check\Password::passwordSet($input_hash)) \CoreLibs\Security\Password::passwordVerify($input, \CoreLibs\Security\Password::passwordSet($input_hash))
); );
} }
@@ -65,7 +65,7 @@ final class CoreLibsCheckPasswordTest extends TestCase
{ {
$this->assertEquals( $this->assertEquals(
$expected, $expected,
\CoreLibs\Check\Password::passwordRehashCheck($input) \CoreLibs\Security\Password::passwordRehashCheck($input)
); );
} }
} }

View File

@@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Security\CreateKey;
use CoreLibs\Security\SymmetricEncryption;
/**
* Test class for Security\SymmetricEncryption and Security\CreateKey
* @coversDefaultClass \CoreLibs\Security\SymmetricEncryption
* @testdox \CoreLibs\Security\SymmetricEncryption method tests
*/
final class CoreLibsSecuritySymmetricEncryptionTest extends TestCase
{
/**
* Undocumented function
*
* @return array
*/
public function providerEncryptDecryptSuccess(): array
{
return [
'valid string' => [
'input' => 'I am a secret',
'expected' => 'I am a secret',
],
];
}
/**
* test encrypt/decrypt produce correct output
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptDecryptSuccess
* @testdox encrypt/decrypt $input must be $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testEncryptDecryptSuccess(string $input, string $expected): void
{
$key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt($input, $key);
$decrypted = SymmetricEncryption::decrypt($encrypted, $key);
$this->assertEquals(
$expected,
$decrypted
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerEncryptFailed(): array
{
return [
'wrong decryption key' => [
'input' => 'I am a secret',
'excpetion_message' => 'Invalid Key'
],
];
}
/**
* Test decryption with wrong key
*
* @covers ::generateRandomKey
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerEncryptFailed
* @testdox decrypt with wrong key $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testEncryptFailed(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt($input, $key);
$wrong_key = CreateKey::generateRandomKey();
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decrypt($encrypted, $wrong_key);
}
/**
* Undocumented function
*
* @return array
*/
public function providerWrongKey(): array
{
return [
'not hex key' => [
'key' => 'not_a_hex_key',
'exception_message' => 'Invalid hex key'
],
'too short hex key' => [
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
'excpetion_message' => 'Key is not the correct size (must be '
. 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).'
],
];
}
/**
* test invalid key provided to decrypt or encrypt
*
* @covers ::encrypt
* @covers ::decrypt
* @dataProvider providerWrongKey
* @testdox wrong key $key throws $exception_message [$_dataName]
*
* @param string $key
* @param string $exception_message
* @return void
*/
public function testWrongKey(string $key, string $exception_message): void
{
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::encrypt('test', $key);
// we must encrypt valid thing first so we can fail with the wrong kjey
$enc_key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt('test', $enc_key);
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decrypt($encrypted, $key);
}
/**
* Undocumented function
*
* @return array
*/
public function providerWrongCiphertext(): array
{
return [
'too short ciphertext' => [
'input' => 'short',
'exception_message' => 'Invalid ciphertext (too short)'
],
];
}
/**
* Undocumented function
*
* @covers ::decrypt
* @dataProvider providerWrongCiphertext
* @testdox too short ciphertext $input throws $exception_message [$_dataName]
*
* @param string $input
* @param string $exception_message
* @return void
*/
public function testWrongCiphertext(string $input, string $exception_message): void
{
$key = CreateKey::generateRandomKey();
$this->expectExceptionMessage($exception_message);
SymmetricEncryption::decrypt($input, $key);
}
}
// __END__

View File

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

View File

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

View File

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