Compare commits

..

562 Commits

Author SHA1 Message Date
Clemens Schwaighofer
9a28c86991 json test file update 2026-01-22 11:51:32 +09:00
Clemens Schwaighofer
38b2ffe82a jsonConvertToArray with JSON_INVALID_UTF8_IGNORE does not work
json_decode JSON_INVALID_UTF8_IGNORE is not honoring any of those flags at the moment
2026-01-22 11:44:11 +09:00
Clemens Schwaighofer
6e547abccb Fix text in check runners 2026-01-22 11:31:01 +09:00
Clemens Schwaighofer
676af5e1a4 Add json validation and update tests
Also add removal of THROW flag for json decode to not throw an exception if if wanted

Add jquery 4.0.0 libs
2026-01-22 11:08:18 +09:00
Clemens Schwaighofer
118aacee28 Bug in RamdomKey::validateRandomKeyData when passing large character sets
The check for stringlength was done on the set key range in the class var and not the variable passed on in the method.
So if we cold call randomKey with a key character list and not the default list it failed with invalid max length
2026-01-14 15:07:38 +09:00
Clemens Schwaighofer
564e23ecd7 Update and fix Strings stringToTime()
Allow parsing of more flexible interval strings including
long names (day, hour, minute, second, millisecond),
negative values, no spaces between components, and
throwing exceptions on invalid input if requested.

The following types are now allowed
- d|day|days
- h|hour|hours
- m|min|mins|minute|minutes
- s|sec|secs|second|seconds
- ms|msec|msecs|msecond|mseconds|millis|millisec|millisecs|millisecond|milliseconds

Also fix the milisecond parsing that was done completly wrong
the milliseoncds where just added after a "." as decimals without converting them at all.
Now the value is divided by 1000 and added to the existing number, and as before only if ms exist

The negative check is now included in the main parse regex, so a second regex check is no longer necessary

Spaces between values, before or anywhere are now more flexible.

Exceptions are thrown if the regex cannot parse anything, or it returns only one master entry and no matches
2026-01-14 13:16:53 +09:00
Clemens Schwaighofer
2889012592 Add no dash character list allowed in Strings parseCharacterRanges function
Same rules with returning unique elements array
2026-01-14 10:49:14 +09:00
Clemens Schwaighofer
cd65604073 Add parseCharacterRanges function to Strings.php and tests
Parses character ranges like A-Z into individual characters and returns as an array
2026-01-14 10:36:09 +09:00
Clemens Schwaighofer
a3cf5f45f9 Fix function name in Create/RandomKey 2026-01-07 17:21:26 +09:00
Clemens Schwaighofer
6f3dacdec0 Remove double color settings entry in Phan config 2026-01-07 13:32:31 +09:00
Clemens Schwaighofer
2ab1ee90ef Add color output in phan 2026-01-07 13:17:54 +09:00
Clemens Schwaighofer
b8c0aff975 Update phive phars with correct version and update scripts to use both
new "-c" switch for all checking scripts to swtich to the composer version from the phive installed version

NOTE: phpstan plugins only work in the composer version.

Default is the phive version
2026-01-06 18:15:50 +09:00
Clemens Schwaighofer
c5fed66237 PHP 8.5 fixes and updates
All tested with PHP 8.4 and PHP 8.3 too

Major changes:
- cube root Math (cbrt) now throws InvalidArgumentException if NAN is returned instead of returning NAN
- Byte convert from string to int will throw errors if value is too large (\LengthException)
- new flag for returning string type but for this bcmath must be installed (\RuntimeException if no bcmath)
- Updated curl class and remove close handler as not needed and deprecated as of PHP 8.5
- Curl phpunit tests: convert string to JSON convert flow for return content check (to avoid per PHP version check)
- image close handler for ImageMagick removed as not needed and deprecated as of PHP 8.5
- updated all check calls too use phive tools if possible (except phpunit) and all scripts can have dynamic php version set
2026-01-06 15:55:47 +09:00
Clemens Schwaighofer
157169d3ba Fix gittignore for package lock json 2025-11-27 17:59:53 +09:00
Clemens Schwaighofer
60fe0e0def Remove package lock for npm, add it to gitignore 2025-11-27 17:58:55 +09:00
Clemens Schwaighofer
e473e7899d Fix logging line and call method information
The logging line number and file was for the previous call position, not for
where the actual log entry was called

Also fix for ErrorMessage class calls with shifting the start position up depending on which method is called.

Output shows file and line where the message/log call was done and the function/class method where the log call was done
2025-11-27 17:54:28 +09:00
Clemens Schwaighofer
8af71b70a3 general SQL update to use uuid for uid, update edit.jq.js for some testing 2025-11-06 11:51:29 +09:00
Clemens Schwaighofer
7d10b4c5af Change UUIdv4 validation to properly check for version 4 UUIDs 2025-11-04 11:51:08 +09:00
Clemens Schwaighofer
c81b602657 phpunit: redirect error_log message to temp file so they are not printed to console
This is for any logging part that logs to emergency where emergency logging is
set to log to the error_log too
2025-10-09 15:05:00 +09:00
Clemens Schwaighofer
59cc5f2060 phpstan checks and fixes 2025-10-09 11:58:47 +09:00
Clemens Schwaighofer
e072aaf4d6 Add ISO Type datetime format to support, and make this default loggint time format 2025-10-09 11:14:06 +09:00
Clemens Schwaighofer
259f9cebf3 Had to roll back previous MessageLevel changes
Keep everything lower case as is
any upper case check have to go through fromName, this will convert to lowercase anyway
2025-09-10 13:41:42 +09:00
Clemens Schwaighofer
bd4f674f0f Add a noset level and add uppercase support to fromName()
Noset is for unset status, which is different from unknown.
Also allow upper case so we can use "OK" and "ERROR" in addition to "ok" and "error".
2025-09-10 13:28:39 +09:00
Clemens Schwaighofer
87293bf633 Add a security info md file 2025-08-26 14:14:36 +09:00
Clemens Schwaighofer
be46d6e101 Readme update for url fix for the JS utils files 2025-08-18 09:36:10 +09:00
Clemens Schwaighofer
433bc3d539 All array variable names are no longer $array to not confuse 2025-07-23 15:22:25 +09:00
Clemens Schwaighofer
4ed645bac3 Update email check with better domain name check (ASCII), logging class debug output update 2025-07-15 13:13:11 +09:00
Clemens Schwaighofer
908376c1a5 Array sort method doc update 2025-06-26 11:43:28 +09:00
Clemens Schwaighofer
c329e7a2da Add JSON_UNESCAPED_UNICODE as default flag for json convert to array calls 2025-06-26 11:39:38 +09:00
Clemens Schwaighofer
ad7b59e26a phan check swich from phive to composer package 2025-06-05 18:02:03 +09:00
Clemens Schwaighofer
c43bb0662d check scripts update: phan from phive is too old 2025-06-05 18:01:12 +09:00
Clemens Schwaighofer
c4e83f94e9 Check scripts update 2025-06-05 17:56:29 +09:00
Clemens Schwaighofer
a292abc2c5 phpstan updates 2025-06-05 15:52:43 +09:00
Clemens Schwaighofer
6c5af91386 Add new Logging option to turn on error_log write
On default the level for error_log write is Emergency.
This can be changed either on class creation or via set/get methods.

If logging is skipped because the logging level does not match the main logging level the error_log write is also skipped.

Added MARK fields in the Logging class
2025-06-05 15:32:39 +09:00
Clemens Schwaighofer
73fc74a43a Fix old basic class call for random key length init 2025-06-05 14:47:02 +09:00
Clemens Schwaighofer
b89238b922 Merge branch 'release/v9.34.0' into feature/TTD-2650/string-class-update-with-string-check-helpers 2025-06-05 14:44:26 +09:00
Clemens Schwaighofer
9115fc9557 phan and phpstan fixes 2025-06-05 14:43:18 +09:00
Clemens Schwaighofer
62d9cda3d0 Add fix for splitFormatString with format parameter without any split characters
Other phpstan fixes
2025-06-05 14:37:42 +09:00
Clemens Schwaighofer
dbc72472f9 Strings regex validation
Update regex validation and add validation class to return full information. Add helper to return last error message from defined constant
2025-06-05 14:29:04 +09:00
Clemens Schwaighofer
3be3519e45 Add new funtions and update
- ksortArray and sortArray
Sort array and return sorted output in one flow. Allows for case insensitve sort, reverse sort

- selectArrayFromOption
select array blocks based on a "key": "value" match.
Can do recusrive with flat or not flat output, strict matching, case insenstivie
The flat combined character can be changed

- findArraysMissingKey
Search an array for a matching value with optional key match and return array block if in this block a key (or keys) are missing
The matching for key and value is always strict, eg 2 and '2' are different,
Found path is added with ":" separators, can be overridden by parameter

- arraySearchSimple
Allow search values to be array for multiple matching (any match)
2025-06-05 13:47:32 +09:00
Clemens Schwaighofer
4707427ff4 Add phpunit tests for checking valid regex 2025-06-04 15:48:32 +09:00
Clemens Schwaighofer
73ac0b68b6 Add valid regex string check 2025-06-04 15:48:08 +09:00
Clemens Schwaighofer
a501fa25de Add jsonPrettyPrint for formatted JSON output
Can be used for any debug output when needed
2025-06-04 15:09:20 +09:00
Clemens Schwaighofer
d4db235e5b Add new split string, update split string format, add create string from array list, char in char list, remove duplicates
NEW:
- remove duplicates in string
- check character string list in other character string list
- build character string from array (or nested array) values
- split string with fixed split length

UPDATE:
- split string with format
* throw exceptions for wrong paramters
* remove the "split chracters", as they get extracted from the format string
2025-06-04 14:15:45 +09:00
Clemens Schwaighofer
c70cdf457f Merge branch 'release/v9.34.0' into feature/TTD-2650/string-class-update-with-string-check-helpers 2025-06-04 14:12:01 +09:00
Clemens Schwaighofer
57aae073d7 Use string buildCharStringsFromList function 2025-06-04 14:11:57 +09:00
Clemens Schwaighofer
4bebec2b47 random key fixes for phpstan checks 2025-06-04 11:58:20 +09:00
Clemens Schwaighofer
991750aa5f Update create random key class with custom character string
- remove set random key length methods, this is now set only during call
- add random key character list update, either via method set or during call
2025-06-04 11:39:58 +09:00
426afdc1ff class test array file updated 2025-05-29 11:23:50 +09:00
ffff65a76d Core JavaSCript libs update 2025-05-29 11:23:16 +09:00
Clemens Schwaighofer
c22e68f19a Phive update 2025-05-20 12:13:04 +09:00
Clemens Schwaighofer
074d5bed4c class test login logging update 2025-05-15 15:37:06 +09:00
Clemens Schwaighofer
93cb7e0cab DB IO Adjustments for cursor set check and table exists check 2025-04-22 11:04:22 +09:00
Clemens Schwaighofer
7fbce6529b Merge branch 'development' of github-omc:TBWA-EGPlus-Japan/Client-Projects.php-core-libraries into development 2025-04-22 10:53:19 +09:00
Clemens Schwaighofer
6e086fe7b3 Add array helper for modifying key of a key value array 2025-04-22 10:52:13 +09:00
Clemens Schwaighofer
0ec19d5b75 Add array helper for modifying key of a key value array 2025-04-22 10:36:54 +09:00
Clemens Schwaighofer
8134da349f DB IO add flag to ignore not existing on cache reset, and ignore in ACL Login
in the ACL login cache reset, set flag to ignore unset query data
2025-04-16 17:42:09 +09:00
Clemens Schwaighofer
8396f7856b ACL Login add page information and lookup
Add the full page information and a new file name to cuid lookup to the acl array.
Add a new method to check if a page name is in the list of pages that can be accessed by the user.
2025-04-15 18:38:14 +09:00
Clemens Schwaighofer
b18866077e Edit user settings class remove password as mandatory 2025-04-15 17:51:32 +09:00
Clemens Schwaighofer
a66cc09095 Fix phpstan problems in test db encryption file 2025-04-15 17:46:41 +09:00
Clemens Schwaighofer
1cfdc45107 Fix edit user missing error example for login user id field 2025-04-15 17:40:54 +09:00
Clemens Schwaighofer
07e46c91ab Add test decryption for pg crypto columns 2025-04-14 09:19:58 +09:00
Clemens Schwaighofer
8aee448c59 Update DB IO for query hash storage and parameter count
The parameter count methods in the PgSQL class have changed
- the function returns a unique list of $ parameters

The count is now done in the DB IO part where it counts over the unique array

Query hash is stored like the query for the current run one (reset on dbExec call).
The method to create the hash is renamed to dbBuildQueryHash instead of "Get".
The dbGetQueryHash function now just returns the last set query hash. There is a matching dbResetQueryHash for unsetting the query hash.
2025-04-09 11:35:02 +09:00
Clemens Schwaighofer
37367db878 Fix regex for $$ PostgresSQL string in convert placeholder 2025-04-07 19:44:18 +09:00
Clemens Schwaighofer
2d30d1d160 Rewrite DB param lookup
* Correct wrong comment lookup
* simplify regex by excluding comment and string blocks before
* simpler lookup for each type
* update checks for more tests for various special cases

In DB IO
* add a function to return all placeholders found in a query
* only numbered parameters are looked up
2025-04-07 17:30:30 +09:00
Clemens Schwaighofer
531229e8b7 Add DB Encryption tests 2025-04-07 12:05:06 +09:00
Clemens Schwaighofer
d09c20ff9d hash test page update 2025-04-07 09:09:45 +09:00
Clemens Schwaighofer
f4ddc5a5fc Add hash hmac to the Create Hash class 2025-04-07 09:05:37 +09:00
Clemens Schwaighofer
1791ec3908 phan and phpstan fixes for hash uses in CoreLibs 2025-04-04 15:17:42 +09:00
Clemens Schwaighofer
3d13f55c35 Update Hash Class
Add new constant: STANDARD_HASH for sha256
Deprecate DEFAULT_HASH is now STANDARD_HASH_SHORT

Deprecated
__sha1Short:
replace with __crc32b with the default parameter use_sha false
replace with sha1Short if use_sha is true

__hash:
replace with hashShort if default hash type
replace with hash for all others with new default STANDARD_HASH

__hashLong:
replace with hashLong

New:
hashShort: returns STANDARD_HASH_SHORT which is __hash default type
hashStd: returns STANDARD_HASH sha256
hash: switches to STANDARD_HASH as default type
2025-04-04 15:08:58 +09:00
Clemens Schwaighofer
cf1989819a phpstan fixes 2025-04-01 11:22:59 +09:00
Clemens Schwaighofer
b302fb4053 Add CombinedDateTime class calcDaysInteral wrapper functions
calcDaysIntervalNamedIndex for force using named index and returning only named index
calcDaysIntervalNumIndex for force using numeric index and returning only numeric index
2025-04-01 11:15:00 +09:00
Clemens Schwaighofer
32decdd037 Readme update 2025-03-28 10:58:07 +09:00
Clemens Schwaighofer
46cda40d37 JavaScript general utils file updates 2025-03-28 10:53:42 +09:00
Clemens Schwaighofer
e71df90144 Fully deprecate prototype edit.js, add deprecation warnings to edit.jq.js and add new utils
Note that all the utils.js are build in an external repository and just copied here
2025-03-10 11:00:02 +09:00
Clemens Schwaighofer
bbcc642fde All "edit.js" development has moved to a new repository
"Code-Blocks.javascript-utils"
2025-03-07 15:09:47 +09:00
Clemens Schwaighofer
558694aa6c Fix DEFAULT_ENCODING that it is string 2025-02-28 10:32:43 +09:00
Clemens Schwaighofer
f3bd09529a phpstan fixes 2025-02-28 10:29:04 +09:00
Clemens Schwaighofer
816bb7c9ee Allow encoding ovrride for htmlentities 2025-02-28 10:19:36 +09:00
Clemens Schwaighofer
fc7b705355 config.master.php file update
- remove not used code
- reorder defines for possible clean up targets
- TARGET and HOST_NAME are set early
HOST NAME is set right at the top
TARGET is set after site configs is read
- add more $_ENV reads
DEFAULT_ACL_LEVEL
LOCALE (encoding is read from locale which should be in the format of nn_CT.ENCODING, eg en_US.UTF-8), falls back to UTF-8
ADMIN.STYLESHEET
ADMIN.JAVASCRIPT
2025-02-28 10:17:10 +09:00
Clemens Schwaighofer
7b96c1f9ca Remove old eslint config, replaced with mjs one 2025-02-17 12:55:20 +09:00
Clemens Schwaighofer
26c6ebcea7 Merge branch 'NewFeatures' into Update-eslintrcToFlatLayout 2025-02-17 12:54:23 +09:00
Clemens Schwaighofer
32dee1692e Fix DateTime days internal counter
Fixed the bad coded include end date with using flags instead
Allow exclude of start date
Reverse counter fixed, and also includes weekend days

Add reverse for weekend in date interval

Login class: add numeric for ACL level

DB IO: some minor code clean up for not needed var set check

Some edit.jq.js clean ups and added
- loadEl: load element by id and return element value or throw error if not found
- goTo: scroll to an element with scroll into view call
2025-02-17 11:16:51 +09:00
Clemens Schwaighofer
6291ed88c0 eslint config update 2025-02-13 19:01:44 +09:00
Clemens Schwaighofer
5e21ead6fa change error catcher for javasript from log to error as output 2025-02-13 18:24:50 +09:00
Clemens Schwaighofer
07fbd13213 Setup npm with eslint 2025-02-13 18:24:30 +09:00
Clemens Schwaighofer
44b825310a Add ACL level number to unit detail 2025-02-07 19:06:35 +09:00
Clemens Schwaighofer
2c234ccef6 On config errors do not exit but throw exception 2025-01-29 09:57:58 +09:00
Clemens Schwaighofer
b493b3c4fd Remove debug message 2025-01-20 20:27:34 +09:00
Clemens Schwaighofer
e7dd96b5d9 Further fixes for PHP 8.4 2025-01-20 20:27:03 +09:00
Clemens Schwaighofer
bcde36ac17 DB IO Cache reset should not be an error
If the query is not found, do not throw an error, just show a warning
2025-01-20 10:45:31 +09:00
Clemens Schwaighofer
8bde34ec7d Fix bug in DB IO prepared statement with INSERT and auto RETURNING add
INSERT will get a RETURNING added automatically if it has a primary key
This was not checked when query was compared for prepared statements.

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

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

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

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

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

*IMPORTANT*
SmartyExtended($l10n, $logger, $cache_id, $compile_id)
The second argument is now the Logger class, this MUST be updated for all calls
2024-12-27 14:00:12 +09:00
Clemens Schwaighofer
fbe827e989 Update Smarty Extended for Smarty-extended v5 upgrade 2024-12-27 11:30:55 +09:00
Clemens Schwaighofer
c778a4eb81 Add phive back in for static tools like phpunit instead of using the composer package 2024-12-27 09:32:54 +09:00
Clemens Schwaighofer
ce1c72a0bc Bug fix for DB IO parameters in CASE calls 2024-12-24 12:43:30 +09:00
Clemens Schwaighofer
10319ef728 Fix throws type for AsymmetricAnonymousEncryption in the phpdoc part 2024-12-23 12:56:57 +09:00
Clemens Schwaighofer
8d0036eaac Fix phpdoc return types 2024-12-23 11:26:50 +09:00
Clemens Schwaighofer
d1e65c702e Allow Seession settings to be changed
eg set the auto write + others
or set/unset can be chagned for single sets
2024-12-20 18:48:00 +09:00
Clemens Schwaighofer
7248906da7 Allow chaining of key set functions for encryption 2024-12-20 15:13:22 +09:00
Clemens Schwaighofer
7f9a4dc04f Merge branch 'Feature-AsymmetricEncryption' into NewFeatures 2024-12-18 10:52:29 +09:00
Clemens Schwaighofer
10935214eb Fix Class file name for asymmetric anonymous encryption 2024-12-18 10:50:27 +09:00
Clemens Schwaighofer
41e116f7d4 phpstan checks for level 9 2024-12-18 10:11:47 +09:00
Clemens Schwaighofer
881c93c343 Asymmetric Anoymouse Encryption phpunit tests 2024-12-18 09:56:48 +09:00
Clemens Schwaighofer
185d044a0b Symmetric encryption key set tests 2024-12-17 18:23:10 +09:00
Clemens Schwaighofer
cc067cc202 Update symmetric encryption with compare/get key, empty key test, unset on end
All key and messages are set SensitiveParameter type
On end, unset the key parameter with sodium mem zero
Get/Compare key set methods
Additional check on empty key
Add missing sodium mem zero for inner function variable clean up
2024-12-17 15:18:06 +09:00
Clemens Schwaighofer
37e2e54b2a Add asymmetric anonymous encryption
Private/Public key encryption for anonymous messages (not receipient)
2024-12-17 15:16:48 +09:00
Clemens Schwaighofer
711b3bfe97 Remove E_STRICT from error reporting, it is deprecated 2024-12-13 18:45:27 +09:00
Clemens Schwaighofer
3bd21c75d8 Make the font-size for ACL Login template a bit smaller
1.5em was too large, 1.3em is better
2024-12-13 13:58:56 +09:00
Clemens Schwaighofer
4971f62490 ecuid name fix in test file 2024-12-13 11:42:45 +09:00
Clemens Schwaighofer
1cf4fdf31a Fix column named for edit_log to eu prefixed
as eucuid and eucuuid
2024-12-13 11:37:52 +09:00
Clemens Schwaighofer
d16b920966 Update arrayReturnMatchinKeyOnly description 2024-12-13 11:29:37 +09:00
Clemens Schwaighofer
ab52bf59b5 phan/phpstan fixes 2024-12-13 10:38:24 +09:00
Clemens Schwaighofer
a8dd076aac Merge branch 'NewFeatures' into Feature-LoginClassAddUuidv4 2024-12-13 10:27:49 +09:00
Clemens Schwaighofer
c17ca1f847 Merge branch 'development' 2024-12-13 10:21:41 +09:00
Clemens Schwaighofer
e349613d60 phpunit updates
Add testsuits for default run
Fix wording in testdox
add a fallback in the Debugging Support test suit
2024-12-13 10:17:28 +09:00
Clemens Schwaighofer
e9cfdb4bf0 Remove all deprecated tests 2024-12-13 09:35:54 +09:00
Clemens Schwaighofer
f966209e0a phpstan param declration fix for ACL Login user status 2024-12-12 21:20:09 +09:00
Clemens Schwaighofer
c13934de99 Fix for wrong key handling in Symmetric encryption 2024-12-12 21:09:41 +09:00
Clemens Schwaighofer
1e90bb677e Fix Symmetric encryption with wrong key handling
- static call encrypt: do not check pre set key
- indirect call: set new if key is different
2024-12-12 21:07:17 +09:00
Clemens Schwaighofer
540269e61f Fix update script for now to clock_timestamp 2024-12-12 19:04:21 +09:00
Clemens Schwaighofer
e793c3975b Change all db now to clock_timestamp for triggers 2024-12-12 12:02:49 +09:00
Clemens Schwaighofer
7d4c9724fe Fix session options argument declaration for phpstan 2024-12-11 21:10:00 +09:00
Clemens Schwaighofer
d1c4611431 Indent fix for ACL Login 2024-12-11 21:06:59 +09:00
Clemens Schwaighofer
8d3882a6fe Session and ACL Login Class update
Session:
regenerate session id after some time or random.
Default is 'never', can be 'interval' form 0 to 1h and random from always to 1 in 100
Session also checks that strict session settings are enabled

Login class:
Automatic re-read of acl settings after some time (default 5min, can be chnaged via option).
Default set strict headers, can be turned off via option
Moved various parts into their own methods and cleaned up double call logic.
Login is now recorded in the last login entry
no more debug flags are read from the database anymore
All options are set via array and not with a single option (was auto login)
2024-12-11 21:05:56 +09:00
Clemens Schwaighofer
2b0434e36b Merge branch 'NewFeatures' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-11 10:36:57 +09:00
Clemens Schwaighofer
ba11a936db DB IO remove debug placeholder output 2024-12-11 10:36:31 +09:00
Clemens Schwaighofer
df591659cb Merge branch 'NewFeatures' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-11 10:35:19 +09:00
Clemens Schwaighofer
5343034768 Fix DB IO placeholder detect and count regex
comment regex: (?:\-\-[^\r\n]*?\r?\n)*

Which is AFTER the element search as the comment can appear anywhere after the tag trigger
2024-12-11 10:30:41 +09:00
Clemens Schwaighofer
dec56c9559 Merge remote-tracking branch 'all/NewFeatures' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-10 15:28:59 +09:00
Clemens Schwaighofer
880f15ac6f Merge branch 'development' 2024-12-10 15:26:24 +09:00
Clemens Schwaighofer
a46601fe03 Sync folder is master and not trunk 2024-12-10 15:25:17 +09:00
Clemens Schwaighofer
022c39e791 Add missing phpunit test folder for deprecated session var load test 2024-12-10 15:24:45 +09:00
Clemens Schwaighofer
fdefaca301 Missing php unit test path for locale check 2024-12-10 15:22:59 +09:00
Clemens Schwaighofer
46e44c19bf edit log table column order update 2024-12-10 14:44:42 +09:00
Clemens Schwaighofer
41cb6358f9 phpunit checks update, update edit_log logging sets 2024-12-10 14:40:07 +09:00
Clemens Schwaighofer
23142a4549 Merge branch 'Bug-DBIOWrongParamCountWithCommentBeforeParam' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-10 13:37:43 +09:00
Clemens Schwaighofer
a7742bd5c8 DB IO count params fix for comments 2024-12-10 13:36:57 +09:00
Clemens Schwaighofer
50f83b822c Merge branch 'Bug-DBIOWrongParamCountWithCommentBeforeParam' into Feature-AclLoginClassUpdateTokenCheckWithUuidV4 2024-12-10 12:02:31 +09:00
Clemens Schwaighofer
78591d6ba4 Fix Param regex lookup
Query was not counting params after "--" comment strings
2024-12-10 12:01:06 +09:00
Clemens Schwaighofer
e8299a123b Update Edit Log with JSONB blocks
all action data goes into a JSON block and the old action columns will be deprecated
Same for ip, new ip address block with all possible ip addeses
Additional HTTP_ data goes into the http_data block
new request_schema column to get if the request was done to http or https
2024-12-10 10:06:49 +09:00
Clemens Schwaighofer
10c320f60c Rename all ACL Login session vars to LOGIN_, remove debug enties
All ACL\Login loaded _SESSION vars are now prefixd with LOGIN_
only the language one stay as "DEFAULT_"
Removed DEBUG_ALL/DB_DEBUG as they are now fully removed from everywhere
- removed the edit user entries
- removed from the edit user table
The LANG direct loaded language entries is removed too. We only use locale and encoding.
No more LOCALE_PATH and DEFAULT_DOMAIN _SESSION are set during the option set
2024-12-09 19:37:23 +09:00
Clemens Schwaighofer
eeca138192 Remove the debug/db_debug flag from the edit user edit interface
These settings are deprecated and do nothing. keep the DB default values for now, but update table create to remove them.
They are set to 0
2024-12-09 19:22:54 +09:00
Clemens Schwaighofer
65715ea9c3 Add Array function to return only array entries based on matching key
A simple key based array filter
2024-12-09 19:13:03 +09:00
Clemens Schwaighofer
a56cbd8e97 ACL Login update layout to work with PC/Smartphone
Updated login template and password change template block
2024-12-09 16:20:21 +09:00
Clemens Schwaighofer
fe50a988a0 Switch session ACL Login user load check to cuuid
Update tests too for using edit user cuuid instead of the primary key
2024-12-06 20:11:28 +09:00
Clemens Schwaighofer
a84ab86e31 Various fixes for ACL Login methods with deprecated calls
make all calls that go through primary keys as deprecated
create CUID calls for all of them
Update phpunit tests with new cuid tests, keep old deprecated tests
2024-12-06 18:07:06 +09:00
Clemens Schwaighofer
b044999772 ACL Login query update to params and heredoc
All queries are in Params and all SQL is in heredoc blocks

Disable 1011 error entry, this is no longer used
2024-12-06 16:31:20 +09:00
Clemens Schwaighofer
98bf3a40cd Add logout button to class.test.php for logout test, ANY placeholder db test 2024-12-06 14:54:09 +09:00
Clemens Schwaighofer
cbd47fb015 edit log table update, Change all DB tests serial to identity for primary key 2024-12-05 14:59:49 +09:00
Clemens Schwaighofer
5f89917abd Add composer keywords 2024-12-05 14:30:12 +09:00
Clemens Schwaighofer
eeaff3042e phpstan config file update with phpVersion information 2024-12-05 14:16:57 +09:00
Clemens Schwaighofer
d070c4e461 phan min php set to 8.2 2024-12-05 13:59:20 +09:00
Clemens Schwaighofer
e57c336dba Clean up to use session methods and not _SESSION directly
Add session_unset for unsetAll and rename this method to "clear"
2024-12-05 13:52:45 +09:00
Clemens Schwaighofer
075fe967d5 Merge branch 'NewFeatures' into Feature-FixSessionClass 2024-12-05 12:18:51 +09:00
Clemens Schwaighofer
0e5f637052 Update Serial to Identity function
Return status as varchar from change.

clean up edit table SQL files with too many empty lines
2024-12-05 12:11:07 +09:00
Clemens Schwaighofer
2e1b767a85 Fix Session class with Many update and get
Update Login and Backend class to use interface when writing to avoid
problems with not written _SESSION vars with session is in write close status
2024-12-05 12:09:58 +09:00
Clemens Schwaighofer
f78c67c378 Fix ACL Login phpunit test 2024-12-04 14:17:16 +09:00
Clemens Schwaighofer
75e69932fc Session class rewrite
create new session on class call, there is no need to delay that at all

new option to auto write close a session

session_id and session_name are stored as class vars

deprecate the __set/__get part because we do not want to set via ->session_var_name
but use the set()/get() methods.
They have been renamed from setS/getS... to set/get alone
2024-12-04 14:10:36 +09:00
Clemens Schwaighofer
7354632479 ACL Login update with cuuid and cuid add/update and move write log to login class
Add a UUIDv4 column to edit_generic as cuuid, add the cuid column to all reads with
the cuuid too

The cuuid will replace the cuid and remove the EUID as the session login var

Moved the adbEditLog to login class as writeLog and renamed the current private writeLog to writeEditLog which is only for internal logging in the class

The Backend log class is deprecated and a new get all action var method has been added to get the action vars into the edit log
2024-12-03 13:16:47 +09:00
Clemens Schwaighofer
5a21d22c7b Add edit user cuid to session and ACL read
This is for phasing out the EUID and replace it with an UUIDv4 for any user settings
2024-12-02 17:09:02 +09:00
Clemens Schwaighofer
cee3b5c2d1 HSB Colorspace skip phpstan colorspace variable never read 2024-12-02 15:45:47 +09:00
Clemens Schwaighofer
47e44c15cc Add a uuid4 validate method 2024-12-02 15:36:21 +09:00
Clemens Schwaighofer
83738adcb6 Remove old code 2024-11-27 14:32:34 +09:00
Clemens Schwaighofer
5454133239 Update SQL\PgSQL with param calls and heredoc, primary key search method update
The primary key currval select is udpated to use proper calls so it works with
serial and identity columns
2024-11-22 17:25:22 +09:00
Clemens Schwaighofer
87f35f23c3 edit_* table update for serial to identity columns 2024-11-22 17:24:34 +09:00
Clemens Schwaighofer
3c4c5d3106 Upgrade PostgreSQL serial to identity columns function
Function to help update PostgreSQL serial columns to identity
2024-11-22 17:21:07 +09:00
Clemens Schwaighofer
b080727ff3 Add missing PgSQL to the Interface 2024-11-21 10:40:24 +09:00
Clemens Schwaighofer
ae044bee6f DB IO Placeholder convert fixers and updates
Add more checks in phpunit for this,

Update the placeholder check and convert and move all regex into the
placeholder convert support class
Move $ placeholder count function to the SQL\PgSQL class

Note: further moves of PgSQL only stuff have to be done for SQLite
SQL class add
2024-11-20 19:07:10 +09:00
Clemens Schwaighofer
529b6a75ba Set base path for config file to load in edit_base.php 2024-11-19 15:43:00 +09:00
Clemens Schwaighofer
8de112ba7e Math Matrix multiplication fix for unbalanced array rows
Test for unbalanced arrays to matrix multiplication and fix unbalanced a array
2024-11-19 10:24:37 +09:00
Clemens Schwaighofer
ad070ebdf4 Composer phpstan update 2.0 2024-11-18 18:33:04 +09:00
Clemens Schwaighofer
9edfc2acb6 phpstan 2.0 update checks 2024-11-18 17:08:28 +09:00
Clemens Schwaighofer
35cc6dbf91 Minor fixes for some calls 2024-11-18 14:52:36 +09:00
Clemens Schwaighofer
cb3d5e1f27 Matrix multiplication fixes 2024-11-18 14:44:18 +09:00
Clemens Schwaighofer
0a45300c21 fix the deprecation version for Colors class calls 2024-11-18 10:12:48 +09:00
Clemens Schwaighofer
54ce378ae2 Text fix for deprecation message 2024-11-18 10:10:39 +09:00
Clemens Schwaighofer
4ac659f7d9 Colors deprecation messages and remove Class Basic color convert calls 2024-11-18 09:50:24 +09:00
Clemens Schwaighofer
497833ca71 phpunit test updated to removal of __get 2024-11-15 19:45:36 +09:00
Clemens Schwaighofer
e5a9b149b1 phpstan fixes with move away from __get to dedicated get 2024-11-15 19:43:30 +09:00
Clemens Schwaighofer
5213805a58 phan updates 2024-11-15 18:18:45 +09:00
Clemens Schwaighofer
a9f1d878f7 Math: add epsilon compare for float, update Color Coordinate calls
Math has a compare with epsilon for float numbers.

Use this for fixing sligth color conversion issues.

NOTE: this might need some adjustment over time

All phpunint tests written and checked
2024-11-15 18:13:16 +09:00
Clemens Schwaighofer
3845bc7ff5 Color Coordinates class udpates
move creation into the main constructor and do not rely on "::create" or
any other pass through creation.

Make all constructors equal with options array so we can create an Interface

Remove all outsite setters. Once a color is set this color stays
2024-11-14 14:51:31 +09:00
Clemens Schwaighofer
32c192a362 Basic colors test add started
Also fixes for various things that come up during test writing

Test phpunit not yet finished (exceptions, etc)

Note: a lot of checks for extreme values are (int) so we do not fail
for small float values
2024-11-13 19:19:35 +09:00
Clemens Schwaighofer
2bd68f32ac Legacy color convert update to use new methods 2024-11-13 13:16:02 +09:00
Clemens Schwaighofer
f5964fed02 Legacy colors test update 2024-11-13 12:45:21 +09:00
Clemens Schwaighofer
625272198d Math matrix phpunit checks added 2024-11-13 11:42:24 +09:00
Clemens Schwaighofer
00821bd5ea Move all Cie XYZ to dedicated class as this is not used in direct frontend convert
Clean up old Colors class with calling new class calls

Test all and set phpstan deprecated messages

Add all missing convert functions for oklab/cielab/oklch/cielch calls

Prepare for test run creation
2024-11-12 18:53:18 +09:00
Clemens Schwaighofer
921b9cb3d9 Remove not used Color Coordinate classes and old oklab convert class 2024-11-12 18:53:02 +09:00
Clemens Schwaighofer
720b78b687 Add CIE XYZ classes for D50/D65 whitespace before clean up 2024-11-12 18:52:24 +09:00
Clemens Schwaighofer
565014e1e2 diff --git c/4dev/tests/Convert/CoreLibsConvertMathTest.php i/4dev/tests/Convert/CoreLibsConvertMathTest.php
index 9a97e37e..c98b4b2a 100644
--- c/4dev/tests/Convert/CoreLibsConvertMathTest.php
+++ i/4dev/tests/Convert/CoreLibsConvertMathTest.php
@@ -113,6 +113,8 @@ final class CoreLibsConvertMathTest extends TestCase
 			\CoreLibs\Convert\Math::initNumeric($input)
 		);
 	}
+
+	// TODO: cbrt tests
 }

 // __END__
diff --git c/www/admin/class_test.convert.colors.php i/www/admin/class_test.convert.colors.php
index 6f809691..a37cb2df 100644
--- c/www/admin/class_test.convert.colors.php
+++ i/www/admin/class_test.convert.colors.php
@@ -19,6 +19,8 @@ $LOG_FILE_ID = 'classTest-convert-colors';
 ob_end_flush();

 use CoreLibs\Convert\Colors;
+use CoreLibs\Convert\Color\Color;
+use CoreLibs\Convert\Color\Coordinates;
 use CoreLibs\Debug\Support as DgS;
 use CoreLibs\Convert\SetVarType;

@@ -52,16 +54,16 @@ try {
 	print "**Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
 }
 // B(valid)
-$rgb = [10, 20, 30];
+$rgb = [50, 20, 30];
 $hex = '#0a141e';
 $hsb = [210, 67, 12];
 $hsb_f = [210.5, 67.5, 12.5];
-$hsl = [210, 50, 7.8];
+$hsb = [210, 50, 7.8];
 print "S::COLOR rgb->hex: $rgb[0], $rgb[1], $rgb[2]: " . Colors::rgb2hex($rgb[0], $rgb[1], $rgb[2]) . "<br>";
 print "S::COLOR hex->rgb: $hex: " . DgS::printAr(SetVarType::setArray(
 	Colors::hex2rgb($hex)
 )) . "<br>";
-print "C::S/COLOR rgb->hext: $hex: " . DgS::printAr(SetVarType::setArray(
+print "C::S/COLOR rgb->hex: $hex: " . DgS::printAr(SetVarType::setArray(
 	CoreLibs\Convert\Colors::hex2rgb($hex)
 )) . "<br>";
 // C(to hsb/hsl)
@@ -82,9 +84,9 @@ print "S::COLOR hsb_f->rgb: $hsb_f[0], $hsb_f[1], $hsb_f[2]: "
 	. DgS::printAr(SetVarType::setArray(
 		Colors::hsb2rgb($hsb_f[0], $hsb_f[1], $hsb_f[2])
 	)) . "<br>";
-print "S::COLOR hsl->rgb: $hsl[0], $hsl[1], $hsl[2]: "
+print "S::COLOR hsl->rgb: $hsb[0], $hsb[1], $hsb[2]: "
 	. DgS::printAr(SetVarType::setArray(
-		Colors::hsl2rgb($hsl[0], $hsl[1], $hsl[2])
+		Colors::hsl2rgb($hsb[0], $hsb[1], $hsb[2])
 	)) . "<br>";

 $hsb = [0, 0, 5];
@@ -102,8 +104,44 @@ print "RANDOM IN: H: " . $h . ", S: " . $s . ", B/L: " . $b . "/" . $l . "<br>";
 print "RANDOM hsb->rgb: <pre>" . DgS::printAr(SetVarType::setArray(Colors::hsb2rgb($h, $s, $b))) . "</pre><br>";
 print "RANDOM hsl->rgb: <pre>" . DgS::printAr(SetVarType::setArray(Colors::hsl2rgb($h, $s, $l))) . "</pre><br>";

+$rgb = [0, 0, 0];
+print "rgb 0,0,0: " . Dgs::printAr($rgb) . " => " . Dgs::printAr(Colors::rgb2hsb($rgb[0], $rgb[1], $rgb[2])) . "<br>";
+
 // TODO: run compare check input must match output

+$hwb = Color::hsbToHwb(Coordinates\HSB::__constructFromArray([
+	160,
+	0,
+	50,
+]));
+print "HWB: " . DgS::printAr($hwb) . "<br>";
+$hsb = Color::hwbToHsb($hwb);
+print "HSB: " . DgS::printAr($hsb) . "<br>";
+
+$oklch = Color::rgbToOkLch(Coordinates\RGB::__constructFromArray([
+	250,
+	0,
+	0
+]));
+print "OkLch: " . DgS::printAr($oklch) . "<br>";
+$rgb = Color::okLchToRgb($oklch);
+print "OkLch -> RGB: " . DgS::printAr($rgb) . "<br>";
+
+$oklab = Color::rgbToOkLab(Coordinates\RGB::__constructFromArray([
+	250,
+	0,
+	0
+]));
+print "OkLab: " . DgS::printAr($oklab) . "<br>";
+$rgb = Color::okLabToRgb($oklab);
+print "OkLab -> RGB: " . DgS::printAr($rgb) . "<br>";
+
+$rgb = Coordinates\RGB::__constructFromArray([250, 100, 10])->toLinear();
+print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
+$rgb = Coordinates\RGB::__constructFromArray([0, 0, 0])->toLinear();
+print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
+
+
 print "</body></html>";

 // __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Color.php i/www/lib/CoreLibs/Convert/Color/Color.php
new file mode 100644
index 00000000..c56f8c11
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Color.php
@@ -0,0 +1,803 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate and Color Space conversions
+ *
+ * We convert between color cooradinates and color spaces
+ * as seen in the list below
+ *
+ * |      |         RGB           |     Oklab     |       Cie
+ * |      |     | HSB |     |     |       |       |        |        |
+ * |      | RGB | HSV | HSL | HWB | OkLab | OkLch | CieLab | CieLch |
+ * -------+-----+-----+-----+-----+-------+-------+--------+--------+
+ * RGB    |  -  |  o  |  o  |  o  |   o   |   o   |        |        |
+ * HSB/HB |  o  |  -  |  o  |  o  |       |       |        |        |
+ * HSL    |  o  |  o  |  -  |  o  |   o   |   o   |        |        |
+ * HWB    |  o  |  o  |  o  |  -  |       |       |        |        |
+ * OkLab  |  o  |     |  o  |     |   -   |       |        |        |
+ * OkLch  |  o  |     |  o  |     |       |   -   |        |        |
+ * CieLab |     |     |     |     |       |       |   -    |        |
+ * CieLch |     |     |     |     |       |       |        |   -    |
+ *
+ * All color coordinates are classes
+ * The data can then be converted to a CSS string
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color;
+
+use CoreLibs\Convert\Math;
+use CoreLibs\Convert\Color\Coordinates\RGB;
+use CoreLibs\Convert\Color\Coordinates\HSL;
+use CoreLibs\Convert\Color\Coordinates\HSB;
+use CoreLibs\Convert\Color\Coordinates\HWB;
+use CoreLibs\Convert\Color\Coordinates\LCH;
+use CoreLibs\Convert\Color\Coordinates\Lab;
+use CoreLibs\Convert\Color\Coordinates\XYZD65;
+
+class Color
+{
+	// MARK: RGB <-> HSL
+
+	/**
+	 * converts a RGB (0-255) to HSL
+	 * return:
+	 * class with hue (0-360), saturation (0-100%) and luminance (0-100%)
+	 *
+	 * @param  RGB $rgb Class for rgb
+	 * @return HSL      Class hue/sat/luminance
+	 */
+	public static function rgbToHsl(RGB $rgb): HSL
+	{
+		$red = $rgb->R / 255;
+		$green = $rgb->G / 255;
+		$blue = $rgb->B / 255;
+
+		$min = min($red, $green, $blue);
+		$max = max($red, $green, $blue);
+		$chroma = $max - $min;
+		$sat = 0;
+		$hue = 0;
+		// luminance
+		$lum = ($max + $min) / 2;
+
+		// achromatic
+		if ($chroma == 0) {
+			// H, S, L
+			return HSL::__constructFromArray([
+				0.0,
+				0.0,
+				$lum * 100,
+			]);
+		} else {
+			$sat = $chroma / (1 - abs(2 * $lum - 1));
+			if ($max == $red) {
+				$hue = fmod((($green - $blue) / $chroma), 6);
+				if ($hue < 0) {
+					$hue = (6 - fmod(abs($hue), 6));
+				}
+			} elseif ($max == $green) {
+				$hue = ($blue - $red) / $chroma + 2;
+			} elseif ($max == $blue) {
+				$hue = ($red - $green) / $chroma + 4;
+			}
+			$hue = $hue * 60;
+			// $sat = 1 - abs(2 * $lum - 1);
+			return HSL::__constructFromArray([
+				$hue,
+				$sat * 100,
+				$lum * 100,
+			]);
+		}
+	}
+
+	/**
+	 * converts an HSL to RGB
+	 * if HSL value is invalid, set this value to 0
+	 *
+	 * @param  HSL $hsl Class with hue: 0-360 (degrees),
+	 *                             saturation: 0-100,
+	 *                             luminance: 0-100
+	 * @return RGB      Class for rgb
+	 */
+	public static function hslToRgb(HSL $hsl): RGB
+	{
+		$hue = $hsl->H;
+		$sat = $hsl->S;
+		$lum = $hsl->L;
+		// calc to internal convert value for hue
+		$hue = (1 / 360) * $hue;
+		// convert to internal 0-1 format
+		$sat /= 100;
+		$lum /= 100;
+		// if saturation is 0
+		if ($sat == 0) {
+			$lum = round($lum * 255);
+			return RGB::__constructFromArray([$lum, $lum, $lum]);
+		} else {
+			$m2 = $lum < 0.5 ? $lum * ($sat + 1) : ($lum + $sat) - ($lum * $sat);
+			$m1 = $lum * 2 - $m2;
+			$hueue = function ($base) use ($m1, $m2) {
+				// base = hue, hue > 360 (1) - 360 (1), else < 0 + 360 (1)
+				$base = $base < 0 ? $base + 1 : ($base > 1 ? $base - 1 : $base);
+				// 6: 60, 2: 180, 3: 240
+				// 2/3 = 240
+				// 1/3 = 120 (all from 360)
+				if ($base * 6 < 1) {
+					return $m1 + ($m2 - $m1) * $base * 6;
+				}
+				if ($base * 2 < 1) {
+					return $m2;
+				}
+				if ($base * 3 < 2) {
+					return $m1 + ($m2 - $m1) * ((2 / 3) - $base) * 6;
+				}
+				return $m1;
+			};
+
+			return RGB::__constructFromArray([
+				255 * $hueue($hue + (1 / 3)),
+				255 * $hueue($hue),
+				255 * $hueue($hue - (1 / 3)),
+			]);
+		}
+	}
+
+		// MARK: RGB <-> HSB
+
+	/**
+	 * rgb2hsb does not clean convert back to rgb in a round trip
+	 * converts RGB to HSB/V values
+	 * returns:
+	 * Class with hue (0-360), sat (0-100%), brightness/value (0-100%)
+	 *
+	 * @param  RGB $rgb Class for rgb
+	 * @return HSB      Class Hue, Sat, Brightness/Value
+	 */
+	public static function rgbToHsb(RGB $rgb): HSB
+	{
+		$red = $rgb->R / 255;
+		$green = $rgb->G / 255;
+		$blue = $rgb->B / 255;
+
+		$MAX = max($red, $green, $blue);
+		$MIN = min($red, $green, $blue);
+		$HUE = 0;
+		$DELTA = $MAX - $MIN;
+
+		// achromatic
+		if ($MAX == $MIN) {
+			return HSB::__constructFromArray([0, 0, $MAX * 100]);
+		}
+		if ($red == $MAX) {
+			$HUE = fmod(($green - $blue) / $DELTA, 6);
+		} elseif ($green == $MAX) {
+			$HUE = (($blue - $red) / $DELTA) + 2;
+		} elseif ($blue == $MAX) {
+			$HUE = (($red - $green) / $DELTA) + 4;
+		}
+		$HUE *= 60;
+		// avoid negative
+		if ($HUE < 0) {
+			$HUE += 360;
+		}
+
+		return HSB::__constructFromArray([
+			$HUE, // Hue
+			($DELTA / $MAX) * 100, // Saturation
+			$MAX * 100, // Brightness
+		]);
+	}
+
+	/**
+	 * hsb2rgb does not clean convert back to hsb in a round trip
+	 * converts HSB/V to RGB values RGB is full INT
+	 * if HSB/V value is invalid, sets this value to 0
+	 *
+	 * @param  HSB $hsb hue 0-360 (int),
+	 *                  saturation 0-100 (int),
+	 *                  brightness/value 0-100 (int)
+	 * @return RGB      Class for RGB
+	 */
+	public static function hsbToRgb(HSB $hsb): RGB
+	{
+		$H = $hsb->H;
+		$S = $hsb->S;
+		$V = $hsb->B;
+		// convert to internal 0-1 format
+		$S /= 100;
+		$V /= 100;
+
+		if ($S == 0) {
+			$V = $V * 255;
+			return RGB::__constructFromArray([$V, $V, $V]);
+		}
+
+		$Hi = floor($H / 60);
+		$f = ($H / 60) - $Hi;
+		$p = $V * (1 - $S);
+		$q = $V * (1 - ($S * $f));
+		$t = $V * (1 - ($S * (1 - $f)));
+
+		switch ($Hi) {
+			case 0:
+				$red = $V;
+				$green = $t;
+				$blue = $p;
+				break;
+			case 1:
+				$red = $q;
+				$green = $V;
+				$blue = $p;
+				break;
+			case 2:
+				$red = $p;
+				$green = $V;
+				$blue = $t;
+				break;
+			case 3:
+				$red = $p;
+				$green = $q;
+				$blue = $V;
+				break;
+			case 4:
+				$red = $t;
+				$green = $p;
+				$blue = $V;
+				break;
+			case 5:
+				$red = $V;
+				$green = $p;
+				$blue = $q;
+				break;
+			default:
+				$red = 0;
+				$green = 0;
+				$blue = 0;
+		}
+
+		return RGB::__constructFromArray([
+			$red * 255,
+			$green * 255,
+			$blue * 255,
+		]);
+	}
+
+	// MARK: HSL <-> HSB
+
+	/**
+	 * Convert HSL to HSB
+	 *
+	 * @param  HSL $hsl
+	 * @return HSB
+	 */
+	public static function hslToHsb(HSL $hsl): HSB
+	{
+		$saturation = $hsl->S / 100;
+		$lightness = $hsl->L / 100;
+		$value  = $lightness + $saturation * min($lightness, 1 - $lightness);
+		// check for black and white
+		$saturation = ($value === 0) ?
+			0 :
+			200 * (1 - $lightness / $value);
+		return HSB::__constructFromArray([
+			$hsl->H,
+			$saturation,
+			$value * 100,
+		]);
+	}
+
+	/**
+	 * Convert HSB to HSL
+	 *
+	 * @param  HSB $hsb
+	 * @return HSL
+	 */
+	public static function hsbToHsl(HSB $hsb): HSL
+	{
+		// hsv/toHsl
+		$hue = $hsb->H;
+		$saturation = $hsb->S / 100;
+		$value = $hsb->V / 100;
+
+		$lightness = $value * (1 - $saturation / 2);
+		// check for B/W
+		$saturation = in_array($lightness, [0, 1], true) ?
+			0 :
+			100 * ($value - $lightness) / min($lightness, 1 - $lightness)
+		;
+
+		return HSL::__constructFromArray([
+			$hue,
+			$saturation,
+			$lightness * 100,
+		]);
+	}
+
+	// MARK: HSB <-> HWB
+
+	/**
+	 * convert HSB to HWB
+	 *
+	 * @param  HSB $hsb
+	 * @return HWB
+	 */
+	public static function hsbToHwb(HSB $hsb): HWB
+	{
+		// hsv\Hwb
+		return HWB::__constructFromArray([
+			$hsb->H, // hue,
+			$hsb->B * (100 - $hsb->S) / 100, // 2: brightness, 1: saturation
+			100 - $hsb->B,
+		]);
+	}
+
+	/**
+	 * convert HWB to HSB
+	 *
+	 * @param  HWB $hwb
+	 * @return HSB
+	 */
+	public static function hwbToHsb(HWB $hwb): HSB
+	{
+		$hue = $hwb->H;
+		$whiteness = $hwb->W / 100;
+		$blackness = $hwb->B / 100;
+
+		$sum = $whiteness + $blackness;
+		// for black and white
+		if ($sum >= 1) {
+			$saturation = 0;
+			$value = $whiteness / $sum * 100;
+		} else {
+			$value = 1 - $blackness;
+			$saturation = $value === 0 ? 0 : (1 - $whiteness / $value) * 100;
+			$value *= 100;
+		}
+
+		return HSB::__constructFromArray([
+			$hue,
+			$saturation,
+			$value,
+		]);
+	}
+
+	// MARK: RGB <-> HWB
+
+	/**
+	 * Convert RGB to HWB
+	 * via rgb -> hsl -> hsb -> hwb
+	 *
+	 * @param  RGB $rgb
+	 * @return HWB
+	 */
+	public static function rgbToHwb(RGB $rgb): HWB
+	{
+		return self::hsbToHwb(
+			self::hslToHsb(
+				self::rgbToHsl($rgb)
+			)
+		);
+	}
+
+	/**
+	 * Convert HWB to RGB
+	 * via hwb -> hsb -> hsl -> rgb
+	 *
+	 * @param  HWB $hwb
+	 * @return RGB
+	 */
+	public static function hwbToRgb(HWB $hwb): RGB
+	{
+		return self::hslToRgb(
+			self::hsbToHsl(
+				self::hwbToHsb($hwb)
+			)
+		);
+	}
+
+	// MARK: HSL <-> HWB
+
+	/**
+	 * Convert HSL to HWB
+	 * via hsl -> hsb -> hwb
+	 *
+	 * @param  HSL $hsl
+	 * @return HWB
+	 */
+	public static function hslToHwb(HSL $hsl): HWB
+	{
+		return self::hsbToHwb(
+			self::hslToHsb(
+				$hsl
+			)
+		);
+	}
+
+	/**
+	 * Convert HWB to HSL
+	 * via hwb -> hsb -> hsl
+	 *
+	 * @param  HWB $hwb
+	 * @return HSL
+	 */
+	public static function hwbToHsl(HWB $hwb): HSL
+	{
+		return self::hsbToHsl(
+			self::hwbToHsb($hwb)
+		);
+	}
+
+	// MARK: OkLch <-> OkLab
+
+	/**
+	 * okLAab to okLCH
+	 *
+	 * @param  Lab $lab
+	 * @return LCH
+	 */
+	public static function okLabToOkLch(Lab $lab): LCH
+	{
+		// okLab\toOkLch
+		$a = $lab->a;
+		$b = $lab->b;
+
+		$hue = atan2($b, $a) * 180 / pi();
+
+		return LCH::__constructFromArray([
+			$lab->L,
+			sqrt($a ** 2 + $b ** 2),
+			$hue >= 0 ? $hue : $hue + 360,
+		]);
+	}
+
+	/**
+	 * okLCH to okLab
+	 *
+	 * @param  LCH $lch
+	 * @return Lab
+	 */
+	public static function okLchToOkLab(LCH $lch): Lab
+	{
+		// oklch/toOkLab
+		// oklch to oklab
+		return Lab::__constructFromArray([
+			$lch->L,
+			$lch->C * cos($lch->H * pi() / 180), // a
+			$lch->C * sin($lch->H * pi() / 180), // b
+		], 'Oklab');
+	}
+
+	// MARK: xyzD65 <-> linearRGB
+
+	/**
+	 * convert linear RGB to xyz D65
+	 * if rgb is not flagged linear, it will be auto converted
+	 *
+	 * @param  RGB  $rgb
+	 * @return XYZD65
+	 */
+	public static function linRgbToXyzD65(RGB $rgb): XYZD65
+	{
+		// if not linear, convert to linear
+		if (!$rgb->linear) {
+			$rgb->toLinear();
+		}
+		return XYZD65::__constructFromArray(Math::multiplyMatrices(
+			[
+				[0.41239079926595934, 0.357584339383878, 0.1804807884018343],
+				[0.21263900587151027, 0.715168678767756, 0.07219231536073371],
+				[0.01933081871559182, 0.11919477979462598, 0.9505321522496607],
+			],
+			$rgb->returnAsArray()
+		));
+	}
+
+	/**
+	 * Convert xyz D65 to linear RGB
+	 *
+	 * @param  XYZD65 $xyzD65
+	 * @return RGB
+	 */
+	public static function xyzD65ToLinRgb(XYZD65 $xyzD65): RGB
+	{
+		// xyz D65 to linrgb
+		return RGB::__constructFromArray(Math::multiplyMatrices(
+			a : [
+				[  3.2409699419045226,  -1.537383177570094,   -0.4986107602930034  ],
+				[ -0.9692436362808796,   1.8759675015077202,   0.04155505740717559 ],
+				[  0.05563007969699366, -0.20397695888897652,  1.0569715142428786  ],
+			],
+			b : $xyzD65->returnAsArray()
+		), linear: true);
+	}
+
+	// MARK: xyzD65 <-> OkLab
+
+	/**
+	 * xyz D65 to OkLab
+	 *
+	 * @param  XYZD65 $xyzD65
+	 * @return Lab
+	 */
+	public static function xyzD65ToOkLab(XYZD65 $xyzD65): Lab
+	{
+		return Lab::__constructFromArray(Math::multiplyMatrices(
+			[
+				[0.2104542553, 0.7936177850, -0.0040720468],
+				[1.9779984951, -2.4285922050, 0.4505937099],
+				[0.0259040371, 0.7827717662, -0.8086757660],
+			],
+			array_map(
+				callback: fn ($v) => pow($v, 1 / 3),
+				array: Math::multiplyMatrices(
+					a: [
+						[0.8190224432164319, 0.3619062562801221, -0.12887378261216414],
+						[0.0329836671980271, 0.9292868468965546, 0.03614466816999844],
+						[0.048177199566046255, 0.26423952494422764, 0.6335478258136937],
+					],
+					b: $xyzD65->returnAsArray(),
+				),
+			)
+		), 'Oklab');
+	}
+
+	/**
+	 * xyz D65 to OkLab
+	 *
+	 * @param  Lab    $lab
+	 * @return XYZD65
+	 */
+	public static function okLabToXyzD65(Lab $lab): XYZD65
+	{
+		return XYZD65::__constructFromArray(Math::multiplyMatrices(
+			a: [
+					[1.2268798733741557, -0.5578149965554813, 0.28139105017721583],
+					[-0.04057576262431372, 1.1122868293970594, -0.07171106666151701],
+					[-0.07637294974672142, -0.4214933239627914, 1.5869240244272418],
+			],
+			b: array_map(
+				callback: fn ($v) => is_numeric($v) ? $v ** 3 : 0,
+				array: Math::multiplyMatrices(
+					a: [
+							[0.99999999845051981432, 0.39633779217376785678, 0.21580375806075880339],
+							[1.0000000088817607767, -0.1055613423236563494, -0.063854174771705903402],
+							[1.0000000546724109177, -0.089484182094965759684, -1.2914855378640917399],
+					],
+					// Divide $lightness by 100 to convert from CSS OkLab
+					b: $lab->returnAsArray(),
+				),
+			),
+		));
+	}
+
+	// MARK: rgb <-> oklab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  RGB $rgb
+	 * @return Lab
+	 */
+	public static function rgbToOkLab(RGB $rgb): Lab
+	{
+		return self::xyzD65ToOkLab(
+			self::linRgbToXyzD65($rgb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return RGB
+	 */
+	public static function okLabToRgb(Lab $lab): RGB
+	{
+		return self::xyzD65ToLinRgb(
+			self::okLabToXyzD65($lab)
+		)->fromLinear();
+	}
+
+	// MARK: rgb <-> oklch
+
+	/**
+	 * convert rgb to OkLch
+	 * via rgb -> linear rgb -> xyz D65 -> OkLab -> OkLch
+	 *
+	 * @param  RGB $rbh
+	 * @return LCH
+	 */
+	public static function rgbToOkLch(RGB $rgb): LCH
+	{
+		return self::okLabToOkLch(
+			self::rgbToOkLab($rgb)
+		);
+	}
+
+	/**
+	 * Convert OkLch to rgb
+	 * via OkLab -> OkLch -> xyz D65 -> linear rgb -> rgb
+	 *
+	 * @param  LCH $lch
+	 * @return RGB
+	 */
+	public static function okLchToRgb(LCH $lch): RGB
+	{
+		return self::okLabToRgb(
+			self::okLchToOkLab($lch)
+		);
+	}
+
+	// MARK: HSL <-> OKLab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSL $hsl
+	 * @return Lab
+	 */
+	public static function hslToOkLab(HSL $hsl): Lab
+	{
+		return self::rgbToOkLab(
+			self::hslToRgb($hsl)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return HSL
+	 */
+	public static function okLabToHsl(Lab $lab): HSL
+	{
+		return self::rgbToHsl(
+			self::okLabToRgb($lab)
+		);
+	}
+
+	// MARK: HSL <-> OKLCH
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSL $hsl
+	 * @return LCH
+	 */
+	public static function hslToOkLch(HSL $hsl): LCH
+	{
+		return self::rgbToOkLch(
+			self::hslToRgb($hsl)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  LCH $lch
+	 * @return HSL
+	 */
+	public static function okLchToHsl(LCH $lch): HSL
+	{
+		return self::rgbToHsl(
+			self::okLchToRgb($lch)
+		);
+	}
+
+	// MARK: HSB <-> OKLab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSB $hsb
+	 * @return Lab
+	 */
+	public static function hsbToOkLab(HSB $hsb): Lab
+	{
+		return self::rgbToOkLab(
+			self::hsbToRgb($hsb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return HSB
+	 */
+	public static function okLabToHsb(Lab $lab): HSB
+	{
+		return self::rgbToHsb(
+			self::okLabToRgb($lab)
+		);
+	}
+
+	// MARK: HSB <-> OKLCH
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSB $hsb
+	 * @return LCH
+	 */
+	public static function hsbToOkLch(HSB $hsb): LCH
+	{
+		return self::rgbToOkLch(
+			self::hsbToRgb($hsb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  LCH $lch
+	 * @return HSB
+	 */
+	public static function okLchToHsb(LCH $lch): HSB
+	{
+		return self::rgbToHsb(
+			self::okLchToRgb($lch)
+		);
+	}
+
+	// MARK: HWB <-> OKLab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HWB $hwb
+	 * @return Lab
+	 */
+	public function hwbToOkLab(HWB $hwb): Lab
+	{
+		return self::rgbToOkLab(
+			self::hwbToRgb($hwb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return HWB
+	 */
+	public function okLabToHwb(Lab $lab): HWB
+	{
+		return self::rgbToHwb(
+			self::okLabToRgb($lab)
+		);
+	}
+
+	// MARK: HWB <-> OKLCH
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HWB $hwb
+	 * @return LCH
+	 */
+	public function hwbToOkLch(HWB $hwb): LCH
+	{
+		return self::rgbToOkLch(
+			self::hwbToRgb($hwb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  LCH $lch
+	 * @return HWB
+	 */
+	public function okLchToHwb(LCH $lch): HWB
+	{
+		return self::rgbToHwb(
+			self::okLchToRgb($lch)
+		);
+	}
+}
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/HSB.php i/www/lib/CoreLibs/Convert/Color/Coordinates/HSB.php
new file mode 100644
index 00000000..b435a9ef
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/HSB.php
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: HSB/HSV
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class HSB
+{
+	/** @var float hue */
+	private float $H = 0.0;
+	/** @var float saturation */
+	private float $S = 0.0;
+	/** @var float brightness / value */
+	private float $B = 0.0;
+
+	/**
+	 * HSB (HSV) color coordinates
+	 * Hue/Saturation/Brightness or Value
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $H Hue
+	 * @param  float $S Saturation
+	 * @param  float $B Brightness
+	 * @return self
+	 */
+	public static function __constructFromSet(float $H, float $S, float $B): self
+	{
+		return (new HSB())->setAsArray([$H, $S, $B]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Hue, 1: Saturation, 2: Brightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsb
+	 * @return self
+	 */
+	public static function __constructFromArray(array $hsb): self
+	{
+		return (new HSB())->setAsArray($hsb);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		$name = strtoupper($name);
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			case 'H':
+				if ($value == 360) {
+					$value = 0;
+				}
+				if ($value < 0 || $value > 359) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for hue is not in the range of 0 to 359',
+						1
+					);
+				}
+				break;
+			case 'S':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
+						2
+					);
+				}
+				break;
+			case 'B':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for brightness is not in the range of 0 to 100',
+						3
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		$name = strtoupper($name);
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Hue, 1: Saturation, 2: Brightness
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->H, $this->S, $this->B];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Hue, 1: Saturation, 2: Brightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsb
+	 * @return self
+	 */
+	public function setAsArray(array $hsb): self
+	{
+		$this->__set('H', $hsb[0]);
+		$this->__set('S', $hsb[1]);
+		$this->__set('B', $hsb[2]);
+		return $this;
+	}
+
+	/**
+	 * no hsb in css
+	 *
+	 * @param  float|string|null $opacity
+	 * @return string
+	 * @throws \ErrorException
+	 */
+	public function toCssString(null|float|string $opacity = null): string
+	{
+		throw new \ErrorException('HSB is not available as CSS color string', 0);
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/HSL.php i/www/lib/CoreLibs/Convert/Color/Coordinates/HSL.php
new file mode 100644
index 00000000..21be7fe5
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/HSL.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: HSL
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class HSL
+{
+	/** @var float hue */
+	private float $H = 0.0;
+	/** @var float saturation */
+	private float $S = 0.0;
+	/** @var float lightness (luminance) */
+	private float $L = 0.0;
+	/**
+	 * Color Coordinate HSL
+	 * Hue/Saturation/Lightness
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $H Hue
+	 * @param  float $S Saturation
+	 * @param  float $L Lightness
+	 * @return self
+	 */
+	public static function __constructFromSet(float $H, float $S, float $L): self
+	{
+		return (new HSL())->setAsArray([$H, $S, $L]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Hue, 1: Saturation, 2: Lightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsl
+	 * @return self
+	 */
+	public static function __constructFromArray(array $hsl): self
+	{
+		return (new HSL())->setAsArray($hsl);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			case 'H':
+				if ($value == 360) {
+					$value = 0;
+				}
+				if ($value < 0 || $value > 359) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for hue is not in the range of 0 to 359',
+						1
+					);
+				}
+				break;
+			case 'S':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
+						2
+					);
+				}
+				break;
+			case 'L':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for luminance is not in the range of 0 to 100',
+						3
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Hue, 1: Saturation, 2: Lightness
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->H, $this->S, $this->L];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Hue, 1: Saturation, 2: Lightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsl
+	 * @return self
+	 */
+	public function setAsArray(array $hsl): self
+	{
+		$this->__set('H', $hsl[0]);
+		$this->__set('S', $hsl[1]);
+		$this->__set('L', $hsl[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/HWB.php i/www/lib/CoreLibs/Convert/Color/Coordinates/HWB.php
new file mode 100644
index 00000000..ee6b7f63
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/HWB.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: HWB
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class HWB
+{
+	/** @var float Hue */
+	private float $H = 0.0;
+	/** @var float Whiteness */
+	private float $W = 0.0;
+	/** @var float Blackness */
+	private float $B = 0.0;
+	/**
+	 * Color Coordinate: HWB
+	 * Hue/Whiteness/Blackness
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $H Hue
+	 * @param  float $W Whiteness
+	 * @param  float $B Blackness
+	 * @return self
+	 */
+	public static function __constructFromSet(float $H, float $W, float $B): self
+	{
+		return (new HWB())->setAsArray([$H, $W, $B]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Hue, 1: Whiteness, 2: Blackness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hwb
+	 * @return self
+	 */
+	public static function __constructFromArray(array $hwb): self
+	{
+		return (new HWB())->setAsArray($hwb);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			case 'H':
+				if ($value == 360) {
+					$value = 0;
+				}
+				if ($value < 0 || $value > 360) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
+						1
+					);
+				}
+				break;
+			case 'W':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
+						2
+					);
+				}
+				break;
+			case 'B':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for luminance is not in the range of 0 to 100',
+						3
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Hue, 1: Whiteness, 2: Blackness
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->H, $this->W, $this->B];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Hue, 1: Whiteness, 2: Blackness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hwb
+	 * @return self
+	 */
+	public function setAsArray(array $hwb): self
+	{
+		$this->__set('H', $hwb[0]);
+		$this->__set('W', $hwb[1]);
+		$this->__set('B', $hwb[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/LCH.php i/www/lib/CoreLibs/Convert/Color/Coordinates/LCH.php
new file mode 100644
index 00000000..648fb466
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/LCH.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: Lch
+ * for oklch or cie
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class LCH
+{
+	/** @var float Lightness/Luminance
+	 * CIE: 0 to 100
+	 * OKlch: 0.0 to 1.0
+	 * BOTH: 0% to 100%
+	 */
+	private float $L = 0.0;
+	/** @var float Chroma
+	 * CIE: 0 to 150, cannot be more than 230
+	 * OkLch: 0 to 0.4, does not exceed 0.5
+	 * BOTH: 0% to 100% (0 to 150, 0 to 0.4)
+	 */
+	private float $C = 0.0;
+	/** @var float Hue
+	 * 0 to 360 deg
+	 */
+	private float $H = 0.0;
+
+	/** @var string color space: either ok or cie */
+	private string $colorspace = '';
+
+	/**
+	 * Color Coordinate Lch
+	 * for oklch
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $L
+	 * @param  float $c
+	 * @param  float $h
+	 * @return self
+	 */
+	public static function __constructFromSet(float $L, float $c, float $h): self
+	{
+		return (new LCH())->setAsArray([$L, $c, $h]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Lightness, 1: Chroma, 2: Hue
+	 *
+	 * @param  array{0:float,1:float,2:float} $lch
+	 * @return self
+	 */
+	public static function __constructFromArray(array $lch): self
+	{
+		return (new LCH())->setAsArray($lch);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			// case 'L':
+			// 	if ($this->colorspace == 'cie' && ($value < 0 || $value > 100)) {
+			// 		throw new \LengthException(
+			// 			'Argument value ' . $value . ' for lightness is not in the range of '
+			// 				. '0 to 100',
+			// 			3
+			// 		);
+			// 	} elseif ($this->colorspace == 'ok' && ($value < 0 || $value > 1)) {
+			// 		throw new \LengthException(
+			// 			'Argument value ' . $value . ' for lightness is not in the range of '
+			// 				. '0 to 1',
+			// 			3
+			// 		);
+			// 	}
+			// 	break;
+			// case 'c':
+			// 	if ($this->colorspace == 'cie' && ($value < 0 || $value > 230)) {
+			// 		throw new \LengthException(
+			// 			'Argument value ' . $value . ' for chroma is not in the range of '
+			// 				. '0 to 230 with normal upper limit of 150',
+			// 			3
+			// 		);
+			// 	} elseif ($this->colorspace == 'ok' && ($value < 0 || $value > 0.5)) {
+			// 		throw new \LengthException(
+			// 			'Argument value ' . $value . ' for chroma is not in the range of '
+			// 				. '0 to 0.5 with normal upper limit of 0.5',
+			// 			3
+			// 		);
+			// 	}
+			// 	break;
+			case 'h':
+				if ($value == 360) {
+					$value = 0;
+				}
+				if ($value < 0 || $value > 360) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for lightness is not in the range of 0 to 360',
+						1
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Lightness, 1: Chroma, 2: Hue
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->L, $this->C, $this->H];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Lightness, 1: Chroma, 2: Hue
+	 *
+	 * @param  array{0:float,1:float,2:float} $lch
+	 * @return self
+	 */
+	public function setAsArray(array $lch): self
+	{
+		$this->__set('L', $lch[0]);
+		$this->__set('C', $lch[1]);
+		$this->__set('H', $lch[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/Lab.php i/www/lib/CoreLibs/Convert/Color/Coordinates/Lab.php
new file mode 100644
index 00000000..e2eb11a4
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/Lab.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: Lab
+ * for oklab or cie
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class Lab
+{
+	/** @var array<string> allowed colorspaces */
+	private const COLORSPACES = ['Oklab', 'cie'];
+
+	/** @var float lightness/luminance
+	 * CIE: 0f to 100f
+	 * OKlab: 0.0 to 1.0
+	 * BOTH: 0% to 100%
+	 */
+	private float $L = 0.0;
+	/** @var float a axis distance
+	 * CIE: -125 to 125, cannot be more than +/- 160
+	 * OKlab: -0.4 to 0.4, cannot exceed +/- 0.5
+	 * BOTH: -100% to 100% (+/-125 or 0.4)
+	 */
+	private float $a = 0.0;
+	/** @var float b axis distance
+	 * CIE: -125 to 125, cannot be more than +/- 160
+	 * OKlab: -0.4 to 0.4, cannot exceed +/- 0.5
+	 * BOTH: -100% to 100% (+/-125 or 0.4)
+	 */
+	private float $b = 0.0;
+
+	/** @var string color space: either ok or cie */
+	private string $colorspace = '';
+
+	/**
+	 * Color Coordinate: Lab
+	 * for oklab or cie
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $L
+	 * @param  float $a
+	 * @param  float $b
+	 * @param  string $colorspace
+	 * @return self
+	 */
+	public static function __constructFromSet(float $L, float $a, float $b, string $colorspace): self
+	{
+		return (new Lab())->setColorspace($colorspace)->setAsArray([$L, $a, $b]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Lightness, 1: a, 2: b
+	 *
+	 * @param  array{0:float,1:float,2:float} $rgb
+	 * @param  string $colorspace
+	 * @return self
+	 */
+	public static function __constructFromArray(array $lab, string $colorspace): self
+	{
+		return (new Lab())->setColorspace($colorspace)->setAsArray($lab);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		// switch ($name) {
+		// 	case 'L':
+		// 		if ($value == 360) {
+		// 			$value = 0;
+		// 		}
+		// 		if ($value < 0 || $value > 360) {
+		// 			throw new \LengthException(
+		// 				'Argument value ' . $value . ' for lightness is not in the range of 0 to 360',
+		// 				1
+		// 			);
+		// 		}
+		// 		break;
+		// 	case 'a':
+		// 		if ($value < 0 || $value > 100) {
+		// 			throw new \LengthException(
+		// 				'Argument value ' . $value . ' for a is not in the range of 0 to 100',
+		// 				2
+		// 			);
+		// 		}
+		// 		break;
+		// 	case 'b':
+		// 		if ($value < 0 || $value > 100) {
+		// 			throw new \LengthException(
+		// 				'Argument value ' . $value . ' for b is not in the range of 0 to 100',
+		// 				3
+		// 			);
+		// 		}
+		// 		break;
+		// }
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * set the colorspace
+	 *
+	 * @param  string $colorspace
+	 * @return self
+	 */
+	private function setColorspace(string $colorspace): self
+	{
+		if (!in_array($colorspace, $this::COLORSPACES)) {
+			throw new \InvalidArgumentException('Not allowed colorspace', 0);
+		}
+		$this->colorspace = $colorspace;
+		return $this;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Lightness, 1: a, 2: b
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->L, $this->a, $this->b];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Lightness, 1: a, 2: b
+	 *
+	 * @param  array{0:float,1:float,2:float} $lab
+	 * @return self
+	 */
+	public function setAsArray(array $lab): self
+	{
+		$this->__set('L', $lab[0]);
+		$this->__set('a', $lab[1]);
+		$this->__set('b', $lab[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/RGB.php i/www/lib/CoreLibs/Convert/Color/Coordinates/RGB.php
new file mode 100644
index 00000000..acc81952
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/RGB.php
@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: RGB
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class RGB
+{
+	/** @var float red 0 to 255 or 0.0f to 1.0f for linear RGB */
+	private float $R = 0.0;
+	/** @var float green 0 to 255 or 0.0f to 1.0f for linear RGB */
+	private float $G = 0.0;
+	/** @var float blue 0 to 255 or 0.0f to 1.0f for linear RGB */
+	private float $B = 0.0;
+
+	/** @var bool set if this is linear */
+	private bool $linear = false;
+
+	/**
+	 * Color Coordinate RGB
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $R Red
+	 * @param  float $G Green
+	 * @param  float $B Blue
+	 * @param  bool $linear [default=false]
+	 * @return self
+	 */
+	public static function __constructFromSet(float $R, float $G, float $B, bool $linear = false): self
+	{
+		return (new RGB())->flagLinear($linear)->setAsArray([$R, $G, $B]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Red, 1: Green, 2: Blue
+	 *
+	 * @param  array{0:float,1:float,2:float} $rgb
+	 * @param  bool $linear [default=false]
+	 * @return self
+	 */
+	public static function __constructFromArray(array $rgb, bool $linear = false): self
+	{
+		return (new RGB())->flagLinear($linear)->setAsArray($rgb);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		// do not allow setting linear from outside
+		if ($name == 'linear') {
+			return;
+		}
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		// if not linear
+		if (!$this->linear && ($value < 0 || $value > 255)) {
+			throw new \LengthException('Argument value ' . $value . ' for color ' . $name
+				. ' is not in the range of 0 to 255', 1);
+		} elseif ($this->linear && ($value < -10E10 || $value > 1)) {
+			// not allow very very small negative numbers
+			throw new \LengthException('Argument value ' . $value . ' for color ' . $name
+				. ' is not in the range of 0 to 1 for linear rgb', 1);
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float|bool
+	 */
+	public function __get(string $name): float|bool
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Red, 1: Green, 2: Blue
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->R, $this->G, $this->B];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Red, 1: Green, 2: Blue
+	 *
+	 * @param  array{0:float,1:float,2:float} $rgb
+	 * @return self
+	 */
+	public function setAsArray(array $rgb): self
+	{
+		$this->__set('R', $rgb[0]);
+		$this->__set('G', $rgb[1]);
+		$this->__set('B', $rgb[2]);
+		return $this;
+	}
+
+	/**
+	 * set as linear
+	 * can be used as chain call on create if input is linear RGB
+	 * RGB::__construct**(...)->flagLinear();
+	 * as it returns self
+	 *
+	 * @return self
+	 */
+	private function flagLinear(bool $linear): self
+	{
+		$this->linear = $linear;
+		return $this;
+	}
+
+	/**
+	 * Both function source:
+	 * https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F
+	 * but reverse f: fromLinear and f_inv for toLinear
+	 * Code copied from here:
+	 * https://stackoverflow.com/a/12894053
+	 *
+	 * converts RGB to linear
+	 * We come from 0-255 so we need to divide by 255
+	 *
+	 * @return self
+	 */
+	public function toLinear(): self
+	{
+		$this->flagLinear(true)->setAsArray(array_map(
+			callback: function (int|float $v) {
+				$v = (float)($v / 255);
+				$abs = abs($v);
+				$sign = ($v < 0) ? -1 : 1;
+				return (float)(
+					$abs <= 0.04045 ?
+						$v / 12.92 :
+						$sign * pow(($abs + 0.055) / 1.055, 2.4)
+				);
+			},
+			array: $this->returnAsArray(),
+		));
+		return $this;
+	}
+
+	/**
+	 * convert back to normal sRGB from linear RGB
+	 * we go to 0-255 rgb so we multiply by 255
+	 *
+	 * @return self
+	 */
+	public function fromLinear(): self
+	{
+		$this->flagLinear(false)->setAsArray(array_map(
+			callback: function (int|float $v) {
+				$abs  = abs($v);
+				$sign = ($v < 0) ? -1 : 1;
+				// during reverse in some situations the values can become negative in very small ways
+				// like -...E16 and ...E17
+				return ($ret = (float)(255 * (
+					$abs <= 0.0031308 ?
+						$v * 12.92 :
+						$sign * (1.055 * pow($abs, 1.0 / 2.4) - 0.055)
+				))) < 0 ? 0 : $ret;
+			},
+			array: $this->returnAsArray(),
+		));
+		// $this->linear = false;
+		return $this;
+	}
+
+	/**
+	 * convert to css string with optional opacity
+	 * Note: if this is a linea RGB, this data will not be correct
+	 *
+	 * @param  float|string|null $opacity
+	 * @return string
+	 */
+	public function toCssString(null|float|string $opacity = null): string
+	{
+		// set opacity, either a string or float
+		if (is_string($opacity)) {
+			$opacity = ' / ' . $opacity;
+		} elseif ($opacity !== null) {
+			$opacity = ' / ' . $opacity;
+		} else {
+			$opacity = '';
+		}
+		return 'rgb('
+			. (int)round($this->R, 0)
+			. ' '
+			. (int)round($this->G, 0)
+			. ' '
+			. (int)round($this->B, 0)
+			. $opacity
+			. ')';
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/XYZD65.php i/www/lib/CoreLibs/Convert/Color/Coordinates/XYZD65.php
new file mode 100644
index 00000000..ebdf633d
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/XYZD65.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: XYZ (Cie)
+ * Note, this is only for the D65 whitepoint
+ * https://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright%E2%80%93Guild_data
+ * https://en.wikipedia.org/wiki/Standard_illuminant#D65_values
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class XYZD65
+{
+	private float $X = 0.0;
+	private float $Y = 0.0;
+	private float $Z = 0.0;
+
+	/**
+	 * Color Coordinate Lch
+	 * for oklch
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $X
+	 * @param  float $Y
+	 * @param  float $Z
+	 * @return self
+	 */
+	public static function __constructFromSet(float $X, float $Y, float $Z): self
+	{
+		return (new XYZD65())->setAsArray([$X, $Y, $Z]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: X, 1: Y, 2: Z
+	 *
+	 * @param  array{0:float,1:float,2:float} $xyzD65
+	 * @return self
+	 */
+	public static function __constructFromArray(array $xyzD65): self
+	{
+		return (new XYZD65())->setAsArray($xyzD65);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		// if ($value < 0 || $value > 255) {
+		// 	throw new \LengthException('Argument value ' . $value . ' for color ' . $name
+		// 		. ' is not in the range of 0 to 255', 1);
+		// }
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: X, 1: Y, 2: Z
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->X, $this->Y, $this->Z];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: X, 1: Y, 2: Z
+	 *
+	 * @param  array{0:float,1:float,2:float} $xyzD65
+	 * @return self
+	 */
+	public function setAsArray(array $xyzD65): self
+	{
+		$this->__set('X', $xyzD65[0]);
+		$this->__set('Y', $xyzD65[1]);
+		$this->__set('Z', $xyzD65[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/OkLab.php i/www/lib/CoreLibs/Convert/Color/OkLab.php
new file mode 100644
index 00000000..2bbdbbc2
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/OkLab.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/7
+ * DESCRIPTION:
+ * oklab conversions
+ * rgb -> oklab
+ * oklab -> rgb
+ * rgb -> okhsl
+ * okshl -> rgb
+ * rgb -> okhsv
+ * okhsv -> rgb
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color;
+
+class OkLab
+{
+	/**
+	 * lines sRGB to oklab
+	 *
+	 * @param  int   $red
+	 * @param  int   $green
+	 * @param  int   $blue
+	 * @return array<float>
+	 */
+	public static function srgb2okLab(int $red, int $green, int $blue): array
+	{
+		$l = (float)0.4122214708 * (float)$red +
+			(float)0.5363325363 * (float)$green +
+			(float)0.0514459929 * (float)$blue;
+		$m = (float)0.2119034982 * (float)$red +
+			(float)0.6806995451 * (float)$green +
+			(float)0.1073969566 * (float)$blue;
+		$s = (float)0.0883024619 * (float)$red +
+			(float)0.2817188376 * (float)$green +
+			(float)0.6299787005 * (float)$blue;
+
+		// cbrtf = 3 root (val)
+		$l_ = pow($l, 1.0 / 3);
+		$m_ = pow($m, 1.0 / 3);
+		$s_ = pow($s, 1.0 / 3);
+
+		return [
+			(float)0.2104542553 * $l_ + (float)0.7936177850 * $m_ - (float)0.0040720468 * $s_,
+			(float)1.9779984951 * $l_ - (float)2.4285922050 * $m_ + (float)0.4505937099 * $s_,
+			(float)0.0259040371 * $l_ + (float)0.7827717662 * $m_ - (float)0.8086757660 * $s_,
+		];
+	}
+
+	/**
+	 * convert okLab to linear sRGB
+	 *
+	 * @param  float $L
+	 * @param  float $a
+	 * @param  float $b
+	 * @return array<int>
+	 */
+	public static function okLab2srgb(float $L, float $a, float $b): array
+	{
+		$l_ = $L + (float)0.3963377774 * $a + (float)0.2158037573 * $b;
+		$m_ = $L - (float)0.1055613458 * $a - (float)0.0638541728 * $b;
+		$s_ = $L - (float)0.0894841775 * $a - (float)1.2914855480 * $b;
+
+		$l = $l_ * $l_ * $l_;
+		$m = $m_ * $m_ * $m_;
+		$s = $s_ * $s_ * $s_;
+
+		return [
+			(int)round(+(float)4.0767416621 * $l - (float)3.3077115913 * $m + (float)0.2309699292 * $s),
+			(int)round(-(float)1.2684380046 * $l + (float)2.6097574011 * $m - (float)0.3413193965 * $s),
+			(int)round(-(float)0.0041960863 * $l - (float)0.7034186147 * $m + (float)1.7076147010 * $s),
+		];
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Stringify.php i/www/lib/CoreLibs/Convert/Color/Stringify.php
new file mode 100644
index 00000000..6ca68431
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Stringify.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Convert color coordinate to CSS string
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color;
+
+use CoreLibs\Convert\Color\Coordinates\RGB;
+use CoreLibs\Convert\Color\Coordinates\HSL;
+use CoreLibs\Convert\Color\Coordinates\HWB;
+use CoreLibs\Convert\Color\Coordinates\Lab;
+use CoreLibs\Convert\Color\Coordinates\LCH;
+
+class Stringify
+{
+	/**
+	 * Undocumented function
+	 *
+	 * @param  RGB|Lab|LCH|HSL|HWB $data
+	 * @param  null|float|string   $opacity
+	 * @return string
+	 */
+	public static function toCssString(RGB|Lab|LCH|HSL|HWB $data, null|float|string $opacity): string
+	{
+		return $data->toCssString($opacity);
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Colors.php i/www/lib/CoreLibs/Convert/Colors.php
index f9f56171..8ff32608 100644
--- c/www/lib/CoreLibs/Convert/Colors.php
+++ i/www/lib/CoreLibs/Convert/Colors.php
@@ -120,26 +120,29 @@ class Colors
 		$MAX = max($red, $green, $blue);
 		$MIN = min($red, $green, $blue);
 		$HUE = 0;
+		$DELTA = $MAX - $MIN;

+		// achromatic
 		if ($MAX == $MIN) {
 			return [0, 0, round($MAX * 100)];
 		}
 		if ($red == $MAX) {
-			$HUE = ($green - $blue) / ($MAX - $MIN);
+			$HUE = fmod(($green - $blue) / $DELTA, 6);
 		} elseif ($green == $MAX) {
-			$HUE = 2 + (($blue - $red) / ($MAX - $MIN));
+			$HUE = (($blue - $red) / $DELTA) + 2;
 		} elseif ($blue == $MAX) {
-			$HUE = 4 + (($red - $green) / ($MAX - $MIN));
+			$HUE = (($red - $green) / $DELTA) + 4;
 		}
 		$HUE *= 60;
+		// avoid negative
 		if ($HUE < 0) {
 			$HUE += 360;
 		}

 		return [
-			(int)round($HUE),
-			(int)round((($MAX - $MIN) / $MAX) * 100),
-			(int)round($MAX * 100)
+			(int)round($HUE), // Hue
+			(int)round(($DELTA / $MAX) * 100), // Saturation
+			(int)round($MAX * 100) // Value/Brightness
 		];
 	}

diff --git c/www/lib/CoreLibs/Convert/Math.php i/www/lib/CoreLibs/Convert/Math.php
index 205abbf1..26739c5f 100644
--- c/www/lib/CoreLibs/Convert/Math.php
+++ i/www/lib/CoreLibs/Convert/Math.php
@@ -56,6 +56,95 @@ class Math
 			return (float)$number;
 		}
 	}
+
+	/**
+	 * calc cube root
+	 *
+	 * @param  float $number Number to cubic root
+	 * @return float         Calculated value
+	 */
+	public static function cbrt(float $number): float
+	{
+		return pow($number, 1.0 / 3);
+	}
+
+	/**
+	 * This function is directly inspired by the multiplyMatrices() function in color.js
+	 * form Lea Verou and Chris Lilley.
+	 * (see https://github.com/LeaVerou/color.js/blob/main/src/multiply-matrices.js)
+	 * From:
+	 * 3842cf51c9/src/utils/utils.php (L507)
+	 *
+	 * It returns an array which is the product of the two number matrices passed as parameters.
+	 *
+	 * @param  array<array<int|float>> $a m x n matrice
+	 * @param  array<array<int|float>> $b n x p matrice
+	 *
+	 * @return array<array<int|float>>    m x p product
+	 */
+	public static function multiplyMatrices(array $a, array $b): array
+	{
+		$m = count($a);
+
+		if (!is_array($a[0] ?? null)) {
+			// $a is vector, convert to [[a, b, c, ...]]
+			$a = [ $a ];
+		}
+
+		if (!is_array($b[0])) {
+			// $b is vector, convert to [[a], [b], [c], ...]]
+			$b = array_map(
+				callback: fn ($v) => [ $v ],
+				array: $b,
+			);
+		}
+
+		$p = count($b[0]);
+
+		// transpose $b:
+		$bCols = array_map(
+			callback: fn ($k) => \array_map(
+				(fn ($i) => $i[$k]),
+				$b,
+			),
+			array: array_keys($b[0]),
+		);
+
+		$product = array_map(
+			callback: fn ($row) => array_map(
+				callback: fn ($col) => is_array($row) ?
+					array_reduce(
+						array: $row,
+						callback: fn ($a, $v, $i = null) => $a + $v * (
+							$col[$i ?? array_search($v, $row)] ?? 0
+						),
+						initial: 0,
+					) :
+					array_reduce(
+						array: $col,
+						callback: fn ($a, $v) => $a + $v * $row,
+						initial: 0,
+					),
+				array: $bCols,
+			),
+			array: $a,
+		);
+
+		if ($m === 1) {
+			// Avoid [[a, b, c, ...]]:
+			$product = $product[0];
+		}
+
+		if ($p === 1) {
+			// Avoid [[a], [b], [c], ...]]:
+			return array_map(
+				callback: fn ($v) => $v[0],
+				array: $product,
+			);
+		}
+
+		return $product;
+	}
 }

 // __END__
2024-11-11 18:48:56 +09:00
Clemens Schwaighofer
d9bcb577d7 some minor test page code fixes 2024-11-07 12:05:23 +09:00
Clemens Schwaighofer
8613e8977b UrlRequests curl: move options set logic to main curl wrapper call
change the curlRequest call to options array and build the options array
there.
Remove any options check + pre build from the get/request calls

Update phpunit tests with string type body return
2024-11-07 11:22:36 +09:00
Clemens Schwaighofer
0c51a3be87 Add phpunit tests for header key/value exceptions 2024-11-06 18:49:48 +09:00
Clemens Schwaighofer
f9cf36524e UrlRequests auth set allowed in requests call
Removed the parseHeaders public call, headers must be set as array

Throw errors on invalid headers before sending them: Key/Value check
Add headers invalid check in phpunit

Auth headers can be set per call and will override global settings if matching
2024-11-06 18:42:35 +09:00
Clemens Schwaighofer
bacb9881ac Fix UrlRequests Interface name, fix header build
Header default build was not done well, pass original headers inside and
set them. On new default start with empty array.

Switch to CoreLibs Json calls, because we use this libarary anyway already
2024-11-06 14:28:15 +09:00
Clemens Schwaighofer
f0fae1f76d Fix Composer package phpunit test url for UrlRequests 2024-11-06 13:35:00 +09:00
Clemens Schwaighofer
1653e6b684 Allow http_errors unset/set on each call
If set or not set, on each call this option can be set.
If set to null on call, the original value or default config value is used
2024-11-06 13:29:19 +09:00
Clemens Schwaighofer
c8bc0062ad URL Requests change error response
Instead of just throwing exception on 401 auth, throw exception for any
error code from 400 on
This can be turned off with the option "http_errors" set to false

Also updaed the exception content to match 400 or 500 error type with
more information attached

General Exception error codes:
Cnnn: Curl errors (FAILURE)
Rnnn: general class errors (ERROR)
Hnnn: http response errors (ERROR)
2024-11-06 12:48:01 +09:00
Clemens Schwaighofer
5c8a2ef8da Update test paths for URLRequests tests 2024-11-06 10:38:30 +09:00
Clemens Schwaighofer
d8379a10d9 URL Request phpunit test added 2024-11-06 10:33:05 +09:00
Clemens Schwaighofer
30e2f33620 Test calls update for admin area 2024-11-06 10:03:33 +09:00
Clemens Schwaighofer
a4f16f4ca9 Various updates and fixes during testing
Move the build auth content to dedicated variables
Add a default User-Agent that is always sent
Default headers like Authorization and User-Agent are always set, even when
request is sent with headers null
Fix timeout, was sent as is and not converted to milliseconds
Fix headers not correctly set to null if array entry was set to null
2024-11-06 10:03:14 +09:00
Clemens Schwaighofer
6e7b9cd033 phpunit URL Requests backend test file 2024-11-01 14:43:10 +09:00
Clemens Schwaighofer
4bc2ad8fa0 URL Requests basic tests file 2024-11-01 14:42:43 +09:00
Clemens Schwaighofer
0d4e959f39 Remove the nice formatter for now 2024-11-01 14:42:04 +09:00
Clemens Schwaighofer
95d567545a URL Requests via curl, a simple library 2024-11-01 14:41:46 +09:00
Clemens Schwaighofer
d89c6d1bde UrlRequests target file renamed 2024-10-29 18:28:19 +09:00
Clemens Schwaighofer
337ebb9032 Add a localhost entry to the hosts config 2024-10-29 18:28:07 +09:00
Clemens Schwaighofer
9538ebce7b Merge branch 'NewFeatures' into Feature-UrlRequestsCurl 2024-10-29 14:14:10 +09:00
Clemens Schwaighofer
1bff19f4b6 Update UrlRequests with patch, admin test page for it
Also update delete to have optional body (content)
2024-10-28 17:05:49 +09:00
Clemens Schwaighofer
66dc72ec67 phpunit test text doc typo fix 2024-10-21 10:19:29 +09:00
Clemens Schwaighofer
f781b5e55f Name update for params/query
Order in methods
url: mandatory
payload: mandatory in post/put
header = []
query = ""

old "params" -> "payload"
2024-10-21 09:52:49 +09:00
Clemens Schwaighofer
934db50b3a Merge branch 'NewFeatures' into Feature-UrlRequestsCurl 2024-10-21 09:52:09 +09:00
Clemens Schwaighofer
573588ad3c Ignore www composer data 2024-10-21 09:36:50 +09:00
Clemens Schwaighofer
d04addba81 Add a SQL lite media folder 2024-10-21 09:33:46 +09:00
Clemens Schwaighofer
a50a38fd40 DB IO Query Placeholder tests 2024-10-21 09:33:18 +09:00
Clemens Schwaighofer
3c5200cd99 Test run for Curl URL Requests 2024-10-21 09:32:20 +09:00
Clemens Schwaighofer
50a4b88f55 UrlRequests\Curl class
Basic interface class to CURL calls

Open:
clean up and check code is neutral
write tests, for this we need a running localhost server for tests to request to
2024-10-21 09:25:39 +09:00
Clemens Schwaighofer
e82929f512 core composer install update 2024-10-18 09:37:18 +09:00
Clemens Schwaighofer
5fc55c53b8 Update composer and ignore composer/vendor and phive/tools folders 2024-10-18 09:28:27 +09:00
Clemens Schwaighofer
47da4d02ff ignore local vendor/composer and tools folder 2024-10-18 09:19:10 +09:00
Clemens Schwaighofer
9d131cf6dd Bug: Remove echo in call 2024-10-17 13:58:52 +09:00
Clemens Schwaighofer
dfcae20f64 Update DB\IO and do not print call steck on DB_INFO calls 2024-10-17 13:52:44 +09:00
Clemens Schwaighofer
61e489ee4c Remove entries from an array wrapper
just wrapper around array_diff
2024-10-17 09:44:24 +09:00
Clemens Schwaighofer
29982f90bc Admin\Backend change non filled dat part comment
the not filled data or data_binary part is a JSON with "type" set to the
type that is used with a general message

To decode try to read both sides if data = JSON + "type" and "message" set
then data is in data_binary else data_binary holds the type on the left side
2024-10-16 16:45:13 +09:00
Clemens Schwaighofer
7cced63c4b Update the Admin\Backend edit log call with query params and different data compressors
All queries uses now Params

On load checks for valid write types for edit log write, eg if bzip and lzip compression
are avaiable

adbEditLog:
Also add JSON type encoding for data outside STRING/SERIAL and BINARY/BZIP (bzip compressed)
Add ZLIB as altnerative to BZIP
Add alert if invalid type was set
Auto fallback to JSON if other write types are not available

adbLiveQueue:
Also convert the live queue query to a params style call
2024-10-16 16:21:51 +09:00
Clemens Schwaighofer
06c2ea5e0d Admin\Backend: make sure we do not access unset ->action vars 2024-10-16 12:34:48 +09:00
Clemens Schwaighofer
2e9239ec23 Ingore node_modules/ folder 2024-10-16 12:18:51 +09:00
Clemens Schwaighofer
545279b9fe First tests with eslint flat layout 2024-10-16 12:17:24 +09:00
Clemens Schwaighofer
0c89840dba Admin\Backend move _POST action read to sub function and trigger not auto loading it
On default it still auto loads the _POST vars for backwards compatible, but add a load class
flag to ignore it "init_action_vars"

also add a get vor tha "acl" array adbGetAcl()
2024-10-16 12:15:19 +09:00
Clemens Schwaighofer
db144493f3 Message system: allow warning level to be logged
Like error messages, they are written to the log if debug is on or the
flag is explicit set
2024-09-24 15:10:53 +09:00
Clemens Schwaighofer
5cec54d508 Add "Success" to message logging levels, fixes for PHP 8.4, other preg_match fixes
The Logger/MessageLevel gets "success" as level 110 to something a bit
heigher than "ok" which is the general "OK" for anything ending without
an error. The "success" is currently only used in file uploads with the
java script ajax file uploader

Fix any "type $var = null" with correctly "?type $var = null" for PHP 8.4 (phphan)

Fix preg match no return catches for DB IO compare version and for language
look up.
2024-09-20 13:33:19 +09:00
Clemens Schwaighofer
8e60c992f1 Fixes phan/phpstan 2024-09-03 12:06:01 +09:00
Clemens Schwaighofer
1b5437b675 Add testing for new added bom utf8 replace 2024-09-03 11:58:36 +09:00
Clemens Schwaighofer
ef80cba561 Add new functions: get IPs, strip UTF8 BOM from text, text updates
Add the following new static methods

Convert\Strings::stripUTF8BomBytes: removes the UTF8 BOM bytes from the beginning of a line
Used for CSV files created in Excel for the first header entry (line 0/row 0)

Get\Systen::getIpAddresses: gets all IP addresses for the the current access user
and returns an array

Moved the frontend folder detection from the first load config to the config.path.php

Cleaned up the translations JS scripts
2024-09-03 09:49:01 +09:00
Clemens Schwaighofer
2d71e760e8 Composer update 2024-08-21 11:45:17 +09:00
Clemens Schwaighofer
a8d07634ff Add soba.egplusww.jp as local development host, jshint esversion update to 11 2024-08-07 13:41:09 +09:00
Clemens Schwaighofer
aa2b60973e HTML::htmlent and HTML::checked updates
Changed Params form ENT_COMPAT | ENT_HTML401 to ENT_QUOTES | ENT_HTML5
Flags can be overwritten on call

Logic clean up for return flow

HTML::checked gets logic updated with less nested ifs
2024-08-05 13:24:37 +09:00
Clemens Schwaighofer
554dd5f73c Fix not closed <head> block in all admin test files 2024-08-05 12:53:48 +09:00
Clemens Schwaighofer
e6f9559fbb DB IO: placeholder fix for JSON/JSONB lists 2024-07-29 16:30:48 +09:00
Clemens Schwaighofer
770d6f30a4 DB\IO Placeholder regex fix for json queries, test data updates
Some doc typo fixes
test updates with remove of not used sub calls

DB IO Placeholder regex now checks for any JSON/JSONB operators
2024-07-29 15:55:38 +09:00
Clemens Schwaighofer
f94f6cbe87 Add .shellcheckrc, move phpstan tmp folder to repository temp folder 2024-07-19 18:40:24 +09:00
Clemens Schwaighofer
9b69390fa2 Merge branch 'development' into NewFeatures 2024-05-22 10:47:12 +09:00
Clemens Schwaighofer
0524d8ac1b Update Symmetric Encryption
Can be used as a class with central key set.

for old static calls:
encrypt -> encryptKey
decrypt -> decryptKey
2024-05-22 10:43:54 +09:00
Clemens Schwaighofer
e933022671 Phive update 2024-05-22 10:27:20 +09:00
Clemens Schwaighofer
c549d34e65 phive tools update 2024-05-15 17:06:09 +09:00
Clemens Schwaighofer
f4ff31721b phpunit test add php 8.3, remove special code for mb_encode_mimeheader
In the past we had a special function to do mb_encode_mimeheader correctly.

Since PHP 8.2 this works perfectly fine, so all the code was removed and
replaced with just the normal "mb_encode_mimeheader" call with the same
settings as before:
- set global encoding to parameter
- run encoding with charset, 'B' for transfer and use the line break given in parameter
- reset the global encoding to previous set
2024-04-17 10:08:12 +09:00
Clemens Schwaighofer
21ac91d2e6 Composer update for Smarty v4.5.2 2024-04-16 18:27:55 +09:00
Clemens Schwaighofer
2d98d26d0b phive update 2024-04-16 16:32:48 +09:00
Clemens Schwaighofer
3fda1bef60 Move Symmetric Encryption Key generation into its own method
Test update for future class based encryption system without static
methods
2024-03-27 11:58:53 +09:00
Clemens Schwaighofer
4f1104c36e phive tools update 2024-03-21 12:38:43 +09:00
Clemens Schwaighofer
db1007ef12 Smarty Update v4.6.1, Bug fixes in DB\Extended\ArrayIO and Form\Generate 2024-03-19 10:43:12 +09:00
Clemens Schwaighofer
13c76efcef Fix new layout path location
Was "www/layout/admin|frontend/..." now is
www/frontend/layout and www/admin/layout
2024-03-07 14:53:00 +09:00
Clemens Schwaighofer
f59c325b05 Bug fix in string path slash fix
if null error return original path
2024-03-07 14:37:05 +09:00
Clemens Schwaighofer
aac5157173 phive update 2024-03-07 14:33:30 +09:00
Clemens Schwaighofer
abc2705c64 Bug fix in Form Generate load list generation
For binary 1/0 blocks make sure we use the actual data and not just the
wrong binary 0/1 source
Also add auto translations for all strings in there
2024-03-07 14:31:30 +09:00
Clemens Schwaighofer
e2fff45b16 Composer update Smarty v4.4.1 2024-03-06 18:44:41 +09:00
Clemens Schwaighofer
b6ae3f99d9 Layout folder move to admin and frontend 2024-02-27 12:02:03 +09:00
Clemens Schwaighofer
a377ab4b61 Strip multiple slashes in CoreLibs Convert Strings 2024-02-14 12:26:17 +09:00
Clemens Schwaighofer
ec9275d8d3 Add mime type get to the Check\File class 2024-01-11 16:15:08 +09:00
Clemens Schwaighofer
10ed5b3ea0 phive packages updates 2024-01-10 17:04:20 +09:00
Clemens Schwaighofer
5f290419c6 Minor php cs fixes 2024-01-10 17:03:18 +09:00
Clemens Schwaighofer
0152af6e65 add php code sniffer xml 2023-12-05 17:37:55 +09:00
Clemens Schwaighofer
4adb9fba30 DB IO Bug Fix for parameter detect after <>, DateTime code fix
Query Parameters where not detected after a <> unequal check

DateTime fix for last element, use array_pop instead of $array[count - 1]
2023-11-29 10:44:21 +09:00
Clemens Schwaighofer
672931ee67 phive packages update 2023-11-29 10:36:49 +09:00
Clemens Schwaighofer
373a9aff2f Add local eslintrc and jsconfig 2023-11-28 11:34:29 +09:00
Clemens Schwaighofer
4e3ceecac8 Phive update 2023-11-02 14:05:49 +09:00
Clemens Schwaighofer
8bf694b2b2 ArrayHandler: find next or previous key in array and return it
get next or previous key in array
2023-11-02 14:01:47 +09:00
Clemens Schwaighofer
caf03421a7 Bug fix for DB\IO param prepare call with wrong param array check
The global param array in the class instead of the param array passed
to the method was checked
2023-10-31 10:18:44 +09:00
Clemens Schwaighofer
facf8adaf7 DB\IO move dbReturn params set before first abort 2023-10-31 10:05:43 +09:00
Clemens Schwaighofer
c8158c8224 Remove echo from phpunit test file 2023-10-23 17:11:06 +09:00
Clemens Schwaighofer
7b9a0043d3 Add Support::getCallStack
returns full call stack for call (excluding self)
2023-10-23 16:59:14 +09:00
Clemens Schwaighofer
6517747fef Move old intervalStringFormat into the class_test.datetime.php file
For current reference testing, will be removed later.

Update phpunit tests (ongoing)
2023-10-18 13:12:48 +09:00
Clemens Schwaighofer
fb7071adc4 Basic used Exception documentation
What PHP Exception is used for what

NOTE: Subject to change
2023-10-18 10:59:15 +09:00
Clemens Schwaighofer
d96c92f9ef Update phpunit tests for Byte class with new exception names 2023-10-18 09:32:36 +09:00
Clemens Schwaighofer
d7c0054ea0 Change default Exception to proper named Exceptions
Byte: InvalidArgumentException for parameters

Image: RunetimeException for cannot write file to system
2023-10-18 09:30:08 +09:00
Clemens Schwaighofer
80d2215f2b Combined\DateTime new intervalStringFormat method
new method to replace old timeStringFormat method:
- has year/month data too
- can format with natural names (minutes, seconds, etc)
- can have normal naming (5 hours, 1 minute and 10 seconds)
- skip or not skip zero values in between (6h 0m 1s -> 6h 1s)
- skip or add trailing zero values (6m 0s -> 6m)
- add or not add milliseconds with decimal nano seconds
- drop nano seconds (115.55ms -> 115ms)
- truncate value after a certain part (eg only show up to days)
- add leading 0s to only milli seconds values (115ms -> 0s 115ms)
- namespace separator (6h -> 6 h)

Bug fix for timeStringFormat
- 1.5s and 1.05s and 1.005s all where 5ms -> fixed to 500ms, 50ms 5ms
- bug fix for 0ms drop even thought show ms is requested

Start unit testing part
2023-10-17 19:14:12 +09:00
Clemens Schwaighofer
b18bf937d3 DB\IO Missing dbWarning updates with call context
all in the __dbSetInsertId method
2023-10-16 16:10:47 +09:00
Clemens Schwaighofer
4b6c5df74f Update all DB\IO params with new array layout 2023-10-16 14:55:39 +09:00
Clemens Schwaighofer
89e3888bf8 Update DB\IO with auto query placeholder rewrite and better error logging
All errors have context that is used to add query, params, etc info
for logging into the DB.
Avoid double logging for PostGreSQL direct errors as those will be
logged now in context to the actual error log
Remove error: 16 missing/empty dbh has this is handled with error 14
in the connect method.

Auto convert ?, :named to $numbered, default off. Activate with
'db_convert_placeholder' flag or method dbSetConvertPlaceholder.
Converted result data for single queries in dbGetPlaceholderConverted
or in the cursor_ext array in placeholer_converted key

Do not auto translate debug queries with placeholder values in query
but keep them in the array in the context array. If needed
'db_debug_replace_placeholder' can be set to show prepared query
with placeholder replaced in the context

New methods:
public function dbSetConvertPlaceholder(bool $flag): void
public function dbGetConvertPlaceholder(): bool
public function dbSetConvertPlaceholderTarget(string $target): bool
public function dbGetConvertPlaceholderTarget(): string
public function dbSetDebugReplacePlaceholder(bool $flag): void
public function dbGetDebugReplacePlaceholder(): bool
public function dbGetPlaceholderConverted(): array

Chagned to public:
public function dbCheckQueryForSelect(string $query): bool
public function dbCheckQueryForInsert(string $query, bool $pure = false): bool
public function dbCheckQueryForUpdate(string $query): bool
2023-10-16 14:43:55 +09:00
Clemens Schwaighofer
c46125aef1 Merge branch 'OutputImageException' into development 2023-10-12 17:14:34 +09:00
Clemens Schwaighofer
a8ca5d7a2b Add missing exceptions to Output\Image image rotate
Also create a basic phpunit class to test exceptions only
2023-10-12 17:08:44 +09:00
Clemens Schwaighofer
ae2d6580a2 Initial add for PDO/PG convert placeholders 2023-10-11 18:36:06 +09:00
Clemens Schwaighofer
97e1b2b63d Update DB\IO numbered params in query detection
- Fix the regex to proper match $n params in al;l kind of combinations
- Move the count into a sub private method and use this instead of using
  the same code twice
2023-10-06 16:41:47 +09:00
Clemens Schwaighofer
d632a2599e PHPunit and VScode intelephense setup information in readme file 2023-10-04 18:26:47 +09:00
Clemens Schwaighofer
2f81009a97 Fix TestCalls class folder name based on namespace 2023-10-04 18:26:38 +09:00
Clemens Schwaighofer
e9799f888b Install PHP CS Fixer 2023-10-04 15:12:14 +09:00
Clemens Schwaighofer
94b24b4eae Basic documentation tools: phpdox/phpdocumentor installed for testing which is best 2023-10-04 15:07:49 +09:00
Clemens Schwaighofer
a304d29698 Switch check to to phive installed tools, fix in phpunit bootsrap file 2023-10-04 14:58:29 +09:00
Clemens Schwaighofer
c4c809be66 Remove all base composer installed packages and move tool phive install for phan/phpstan/etc 2023-10-04 14:47:48 +09:00
Clemens Schwaighofer
a3bf38f6cf Update bootstrap file for phpunit to work with different folder layouts
we have the autoloader in different base folders for some dev work
2023-10-04 14:35:53 +09:00
Clemens Schwaighofer
c1b906e701 Install composer viea phive and remove phpunit from composer dev install
Update comoser test files to work with both, but primary is the phive
installed one.

run tests via 4dev/check/phpunit.sh as before
2023-10-04 14:22:24 +09:00
Clemens Schwaighofer
32f8e1440d Update Logging\ErrorMsg jump target with level for css
So we can have different stylesheets for the levels like in the error
messages.

Output changes to ...[$target] = ['info' ..., 'level'] and on return
this is converted into an array for each entry so it can be handled
like the error msg return string
2023-10-02 17:31:11 +09:00
Clemens Schwaighofer
2f8f98642b Update Logging\ErrorMsg to add a Jump Target list for direct jumps
So we can return a list for css element ids we want to jump directly to
2023-10-02 14:02:00 +09:00
Clemens Schwaighofer
7ab03913ac Merge remote-tracking branch 'all/master' into development 2023-10-02 12:27:36 +09:00
Clemens Schwaighofer
a7853171e0 Merge remote-tracking branch 'all/master' into development 2023-10-02 12:26:35 +09:00
Clemens Schwaighofer
dfdfcf87f2 Merge branch 'development' 2023-10-02 12:26:14 +09:00
Clemens Schwaighofer
6218e0a6a8 Output\Form\Generate fix for phan check 2023-10-02 12:25:07 +09:00
Clemens Schwaighofer
a84a745be2 Logging\ErrorMsg class update to log error level automatically for debug
if log level is debug, automatically log the error messages.
We still skip warn and info levels from logging.

The rest is based on the logging level (notice eg only gets logged if
log level is at least notice)
2023-10-02 12:23:44 +09:00
Clemens Schwaighofer
312762e92e Composer update 2023-10-02 12:23:15 +09:00
Clemens Schwaighofer
fa4c1f0597 Form\Generate and DB\Extend\ArrayIO split
Move DB\Extend\ArrayIO to internal class in variable and do not extend
Form\Generate from it (as we do not have a base class anymore, this is
no longer neded)

Update all calls in connected classes.

Add interface methods for DB\Extend\ArrayIO to interface with all class
vars that are now all private
2023-09-29 19:05:58 +09:00
Clemens Schwaighofer
438a75af23 Merge branch 'development' 2023-09-27 11:42:32 +09:00
Clemens Schwaighofer
afd8ff3e31 Composer updates 2023-09-27 11:42:07 +09:00
Clemens Schwaighofer
4343af7937 Composer core updates 2023-09-27 11:41:41 +09:00
Clemens Schwaighofer
d06769c48b empty admin folder page for edit base page creation tests 2023-09-27 11:38:32 +09:00
Clemens Schwaighofer
4c0390f082 ErrorMessage class: add notice for non error logging to log file
info is already used for write back to front. So we use notice for
non error level messages into the log file
2023-09-27 11:37:26 +09:00
Clemens Schwaighofer
95bee3dc8c Update DB\IO dbWriteDataExt to allow null primary key
So we do not get an error on a new data call with a null primary key
2023-09-27 11:22:16 +09:00
Clemens Schwaighofer
65132d8a4a Fix DB\ArrayIO access to unset pk_id 2023-09-27 09:41:41 +09:00
Clemens Schwaighofer
b2243cd06d Add EditOrder dummy template class for edit_order page
without this dummy class the Form\Generate cannot load.

We don't need it, except for the set page name
2023-09-26 18:36:29 +09:00
Clemens Schwaighofer
8f09b67d86 Update for phpunit tests 2023-09-15 18:31:41 +09:00
Clemens Schwaighofer
fe459aec80 Update ErrorMsg with optional target_style string for one style
Applied to master or to target style
2023-09-15 18:22:38 +09:00
Clemens Schwaighofer
de0ed058ca ErrorMessage: new flag for logging 'error' level to log 2023-09-11 13:35:35 +09:00
Clemens Schwaighofer
f90bd193d9 Change setError to setMessage to make it clear what is the corret name 2023-09-08 18:54:44 +09:00
Clemens Schwaighofer
0e31180868 Rename setErrorMsgLevel to setError for backend calls 2023-09-08 18:47:44 +09:00
Clemens Schwaighofer
68c9164eaa New ErrorMessage class for frontend return error messages 2023-09-08 18:30:05 +09:00
Clemens Schwaighofer
c2389db1c9 Composer updates 2023-09-08 18:30:02 +09:00
Clemens Schwaighofer
f9558cd3aa Fix for the SetVarType / SetVarTypeNull
string: if it is stringable in anyway, set string (it converts)
this is not check IF it is a string value as it was before
int/float: same, if it is numerc it will be convert to int or float
All other stay the same

Note "set ..." imply to set, and not to convert to 0 if it is int
string that can be covnerted to int
2023-09-05 14:26:57 +09:00
Clemens Schwaighofer
ae3011fe22 php unit test fix for DB\IO 2023-09-01 18:29:00 +09:00
Clemens Schwaighofer
9b9dfeac69 phan error supress 2023-09-01 18:21:25 +09:00
Clemens Schwaighofer
33cb05a002 Update to Exceptions: add codes, update phpunit tests
DB Class throws Exception if on init it fails to connect to the DB,
will not throw Exception if failed connection during execution but
will do the normal retry and soft failure run
DB\ArrayIO will throw Exception on missing table array and table name

All Exceptions have a code set
2023-09-01 08:37:15 +09:00
Clemens Schwaighofer
ec110499a8 phan fixs 2023-08-31 19:06:16 +09:00
Clemens Schwaighofer
09839f3451 phpstan fixes 2023-08-31 19:04:45 +09:00
Clemens Schwaighofer
067e0aed5d L10n change Exception to RuntimeException 2023-08-31 18:08:34 +09:00
Clemens Schwaighofer
545de5c4a1 Fixed more Exceptions to be not Errors but Exceptions
DateTime, Session, FileWrite, Image, SymmetricEncryption

phpunit tests updated, run checks added
2023-08-31 18:06:02 +09:00
Clemens Schwaighofer
2fe37bf92a Exceptions change in Check\Colors, add in Cmbined\ArrayHandler
Chech\Colors now throws correct exceptions for wrong values
Combined\ArrayHandler will throw errors and not return false
2023-08-31 12:07:28 +09:00
Clemens Schwaighofer
cd81d15d9a Convert\Color methods will throw Exception instead of false on error
All Color methods will throw Exceptions:
LengthException,
InvalidArgumentException,
UnexpectedValueException

instead of returning bool: false

All methods will return valid color data as expected only
2023-08-31 10:45:33 +09:00
Clemens Schwaighofer
8a33ee5c15 Slight update for ACL\Login class exit codes
exit will add message as first parameter (string) next to code (int)
Log this to info or critical.
3000 -> 100: info
rest >=1000: critical
previous 4000 = 3000 (options not set)

update unit tests for this

Possible change idea: critical abort throw error?
2023-08-31 10:41:44 +09:00
Clemens Schwaighofer
46e1419ef5 phan checks and updates 2023-08-30 19:26:13 +09:00
Clemens Schwaighofer
c441063437 Composer updates 2023-08-30 19:25:48 +09:00
Clemens Schwaighofer
5290d5f351 Update db class tests in admin run 2023-08-28 09:28:17 +09:00
Clemens Schwaighofer
2635ccb82b edit.css: rename animation, Bug fix in DB\IO cursor_ext access and others
Make sure cursor_ext is set before we access it, else return null for
not set yet.
false for errors, else data value

Other class var access checks to be sure to never fail
2023-08-28 07:40:41 +09:00
Clemens Schwaighofer
4f2ac2ed1b Change Logging class / method name and Debug Support for backtrace
Debug Support:
getCallerClass now returns level 1 class from the trace like the
getCallerMethod. There is also a new getCallerClassMethod that returns
namespace\class->method (or :: for static).

getCallerTopLevelClass works like getCallerClass did before and returns
the TOP level (first entry on the call stack that has a set class name)

Logging:
Do not use the Support getCallerClass/Method/File but call it inside
and use level 2 in trace to get the data we need For the last call
before debug call
Also update the strack trace for the debug call to use ->/:: for method
type
2023-08-22 13:28:59 +09:00
Clemens Schwaighofer
5b8e4e4e3e Core composer packages update 2023-08-22 13:04:19 +09:00
Clemens Schwaighofer
53192da571 www folder composer updates 2023-08-22 13:04:01 +09:00
Clemens Schwaighofer
f29e915068 class_test fixes for phpstan checks 2023-08-02 16:32:11 +09:00
Clemens Schwaighofer
46bc5f2da6 Json phpunit tests updates, fixes in test php with ignore for deprecated 2023-08-02 16:22:09 +09:00
Clemens Schwaighofer
d70182a84e Add JSON convert array to json with always return string
Allows the same post run error check like the other way around
2023-08-02 16:12:01 +09:00
Clemens Schwaighofer
7243f69826 Email Type class returns correct "false" instead of "bool" 2023-08-02 16:08:39 +09:00
Clemens Schwaighofer
1fc144e178 Composer Workspace global packages 2023-08-02 14:52:33 +09:00
Clemens Schwaighofer
c383a7b7b7 Update convert colors to return false and not bool
All convert color either return the color value or false.
To make sure any checker knows that we only return "value" or "false"
change all return bool to false
2023-08-02 07:29:49 +09:00
Clemens Schwaighofer
69077c384c phpdoc fix for DB\IO dbGetCursorNumRows 2023-08-01 09:00:43 +09:00
Clemens Schwaighofer
cfd49947ad Bug fix in ACL\Login: mnake sure ['base'] acl is int 2023-07-26 11:48:56 +09:00
Clemens Schwaighofer
6985dc4e9d ACL\Login fix for UNIT DEFAULT return
It has to be int or null but because the SQL result is undefined (string)
it needs to be converted on return if it is a numeric value, else
null will be returned (it is the edit access id PK so it has to be
numeric)
2023-07-24 09:11:32 +09:00
Clemens Schwaighofer
5f2668b011 ACL\Login
Remove log per class flag set inside Login.
If per class logging is needed here, set that BEFORE and AFTER the class
call
2023-07-21 19:03:24 +09:00
Clemens Schwaighofer
eba1ef9c59 Init DB\IO dbh with null to avoid any problems
There could have been some problems where the dbh var was not touched
even thought it was inited.
2023-07-21 17:48:09 +09:00
Clemens Schwaighofer
8497144053 Admin\Backend level check, DB\IO Error/Warning message update
on Admin\Backend init check that the provided default acl level is valid

DB\IO warning and error drop the "db :" prefix part as this is not needed
we have [DB_ERROR] and [DB_WARNING] sub prefixes anyway, also we run
dedicated log level alerts with context
2023-07-14 15:01:46 +09:00
Clemens Schwaighofer
2006798388 Init set empty db config if db config not found 2023-07-10 08:18:49 +09:00
Clemens Schwaighofer
bf63d850ca Add new DateTime class has date range weekened method
dateRangeHasWeekend with two dates, checks if between those two dates
a weekend (sat or sun) is set
2023-07-04 11:43:27 +09:00
Clemens Schwaighofer
53e267ce24 Updates and fixes from phan/phpstan runs 2023-06-28 15:30:08 +09:00
Clemens Schwaighofer
1754ecf2ee HtmlBuilder: change all return error to Throw, update Element/StrinReplace
We do not return old style bool on error, we throw Exceptions: HtmlBuilderExcpetion

Element has more classes to set tag, id, etc with basic checks for valid data

String Replace to set strings is one array with key -> value entries

Errors thrown on index for element/replace blocks
2023-06-28 14:16:44 +09:00
Clemens Schwaighofer
3c37899a48 Rename Replace to StringReplace to match the actual content
Replace is too general. it is String Replace
2023-06-27 18:33:04 +09:00
Clemens Schwaighofer
0436cfe3da HtmlBuilder classes for Object, Array, String Replace build
Object build is a replicata from the JS one
Array is similar but build on pure Array elements
String replace is just a simple string replacer for now

General\Error for overall error handling
General\Settings for Object/Array based checks and settings
2023-06-27 18:30:26 +09:00
Clemens Schwaighofer
3606de1a00 Back add some checks for the phfo function that matches the php one 2023-06-27 18:30:00 +09:00
Clemens Schwaighofer
3081439eda Switch edit_base CSS name from ADMIN_STYLESHEET to EDIT_BASE_STYLESHEET 2023-06-27 18:29:28 +09:00
Clemens Schwaighofer
7af0e74b85 Fix phpUnit test name for Security\SymmetricEncryption 2023-06-26 14:25:36 +09:00
Clemens Schwaighofer
7748b83a6b DB\IO add stack trace to debug/error/warning calls
To add the actuall call reference for DB IO debug calls we add the
call trace as context options
2023-06-16 13:17:49 +09:00
Clemens Schwaighofer
f83293ff1a Info comments for DB\IO convert options 2023-06-13 11:47:38 +09:00
Clemens Schwaighofer
9c3be2942e phan and phpstan fixes, also add a convert flag reset to original
dbResetConvertFlag resets to the settings given on init of class
2023-06-09 18:23:28 +09:00
Clemens Schwaighofer
ee62bd98ee Add auto type convert for DB\IO
set via db options "db_convert_type" as array with "on", "json", "numeric",
"bytea"

"on" only converts know good types: "bool", "int"

"json" will convert json/jsonb to array
"bytea" will decode escaped bytea to string (note: this might change to resource)

"numeric" will convert to float.
NOTE: if a numeric number is too large a covnersion might drop data.
Use with care.

Convert flags can be chagned with dbSetConvertFlag and dbUnsetConvertFlag

All convert flags are in "DB\Options\Convert" as enum.
2023-06-09 17:01:03 +09:00
Clemens Schwaighofer
02e9610fad Add a general log method to Logger class
the params order is the actual correct one:
log level, message, context, group_id, prefix

not that group_id and prefix are only used if log level is debug

Switched debug params order for context and prefix so prefix is last
2023-06-09 16:59:22 +09:00
Clemens Schwaighofer
8a41db4649 Output\Image cleanup for phpstan 2023-06-09 12:16:49 +09:00
Clemens Schwaighofer
e27ea3dc9f DB\IO phpdoc update 2023-06-09 10:24:26 +09:00
Clemens Schwaighofer
ec4bf54d81 DB\IO phpdoc layout update
Add line between params and method description
2023-06-09 10:21:02 +09:00
Clemens Schwaighofer
ec3ca787fa Logging: prepare message only if log level is high enough
Also some clean ups on internal method call parameters
2023-06-05 09:30:26 +09:00
Clemens Schwaighofer
86acbbb85b Per run logging was not set if flag changed
if setFlag was set for per_run or per_date the init values where not set

Fixed that on setFlag it is checked if we have per_date or per_run and
then set if not set.

Not that for date, set the flag will set a new date, for per run no.
2023-06-02 17:38:09 +09:00
Clemens Schwaighofer
8e0af7a5f7 Class var init fixes 2023-06-01 13:03:46 +09:00
Clemens Schwaighofer
b022662dfc Removed restrictions for printAr to only accept arrays
print_r call takes any value, changed that for all functions

array -> mixed
2023-06-01 12:00:20 +09:00
Clemens Schwaighofer
3039ebf913 Unlink files after test 2023-06-01 11:02:53 +09:00
Clemens Schwaighofer
e2e080c404 Bug fix for DB\IO 2023-06-01 10:58:11 +09:00
Clemens Schwaighofer
4671143d1c phpunit debug fixes 2023-06-01 09:13:59 +09:00
Clemens Schwaighofer
b492558cca Logging unit test regex fix 2023-06-01 08:54:47 +09:00
Clemens Schwaighofer
64e76530d4 deprecated log method call name change in test file 2023-06-01 08:43:47 +09:00
Clemens Schwaighofer
0b93f9f146 Bug fixes and minor updates
- Removed echo from Support Debug dumpVar call
- deprecated DB\IO toggle dbDebug and changed set/get to be like normal
  ones where set just sets and doesn't return anything
- Renamed the logJsDebug to loggingLevelIsDebug
  (other levels can be checked with ->getLoggingLevel()->includes(Level::...))

Adjusted tests for all changes
2023-06-01 08:40:55 +09:00
Clemens Schwaighofer
4c6fe1cd6c Fix Logger\Logger Psr Excpetion calls 2023-05-31 18:44:40 +09:00
Clemens Schwaighofer
83ba48f598 Remove db_debug flag from DB\IO, is set from Logging level 2023-05-31 18:41:13 +09:00
Clemens Schwaighofer
62c6de8244 minor phan config fix 2023-05-31 17:32:00 +09:00
Clemens Schwaighofer
1c2f9f0c2c ACL\Login if euid was set from SESSION and EUID is not int, error
Force to (int)
2023-05-31 16:30:25 +09:00
Clemens Schwaighofer
30bb0e8895 phpunit checks and fixes 2023-05-31 16:14:40 +09:00
Clemens Schwaighofer
75c4c98de8 Convert all classes to strict variable types
All variable declarations in all classes have a strict type set

Exception: constants (will be setable from PHP 8.3 on), resources (no type)

Debug\LoggingLegacy is kept as is, will be deprecated
2023-05-31 15:58:06 +09:00
Clemens Schwaighofer
f72055909b Update in class error/warning/etc calls instead of debug calls
For anything that is not debug use the proper reporting level
2023-05-30 18:30:31 +09:00
Clemens Schwaighofer
b0a8783276 Logging class major change, Debug\Support update
old Debug\Logging is in Debug\LoggingLegacy and Debug\Logging extends
Logging\Logging

Logging\Logging is a new class with most of the functionality except
there is no more print/outout to screen, but we use the default log
levels (RFC5424)
The plan is to be a frontend between the old type class and
Monolog\Monolog

Updated all other classes to use new class interface
2023-05-30 18:12:24 +09:00
Clemens Schwaighofer
7b5ad92e66 Composer updates 2023-05-29 16:21:51 +09:00
Clemens Schwaighofer
250067927a fdebug deprecated message update, update debug logger tester script 2023-05-25 17:55:04 +09:00
Clemens Schwaighofer
7c2cbbaca7 Fix Password phpunit test class name 2023-05-24 15:57:34 +09:00
Clemens Schwaighofer
ac037eabde New Secruity namespace added
Move Passwords from Check to Security and deprecate old

Add new SymmetricEncryption and CreateKey

CreateKey class just creates keys for the SymmetricEncryption
SymmetricEncryption uses the hex2bin calls to convert the hex key to the
internal binary key

Example:
$key = CreateKey::generateRandomKey();
$encrypted = SymmetricEncryption::encrypt($string, $key);
$decrypted = SymmetricEncryption::decrypt($encrypted, $key);

Above $key must be stored in some secure location (.env file)
2023-05-24 15:47:02 +09:00
Clemens Schwaighofer
0250b86b3f Merge branch 'development' of git.tokyo.tequila.jp:E-GRAPHICS_Communications_Japan/PHP_Core_Libraries into development 2023-05-18 15:14:40 +09:00
Clemens Schwaighofer
e45acc412b 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:14:29 +09:00
Clemens Schwaighofer
854206bc70 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:08:45 +09:00
Clemens Schwaighofer
b192e98a8a edit.jq.js docstring fix
not needed [ in param part
2023-05-10 08:26:40 +09:00
Clemens Schwaighofer
c4e2c781c6 Bug fix in arraySearchKey
Path was fully reset, reven if we went up just one level.
Now splice the reset off but keep everything before
2023-04-26 15:38:48 +09:00
Clemens Schwaighofer
e80b3b8dfd arraySearchKey: search for key in array and return value or path set
search in an array in any format and returns value of key if found.
Either as set with path to key, or as flat with only values.
Optional possible to prefix with the search keys to group found entries
2023-04-26 14:40:39 +09:00
Clemens Schwaighofer
2b079ff836 DB\IO: add missing debug query, clean up not needed code
in dbReturn with params on not matching param the system exited on fail
without printing the query making it hard to find where the error is.
Added debug output in case the params count is not matching.
Same move in the dbExecute call

removed param count check from dbReturnRow/dbReturnArray as this check
is done in the dbExecParams call anyway
2023-04-11 10:58:38 +09:00
Clemens Schwaighofer
37201799b5 DB\IO params debug output fix for dbReturn/dbReturnParams calls
Those two calls did not replace the params with values for debug output
2023-04-10 17:20:53 +09:00
Clemens Schwaighofer
b9d8911c7b ACL\Login
load and export the additional acl json arrays for
* user: USER_ADDITIONAL_ACL
* group: GROUP_ADDITIONAL_ACL
* access: array element 'additional_acl'

also added to the master acl array:
'additional_acl' => ['user' => [], 'group' => []]
'unit_detail' => [] => ['additional_acl' => []]
2023-04-10 14:32:32 +09:00
Clemens Schwaighofer
c51ceb926e Bug fix for DB\IO params detection
Param detection found too many params, for example '$1'.
Fixed the regex to only allow params that are no preceeded by '
And must start with space/tab, =, (
2023-04-07 14:34:13 +09:00
Clemens Schwaighofer
b4b33d6873 Bug fix for DB\IO returning detection
it was still coded with old one line and not taking in possible
line breaks in the returning code part
2023-04-03 15:02:39 +09:00
Clemens Schwaighofer
959240b0fa Fixes in db class tests files 2023-03-29 09:59:30 +09:00
Clemens Schwaighofer
7eace1013e DB\IO switch dbReturn, dbReturnParams to NO_CACHE as default
Cache is never used, so to keep memory default lower, lets switch to
NO_CACHE
2023-03-29 09:55:09 +09:00
Clemens Schwaighofer
be1e55cad7 Add more DB tests with params methods 2023-03-28 17:01:02 +09:00
Clemens Schwaighofer
11a8c6440b Update DB\IO debug output for parameter queries
if value null set "NULL" else convert to string

Update class basic test with various type tests
2023-03-28 16:46:34 +09:00
Clemens Schwaighofer
742cbc31df DB\IO add dbExec*, dbReturn* Params methods
Instead of prepare/execute, add the proper quary_params calls for all
the dbExec*, dbReturn* calls

Also add field types to cursor info
2023-03-28 15:31:07 +09:00
Clemens Schwaighofer
28909fdc03 composer base packages updates 2023-03-28 11:51:54 +09:00
Clemens Schwaighofer
c3b29ad0d7 comment formatting update only 2023-03-27 16:25:11 +09:00
Clemens Schwaighofer
6d481657df class test file update for DB with ANY calls 2023-03-16 18:21:48 +09:00
Clemens Schwaighofer
fc57aabf5d Updates in SmartyExtend set var calls
Removed cms object from Frontend and replaced with optional smarty data
array (HEADER, DATA, DEBUG_DATA)
Updated admin call that if $cms is given above data will be extracted.
Added a CONTENT_PATH option for admin, must be set if $cms is set
Is used for the adbTopMenu call
Moved the $cms global check and trigger to the admin call branch only
2023-03-13 11:29:21 +09:00
Clemens Schwaighofer
d56ee68482 Fix missing default null in setSmartyVarsFrontend 2023-03-13 10:36:05 +09:00
Clemens Schwaighofer
b89ab09e12 SmartyExtend parameter phpdoc order fix 2023-03-13 09:25:50 +09:00
Clemens Schwaighofer
e873ade6c0 Change the SmartyExtended Vars set calls to use options array
Instead of having each parameter single, group them into an options array
so we do not have to worry about updating the whole function call.

Keep the main core call as is
2023-03-13 09:15:59 +09:00
Clemens Schwaighofer
5910b884ac Bug fix in SmartyExtend class and user name set 2023-03-10 15:25:54 +09:00
Clemens Schwaighofer
e3bd2c1c3b Remove more _SESSION calls in classes, test updates
Admin\EditBase now has ACL\Login class as mandatory class parameter
Output\Form\Generate has loginAcl array parameter as mandatory
2023-03-10 15:08:56 +09:00
Clemens Schwaighofer
90a8c5540f Deprecate the GetLocale class and replace with ACL\Login locales set
If ACL\Login is used, the loginGetLocale() call should be used to fetch
the locale data set during login

Language\L10n now has new encoding/charset optional parameter 4
If the locale does not have a charset set the defautl charset is UTF-8
unless set via the new parameter or setOverrideEncoding()

Because of the deprecation of the getLocales call the following methods
do no longer need $locales
- Admin\Backend
- Admin\EditBase
- Output\Form\Generate
- Template\SmartyExtend

They all use the new l10n->getLocaleAsArray() call to set all the local
variables

* Admin\EditBase has new parameter "options" to set
- cache_id
- compile_id
which must be passed on the SmartyExtend class init
2023-03-10 13:43:43 +09:00
Clemens Schwaighofer
ea503fffe9 Merge branch 'development' 2023-03-09 16:45:07 +09:00
Clemens Schwaighofer
feba79a2e8 Install psalm as dev, sync scripts updates 2023-03-09 16:27:10 +09:00
Clemens Schwaighofer
6bec59e387 Deprecate check for GetDotEnv tests 2023-03-09 16:17:52 +09:00
Clemens Schwaighofer
03fbcaecfb Code Clean up, more testing
Remove unused code and classes.
Clean up code to remove all named constant from them and throw
deprecation alerts if used.
Add basic psalm setup in root folder and remove from www folder
2023-03-09 15:55:57 +09:00
Clemens Schwaighofer
283e7de1dc Remove Jason class, now in Json, deprecated since v7 2023-03-02 15:35:13 +09:00
Clemens Schwaighofer
d952c5f774 Remove Jason class, now in Json, deprecated since v7 2023-03-02 15:26:40 +09:00
Clemens Schwaighofer
cd8351d761 Update composer package create readme 2023-03-02 11:51:39 +09:00
Clemens Schwaighofer
b992901072 Move all tests into sub folders for a more clear structure 2023-03-02 11:51:29 +09:00
Clemens Schwaighofer
1596654149 Moved minimum php version to 8.1
All PostgreSQL calls are now Connection/Resource object types and not
resource
All methods have parameter type set
2023-02-28 17:36:19 +09:00
Clemens Schwaighofer
44f37b7f74 Fix in EditUser Table Array query load for languages
Also split all queries into multi line ones
Fixes in Form\Generate for TableArray Interface location move
Update EditBase to new and old edit schema (scheme) file name
2023-02-28 10:31:34 +09:00
Clemens Schwaighofer
829f5c567f Update composer autoload map files 2023-02-28 06:40:19 +09:00
Clemens Schwaighofer
710a48abcd Move the TableArrays Interface to a sub folder in TableArrays
to avoid strange path lookups
2023-02-28 06:36:11 +09:00
Clemens Schwaighofer
f564c27319 Add readme file for composer package deploy flow 2023-02-24 16:44:21 +09:00
Clemens Schwaighofer
00b98e7230 phpunit test scritps fix 2023-02-24 09:45:52 +09:00
Clemens Schwaighofer
7cae3e701a phpunit test fixes 2023-02-24 09:43:24 +09:00
Clemens Schwaighofer
da67d1bde3 phpstan update 2023-02-22 06:47:34 +09:00
Clemens Schwaighofer
16c3653cee phpstan updates for Smarty move to composer package 2023-02-22 06:42:53 +09:00
Clemens Schwaighofer
47c4c5cb69 Install smarty from composer package 2023-02-22 06:36:34 +09:00
Clemens Schwaighofer
7b9dc9c8b2 Rename the VarSetType class to SetVarType
The old name made no sense and it was only used in two projects, so the
deprecation step has been skipped
2023-02-22 06:32:58 +09:00
Clemens Schwaighofer
6133da9069 phpunit test must be installed in www/ folder
Because www folder is source base, if phpunit is installed outside it
cannot find the classes
2023-02-22 06:31:50 +09:00
Clemens Schwaighofer
fa0b102d1a Fix test file for Form\Generate
There is no global variable load anymore

Override table arrays have to be set on load
2023-02-17 13:19:59 +09:00
Clemens Schwaighofer
0e99700bbe Composer vendor name change 2023-02-16 13:04:20 +09:00
Clemens Schwaighofer
2f0b9fb360 Move Fonts folder from lib to includes 2023-02-16 12:32:15 +09:00
Clemens Schwaighofer
c7cc3c2938 Move all dev tools from www composer to outside master composer 2023-02-13 16:36:51 +09:00
Clemens Schwaighofer
f508b607a6 Add new class for auto set vars to certain types or null
Convert\VarSetType for always return type expected (eg string will be string)
or Convert\VarSetTypeNull to return string or null on failure

The return value for failure if not matching type can be set for both, but
only for Convert\VarSetTypeNull the return value can be set to null.
2023-02-08 12:12:46 +09:00
Clemens Schwaighofer
f94b350ba4 Install phan/phpstan local 2023-02-08 12:02:18 +09:00
Clemens Schwaighofer
53eef03387 Fixes in DB\IO query detection regex
- start with allow whitespace in front
- returning allows more white space types between keyword and parameter list
2023-02-02 10:27:07 +09:00
Clemens Schwaighofer
5a81445a28 DB\IO table match regex fix
UPDATE with SET can have spaces with variable length before
2023-01-27 11:31:26 +09:00
Clemens Schwaighofer
4bbbd653cd DB\IO fix for regex query detection
Fix for basic query detection:
Simeple starts with
SELECT/WITH/SHOW
INSERT INTO/UPDATE/DELETE FROM
UPDATE

Above does no complex query detection, just if the string starts with this

Fix form table detection for primary key auto set trial.
2023-01-27 11:12:46 +09:00
Clemens Schwaighofer
4c28e6d0ec Change DB\IO Returning check regex
Better regex to only get last returning in query, with proper parameter
check
2023-01-26 16:37:22 +09:00
Clemens Schwaighofer
66b7e81463 Bug fix in DB\IO for EOM string build queries with returning
on EOM string build queries it was not checked that RETURNING could have
no space in front.

Fixed and updated test calls
2023-01-25 16:47:31 +09:00
Clemens Schwaighofer
cf58f86802 Remove not needed ?? '' in ACL\Login 2023-01-16 14:29:25 +09:00
Clemens Schwaighofer
ff644310cd Readme file update 2023-01-11 09:22:18 +09:00
Clemens Schwaighofer
58988b9c0f Rename edit schemes pages to schemas 2023-01-11 09:12:56 +09:00
Clemens Schwaighofer
fe75f1d724 Add missing table arrays and name fix schim
missed two table arrays as class EditVisibleGroup and EditAccess

also fix wrong name for EditSchemas (wrong: EditSchemes) with a shim
lookup.

edit_schemes.php file will stay the same for now.
2023-01-11 07:06:28 +09:00
Clemens Schwaighofer
0607cdc3be Add logger $log public entry in Form class
We need that for sub calls to debugger from TableArray loads
2023-01-10 18:19:20 +09:00
Clemens Schwaighofer
6cb14daf49 Move includes/table_arrays to class Output\Form\TableArrays
also remove the legacy edit_base.LEGACY.php file

All previous includes/table_arrays load via include are now moved to a
class system so we have all implemented in one folder and can easy update
and add unit tests to it.
2023-01-10 18:04:29 +09:00
Clemens Schwaighofer
330582f273 Add $this identifier to class in array_edit_users 2023-01-10 16:42:42 +09:00
Clemens Schwaighofer
b0293b52bd Fix edit_users load problem with removed globals
acl_admin/base_acl_leve

Added public access methods to read this when array_* files are included.
2023-01-10 16:01:02 +09:00
Clemens Schwaighofer
00591deb00 Add Check Color class
checks html/css color string for valid
eg, hex #, hex alpha #, rgb/rgba, hsl/hsla
2023-01-10 15:43:33 +09:00
Clemens Schwaighofer
737f70fac5 Fix phpdoc Exception name with missing \ 2023-01-10 14:40:16 +09:00
Clemens Schwaighofer
0328ccd2fe Convert\Colors fixes for from HSB/HSL Hue 360
If hue 360 is given, it is no longer an error but internally converted to 0
2023-01-10 14:07:01 +09:00
Clemens Schwaighofer
eba1e2885f Convert\Byte add exception
And exception is thrown for invalid flags
2023-01-10 14:06:09 +09:00
Clemens Schwaighofer
53813261fb Form\Generate update
- remove auto load _POST vars
- Update color settings to # leading 6/8 digit hex code
- remove any global variable calls/requests
- fix some isset/empty clean ups (isset + set = !empty)
- fix on delete of reference data that loaded data was not shown again
- fix on reference data save error that wrong data is still shown and not removed
2023-01-10 11:25:51 +09:00
Clemens Schwaighofer
df2ae66942 Bug fix for loading after new/save/delete 2023-01-06 15:16:01 +09:00
Clemens Schwaighofer
78e1d73cd9 Move code from edit_base.php to class file 2023-01-06 15:07:15 +09:00
Clemens Schwaighofer
620a5878c1 Update dotenv reader from Composer package 2023-01-06 10:51:12 +09:00
Clemens Schwaighofer
5a0b09a916 Add DB\IO get prepare cursor array entries 2023-01-05 17:26:26 +09:00
Clemens Schwaighofer
98c6033c75 mo to js script set exectueable 2023-01-05 15:31:35 +09:00
Clemens Schwaighofer
6dcebc9b67 mo to js shell script 2023-01-05 15:30:51 +09:00
Clemens Schwaighofer
c97520e186 Update flatpickr javascript lib 2023-01-05 15:14:08 +09:00
Clemens Schwaighofer
764ca1f098 fix Generate\Form test with missing session init 2023-01-05 11:32:39 +09:00
Clemens Schwaighofer
3d23e5b066 Composer update 2023-01-05 10:55:01 +09:00
Clemens Schwaighofer
90e418ba24 Smarty update to 4.3.0 2023-01-05 10:38:51 +09:00
Clemens Schwaighofer
b6a0937e0c autoloader update 2022-12-28 17:46:40 +09:00
Clemens Schwaighofer
b3f6f8ef18 Raname Progressbar.php to ProgressBar.php 2022-12-28 16:14:38 +09:00
Clemens Schwaighofer
d9d5400498 Add Test for get run times for unrun queryies 2022-12-28 11:31:39 +09:00
Clemens Schwaighofer
b1be681afb Bug fix in DB\IO for wrong array check
Did not use empty to check if query called hash entry exists
2022-12-28 11:26:56 +09:00
Clemens Schwaighofer
8ef309d479 PHP unit 8.2, cel builder br/input fix, doctype add, sync .user.ini
- do not sync .user.ini file in sync template
- add PHP 8.2 for test target phpunit
- cel/phfo builder update to not close br or img tags (besides input)
- psalm settings update
- add doctype to all base templates
2022-12-27 16:58:51 +09:00
Clemens Schwaighofer
6e59b63791 DB\IO phpdoc and phpstan fixes 2022-12-14 14:18:33 +09:00
Clemens Schwaighofer
9c7b3cea83 SQL Interface docstring fix 2022-12-14 13:53:54 +09:00
Clemens Schwaighofer
26af6a07f4 PHP unit tests updates for class changes 2022-12-09 16:53:10 +09:00
Clemens Schwaighofer
b7c6d4b478 DB\IO add unescape bytea data 2022-12-09 16:33:58 +09:00
Clemens Schwaighofer
9936fc04da Convert\Byte class fix string in abs call 2022-12-09 16:33:47 +09:00
Clemens Schwaighofer
1e0dfa2106 Update documentation for Convert\Byte Class
add more info in parameters flag
2022-12-09 13:43:15 +09:00
Clemens Schwaighofer
3af6f6a8f0 edit interface, hard set edit.css style sheet 2022-12-07 06:56:47 +09:00
Clemens Schwaighofer
1e793c0d16 Switch all SQL from tab to spaces 2022-12-06 09:27:08 +09:00
Clemens Schwaighofer
5be34453ce Fix in Smarty Template class and PAGE WIDTH set 2022-11-10 10:26:59 +09:00
Clemens Schwaighofer
7773b78e17 Fix in CoreLib Bytes for null to trim 2022-11-09 18:11:14 +09:00
Clemens Schwaighofer
2a3798c8c2 Create mo file update, basic edit css/javascript updates 2022-10-25 16:48:34 +09:00
Clemens Schwaighofer
bc8303fe5f Composer update 2022-10-25 16:48:12 +09:00
Clemens Schwaighofer
ba89b188d9 Mo create script update, phpstan fixes, document updates
create_mo:
set auto base folder bases on current location so we do not need do
adjust paths in script

phpstan fixes:
edit_base.php
CoreLibs/Basci.php
CoreLibs/DB/SQL/PgSQL.php

Update documentation in method headers
CoreLibs/Combined/DateTime.php
2022-10-12 09:48:22 +09:00
Clemens Schwaighofer
d15618cde4 Add JS_TRANSLATE file to smart template flow 2022-10-04 17:35:46 +09:00
Clemens Schwaighofer
0fd89727e9 Update Output\Form to allow ACL controlled edit/view entries
Fixed phpunit test runs with encoding test run and not resetting the
subsitute character back to default

Note: There are mime encoding failures for php 7.4 and 8.0 and one ACL
login failure test for php 7.4
2022-09-06 11:17:03 +09:00
Clemens Schwaighofer
a8e75d158b Update composer installed packages 2022-09-06 11:16:33 +09:00
Clemens Schwaighofer
4b3fbaa309 Updates based on latest phpstan run 2022-09-02 17:00:22 +09:00
Clemens Schwaighofer
1a6c65df0e Minor test updates, comment typo updates, DB_CONFIG_SET for default
$DB_CONFIG_SET is now default current selcted db config instead of
$DB_CONFIG so to not overwrite the array itself
2022-08-05 12:43:57 +09:00
Clemens Schwaighofer
24f553a17e Update comments for split element counter 2022-07-29 13:20:54 +09:00
Clemens Schwaighofer
9a3ea2f7db update strings class with split counter method 2022-07-29 13:19:45 +09:00
Clemens Schwaighofer
bcdb877d90 Phan/phpstan fixes 2022-07-29 11:06:53 +09:00
Clemens Schwaighofer
6d0e528c38 Combined\Datetime date/number to weekday conversion
Convert functions for date or weekday number to weekday name or date to
weekday number
2022-07-29 11:02:25 +09:00
Clemens Schwaighofer
7e6474195b String split format fix for non ascii characters
Currently just abort and return string as is
2022-07-29 10:38:32 +09:00
Clemens Schwaighofer
1795d3ba6c String convert class added
Currently with one method: splitFormatString

Converts a string to a string with separater characters based on a
format string
2022-07-29 07:00:02 +09:00
Clemens Schwaighofer
e1340acf55 Class header info string fix 2022-07-29 06:59:33 +09:00
Clemens Schwaighofer
b5ead3e266 Minor code block comment clean ups 2022-07-15 17:42:38 +09:00
Clemens Schwaighofer
f5daaca598 Fixes for Create\Email docstrings 2022-07-08 17:25:45 +09:00
Clemens Schwaighofer
6b4f310cd2 Email\Sending add dedicated kv folding flag
kv folding is now done via flag and only if an encoded is detected that
is japanese or UTF-8
2022-07-04 15:45:51 +09:00
Clemens Schwaighofer
7b5bddb529 Create\Email fixes
Remove not needed reaplce content count for first global replace
Move logger out of test only and log if a logger class is attached
2022-07-01 08:57:14 +09:00
Clemens Schwaighofer
0a6fdf1248 Use logged encoding from JSON debug block for non UTF-8 tests 2022-06-30 18:18:17 +09:00
Clemens Schwaighofer
3220180d58 Bug fix in Email for encoding subject/body with empty replace
Also store encoding in json log if test and debug print is given
2022-06-30 18:16:28 +09:00
Clemens Schwaighofer
8c8f14ec74 Fix logging per run to be setable not only on start
Move per run set into method.
Add set/get method and add set method override (set new) flag

Update phpUnit testing and move providers to test methods
2022-06-30 18:15:36 +09:00
Clemens Schwaighofer
643991c3fd Update Debug\Support, add Create\Email
update debug support to add html escape for html strings on request.
Default is keep as is. debugString gets new third parameter for this as
bool flag.

Add Create\Email to send basic text emails to several too addresses.
Content replace in subject and body is possible with {} entries.
Default encoding is UTF-8 but others can be set and content will be
converted to this.
The dynamic replace works on all data or can be set per receiver.
2022-06-28 17:29:31 +09:00
Clemens Schwaighofer
c81c46d426 Move read_env_file.php to deprecated folder 2022-06-23 14:46:19 +09:00
Clemens Schwaighofer
d97b173ee7 ACL\Login move public var to private: login
the former public var $login is now private and if it is set can be
checked with loginActionSet (true if login_login was in _POST as login
action.

Some info update for phpUnit ACL\Login test file
2022-06-23 09:12:46 +09:00
Clemens Schwaighofer
b61152f10e Skipped/Incomplete tests update 2022-06-23 07:09:19 +09:00
Clemens Schwaighofer
0c68ebe652 Login\ACL revalidate flow fixes
- DB function had wrong column name
- Queries in ACL\Login had wrong column name
- Renamed from login_user_id_last_login to login_user_id_last_revalidate
  to make it more clear what this column is
- add edit_user admin page output for this column
- add phpUnit test case for revalidate is needed and login with next
  loginUserId is ok again
2022-06-23 06:50:07 +09:00
Clemens Schwaighofer
31d0cdb8ad Fix revalidate after flow in ACL\Login
After revalidate time was reached, it was never reset because it used
the original loginUserId set date.
A new column has been added that gets reset every time the user logs in
with username and password if a loginUserId is set in the database
2022-06-22 19:38:03 +09:00
Clemens Schwaighofer
0f823bd283 Just minor line length fixes in ArrayIO class 2022-06-22 18:10:23 +09:00
Clemens Schwaighofer
6385a48824 add back unique constraint because null login_user_id are allowed 2022-06-22 16:56:51 +09:00
Clemens Schwaighofer
a754d897cf Bug fixes for emptynull type in ArrayIO, Form\Generate interval
Form\Generate for intervals also allows day(s), month(s), year(s), call
case insensitive

ArrayIO fix for missing escale literal for
date/datetime/interval/emptynull text type
2022-06-22 16:17:16 +09:00
Clemens Schwaighofer
4600f8f7bf Update edit_user form page and also minor updates to Form and ArraIO
login_user_id is unique if not null (as index, constraint only with
PostgreSQL 15)
login_user_id_revalidate_after is not longer not null and default set,
no need for this

DB\Extended\ArrayIO:
add sql_read for datetime fields to change amount of data (eg only up
to minute) with to_char() method. sample: YYYY-MM-DD HH24:MI
Add date/datetime/emptynull for setting empty fields to null and not
empty string

Output\From\Generate:
Remove all fill for spacer and change them to placeholder html types.
Add datetime check next to date, time only checks

edit_user Admin Form:
add all new columns there
2022-06-22 15:50:07 +09:00
Clemens Schwaighofer
04e4fe46f2 Update ACL\Login class with _GET/_POST login parameter
loginUserId parameter in _GET or _POST for direct login without username
and password.

This can be secured by:
- must login after x days from set loginUserId on
- can only login with loginUserId in given time range
- flag lock loginUserId
2022-06-22 13:52:47 +09:00
Clemens Schwaighofer
c35d3c9324 Remove constant from deprecated ReadEnvFile class (now dotEnv) 2022-06-14 16:27:00 +09:00
Clemens Schwaighofer
e92a682a8c ACL\Login phpUnit tets update for ajax page tests 2022-06-14 16:24:17 +09:00
Clemens Schwaighofer
ea07e4b95b Update ACL\Login with internal ajax_page flag override
Also ajax flag return method
2022-06-14 16:23:24 +09:00
Clemens Schwaighofer
88178cb08d Rename class ReadEnvFile to DotEnv and add test cases
previous named Get\ReadEnvFile is no Get\DotEnv, static method is the
same.
Update for not parsing comments at the end of a line if the line was not
in quotes. Strips everything after comment mark and also right trims any
trailing spaces

Old:
FOO=Test # Comment -> $_ENV['FOO'] = "Test # Comment"

New:
FOO=Test # Comment -> $_ENV['FOO'] = "Test"

Add phpUnit tests for DotEnv class.
Update config.php with new class name

The old class name exists and is markted as deprecated until next major
release
2022-06-09 09:05:48 +09:00
Clemens Schwaighofer
5d98be06be Code clean up for phan and phpstan 2022-06-07 18:16:23 +09:00
Clemens Schwaighofer
183cadb0fd Class ACL\Login update with phpunit testing
Move logic from constructor to separate function
Add more public access methods for internal variable access (password
min length settings, error login code, error login string error)
All error messages are declared in constructor with wrapper function to
create html error string for template creation
Add wrapper function for exit/abort and page name read for easier mocking
in testing
Fixes for multi login main function caller and cached query problem: do
not cache query for login
Add reverse default access list SESSION variable and public readers
Update logout with unset of full SESSION array to empty, use external
session class for all session calls. Also unset euid on logout
2022-06-07 18:05:50 +09:00
Clemens Schwaighofer
2067a6fe1d Config master SSL site detection update 2022-06-07 18:05:34 +09:00
Clemens Schwaighofer
f5b6c639fb Fix primary key base number after more inserts are done 2022-06-03 11:13:35 +09:00
Clemens Schwaighofer
cde29c0362 Update DB\IO dbReturn and update connected tests
Switch the code point for these below for logic reasons
CLEAR_CACHE 1 => 2 (clear cache AFTER END read)
READ_NEW 2 => 1 (clear cache BEFORE first read)

in dbReturn cursor ext array:
remove firstcall entry because it is not needed
add new:
- cache_flag: $cache method call number
- assoc_flag: the assoc read flag from the method call
- cached: if there is data cached in the cursor ext array this is true
- finished: true if the last read was false
- db_read_finished: if true the db read has fiinished (read_rows =
  num_rows)
- read_finished: if true the current read (cache or db) via pos =
  num_rows is done
- log_pos: sequential number for each call with the same query hash
- log: array with current actions done in the last read

Update DB IO class test with all cursor, cursor ext, read single step,
read in loop, read again, etc tests
2022-06-03 11:03:06 +09:00
Clemens Schwaighofer
10234000b7 ACL\Login test class add
- db create shell script for ACL\Login to reset full database to known
  good stated
- basic tests written to check core login class
2022-06-02 18:14:58 +09:00
Clemens Schwaighofer
a63a50a412 add tests for dbReturn only
dbReturn special call test file, update main db test file

Main class_test file with links update
2022-06-02 16:38:10 +09:00
Clemens Schwaighofer
59da10b649 Session class update with session destroy / start wrapper
session start wrapper as protected method

session destroy wrapper with _SESSION array unset
2022-06-02 16:35:40 +09:00
Clemens Schwaighofer
b714de498f Add print boolean stand allone support function
in the Debug\Support add printBool to print out bool as string.
Same as printAsString with bool alone but you can control prefix name,
and true/false string names

Add printArray alias to prAr
2022-06-02 16:31:35 +09:00
Clemens Schwaighofer
aa11937ab2 Update Running time for better high resolution timer
Instead of ending with the second hr timer call, we print out difference
to the last one.
Add new method to print out from start time difference and add a reset
method
2022-06-02 16:30:22 +09:00
Clemens Schwaighofer
d64d5f081c Add Class for Memory Usage tracking 2022-06-02 16:26:55 +09:00
Clemens Schwaighofer
3085b52714 Update PHPdoc parts
Add empty line between description and first @param entry.
Some other minor comment layout and text fixes
2022-06-02 16:07:38 +09:00
Clemens Schwaighofer
9949a5ef7f Update core edit_* tables with various fixes
- import script with write to file option
- fix file names for functions
- add generic (non edit tables) for only set date, set uid or combine
- fix edit table edit_language insert data
- all trigger create remove the drop on exists, as in the flow the trigger will never exists
2022-05-31 20:17:32 +09:00
Clemens Schwaighofer
74ba935e96 Comment info update in L10n class 2022-05-27 15:01:30 +09:00
Clemens Schwaighofer
39a62ed59d Update phpunit tests for Debug\Logging 2022-05-26 15:20:19 +09:00
Clemens Schwaighofer
1379cf1519 Add print bool in logging class 2022-05-26 14:31:48 +09:00
Clemens Schwaighofer
2d15b78d21 Composer update 2022-05-26 09:52:37 +09:00
Clemens Schwaighofer
92ebdb4b9e Update session class and write session clas tester, update DB\IO Tester
DB\IO Tester now has correct testing for pgVersion string compare. Uses
mocked pgVersion return for this

Session class update with full magic set/get or method set/get for
_SESSION var. Also added full testing for this
2022-05-26 09:27:57 +09:00
Clemens Schwaighofer
a523a4e831 ACL\Login class update for phpstan check 2022-05-24 16:50:37 +09:00
Clemens Schwaighofer
db8e17ae7c Convert static Session class to normal session class
All static Session:: calls (except for checking valid session name) are
converted to object type. This Object is passed on to Login, Admin
Backend and any other class that needs basic session checking
2022-05-24 15:00:04 +09:00
Clemens Schwaighofer
5b581c2ed6 Fix checkCLI call on false return from php_sapi_name() 2022-05-24 13:12:17 +09:00
Clemens Schwaighofer
1e734581d7 Session class update, cli check add, tests updates, edit table update
Update edit_access_data table and set unique check for edit_access_id +
name so we do not have two identical keys for one edit access set

Update config host and add more test domains for various access tests

Update Session and move cli check to Get\System class. Some other minor
session info updates

New method \Get\System::checkCLI() returns true if the sapi name has
cli inside, else false
2022-05-24 11:36:03 +09:00
Clemens Schwaighofer
aecdda3557 Update per level log file writing and with level check
remove all non alphanumeric characters from the level string (debug call
first parameter) so the file name is not invalid:wq
2022-05-19 13:24:28 +09:00
Clemens Schwaighofer
2119b757b1 Session class docu not needed text delete 2022-05-19 10:22:39 +09:00
Clemens Schwaighofer
27087a0e0e Update session class with checks, etc
Add a write close session call to end a session for AJAX calls
Add error strings to session start method
Add check call for session names that they are valid
2022-05-13 14:14:08 +09:00
Clemens Schwaighofer
2b689b666a Update all class test files in admin with proper named header 2022-05-13 13:34:54 +09:00
Clemens Schwaighofer
63aeebdee0 phpstan ingore for Error Handlung function 2022-05-12 15:19:09 +09:00
Clemens Schwaighofer
51e700cd10 Fixes from phan/phpstan tests 2022-04-28 14:17:30 +09:00
3026 changed files with 71763 additions and 204236 deletions

10
.gitignore vendored
View File

@@ -0,0 +1,10 @@
.libs
node_modules/
composer.lock
vendor/
tools/
www/composer.lock
www/vendor
**/.env
**/.target
package-lock.json

View File

@@ -26,6 +26,8 @@
use Phan\Config;
return [
// "target_php_version" => "8.3",
"minimum_target_php_version" => "8.3",
// turn color on (-C)
"color_issue_messages_if_supported" => true,
// If true, missing properties will be created when
@@ -94,8 +96,6 @@ return [
"exclude_analysis_directory_list" => [
'www/vendor',
'www/tests',
'www/lib/Smarty',
'www/lib/smarty-4.1.0',
'www/templates_c',
'www/log',
'www/tmp',
@@ -116,8 +116,6 @@ return [
// ignore the old qq tests
'www/admin/qq_file_upload_front.php',
'www/admin/qq_file_upload_ajax.php',
// symlink ignore
'www/lib/smarty-4.1.0/libs/Smarty.class.php'
],
// what not to show as problem
@@ -130,7 +128,12 @@ return [
'PhanWriteOnlyPublicProperty',
'PhanUnreferencedConstant',
'PhanWriteOnlyPublicProperty',
'PhanReadOnlyPublicProperty'
'PhanReadOnlyPublicProperty',
// start ignore annotations
'PhanUnextractableAnnotationElementName',
'PhanUnextractableAnnotationSuffix',
// enum problems in comments
'PhanCommentObjectInClassConstantType'
],
// Override to hardcode existence and types of (non-builtin) globals in the global scope.

12
.phive/phars.xml Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="~9.6" installed="9.6.31" location="./tools/phpunit" copy="false"/>
<phar name="phpcbf" version="4" installed="4.0.1" location="./tools/phpcbf" copy="false"/>
<phar name="phpcs" version="4" installed="4.0.1" location="./tools/phpcs" copy="false"/>
<phar name="phpstan" version="^2.0" installed="2.1.33" location="./tools/phpstan" copy="false"/>
<phar name="phan" version="^5.4.3" installed="5.5.2" location="./tools/phan" copy="false"/>
<phar name="psalm" version="^5.26.1" installed="5.26.1" location="./tools/psalm" copy="false"/>
<phar name="phpdox" version="^0.12.0" installed="0.12.0" location="./tools/phpdox" copy="false"/>
<phar name="phpdocumentor" version="^3.4.2" installed="3.9.1" location="./tools/phpDocumentor" copy="false"/>
<phar name="php-cs-fixer" version="^3.34.1" installed="3.92.4" location="./tools/php-cs-fixer" copy="false"/>
</phive>

2
.shellcheckrc Normal file
View File

@@ -0,0 +1,2 @@
shell=bash
external-sources=true

View File

@@ -1,14 +1,20 @@
#!/usr/bin/env bash
base_folder='/var/www/html/developers/clemens/core_data/php_libraries/trunk/www/';
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
# Assume script is in 4dev/bin
base_folder="${BASE_FOLDER}../../www/";
# locale gettext po to mo translator master
for file in $(ls -1 ${base_folder}../4dev/locale/*.po); do
file=$(basename $file .po);
echo "Translate language ${file}";
for file in "${base_folder}"../4dev/locale/*.po; do
[[ -e "$file" ]] || break
file=$(basename "$file" .po);
locale=$(echo "${file}" | cut -d "-" -f 1);
domain=$(echo "${file}" | cut -d "-" -f 2);
msgfmt -o ${base_folder}/includes/locale/${locale}/LC_MESSAGES/${domain}.mo ${base_folder}../4dev/locale/${locale}-${domain}.po;
echo "- Translate language file '${file}' for locale '${locale}' and domain '${domain}':";
if [ ! -d "${base_folder}/includes/locale/${locale}/LC_MESSAGES/" ]; then
mkdir -p "${base_folder}/includes/locale/${locale}/LC_MESSAGES/";
fi;
msgfmt -o "${base_folder}/includes/locale/${locale}/LC_MESSAGES/${domain}.mo" "${base_folder}../4dev/locale/${locale}-${domain}.po";
done;
# __END__

176
4dev/bin/mo_to_js.sh Executable file
View File

@@ -0,0 +1,176 @@
#!/bin/bash
# read source mo files and writes target js files in object form
# check for ARG 1 is "no-move"
# then do not move the files directly for manual check
FILE_MOVE=1;
if [ "${1}" = "no-move" ]; then
echo "+++ CREATE TEMPORARY FILES +++";
FILE_MOVE=0;
else
echo "*** Direct write ***";
fi;
target='';
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
# Assume script is in 4dev/bin
base_folder="${BASE_FOLDER}../../www/";
po_folder='../4dev/locale/'
mo_folder='includes/locale/';
target_folder='';
template_file_stump='##SUFFIX##translate-##LANGUAGE##.TMP.js';
# for output file names
source_list=(iw);
language_list=(en ja);
# set target names
if [ "${target}" == '' ]; then
echo "*** Non smarty ***";
TEXTDOMAINDIR=${base_folder}${mo_folder}.
# default is admin
TEXTDOMAIN="admin";
fi;
js_folder="${TEXTDOMAIN}/layout/javascript/";
error=0;
# this checks if the TEXTDOMAIN target actually exists
if [ ! -d "${base_folder}${js_folder}" ]; then
echo "Cannot find target javascript folder ${base_folder}${js_folder}";
error=1;
else
target_folder="${base_folder}${js_folder}";
fi;
if [ ${error} -eq 1 ]; then
exit;
fi;
# locale gettext po to mo translator master
for file in "${base_folder}"../4dev/locale/*.po; do
[[ -e "$file" ]] || break
file=$(basename "$file" .po);
locale=$(echo "${file}" | cut -d "-" -f 1);
domain=$(echo "${file}" | cut -d "-" -f 2);
echo "- Translate language file '${file}' for locale '${locale}' and domain '${domain}':";
if [ ! -d "${base_folder}/includes/locale/${locale}/LC_MESSAGES/" ]; then
mkdir -p "${base_folder}/includes/locale/${locale}/LC_MESSAGES/";
fi;
msgfmt -o "${base_folder}/includes/locale/${locale}/LC_MESSAGES/${domain}.mo" "${base_folder}${po_folder}${locale}-${domain}.po";
done;
rx_msgid_empty="^msgid \"\"";
rx_msgid="^msgid \"";
rx_msgstr="^msgstr \""
# quick copy string at the end
quick_copy='';
for language in "${language_list[@]}"; do
# I don't know which one must be set, but I think at least LANGUAGE
case ${language} in
ja)
LANG=ja_JP.UTF-8;
ENCODING=UTF-8;
LANGUAGE=ja;
;;
en)
# was en_JP.UTF-8
LANG=en_US.UTF-8;
ENCODING=UTF-8;
LANGUAGE=en;
;;
esac;
# write only one for language and then symlink files
template_file=$(echo ${template_file_stump} | sed -e "s/##SUFFIX##//" | sed -e "s/##LANGUAGE##/${LANG}/");
# original_file=$(echo ${template_file} | sed -e 's/\.TMP//g');
original_file=${template_file//.TMP/};
if [ "${FILE_MOVE}" -eq 0 ]; then
file=${target_folder}${template_file};
else
file=${target_folder}${original_file};
fi;
echo "===> Write translation file ${file}";
echo ". = normal, : = escape, x = skip";
# init line [aka don't touch this file]
echo "// AUTO FILL, changes will be overwritten" > "$file";
{
echo "// source: ${suffix}, language: ${language}";
echo "// Translation strings in the format";
echo "// \"Original\":\"Translated\""$'\n'
echo "var i18n = {"
} >> "$file"
# translations stuff
# read the po file
pos=0; # do we add a , for the next line
cat "${base_folder}${po_folder}${language}-${TEXTDOMAIN}.po" |
while read -r str; do
# echo "S: ${str}";
# skip empty
if [[ "${str}" =~ ${rx_msgid_empty} ]]; then
# skip on empty
echo -n "x";
# msgid is left, msgstr is right
elif [[ "${str}" =~ ${rx_msgid} ]]; then
echo -n ".";
# open left side
# TODO: how to handle multi line strings: or don't use them
# extract from between ""
str_source=$(echo "${str}" | sed -e "s/^msgid \"//" | sed -e "s/\"$//");
# close right side, if not last add ,
if [ "${pos}" -eq 1 ]; then
echo -n "," >> "$file";
fi;
# all " inside string need to be escaped
# str_source=$(echo "${str_source}" | sed -e 's/"/\\"/g');
str_source=${str_source//\"/\\\"}
# fix with proper layout
echo -n "\"$str_source\":\"$(TEXTDOMAINDIR=${TEXTDOMAINDIR} LANGUAGE=${language} LANG=${LANG} gettext "${TEXTDOMAIN}" "${str_source}")\"" >> "$file";
pos=1;
elif [[ "${str}" =~ ${rx_msgstr} ]]; then
# open right side (ignore)
echo -n "";
else
# general ignore (anything between or comments)
echo -n "";
fi;
done;
echo "" >> "$file";
echo "};" >> "$file";
echo " [DONE]";
# on no move
if [ "${FILE_MOVE}" -eq 0 ]; then
echo "===> Confirm all changes in ${file} and then move data to original";
echo "";
quick_copy=${quick_copy}"mv ${template_file} ${original_file}"$'\n';
fi;
# symlink to master file
for suffix in "${source_list[@]}"; do
# symlink with full lang name
symlink_file[0]=$(echo ${template_file_stump} | sed -e "s/##SUFFIX##/${suffix}_/" | sed -e "s/##LANGUAGE##/${LANG}/" | sed -e 's/\.TMP//g');
# create second one with lang (no country) + encoding
symlink_file[1]=$(echo ${template_file_stump} | sed -e "s/##SUFFIX##/${suffix}_/" | sed -e "s/##LANGUAGE##/${LANGUAGE}\.${ENCODING}/" | sed -e 's/\.TMP//g');
for template_file in "${symlink_file[@]}"; do
# if this is not symlink, create them
if [ ! -h "${template_file}" ]; then
echo "Create symlink: ${template_file}";
# symlik to original
cd "${target_folder}" || exit;
ln -sf "${original_file}" "${template_file}";
cd - >/dev/null || exit;
fi;
done;
done;
done;
if [ "${FILE_MOVE}" -eq 0 ]; then
echo "";
echo "-- IN FOLDER: ${target_folder}";
echo "-- START: copy lines below to copy created over original --";
echo "${quick_copy}";
echo "-- END ----------------------------------------------------";
fi;
# __END__

View File

@@ -1,3 +1,100 @@
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
# must be run in ${base}www/
phan --progress-bar -C --analyze-twice
#!/bin/env bash
function error() {
if [ -t 1 ]; then echo "[MAK] ERROR: $*" >&2; fi; exit 0;
}
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h | --help] [-p | --php VERSION] [-c | --composer]
Runs phan static analyzer.
If -p is not set, the default intalled PHP is used.
Available options:
-h, --help Print this help and exit
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
-c, --composer Use composer version and not the default phives bundle
EOF
exit
}
BASE_PATH=$(pwd)"/";
PHP_BIN_PATH=$(which php);
if [ -z "${PHP_BIN_PATH}" ]; then
echo "Cannot find php binary";
exit;
fi;
DEFAULT_PHP_VERSION=$(${PHP_BIN_PATH} -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;");
if [ -z "${DEFAULT_PHP_VERSION}" ]; then
echo "Cannot set default PHP version";
exit;
fi;
php_version="";
no_php_version=0;
use_composer=0;
while [ -n "${1-}" ]; do
case "${1}" in
-p | --php)
php_version="${2-}";
shift
;;
-c | --composer)
use_composer=1;
shift
;;
-h | --help)
usage
;;
# invalid option
-?*)
error "[!] Unknown option: '$1'."
;;
esac
shift;
done;
if [ -z "${php_version}" ]; then
php_version="${DEFAULT_PHP_VERSION}";
no_php_version=1;
fi;
php_bin="${PHP_BIN_PATH}${php_version}";
echo "Use PHP Version: ${php_version}";
if [ "${use_composer}" -eq 1 ]; then
echo "Use composer installed";
else
echo "Use phive installed";
fi;
if [ ! -f "${php_bin}" ]; then
echo "Set php ${php_bin} does not exist";
exit;
fi;
# must be run in ${base}
cd "$BASE_PATH" || exit;
export PHAN_DISABLE_XDEBUG_WARN=1;
PHAN_CALL=(
"${php_bin}"
);
if [ "${use_composer}" -eq 1 ]; then
PHAN_CALL+=("${BASE_PATH}vendor/bin/phan");
else
PHAN_CALL+=("${BASE_PATH}tools/phan");
fi;
PHAN_CALL+=(
"--progress-bar"
"-C"
"--analyze-twice"
)
"${PHAN_CALL[@]}";
if [ "${no_php_version}" -eq 0 ]; then
echo "*** CALLED WITH PHP ${php_bin} ***";
${php_bin} --version;
else
echo "Default PHP used: $(php --version)";
fi;
cd ~ || exit;
# __END__

View File

@@ -1,3 +1,92 @@
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
# must be run in ${base}www/
phpstan
#!/bin/env bash
function error() {
if [ -t 1 ]; then echo "[MAK] ERROR: $*" >&2; fi; exit 0;
}
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-h | --help] [-p | --php VERSION] [-c | --composer]
Runs phan static analyzer.
If -p is not set, the default intalled PHP is used.
Available options:
-h, --help Print this help and exit
-p, --php VERSION Chose PHP version in the form of "N.N", if not found will exit
-c, --composer Use composer version and not the default phives bundle
EOF
exit
}
BASE_PATH=$(pwd)"/";
PHP_BIN_PATH=$(which php);
if [ -z "${PHP_BIN_PATH}" ]; then
echo "Cannot find php binary";
exit;
fi;
DEFAULT_PHP_VERSION=$(${PHP_BIN_PATH} -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;");
if [ -z "${DEFAULT_PHP_VERSION}" ]; then
echo "Cannot set default PHP version";
exit;
fi;
php_version="";
no_php_version=0;
use_composer=0;
while [ -n "${1-}" ]; do
case "${1}" in
-p | --php)
php_version="${2-}";
shift
;;
-c | --composer)
use_composer=1;
shift
;;
-h | --help)
usage
;;
# invalid option
-?*)
error "[!] Unknown option: '$1'."
;;
esac
shift;
done;
if [ -z "${php_version}" ]; then
php_version="${DEFAULT_PHP_VERSION}";
no_php_version=1;
fi;
php_bin="${PHP_BIN_PATH}${php_version}";
echo "Use PHP Version: ${php_version}";
if [ "${use_composer}" -eq 1 ]; then
echo "Use composer installed";
else
echo "Use phive installed";
fi;
if [ ! -f "${php_bin}" ]; then
echo "Set php ${php_bin} does not exist";
exit;
fi;
BASE_PATH=$(pwd)"/";
cd "$BASE_PATH" || exit;
PHPSTAN_CALL=(
"${php_bin}"
);
if [ "${use_composer}" -eq 1 ]; then
PHPSTAN_CALL+=("${BASE_PATH}vendor/bin/phpstan");
else
PHPSTAN_CALL+=("${BASE_PATH}tools/phpstan");
fi;
"${PHPSTAN_CALL[@]}";
if [ "${no_php_version}" -eq 0 ]; then
echo "*** CALLED WITH PHP ${php_bin} ***";
${php_bin} --version;
else
echo "Default PHP used: $(php --version)";
fi;
cd ~ || exit;

View File

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

View File

@@ -0,0 +1,22 @@
#!/bin/env bash
# syncs
# 4dev/tests/
# www/lib/CoreLibs/
#
# to the composer corelibs all repo
GO="${1}";
DRY_RUN="";
if [ "${GO}" != "go" ]; then
DRY_RUN="-n ";
fi;
BASE="/storage/var/www/html/developers/clemens/core_data/";
SOURCE="${BASE}php_libraries/master/"
TARGET="${BASE}composer-packages/CoreLibs-Composer-All/"
rsync ${DRY_RUN}-Plzvrupt --stats --delete ${SOURCE}4dev/tests/ ${TARGET}test/phpunit/
rsync ${DRY_RUN}-Plzvrupt --stats --delete ${SOURCE}www/lib/CoreLibs/ ${TARGET}src/
# __END__

View File

@@ -1,10 +1,11 @@
# this list only holds edit_* related table data
# functions
function/set_uid.sql
function/set_generic.sql
function/random_string.sql
function/set_edit_generic.sql
function/edit_set_access_uid.sql
function/edit_access_set_uid.sql
function/edit_group_set_uid.sql
function/edit_log_partition_insert.sql
function/edit_user_set_login_user_id_set_date.sql
# generic tables
table/edit_temp_files.sql
table/edit_generic.sql

View File

@@ -6,20 +6,25 @@
#exit;
# if flagged 'y' then it will ask after each import to continue
development='y';
development='n';
# do not import anything, just order flow output
test='n';
input='';
# write to file do not write to DB directly
write='y';
# database connection info
db='<db name>';
host='<db host>';
user='<db user>';
schema="public";
schemas='public';
export PGPASSWORD='';
# log files
error_file="log/error";
output_file="log/output";
error_file='log/error';
output_file='log/output';
data_file='database_create_data.sql';
if [ "$write" = 'y' ]; then
rm -f "${data_file}";
fi;
if [ ! -f ORDER ]; then
echo "Could not find ORDER file";
exit;
@@ -40,26 +45,37 @@ while read file <&3; do
if [ -f "$file" ]; then
for path in "$schemas"; do
echo "[+] WORK ON '${file}' @ '${path}'";
# skip all on test
if [ "$test" = 'n' ]; then
echo "=== START [$file] ===>" >> ${error_file};
psql -U ${user} -h ${host} -f "${file}" ${db} 1>> ${output_file} 2>> ${error_file}
echo "=== END [$file] ===>" >> ${error_file};
fi;
if [ "$development" = "y" ]; then
echo "Press 'y' to move to next. Press 'r' to reload last file. ^c to abort";
fi;
while [ "$development" = "y" ] && [ "$input" != "y" ]; do
read -ep "Continue (y|r|^c): " input;
if [ "$input" = "r" ]; then
echo "Reload File '${file}' ...";
if [ "$test" = 'n' ]; then
echo "=== START RELOAD [$file] ===>" >> ${error_file};
psql -U ${user} -h ${host} -f "${file}" ${db} 1>> ${output_file} 2>> ${error_file}
echo "=== END RELOAD [$file] ===>" >> ${error_file};
# write to file
if [ "$write" = 'y' ]; then
echo "-- START: ${file}" >> ${data_file};
cat "${file}" >> ${data_file};
echo "-- END: ${file}" >> ${data_file};
else
# write to DB
echo "=== START [$file] ===>" >> ${error_file};
psql -U ${user} -h ${host} -f "${file}" ${db} 1>> ${output_file} 2>> ${error_file}
echo "=== END [$file] ===>" >> ${error_file};
# next wait for dev
if [ "$development" = "y" ]; then
echo "Press 'y' to move to next. Press 'r' to reload last file. ^c to abort";
fi;
# loop run for reload on failed
while [ "$development" = "y" ] && [ "$input" != "y" ]; do
read -ep "Continue (y|r|^c): " input;
if [ "$input" = "r" ]; then
echo "Reload File '${file}' ...";
if [ "$test" = 'n' ]; then
echo "=== START RELOAD [$file] ===>" >> ${error_file};
psql -U ${user} -h ${host} -f "${file}" ${db} 1>> ${output_file} 2>> ${error_file}
echo "=== END RELOAD [$file] ===>" >> ${error_file};
fi;
fi;
done;
input='';
fi;
done;
input='';
fi;
done;
elif [[ ${file::1} != "#" ]]; then
echo "[!] COULD NOT FIND FILE: '${file}'";

View File

@@ -68,10 +68,11 @@ INSERT INTO edit_scheme (name, header_color, enabled) VALUES ('Visitor', 'B0C4B3
INSERT INTO edit_scheme (name, header_color, enabled) VALUES ('User', '1E789E', 1);
-- edit language
-- short_name = locale without encoding
-- iso_name = encoding
DELETE FROM edit_language;
INSERT INTO edit_language (long_name, short_name, iso_name, order_number, enabled, lang_default) VALUES ('English', 'en_US', 'UTF-8', 1, 1, 1);
INSERT INTO edit_language (long_name, short_name, long_name, iso_name, order_number, enabled, lang_default) VALUES ('Japanese', 'ja_JP', 'UTF-8', 2, 1, 0);
INSERT INTO edit_language (long_name, short_name, iso_name, order_number, enabled, lang_default) VALUES ('Japanese', 'ja_JP', 'UTF-8', 2, 1, 0);
-- edit group
DELETE FROM edit_group;
@@ -130,7 +131,7 @@ INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_
-- inserts admin user so basic users can be created
DELETE FROM edit_user;
INSERT INTO edit_user (username, password, enabled, debug, db_debug, email, protected, admin, edit_language_id, edit_group_id, edit_scheme_id, edit_access_right_id) VALUES ('admin', 'admin', 1, 1, 1, '', 1, 1,
(SELECT edit_language_id FROM edit_language WHERE short_name = 'en'),
(SELECT edit_language_id FROM edit_language WHERE short_name = 'en_US'),
(SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
(SELECT edit_scheme_id FROM edit_scheme WHERE name = 'Admin'),
(SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
-- add uid add for edit_access table
CREATE OR REPLACE FUNCTION set_edit_access_uid() RETURNS TRIGGER AS
$$
DECLARE
myrec RECORD;
v_uid VARCHAR;
BEGIN
-- skip if NEW.name is not set
IF NEW.name IS NOT NULL AND NEW.name <> '' THEN
-- use NEW.name as base, remove all spaces
-- name data is already unique, so we do not need to worry about this here
v_uid := REPLACE(NEW.name, ' ', '');
IF TG_OP = 'INSERT' THEN
-- always set
NEW.uid := v_uid;
ELSIF TG_OP = 'UPDATE' THEN
-- check if not set, then set
SELECT INTO myrec t.* FROM edit_access t WHERE edit_access_id = NEW.edit_access_id;
IF FOUND THEN
NEW.uid := v_uid;
END IF;
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -0,0 +1,28 @@
-- add uid add for edit_group table
CREATE OR REPLACE FUNCTION set_edit_group_uid() RETURNS TRIGGER AS
$$
DECLARE
myrec RECORD;
v_uid VARCHAR;
BEGIN
-- skip if NEW.name is not set
IF NEW.name IS NOT NULL AND NEW.name <> '' THEN
-- use NEW.name as base, remove all spaces
-- name data is already unique, so we do not need to worry about this here
v_uid := REPLACE(NEW.name, ' ', '');
IF TG_OP = 'INSERT' THEN
-- always set
NEW.uid := v_uid;
ELSIF TG_OP = 'UPDATE' THEN
-- check if not set, then set
SELECT INTO myrec t.* FROM edit_group t WHERE edit_group_id = NEW.edit_group_id;
IF FOUND THEN
NEW.uid := v_uid;
END IF;
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -9,142 +9,142 @@ CREATE OR REPLACE FUNCTION edit_log_insert_trigger ()
RETURNS TRIGGER AS
$$
DECLARE
start_date DATE := '2010-01-01';
end_date DATE;
timeformat TEXT := 'YYYY';
selector TEXT := 'year';
base_table TEXT := 'edit_log';
_interval INTERVAL := '1 ' || selector;
_interval_next INTERVAL := '2 ' || selector;
table_name TEXT;
-- compare date column
compare_date DATE := NEW.event_date;
compare_date_name TEXT := 'event_date';
-- the create commands
command_create_table TEXT := 'CREATE TABLE IF NOT EXISTS {TABLE_NAME} (CHECK({COMPARE_DATE_NAME} >= {START_DATE} AND {COMPARE_DATE_NAME} < {END_DATE})) INHERITS ({BASE_NAME})';
command_create_primary_key TEXT := 'ALTER TABLE {TABLE_NAME} ADD PRIMARY KEY ({BASE_TABLE}_id)';
command_create_foreign_key_1 TEXT := 'ALTER TABLE {TABLE_NAME} ADD CONSTRAINT {TABLE_NAME}_euid_fkey FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL';
command_create_trigger_1 TEXT = 'CREATE TRIGGER trg_{TABLE_NAME} BEFORE INSERT OR UPDATE ON {TABLE_NAME} FOR EACH ROW EXECUTE PROCEDURE set_edit_generic()';
start_date DATE := '2010-01-01';
end_date DATE;
timeformat TEXT := 'YYYY';
selector TEXT := 'year';
base_table TEXT := 'edit_log';
_interval INTERVAL := '1 ' || selector;
_interval_next INTERVAL := '2 ' || selector;
table_name TEXT;
-- compare date column
compare_date DATE := NEW.event_date;
compare_date_name TEXT := 'event_date';
-- the create commands
command_create_table TEXT := 'CREATE TABLE IF NOT EXISTS {TABLE_NAME} (CHECK({COMPARE_DATE_NAME} >= {START_DATE} AND {COMPARE_DATE_NAME} < {END_DATE})) INHERITS ({BASE_NAME})';
command_create_primary_key TEXT := 'ALTER TABLE {TABLE_NAME} ADD PRIMARY KEY ({BASE_TABLE}_id)';
command_create_foreign_key_1 TEXT := 'ALTER TABLE {TABLE_NAME} ADD CONSTRAINT {TABLE_NAME}_euid_fkey FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL';
command_create_trigger_1 TEXT = 'CREATE TRIGGER trg_{TABLE_NAME} BEFORE INSERT OR UPDATE ON {TABLE_NAME} FOR EACH ROW EXECUTE PROCEDURE set_edit_generic()';
BEGIN
-- we are in valid start time area
IF (NEW.event_date >= start_date) THEN
-- current table name
table_name := base_table || '_' || to_char(NEW.event_date, timeformat);
BEGIN
EXECUTE 'INSERT INTO ' || quote_ident(table_name) || ' SELECT ($1).*' USING NEW;
-- if insert failed because of missing table, create new below
EXCEPTION
WHEN undefined_table THEN
-- another block, so in case the creation fails here too
BEGIN
-- create new table here + all indexes
start_date := date_trunc(selector, NEW.event_date);
end_date := date_trunc(selector, NEW.event_date + _interval);
-- creat table
EXECUTE format(REPLACE( -- end date
REPLACE( -- start date
REPLACE( -- compare date name
REPLACE( -- base name (inherit)
REPLACE( -- table name
command_create_table,
'{TABLE_NAME}',
table_name
),
'{BASE_NAME}',
base_table
),
'{COMPARE_DATE_NAME}',
compare_date_name
),
'{START_DATE}',
quote_literal(start_date)
),
'{END_DATE}',
quote_literal(end_date)
));
-- create all indexes and triggers
EXECUTE format(REPLACE(
REPLACE(
command_create_primary_key,
'{TABLE_NAME}',
table_name
),
'{BASE_TABLE}',
base_table
));
-- FK constraints
EXECUTE format(REPLACE(command_create_foreign_key_1, '{TABLE_NAME}', table_name));
-- generic trigger
EXECUTE format(REPLACE(command_create_trigger_1, '{TABLE_NAME}', table_name));
-- we are in valid start time area
IF (NEW.event_date >= start_date) THEN
-- current table name
table_name := base_table || '_' || to_char(NEW.event_date, timeformat);
BEGIN
EXECUTE 'INSERT INTO ' || quote_ident(table_name) || ' SELECT ($1).*' USING NEW;
-- if insert failed because of missing table, create new below
EXCEPTION
WHEN undefined_table THEN
-- another block, so in case the creation fails here too
BEGIN
-- create new table here + all indexes
start_date := date_trunc(selector, NEW.event_date);
end_date := date_trunc(selector, NEW.event_date + _interval);
-- creat table
EXECUTE format(REPLACE( -- end date
REPLACE( -- start date
REPLACE( -- compare date name
REPLACE( -- base name (inherit)
REPLACE( -- table name
command_create_table,
'{TABLE_NAME}',
table_name
),
'{BASE_NAME}',
base_table
),
'{COMPARE_DATE_NAME}',
compare_date_name
),
'{START_DATE}',
quote_literal(start_date)
),
'{END_DATE}',
quote_literal(end_date)
));
-- create all indexes and triggers
EXECUTE format(REPLACE(
REPLACE(
command_create_primary_key,
'{TABLE_NAME}',
table_name
),
'{BASE_TABLE}',
base_table
));
-- FK constraints
EXECUTE format(REPLACE(command_create_foreign_key_1, '{TABLE_NAME}', table_name));
-- generic trigger
EXECUTE format(REPLACE(command_create_trigger_1, '{TABLE_NAME}', table_name));
-- insert try again
EXECUTE 'INSERT INTO ' || quote_ident(table_name) || ' SELECT ($1).*' USING NEW;
EXCEPTION
WHEN OTHERS THEN
-- if this faled, throw it into the overflow table (so we don't loose anything)
INSERT INTO edit_log_overflow VALUES (NEW.*);
END;
-- other errors, insert into overlow
WHEN OTHERS THEN
-- if this faled, throw it into the overflow table (so we don't loose anything)
INSERT INTO edit_log_overflow VALUES (NEW.*);
END;
-- main insert run done, check if we have to create next months table
BEGIN
-- check if next month table exists
table_name := base_table || '_' || to_char((SELECT NEW.event_date + _interval)::DATE, timeformat);
-- RAISE NOTICE 'SEARCH NEXT: %', table_name;
IF (SELECT to_regclass(table_name)) IS NULL THEN
-- move inner interval same
start_date := date_trunc(selector, NEW.event_date + _interval);
end_date := date_trunc(selector, NEW.event_date + _interval_next);
-- RAISE NOTICE 'CREATE NEXT: %', table_name;
-- create table
EXECUTE format(REPLACE( -- end date
REPLACE( -- start date
REPLACE( -- compare date name
REPLACE( -- base name (inherit)
REPLACE( -- table name
command_create_table,
'{TABLE_NAME}',
table_name
),
'{BASE_NAME}',
base_table
),
'{COMPARE_DATE_NAME}',
compare_date_name
),
'{START_DATE}',
quote_literal(start_date)
),
'{END_DATE}',
quote_literal(end_date)
));
-- create all indexes and triggers
EXECUTE format(REPLACE(
REPLACE(
command_create_primary_key,
'{TABLE_NAME}',
table_name
),
'{BASE_TABLE}',
base_table
));
-- FK constraints
EXECUTE format(REPLACE(command_create_foreign_key_1, '{TABLE_NAME}', table_name));
-- generic trigger
EXECUTE format(REPLACE(command_create_trigger_1, '{TABLE_NAME}', table_name));
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Failed to create next table: %', table_name;
END;
ELSE
-- if outside valid date, insert into overflow
INSERT INTO edit_log_overflow VALUES (NEW.*);
END IF;
RETURN NULL;
-- insert try again
EXECUTE 'INSERT INTO ' || quote_ident(table_name) || ' SELECT ($1).*' USING NEW;
EXCEPTION
WHEN OTHERS THEN
-- if this faled, throw it into the overflow table (so we don't loose anything)
INSERT INTO edit_log_overflow VALUES (NEW.*);
END;
-- other errors, insert into overlow
WHEN OTHERS THEN
-- if this faled, throw it into the overflow table (so we don't loose anything)
INSERT INTO edit_log_overflow VALUES (NEW.*);
END;
-- main insert run done, check if we have to create next months table
BEGIN
-- check if next month table exists
table_name := base_table || '_' || to_char((SELECT NEW.event_date + _interval)::DATE, timeformat);
-- RAISE NOTICE 'SEARCH NEXT: %', table_name;
IF (SELECT to_regclass(table_name)) IS NULL THEN
-- move inner interval same
start_date := date_trunc(selector, NEW.event_date + _interval);
end_date := date_trunc(selector, NEW.event_date + _interval_next);
-- RAISE NOTICE 'CREATE NEXT: %', table_name;
-- create table
EXECUTE format(REPLACE( -- end date
REPLACE( -- start date
REPLACE( -- compare date name
REPLACE( -- base name (inherit)
REPLACE( -- table name
command_create_table,
'{TABLE_NAME}',
table_name
),
'{BASE_NAME}',
base_table
),
'{COMPARE_DATE_NAME}',
compare_date_name
),
'{START_DATE}',
quote_literal(start_date)
),
'{END_DATE}',
quote_literal(end_date)
));
-- create all indexes and triggers
EXECUTE format(REPLACE(
REPLACE(
command_create_primary_key,
'{TABLE_NAME}',
table_name
),
'{BASE_TABLE}',
base_table
));
-- FK constraints
EXECUTE format(REPLACE(command_create_foreign_key_1, '{TABLE_NAME}', table_name));
-- generic trigger
EXECUTE format(REPLACE(command_create_trigger_1, '{TABLE_NAME}', table_name));
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Failed to create next table: %', table_name;
END;
ELSE
-- if outside valid date, insert into overflow
INSERT INTO edit_log_overflow VALUES (NEW.*);
END IF;
RETURN NULL;
END
$$
LANGUAGE 'plpgsql';

View File

@@ -1,28 +0,0 @@
-- add uid add for edit_access table
CREATE OR REPLACE FUNCTION set_edit_access_uid() RETURNS TRIGGER AS
$$
DECLARE
myrec RECORD;
v_uid VARCHAR;
BEGIN
-- skip if NEW.name is not set
IF NEW.name IS NOT NULL AND NEW.name <> '' THEN
-- use NEW.name as base, remove all spaces
-- name data is already unique, so we do not need to worry about this here
v_uid := REPLACE(NEW.name, ' ', '');
IF TG_OP = 'INSERT' THEN
-- always set
NEW.uid := v_uid;
ELSIF TG_OP = 'UPDATE' THEN
-- check if not set, then set
SELECT INTO myrec t.* FROM edit_access t WHERE edit_access_id = NEW.edit_access_id;
IF FOUND THEN
NEW.uid := v_uid;
END IF;
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -1,28 +0,0 @@
-- add uid add for edit_group table
CREATE OR REPLACE FUNCTION set_edit_group_uid() RETURNS TRIGGER AS
$$
DECLARE
myrec RECORD;
v_uid VARCHAR;
BEGIN
-- skip if NEW.name is not set
IF NEW.name IS NOT NULL AND NEW.name <> '' THEN
-- use NEW.name as base, remove all spaces
-- name data is already unique, so we do not need to worry about this here
v_uid := REPLACE(NEW.name, ' ', '');
IF TG_OP = 'INSERT' THEN
-- always set
NEW.uid := v_uid;
ELSIF TG_OP = 'UPDATE' THEN
-- check if not set, then set
SELECT INTO myrec t.* FROM edit_group t WHERE edit_group_id = NEW.edit_group_id;
IF FOUND THEN
NEW.uid := v_uid;
END IF;
END IF;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -0,0 +1,26 @@
-- set edit user login_user_id_set_date if login_user_id is set
-- NOW() if not empty
CREATE OR REPLACE FUNCTION set_login_user_id_set_date()
RETURNS TRIGGER AS
$$
BEGIN
-- if new is not null/empty
-- and old one is null or old one different new one
-- set NOW()
-- if new one is NULL
-- set NULL
IF
NEW.login_user_id IS NOT NULL AND NEW.login_user_id <> '' AND
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -1,18 +1,20 @@
-- create random string with length X
CREATE FUNCTION random_string(randomLength int)
RETURNS text AS $$
RETURNS text AS
$$
SELECT array_to_string(
ARRAY(
SELECT substring(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
trunc(random() * 62)::int + 1,
1
)
FROM generate_series(1, randomLength) AS gs(x)
),
''
ARRAY(
SELECT substring(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
trunc(random() * 62)::int + 1,
1
)
FROM generate_series(1, randomLength) AS gs(x)
),
''
)
$$ LANGUAGE SQL
$$
LANGUAGE SQL
RETURNS NULL ON NULL INPUT
VOLATILE; -- LEAKPROOF;
VOLATILE; -- LEAKPROOF;

View File

@@ -0,0 +1,15 @@
-- adds the created or updated date tags
CREATE OR REPLACE FUNCTION set_date()
RETURNS TRIGGER AS
$$
BEGIN
IF TG_OP = 'INSERT' THEN
NEW.date_created := clock_timestamp();
ELSIF TG_OP = 'UPDATE' THEN
NEW.date_updated := clock_timestamp();
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -1,15 +1,19 @@
-- adds the created or updated date tags
CREATE OR REPLACE FUNCTION set_edit_generic() RETURNS TRIGGER AS '
DECLARE
random_length INT = 12; -- that should be long enough
BEGIN
IF TG_OP = ''INSERT'' THEN
NEW.date_created := ''now'';
NEW.cuid := random_string(random_length);
ELSIF TG_OP = ''UPDATE'' THEN
NEW.date_updated := ''now'';
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
CREATE OR REPLACE FUNCTION set_edit_generic()
RETURNS TRIGGER AS
$$
DECLARE
random_length INT = 25; -- that should be long enough
BEGIN
IF TG_OP = 'INSERT' THEN
NEW.date_created := clock_timestamp();
NEW.cuid := random_string(random_length);
NEW.cuuid := gen_random_uuid();
ELSIF TG_OP = 'UPDATE' THEN
NEW.date_updated := clock_timestamp();
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -1,12 +0,0 @@
-- adds the created or updated date tags
CREATE OR REPLACE FUNCTION set_generic() RETURNS TRIGGER AS '
BEGIN
IF TG_OP = ''INSERT'' THEN
NEW.date_created := ''now'';
ELSIF TG_OP = ''UPDATE'' THEN
NEW.date_updated := ''now'';
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';

View File

@@ -0,0 +1,21 @@
-- set generic with date and uid combined
-- don't use with set_generic/set_uid together
CREATE OR REPLACE FUNCTION set_generic()
RETURNS TRIGGER AS
$$
DECLARE
random_length INT = 32; -- long for massive data
BEGIN
IF TG_OP = 'INSERT' THEN
NEW.date_created := clock_timestamp();
IF NEW.uid IS NULL THEN
NEW.uid := random_string(random_length);
END IF;
ELSIF TG_OP = 'UPDATE' THEN
NEW.date_updated := clock_timestamp();
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -1,12 +1,15 @@
-- adds the created or updated date tags
CREATE OR REPLACE FUNCTION set_uid() RETURNS TRIGGER AS '
DECLARE
random_length INT = 32; -- that should be long enough
BEGIN
IF TG_OP = ''INSERT'' THEN
NEW.uid := random_string(random_length);
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
CREATE OR REPLACE FUNCTION set_uid()
RETURNS TRIGGER AS
$$
DECLARE
random_length INT = 32; -- that should be long enough
BEGIN
IF TG_OP = 'INSERT' THEN
NEW.uid := random_string(random_length);
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

View File

@@ -1,16 +0,0 @@
-- adds the created or updated date tags
-- OLD, DEPRECATED, use set_generic.sql
-- CREATE OR REPLACE FUNCTION set_generic() RETURNS TRIGGER AS '
-- BEGIN
-- IF TG_OP = ''INSERT'' THEN
-- NEW.date_created := clock_timestamp();
-- NEW.user_created := current_user;
-- ELSIF TG_OP = ''UPDATE'' THEN
-- NEW.date_updated := clock_timestamp();
-- NEW.user_updated := current_user;
-- END IF;
-- RETURN NEW;
-- END;
-- ' LANGUAGE 'plpgsql';

View File

@@ -0,0 +1,110 @@
-- Upgrade serial to identity type
--
-- Original: https://www.enterprisedb.com/blog/postgresql-10-identity-columns-explained#section-6
--
-- @param reclass tbl The table where the column is located, prefix with 'schema.' if different schema
-- @param name col The column to be changed
-- @param varchar identity_type [default=a] Allowed a, d, assigned, default
-- @param varchar col_type [default=''] Allowed smallint, int, bigint, int2, int4, int8
-- @returns varchar status tring
-- @raises EXCEPTON on column not found, no linked sequence, more than one linked sequence found, invalid col type
--
CREATE OR REPLACE FUNCTION upgrade_serial_to_identity(
tbl regclass,
col name,
identity_type varchar = 'a',
col_type varchar = ''
)
RETURNS varchar
LANGUAGE plpgsql
AS $$
DECLARE
colnum SMALLINT;
seqid OID;
count INT;
col_type_oid INT;
col_type_len INT;
current_col_atttypid OID;
current_col_attlen INT;
status_string VARCHAR;
BEGIN
-- switch between always (default) or default identiy type
IF identity_type NOT IN ('a', 'd', 'assigned', 'default') THEN
identity_type := 'a';
ELSE
IF identity_type = 'default' THEN
identity_type := 'd';
ELSIF identity_type = 'assigned' THEN
identity_type := 'a';
END IF;
END IF;
-- find column number, attribute oid and attribute len
SELECT attnum, atttypid, attlen
INTO colnum, current_col_atttypid, current_col_attlen
FROM pg_attribute
WHERE attrelid = tbl AND attname = col;
IF NOT FOUND THEN
RAISE EXCEPTION 'column does not exist';
END IF;
-- find sequence
SELECT INTO seqid objid
FROM pg_depend
WHERE (refclassid, refobjid, refobjsubid) = ('pg_class'::regclass, tbl, colnum)
AND classid = 'pg_class'::regclass AND objsubid = 0
AND deptype = 'a';
GET DIAGNOSTICS count = ROW_COUNT;
IF count < 1 THEN
RAISE EXCEPTION 'no linked sequence found';
ELSIF count > 1 THEN
RAISE EXCEPTION 'more than one linked sequence found';
END IF;
IF col_type <> '' AND col_type NOT IN ('smallint', 'int', 'bigint', 'int2', 'int4', 'int8') THEN
RAISE EXCEPTION 'Invalid col type: %', col_type;
END IF;
-- drop the default
EXECUTE 'ALTER TABLE ' || tbl || ' ALTER COLUMN ' || quote_ident(col) || ' DROP DEFAULT';
-- change the dependency between column and sequence to internal
UPDATE pg_depend
SET deptype = 'i'
WHERE (classid, objid, objsubid) = ('pg_class'::regclass, seqid, 0)
AND deptype = 'a';
-- mark the column as identity column
UPDATE pg_attribute
-- set to 'd' for default
SET attidentity = identity_type
WHERE attrelid = tbl
AND attname = col;
status_string := 'Updated to identity for table "' || tbl || '" and columen "' || col || '" with type "' || identity_type || '"';
-- set type if requested and not empty
IF col_type <> '' THEN
-- rewrite smallint, int, bigint
IF col_type = 'smallint' THEN
col_type := 'int2';
ELSIF col_type = 'int' THEN
col_type := 'int4';
ELSIF col_type = 'bigint' THEN
col_type := 'int8';
END IF;
-- get the length and oid for selected
SELECT oid, typlen INTO col_type_oid, col_type_len FROM pg_type WHERE typname = col_type;
-- set only if diff or hight
IF current_col_atttypid <> col_type_oid AND col_type_len > current_col_attlen THEN
status_string := status_string || '. Change col type: ' || col_type;
-- update type
UPDATE pg_attribute
SET
atttypid = col_type_oid, attlen = col_type_len
WHERE attrelid = tbl
AND attname = col;
END IF;
END IF;
RETURN status_string;
END;
$$;

View File

@@ -7,13 +7,13 @@
-- DROP TABLE edit_access;
CREATE TABLE edit_access (
edit_access_id SERIAL PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0,
protected SMALLINT DEFAULT 0,
deleted SMALLINT DEFAULT 0,
uid VARCHAR,
name VARCHAR UNIQUE,
description VARCHAR,
color VARCHAR,
additional_acl JSONB
edit_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0,
protected SMALLINT DEFAULT 0,
deleted SMALLINT DEFAULT 0,
uid VARCHAR,
name VARCHAR UNIQUE,
description VARCHAR,
color VARCHAR,
additional_acl JSONB
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,10 +7,14 @@
-- DROP TABLE edit_access_data;
CREATE TABLE edit_access_data (
edit_access_data_id SERIAL PRIMARY KEY,
edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR,
value VARCHAR
edit_access_data_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR,
value VARCHAR
) INHERITS (edit_generic) WITHOUT OIDS;
-- create a unique index for each attached data block for each edit access can
-- only have ONE value;
CREATE UNIQUE INDEX edit_access_data_edit_access_id_name_ukey ON edit_access_data (edit_access_id, name);

View File

@@ -8,9 +8,9 @@
-- DROP TABLE edit_access_right;
CREATE TABLE edit_access_right (
edit_access_right_id SERIAL PRIMARY KEY,
name VARCHAR,
level SMALLINT,
type VARCHAR,
UNIQUE (level,type)
edit_access_right_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR,
level SMALLINT,
type VARCHAR,
UNIQUE (level,type)
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,13 +7,13 @@
-- DROP TABLE edit_access_user;
CREATE TABLE edit_access_user (
edit_access_user_id SERIAL PRIMARY KEY,
edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_user_id INT NOT NULL,
FOREIGN KEY (edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_default SMALLINT DEFAULT 0,
enabled SMALLINT NOT NULL DEFAULT 0
edit_access_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_access_id INT NOT NULL,
FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_user_id INT NOT NULL,
FOREIGN KEY (edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_default SMALLINT DEFAULT 0,
enabled SMALLINT NOT NULL DEFAULT 0
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,7 +7,8 @@
-- DROP TABLE edit_generic;
CREATE TABLE edit_generic (
cuid VARCHAR,
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
date_updated TIMESTAMP WITHOUT TIME ZONE
cuid VARCHAR,
cuuid UUID DEFAULT gen_random_uuid(),
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
date_updated TIMESTAMP WITHOUT TIME ZONE
);

View File

@@ -7,14 +7,14 @@
-- DROP TABLE edit_group;
CREATE TABLE edit_group (
edit_group_id SERIAL PRIMARY KEY,
edit_scheme_id INT,
FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0,
deleted SMALLINT DEFAULT 0,
uid VARCHAR,
name VARCHAR,
additional_acl JSONB
edit_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_scheme_id INT,
FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0,
deleted SMALLINT DEFAULT 0,
uid VARCHAR,
name VARCHAR,
additional_acl JSONB
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -8,11 +8,11 @@
-- DROP TABLE edit_language;
CREATE TABLE edit_language (
edit_language_id SERIAL PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0,
lang_default SMALLINT NOT NULL DEFAULT 0,
long_name VARCHAR,
short_name VARCHAR, -- en_US, en or en_US@latin without encoding
iso_name VARCHAR, -- should actually be encoding
order_number INT
edit_language_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0,
lang_default SMALLINT NOT NULL DEFAULT 0,
long_name VARCHAR,
short_name VARCHAR, -- en_US, en or en_US@latin without encoding
iso_name VARCHAR, -- should actually be encoding
order_number INT
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,35 +7,54 @@
-- DROP TABLE edit_log;
CREATE TABLE edit_log (
edit_log_id SERIAL PRIMARY KEY,
euid INT, -- this is a foreign key, but I don't nedd to reference to it
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
username VARCHAR,
password VARCHAR,
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
ip VARCHAR,
error TEXT,
event TEXT,
data_binary BYTEA,
data TEXT,
page VARCHAR,
action VARCHAR,
action_id VARCHAR,
action_yes VARCHAR,
action_flag VARCHAR,
action_menu VARCHAR,
action_loaded VARCHAR,
action_value VARCHAR,
action_type VARCHAR,
action_error VARCHAR,
user_agent VARCHAR,
referer VARCHAR,
script_name VARCHAR,
query_string VARCHAR,
server_name VARCHAR,
http_host VARCHAR,
http_accept VARCHAR,
http_accept_charset VARCHAR,
http_accept_encoding VARCHAR,
session_id VARCHAR
edit_log_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
euid INT, -- this is a foreign key, but I don't nedd to reference to it
FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
eucuid VARCHAR,
eucuuid UUID, -- this is the one we want to use, full UUIDv4 from the edit user table
-- date_created equal, but can be overridden
event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
-- session ID if set
session_id VARCHAR,
-- username
username VARCHAR,
-- DEPRECATED [password]
password VARCHAR,
ip_address JSONB, -- REMOTE_IP and all other IPs (X_FORWARD, etc) as JSON block
-- DEPRECATED [ip]
ip VARCHAR, -- just the REMOTE_IP, full set see ip_address
-- string blocks, general
error TEXT,
event TEXT,
-- bytea or string type storage of any data
data_binary BYTEA,
data TEXT,
-- set page name only
page VARCHAR,
-- various info data sets
user_agent VARCHAR,
referer VARCHAR,
script_name VARCHAR,
query_string VARCHAR,
request_scheme VARCHAR, -- http or https
server_name VARCHAR,
http_host VARCHAR,
http_data JSONB,
-- DEPRECATED [http*]
http_accept VARCHAR, -- in http_data
http_accept_charset VARCHAR, -- in http_data
http_accept_encoding VARCHAR, -- in http_data
-- any action var, -> same set in action_data as JSON
action_data JSONB,
-- DEPRECATED [action*]
action VARCHAR, -- in action_data
action_id VARCHAR, -- in action_data
action_sub_id VARCHAR, -- in action_data
action_yes VARCHAR, -- in action_data
action_flag VARCHAR, -- in action_data
action_menu VARCHAR, -- in action_data
action_loaded VARCHAR, -- in action_data
action_value VARCHAR, -- in action_data
action_type VARCHAR, -- in action_data
action_error VARCHAR -- in action_data
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,10 +7,8 @@
-- DROP TABLE edit_menu_group;
CREATE TABLE edit_menu_group (
edit_menu_group_id SERIAL PRIMARY KEY,
name VARCHAR,
flag VARCHAR,
order_number INT NOT NULL
edit_menu_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR,
flag VARCHAR,
order_number INT NOT NULL
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,16 +7,16 @@
-- DROP TABLE edit_page;
CREATE TABLE edit_page (
edit_page_id SERIAL PRIMARY KEY,
content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages
FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
filename VARCHAR,
name VARCHAR UNIQUE,
order_number INT NOT NULL,
online SMALLINT NOT NULL DEFAULT 0,
menu SMALLINT NOT NULL DEFAULT 0,
popup SMALLINT NOT NULL DEFAULT 0,
popup_x SMALLINT,
popup_y SMALLINT,
hostname VARCHAR
edit_page_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages
FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
filename VARCHAR,
name VARCHAR UNIQUE,
order_number INT NOT NULL,
online SMALLINT NOT NULL DEFAULT 0,
menu SMALLINT NOT NULL DEFAULT 0,
popup SMALLINT NOT NULL DEFAULT 0,
popup_x SMALLINT,
popup_y SMALLINT,
hostname VARCHAR
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,14 +7,12 @@
-- DROP TABLE edit_page_access;
CREATE TABLE edit_page_access (
edit_page_access_id SERIAL PRIMARY KEY,
edit_group_id INT NOT NULL,
FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0
edit_page_access_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_group_id INT NOT NULL,
FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -8,13 +8,13 @@
-- DROP TABLE edit_page_content;
CREATE TABLE edit_page_content (
edit_page_content_id SERIAL PRIMARY KEY,
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
uid VARCHAR UNIQUE,
name VARCHAR,
order_number INT NOT NULL,
online SMALLINT NOT NULL DEFAULT 0
edit_page_content_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
uid VARCHAR UNIQUE,
name VARCHAR,
order_number INT NOT NULL,
online SMALLINT NOT NULL DEFAULT 0
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,8 +7,8 @@
-- DROP TABLE edit_page_menu_group;
CREATE TABLE edit_page_menu_group (
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_menu_group_id INT NOT NULL,
FOREIGN KEY (edit_menu_group_id) REFERENCES edit_menu_group (edit_menu_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_menu_group_id INT NOT NULL,
FOREIGN KEY (edit_menu_group_id) REFERENCES edit_menu_group (edit_menu_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE
);

View File

@@ -7,8 +7,8 @@
-- DROP TABLE edit_page_visible_group;
CREATE TABLE edit_page_visible_group (
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_visible_group_id INT NOT NULL,
FOREIGN KEY (edit_visible_group_id) REFERENCES edit_visible_group (edit_visible_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_visible_group_id INT NOT NULL,
FOREIGN KEY (edit_visible_group_id) REFERENCES edit_visible_group (edit_visible_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE
);

View File

@@ -7,11 +7,11 @@
-- DROP TABLE edit_query_string;
CREATE TABLE edit_query_string (
edit_query_string_id SERIAL PRIMARY KEY,
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR,
value VARCHAR,
dynamic SMALLINT NOT NULL DEFAULT 0
edit_query_string_id SERIAINT GENERATED ALWAYS AS IDENTITYL PRIMARY KEY,
edit_page_id INT NOT NULL,
FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR,
value VARCHAR,
dynamic SMALLINT NOT NULL DEFAULT 0
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,10 +7,10 @@
-- DROP TABLE edit_scheme;
CREATE TABLE edit_scheme (
edit_scheme_id SERIAL PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR,
header_color VARCHAR,
css_file VARCHAR,
template VARCHAR
edit_scheme_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
enabled SMALLINT NOT NULL DEFAULT 0,
name VARCHAR,
header_color VARCHAR,
css_file VARCHAR,
template VARCHAR
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -7,6 +7,6 @@
-- DROP TABLE temp_files;
CREATE TABLE temp_files (
filename VARCHAR,
folder VARCHAR
filename VARCHAR,
folder VARCHAR
);

View File

@@ -7,42 +7,91 @@
-- DROP TABLE edit_user;
CREATE TABLE edit_user (
edit_user_id SERIAL PRIMARY KEY,
connect_edit_user_id INT, -- possible reference to other user
FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_language_id INT NOT NULL,
FOREIGN KEY (edit_language_id) REFERENCES edit_language (edit_language_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_group_id INT NOT NULL,
FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_scheme_id INT,
FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
enabled SMALLINT NOT NULL DEFAULT 0,
deleted SMALLINT NOT NULL DEFAULT 0,
username VARCHAR UNIQUE,
password VARCHAR,
first_name VARCHAR,
last_name VARCHAR,
first_name_furigana VARCHAR,
last_name_furigana VARCHAR,
debug SMALLINT NOT NULL DEFAULT 0,
db_debug SMALLINT NOT NULL DEFAULT 0,
email VARCHAR,
protected SMALLINT NOT NULL DEFAULT 0,
admin SMALLINT NOT NULL DEFAULT 0,
last_login TIMESTAMP WITHOUT TIME ZONE,
login_error_count INT DEFAULT 0,
login_error_date_last TIMESTAMP WITHOUT TIME ZONE,
login_error_date_first TIMESTAMP WITHOUT TIME ZONE,
strict SMALLINT DEFAULT 0,
locked SMALLINT DEFAULT 0,
password_change_date TIMESTAMP WITHOUT TIME ZONE, -- only when password is first set or changed
password_change_interval INTERVAL, -- null if no change is needed, or d/m/y time interval
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
edit_user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
connect_edit_user_id INT, -- possible reference to other user
FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_language_id INT NOT NULL,
FOREIGN KEY (edit_language_id) REFERENCES edit_language (edit_language_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_group_id INT NOT NULL,
FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_scheme_id INT,
FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
edit_access_right_id INT NOT NULL,
FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
-- username/password
username VARCHAR UNIQUE,
password VARCHAR,
-- name block
first_name VARCHAR,
last_name VARCHAR,
first_name_furigana VARCHAR,
last_name_furigana VARCHAR,
-- email
email VARCHAR,
-- eanbled/deleted flag
enabled SMALLINT NOT NULL DEFAULT 0,
deleted SMALLINT NOT NULL DEFAULT 0,
-- general flags
strict SMALLINT DEFAULT 0,
locked SMALLINT DEFAULT 0,
protected SMALLINT NOT NULL DEFAULT 0,
-- is admin user
admin SMALLINT NOT NULL DEFAULT 0,
-- force lgout counter
force_logout INT DEFAULT 0,
-- last login log
last_login TIMESTAMP WITHOUT TIME ZONE,
-- login error
login_error_count INT DEFAULT 0,
login_error_date_last TIMESTAMP WITHOUT TIME ZONE,
login_error_date_first TIMESTAMP WITHOUT TIME ZONE,
-- time locked
lock_until TIMESTAMP WITHOUT TIME ZONE,
lock_after TIMESTAMP WITHOUT TIME ZONE,
-- password change
password_change_date TIMESTAMP WITHOUT TIME ZONE, -- only when password is first set or changed
password_change_interval INTERVAL, -- null if no change is needed, or d/m/y time interval
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login
login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS;
-- create unique index
-- CREATE UNIQUE INDEX edit_user_login_user_id_key ON edit_user (login_user_id) WHERE login_user_id IS NOT NULL;
COMMENT ON COLUMN edit_user.username IS 'Login username, must set';
COMMENT ON COLUMN edit_user.password IS 'Login password, must set';
COMMENT ON COLUMN edit_user.enabled IS 'Login is enabled (master switch)';
COMMENT ON COLUMN edit_user.deleted IS 'Login is deleted (master switch), overrides all other';
COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be locked, default off';
COMMENT ON COLUMN edit_user.locked IS 'Locked from too many wrong password logins';
COMMENT ON COLUMN edit_user.protected IS 'User can only be chnaged by admin user';
COMMENT ON COLUMN edit_user.admin IS 'If set, this user is SUPER admin';
COMMENT ON COLUMN edit_user.force_logout IS 'Counter for forced log out, if this one is higher than the session set one the session gets terminated';
COMMENT ON COLUMN edit_user.last_login IS 'Last succesfull login tiemstamp';
COMMENT ON COLUMN edit_user.login_error_count IS 'Number of failed logins, reset on successful login';
COMMENT ON COLUMN edit_user.login_error_date_last IS 'Last login error date';
COMMENT ON COLUMN edit_user.login_error_date_first IS 'First login error date, reset on successfull login';
COMMENT ON COLUMN edit_user.lock_until IS 'Account is locked until this date, <';
COMMENT ON COLUMN edit_user.lock_after IS 'Account is locked after this date, >';
COMMENT ON COLUMN edit_user.password_change_date IS 'Password was changed on';
COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the password has to be changed';
COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';

View File

@@ -7,7 +7,7 @@
-- DROP TABLE edit_visible_group;
CREATE TABLE edit_visible_group (
edit_visible_group_id SERIAL PRIMARY KEY,
name VARCHAR,
flag VARCHAR
edit_visible_group_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR,
flag VARCHAR
) INHERITS (edit_generic) WITHOUT OIDS;

View File

@@ -1,12 +1,14 @@
-- AUTHOR: Clemens Schwaighofer
-- DATE: 2005/07/05
-- DESCRIPTION:
-- cms tables; generic basic table
-- generic basic table with date and uid column
-- TABLE: generic
-- HISTORY:
-- DROP TABLE edit_generic;
-- DROP TABLE generic;
CREATE TABLE generic (
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
date_updated TIMESTAMP WITHOUT TIME ZONE
date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
date_updated TIMESTAMP WITHOUT TIME ZONE,
uuid UUID DEFAULT gen_random_uuid(),
uid VARCHAR
);

View File

@@ -0,0 +1,81 @@
--
SELECT
eu.cuid, eu.username,
eu.lock_until, eu.lock_after,
CASE WHEN (
(eu.lock_until IS NULL
OR (eu.lock_until IS NOT NULL AND NOW() >= eu.lock_until))
AND (eu.lock_after IS NULL
OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after))
) THEN 0::INT ELSE 1::INT END locked_period
FROM edit_user eu
WHERE eu.username = 'empty';
UPDATE edit_user SET
lock_until = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_after = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_until = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_after = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET lock_until = NULL, lock_after = NULL WHERE username = 'empty';
--
SELECT
eu.cuid, eu.username,
eu.login_user_id, login_user_id_set_date, eu.login_user_id_last_revalidate,
(eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE AS reval_date, NOW()::DATE,
eu.login_user_id_valid_from, eu.login_user_id_valid_until,
eu.login_user_id_revalidate_after,
CASE WHEN (
(eu.login_user_id_valid_from IS NULL
OR (eu.login_user_id_valid_from IS NOT NULL AND NOW() >= eu.login_user_id_valid_from))
AND (eu.login_user_id_valid_until IS NULL
OR (eu.login_user_id_valid_until IS NOT NULL AND NOW() <= eu.login_user_id_valid_until))
) THEN 1::INT ELSE 0::INT END AS login_user_id_valid_date,
CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL
AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL
AND (eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE <= NOW()::DATE
THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate
FROM edit_user eu
WHERE eu.username = 'empty';
-- init
UPDATE edit_user SET login_user_id = random_string(5) WHERE username = 'empty';
-- outside valid
UPDATE edit_user SET
login_user_id_valid_from = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
login_user_id_valid_until = NOW() + '1 day'::interval
WHERE username = 'empty';
-- inside valid
UPDATE edit_user SET
login_user_id_valid_from = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
login_user_id_valid_until = NOW() - '1 day'::interval
WHERE username = 'empty';
-- revalidate must
UPDATE edit_user SET
login_user_id_last_revalidate = NOW() - '1 day'::interval,
login_user_id_revalidate_after = '1 day'::interval
WHERE username = 'empty';
-- revalidate not yet
UPDATE edit_user SET
login_user_id_last_revalidate = NOW(),
login_user_id_revalidate_after = '6 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET login_user_id_set_date = NULL, login_user_id_last_revalidate = NULL, login_user_id_valid_from = NULL, login_user_id_valid_until = NULL, login_user_id_revalidate_after = NULL WHERE username = 'empty';

View File

@@ -1,9 +1,9 @@
DROP TRIGGER IF EXISTS trg_edit_access ON edit_access;
-- DROP TRIGGER IF EXISTS trg_edit_access ON edit_access;
CREATE TRIGGER trg_edit_access
BEFORE INSERT OR UPDATE ON edit_access
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
DROP TRIGGER IF EXISTS trg_set_edit_access_uid ON edit_access;
-- DROP TRIGGER IF EXISTS trg_set_edit_access_uid ON edit_access;
CREATE TRIGGER trg_set_edit_access_uid
BEFORE INSERT OR UPDATE ON edit_access
FOR EACH ROW EXECUTE PROCEDURE set_edit_access_uid();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_access_data ON edit_access_data;
-- DROP TRIGGER IF EXISTS trg_edit_access_data ON edit_access_data;
CREATE TRIGGER trg_edit_access_data
BEFORE INSERT OR UPDATE ON edit_access_data
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_access_right ON edit_access_right;
-- DROP TRIGGER IF EXISTS trg_edit_access_right ON edit_access_right;
CREATE TRIGGER trg_edit_access_right
BEFORE INSERT OR UPDATE ON edit_access_right
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_access_user ON edit_access_user;
-- DROP TRIGGER IF EXISTS trg_edit_access_user ON edit_access_user;
CREATE TRIGGER trg_edit_access_user
BEFORE INSERT OR UPDATE ON edit_access_user
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,9 +1,9 @@
DROP TRIGGER IF EXISTS trg_edit_group ON edit_group;
-- DROP TRIGGER IF EXISTS trg_edit_group ON edit_group;
CREATE TRIGGER trg_edit_group
BEFORE INSERT OR UPDATE ON edit_group
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
DROP TRIGGER IF EXISTS trg_set_edit_group_uid ON edit_group;
-- DROP TRIGGER IF EXISTS trg_set_edit_group_uid ON edit_group;
CREATE TRIGGER trg_set_edit_group_uid
BEFORE INSERT OR UPDATE ON edit_group
FOR EACH ROW EXECUTE PROCEDURE set_edit_group_uid();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_language ON edit_language;
-- DROP TRIGGER IF EXISTS trg_edit_language ON edit_language;
CREATE TRIGGER trg_edit_language
BEFORE INSERT OR UPDATE ON edit_language
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,9 +1,9 @@
DROP TRIGGER IF EXISTS trg_edit_log ON edit_log;
-- DROP TRIGGER IF EXISTS trg_edit_log ON edit_log;
CREATE TRIGGER trg_edit_log
BEFORE INSERT OR UPDATE ON edit_log
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
DROP TRIGGER IF EXISTS trg_edit_log_insert_partition ON edit_log;
-- DROP TRIGGER IF EXISTS trg_edit_log_insert_partition ON edit_log;
CREATE TRIGGER trg_edit_log_insert_partition
BEFORE INSERT OR UPDATE ON edit_log
FOR EACH ROW EXECUTE PROCEDURE edit_log_insert_trigger();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_log_overflow ON edit_log_overflow;
-- DROP TRIGGER IF EXISTS trg_edit_log_overflow ON edit_log_overflow;
CREATE TRIGGER trg_edit_log_overflow
BEFORE INSERT OR UPDATE ON edit_log_overflow
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_menu_group ON edit_menu_group;
-- DROP TRIGGER IF EXISTS trg_edit_menu_group ON edit_menu_group;
CREATE TRIGGER trg_edit_menu_group
BEFORE INSERT OR UPDATE ON edit_menu_group
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_page ON edit_page;
-- DROP TRIGGER IF EXISTS trg_edit_page ON edit_page;
CREATE TRIGGER trg_edit_page
BEFORE INSERT OR UPDATE ON edit_page
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_page_access ON edit_page_access;
-- DROP TRIGGER IF EXISTS trg_edit_page_access ON edit_page_access;
CREATE TRIGGER trg_edit_page_access
BEFORE INSERT OR UPDATE ON edit_page_access
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_page_content ON edit_page_content;
-- DROP TRIGGER IF EXISTS trg_edit_page_content ON edit_page_content;
CREATE TRIGGER trg_edit_page_content
BEFORE INSERT OR UPDATE ON edit_page_content
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_query_string ON edit_query_string;
-- DROP TRIGGER IF EXISTS trg_edit_query_string ON edit_query_string;
CREATE TRIGGER trg_edit_query_string
BEFORE INSERT OR UPDATE ON edit_query_string
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_scheme ON edit_scheme;
-- DROP TRIGGER IF EXISTS trg_edit_scheme ON edit_scheme;
CREATE TRIGGER trg_edit_scheme
BEFORE INSERT OR UPDATE ON edit_scheme
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -1,4 +1,9 @@
DROP TRIGGER IF EXISTS trg_edit_user ON edit_user;
-- DROP TRIGGER IF EXISTS trg_edit_user ON edit_user;
CREATE TRIGGER trg_edit_user
BEFORE INSERT OR UPDATE ON edit_user
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
-- DROP TRIGGER IF EXISTS trg_edit_user_set_login_user_id_set_date ON edit_user;
CREATE TRIGGER trg_edit_user_set_login_user_id_set_date
BEFORE INSERT OR UPDATE ON edit_user
FOR EACH ROW EXECUTE PROCEDURE set_login_user_id_set_date();

View File

@@ -1,4 +1,4 @@
DROP TRIGGER IF EXISTS trg_edit_visible_group ON edit_visible_group;
-- DROP TRIGGER IF EXISTS trg_edit_visible_group ON edit_visible_group;
CREATE TRIGGER trg_edit_visible_group
BEFORE INSERT OR UPDATE ON edit_visible_group
FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();

View File

@@ -25,7 +25,7 @@ declare(strict_types=1);
* 1 for file loadable, but no data inside
* 2 for file not readable
* 3 for file not found
* @deprecated V6 Use \CoreLibs\Get\ReadEnvFile::readEnvFile()
* @deprecated V6 Use \CoreLibs\Get\DotEnv::readEnvFile()
*/
function readEnvFile(string $path = __DIR__, string $env_file = '.env'): int
{

View File

@@ -0,0 +1,31 @@
# DB Query Params ? and : to $
dbReturn*
dbExec
keep
->query
->params
for reference
## : named params
in order for each named found replace with order number:
```txt
:name, :foo, :bar, :name =>
$1, $2, $3, $1
```
```php
$query = str_replace(
[':name', ':foo', ':bar'],
['$1', '$2', '$3'],
$query
);
```
## ? Params
Foreach ? set $1 to $n and store that in new params array
in QUERY for each ? replace with matching $n

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# create path
path=$(pwd)"/"$0;
@@ -10,6 +10,11 @@ TARGET_HOST_WEB="<user>@<host>";
TMP_DIR=$LOCAL_BASE_DIR"/4dev/tmp/";
tmpf_web=$TMP_DIR"sync.exclude.tmp";
if [ ! -d "$LOCAL_BASE_DIR" ]; then
echo "Folder: $LOCAL_BASE_DIR not found";
exit;
fi;
# if vendor be sure group folder is +x
chmod -R ug+rX ${LOCAL_DIR}/vender/
@@ -18,6 +23,7 @@ rm -f $tmpf_web;
echo ".*.swp" >> $tmpf_web;
echo "._*" >> $tmpf_web;
echo ".DS_Store" >> $tmpf_web;
echo ".user.ini" >> $tmpf_web;
echo ".svn" >> $tmpf_web;
echo ".svnignore" >> $tmpf_web;
echo ".git" >> $tmpf_web;

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test base setup
* @testdox AAASetupData\AAASetupDataTest just setup BASE
*/
final class CoreLibsAAASetupDataTest extends TestCase
{
/**
* Covers nothing
*
* @testdox Just setup BASE
*
* @return void
*/
public function testSetupData(): void
{
if (!defined('BASE')) {
define(
'BASE',
str_replace('/configs', '', __DIR__)
. DIRECTORY_SEPARATOR
);
}
$this->assertEquals(
str_replace('/configs', '', __DIR__)
. DIRECTORY_SEPARATOR,
BASE,
'BASE Path set check'
);
}
}
// __END__

View File

@@ -0,0 +1 @@
../Language/includes/

1
4dev/tests/AAASetupData/log Symbolic link
View File

@@ -0,0 +1 @@
../Debug/log/

View File

@@ -0,0 +1,65 @@
<?php // phpcs:ignore PSR1.Files.SideEffects
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: Ymd
* DESCRIPTION:
* DescriptionHere
*/
declare(strict_types=1);
/**
* build return json
*
* @param array<string,mixed> $http_headers
* @param ?string $body
* @return string
*/
function buildContent(array $http_headers, ?string $body): string
{
if (is_string($body) && !empty($body)) {
$_body = json_decode($body, true);
if (!is_array($_body)) {
$body = [$body];
} else {
$body = $_body;
}
} elseif (is_string($body)) {
$body = [];
}
return json_encode([
'HEADERS' => $http_headers,
"REQUEST_TYPE" => $_SERVER['REQUEST_METHOD'],
"PARAMS" => $_GET,
"BODY" => $body,
]);
}
$http_headers = array_filter($_SERVER, function ($value, $key) {
if (str_starts_with($key, 'HTTP_')) {
return true;
}
}, ARRAY_FILTER_USE_BOTH);
header("Content-Type: application/json; charset=UTF-8");
// if the header has Authorization and RunAuthTest then exit with 401
if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) {
header("HTTP/1.1 401 Unauthorized");
print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}');
exit(1);
}
// if server request type is get set file_get to null -> no body
if ($_SERVER['REQUEST_METHOD'] == "GET") {
$file_get = null;
} elseif (($file_get = file_get_contents('php://input')) === false) {
header("HTTP/1.1 404 Not Found");
print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}');
exit(1);
}
print buildContent($http_headers, $file_get);
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# note: there is currently no port selection, standard 5432 port is assumed
# note: we use the default in path postgresql commands and connect to whatever default DB is set
# PARAMETER 1: database data file to load
# PARAMETER 2: db user WHO MUST BE ABLE TO CREATE A DATABASE
# PARAMETER 3: db name
# PARAMETER 4: db host
# PARAMETER 5: print out for testing
load_sql="${1}";
# abort with 1 if we cannot find the file
if [ ! -f "${load_sql}" ]; then
echo 1;
exit 1;
fi;
db_user="${2}";
db_name="${3}";
db_host="${4}";
# empty db name or db user -> exit with 2
if [ -z "${db_user}" ] || [ -z "${db_name}" ] || [ -z "${db_host}" ]; then
echo 2;
exit 2;
fi;
# drop database, on error exit with 3
dropdb -U ${db_user} -h ${db_host} ${db_name} 2>&1;
if [ $? -ne 0 ]; then
echo 3;
exit 3;
fi;
# create database, on error exit with 4
createdb -U ${db_user} -O ${db_user} -h ${db_host} -E utf8 ${db_name} 2>&1;
if [ $? -ne 0 ]; then
echo 4;
exit 4;
fi;
# if error 5 thrown, test with enabled below
if [ ! -z "${5}" ]; then
psql -U ${db_user} -h ${db_host} -f ${load_sql} ${db_name};
else
# load data (redirect ALL error to null), on error exit with 5
psql -U ${db_user} -h ${db_host} -f ${load_sql} ${db_name} 2>&1 1>/dev/null 2>/dev/null;
fi;
if [ $? -ne 0 ]; then
echo 5;
exit 5;
fi;
echo 0;
exit 0;
# __END__

File diff suppressed because it is too large Load Diff

1
4dev/tests/ACL/includes Symbolic link
View File

@@ -0,0 +1 @@
../AAASetupData/includes

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for Admin\EditPage
* @coversDefaultClass \CoreLibs\Admin\EditPage
* @testdox \CoreLibs\Admin\EditPage method tests
*/
final class CoreLibsAdminEditPageTest extends TestCase
{
/**
* Undocumented function
*
* @return void
*/
protected function setUp(): void
{
if (!extension_loaded('pgsql')) {
$this->markTestSkipped(
'The PgSQL extension is not available.'
);
}
}
/**
* Undocumented function
*
* @testdox Admin\EditPage Class tests
*
* @return void
*/
public function testAdminEditPage()
{
/* $this->assertTrue(true, 'ACL Login Tests not implemented');
$this->markTestIncomplete(
'ACL\Login Tests have not yet been implemented'
); */
$this->markTestSkipped('No implementation for Admin\EditPage at the moment');
}
}
// __END__

View File

@@ -0,0 +1,334 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for Check\Colors
* @coversDefaultClass \CoreLibs\Check\Colors
* @testdox \CoreLibs\Check\Colors method tests
*/
final class CoreLibsCheckColorsTest extends TestCase
{
/**
* Undocumented function
*
* @return array<mixed>
*/
public function validateColorProvider(): array
{
/*
0: input color string
1: flag (or flags to set)
2: expected result (bool)
*/
return [
// * hex
'valid hex rgb, flag ALL (default)' => [
'#ab12cd',
null,
true,
],
'valid hex rgb, flag ALL' => [
'#ab12cd',
\CoreLibs\Check\Colors::ALL,
true,
],
'valid hex rgb, flag HEX_RGB' => [
'#ab12cd',
\CoreLibs\Check\Colors::HEX_RGB,
true,
],
'valid hex rgb, wrong flag' => [
'#ab12cd',
\CoreLibs\Check\Colors::RGB,
false,
],
// error
'invalid hex rgb A' => [
'#ab12zz',
null,
false,
],
'invalid hex rgb B' => [
'#ZyQfo',
null,
false,
],
// other valid hex checks
'valid hex rgb, alt A' => [
'#AB12cd',
null,
true,
],
// * hax alpha
'valid hex rgb alpha, flag ALL (default)' => [
'#ab12cd12',
null,
true,
],
'valid hex rgb alpha, flag ALL' => [
'#ab12cd12',
\CoreLibs\Check\Colors::ALL,
true,
],
'valid hex rgb alpha, flag HEX_RGBA' => [
'#ab12cd12',
\CoreLibs\Check\Colors::HEX_RGBA,
true,
],
'valid hex rgb alpha, wrong flag' => [
'#ab12cd12',
\CoreLibs\Check\Colors::RGB,
false,
],
// error
'invalid hex rgb alpha A' => [
'#ab12dd1',
null,
false,
],
'invalid hex rgb alpha B' => [
'#ab12ddzz',
null,
false,
],
'valid hex rgb alpha, alt A' => [
'#ab12cdEE',
null,
true,
],
// * rgb
'valid rgb, flag ALL (default)' => [
'rgb(255, 10, 20)',
null,
true,
],
'valid rgb, flag ALL' => [
'rgb(255, 10, 20)',
\CoreLibs\Check\Colors::ALL,
true,
],
'valid rgb, flag RGB' => [
'rgb(255, 10, 20)',
\CoreLibs\Check\Colors::RGB,
true,
],
'valid rgb, wrong flag' => [
'rgb(255, 10, 20)',
\CoreLibs\Check\Colors::HEX_RGB,
false,
],
// error
'invalid rgb A' => [
'rgb(356, 10, 20)',
null,
false,
],
// other valid rgb conbinations
'valid rgb, alt A (percent)' => [
'rgb(100%, 10%, 20%)',
null,
true,
],
// TODO check all % and non percent combinations
'valid rgb, alt B (percent, mix)' => [
'rgb(100%, 10, 40)',
null,
true,
],
// * rgb alpha
'valid rgba, flag ALL (default)' => [
'rgba(255, 10, 20, 0.5)',
null,
true,
],
'valid rgba, flag ALL' => [
'rgba(255, 10, 20, 0.5)',
\CoreLibs\Check\Colors::ALL,
true,
],
'valid rgba, flag RGB' => [
'rgba(255, 10, 20, 0.5)',
\CoreLibs\Check\Colors::RGBA,
true,
],
'valid rgba, wrong flag' => [
'rgba(255, 10, 20, 0.5)',
\CoreLibs\Check\Colors::HEX_RGB,
false,
],
// error
'invalid rgba A' => [
'rgba(356, 10, 20, 0.5)',
null,
false,
],
// other valid rgba combinations
'valid rgba, alt A (percent)' => [
'rgba(100%, 10%, 20%, 0.5)',
null,
true,
],
// TODO check all % and non percent combinations
'valid rgba, alt B (percent, mix)' => [
'rgba(100%, 10, 40, 0.5)',
null,
true,
],
// TODO check all % and non percent combinations with percent transparent
'valid rgba, alt C (percent transparent)' => [
'rgba(100%, 10%, 20%, 50%)',
null,
true,
],
/*
// hsl
'hsl(100, 50%, 60%)',
'hsl(100, 50.5%, 60.5%)',
'hsla(100, 50%, 60%)',
'hsla(100, 50.5%, 60.5%)',
'hsla(100, 50%, 60%, 0.5)',
'hsla(100, 50.5%, 60.5%, 0.5)',
'hsla(100, 50%, 60%, 50%)',
'hsla(100, 50.5%, 60.5%, 50%)',
*/
// * hsl
'valid hsl, flag ALL (default)' => [
'hsl(100, 50%, 60%)',
null,
true,
],
'valid hsl, flag ALL' => [
'hsl(100, 50%, 60%)',
\CoreLibs\Check\Colors::ALL,
true,
],
'valid hsl, flag RGB' => [
'hsl(100, 50%, 60%)',
\CoreLibs\Check\Colors::HSL,
true,
],
'valid hsl, wrong flag' => [
'hsl(100, 50%, 60%)',
\CoreLibs\Check\Colors::HEX_RGB,
false,
],
'invalid hsl A' => [
'hsl(500, 50%, 60%)',
null,
false,
],
'valid hsl, alt A' => [
'hsl(100, 50.5%, 60.5%)',
null,
true,
],
// * hsl alpha
'valid hsla, flag ALL (default)' => [
'hsla(100, 50%, 60%, 0.5)',
null,
true,
],
'valid hsla, flag ALL' => [
'hsla(100, 50%, 60%, 0.5)',
\CoreLibs\Check\Colors::ALL,
true,
],
'valid hsla, flag RGB' => [
'hsla(100, 50%, 60%, 0.5)',
\CoreLibs\Check\Colors::HSLA,
true,
],
'valid hsla, wrong flag' => [
'hsla(100, 50%, 60%, 0.5)',
\CoreLibs\Check\Colors::HEX_RGB,
false,
],
'invalid hsla A' => [
'hsla(500, 50%, 60%, 0.5)',
null,
false,
],
'valid hsla, alt A (percent alpha' => [
'hsla(100, 50%, 60%, 50%)',
null,
true,
],
'valid hsla, alt A (percent alpha' => [
'hsla(100, 50.5%, 60.5%, 50%)',
null,
true,
],
// * combined flag checks
'valid rgb, flag RGB|RGBA' => [
'rgb(100%, 10%, 20%)',
\CoreLibs\Check\Colors::RGB | \CoreLibs\Check\Colors::RGBA,
true,
],
// TODO other combined flag checks all combinations
// * invalid string
'invalid string A' => [
'invalid string',
null,
false,
],
'invalid string B' => [
'(hsla(100, 100, 100))',
null,
false,
],
'invalid string C' => [
'hsla(100, 100, 100',
null,
false,
],
];
}
/**
* Undocumented function
*
* @covers ::validateColor
* @dataProvider validateColorProvider
* @testdox validateColor $input with flags $flags be $expected [$_dataName]
*
* @param string $input
* @param int|null $flags
* @param bool $expected
* @return void
*/
public function testValidateColor(string $input, ?int $flags, bool $expected)
{
if ($flags === null) {
$result = \CoreLibs\Check\Colors::validateColor($input);
} else {
$result = \CoreLibs\Check\Colors::validateColor($input, $flags);
}
$this->assertEquals(
$expected,
$result
);
}
/**
* Undocumented function
*
* @covers ::validateColor
* @testWith [99]
* @testdox Check Exception throw for $flag
*
* @param int $flag
* @return void
*/
public function testValidateColorException(int $flag): void
{
$this->expectException(\UnexpectedValueException::class);
\CoreLibs\Check\Colors::validateColor('#ffffff', $flag);
}
}
// __END__

View File

@@ -24,12 +24,12 @@ final class CoreLibsCheckEmailTest extends TestCase
'get email regex invalid -1, will be 0' => [
-1,
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
],
'get email regex invalid 10, will be 0' => [
10,
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
],
'get email regex valid 1, will be 1' => [
1,
@@ -157,7 +157,7 @@ final class CoreLibsCheckEmailTest extends TestCase
'error' => 0,
'message' => 'Invalid email address',
'regex' => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
]
],
'error 1 will return double @ error' => [
@@ -181,7 +181,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 3,
'message' => 'Invalid domain part after @ sign',
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$"
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
]
],
'error 4 will be invalid domain' => [
@@ -189,7 +189,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 4,
'message' => 'Invalid domain name part',
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\."
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\."
]
],
'error 5 will be invalid domain top level only' => [
@@ -197,7 +197,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 5,
'message' => 'Wrong domain top level part',
'regex' => "\.([a-zA-Z]{2,6}){1}$"
'regex' => "\.[a-zA-Z]{2,6}$"
]
],
'error 6 will be domain double dot' => [

View File

@@ -92,6 +92,7 @@ final class CoreLibsCheckEncodingTest extends TestCase
$error_char,
$expected
): void {
$current_subsitute_character = mb_substitute_character();
if ($error_char !== null) {
\CoreLibs\Check\Encoding::setErrorChar($error_char);
if (!in_array($error_char, ['none', 'long', 'entity'])) {
@@ -111,6 +112,8 @@ final class CoreLibsCheckEncodingTest extends TestCase
$expected,
$return
);
// reset after test
mb_substitute_character($current_subsitute_character);
}
}

View File

@@ -28,10 +28,10 @@ final class CoreLibsCheckFileTest extends TestCase
public function filesList(): array
{
return [
['filename.txt', 'txt', 5],
['filename.csv', 'csv', 15],
['filename.tsv', 'tsv', 0],
['file_does_not_exits', '', -1],
['filename.txt', 'txt', 5, 'text/plain'],
['filename.csv', 'csv', 15, 'text/csv'],
['filename.tsv', 'tsv', 0, 'text/plain'],
['file_does_not_exits', '', -1, ''],
];
}
@@ -63,6 +63,15 @@ final class CoreLibsCheckFileTest extends TestCase
return $list;
}
public function mimeTypeProvider(): array
{
$list = [];
foreach ($this->filesList() as $row) {
$list[$row[0] . ' must be mime type ' . $row[3]] = [$row[0], $row[3]];
}
return $list;
}
/**
* Tests if file extension matches
*
@@ -115,6 +124,51 @@ final class CoreLibsCheckFileTest extends TestCase
unlink($this->base_folder . $input);
}
}
/**
* Undocumented function
*
* @covers ::getMimeType
* @dataProvider mimeTypeProvider
* @testdox getMimeType $input must be mime type $expected [$_dataName]
*
* @param string $input
* @param string $expected
* @return void
*/
public function testGetMimeType(string $input, string $expected): void
{
if (!empty($expected)) {
$file = $this->base_folder . $input;
$fp = fopen($file, 'w');
switch ($expected) {
case 'text/csv':
for ($i = 1; $i <= 10; $i++) {
fwrite($fp, '"This is row","' . $expected . '",' . $i . PHP_EOL);
}
break;
case 'text/tsv':
for ($i = 1; $i <= 10; $i++) {
fwrite($fp, "\"This is row\"\t\"" . $expected . "\"\t\"" . $i . PHP_EOL);
}
break;
case 'text/plain':
fwrite($fp, 'This is mime type: ' . $expected . PHP_EOL);
break;
}
fclose($fp);
} else {
$this->expectException(\UnexpectedValueException::class);
}
$this->assertEquals(
$expected,
\CoreLibs\Check\File::getMimeType($this->base_folder . $input)
);
// unlink file
if (is_file($this->base_folder . $input)) {
unlink($this->base_folder . $input);
}
}
}
// __END__

View File

@@ -0,0 +1,404 @@
<?php
// This code was created by Claude Sonnet 4
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Combined\ArrayHandler;
class CoreLibsCombinedArrayHandlerFindArraysMissingKeyTest extends TestCase
{
private const DATA_SEPARATOR = ':'; // Updated to match your class's separator
/**
* Test finding missing single key when searching by value without specific key
*/
public function testFindMissingSingleKeyWithValueSearch()
{
$array = [
'item1' => [
'name' => 'John',
'age' => 25
// missing 'email' key
],
'item2' => [
'name' => 'Jane',
'age' => 30,
'email' => 'jane@example.com'
],
'item3' => [
'name' => 'John', // same value as item1
'age' => 35,
'email' => 'john2@example.com'
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'John', 'email');
$this->assertCount(1, $result);
$this->assertEquals($array['item1'], $result[0]['content']);
$this->assertEquals('item1', $result[0]['path']);
$this->assertEquals(['email'], $result[0]['missing_key']);
}
/**
* Test finding missing single key when searching by specific key-value pair
*/
public function testFindMissingSingleKeyWithKeyValueSearch()
{
$array = [
'user1' => [
'id' => 1,
'name' => 'Alice'
// missing 'status' key
],
'user2' => [
'id' => 2,
'name' => 'Bob',
'status' => 'active'
],
'user3' => [
'id' => 1, // same id as user1
'name' => 'Charlie',
'status' => 'inactive'
]
];
$result = ArrayHandler::findArraysMissingKey($array, 1, 'status', 'id');
$this->assertCount(1, $result);
$this->assertEquals($array['user1'], $result[0]['content']);
$this->assertEquals('user1', $result[0]['path']);
$this->assertEquals(['status'], $result[0]['missing_key']);
}
/**
* Test finding missing multiple keys
*/
public function testFindMissingMultipleKeys()
{
$array = [
'record1' => [
'name' => 'Test',
'value' => 100
// missing both 'date' and 'status' keys
],
'record2' => [
'name' => 'Test',
'value' => 200,
'date' => '2023-01-01'
// missing 'status' key
],
'record3' => [
'name' => 'Test',
'value' => 300,
'date' => '2023-01-02',
'status' => 'complete'
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'Test', ['date', 'status']);
$this->assertCount(2, $result);
// First result should be record1 missing both keys
$this->assertEquals($array['record1'], $result[0]['content']);
$this->assertEquals('record1', $result[0]['path']);
$this->assertContains('date', $result[0]['missing_key']);
$this->assertContains('status', $result[0]['missing_key']);
$this->assertCount(2, $result[0]['missing_key']);
// Second result should be record2 missing status key
$this->assertEquals($array['record2'], $result[1]['content']);
$this->assertEquals('record2', $result[1]['path']);
$this->assertEquals(['status'], $result[1]['missing_key']);
}
/**
* Test with nested arrays
*/
public function testFindMissingKeyInNestedArrays()
{
$array = [
'section1' => [
'items' => [
'item1' => [
'name' => 'Product A',
'price' => 99.99
// missing 'category' key
],
'item2' => [
'name' => 'Product B',
'price' => 149.99,
'category' => 'electronics'
]
]
],
'section2' => [
'data' => [
'name' => 'Product A', // same name as nested item
'category' => 'books'
]
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'Product A', 'category');
$this->assertCount(1, $result);
$this->assertEquals($array['section1']['items']['item1'], $result[0]['content']);
$this->assertEquals('section1:items:item1', $result[0]['path']);
$this->assertEquals(['category'], $result[0]['missing_key']);
}
/**
* Test when no arrays are missing the required key
*/
public function testNoMissingKeys()
{
$array = [
'item1' => [
'name' => 'John',
'email' => 'john@example.com'
],
'item2' => [
'name' => 'Jane',
'email' => 'jane@example.com'
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'John', 'email');
$this->assertEmpty($result);
}
/**
* Test when search value is not found in any array
*/
public function testSearchValueNotFound()
{
$array = [
'item1' => [
'name' => 'John',
'age' => 25
],
'item2' => [
'name' => 'Jane',
'age' => 30
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'Bob', 'email');
$this->assertEmpty($result);
}
/**
* Test with different data types for search value
*/
public function testDifferentSearchValueTypes()
{
$array = [
'item1' => [
'active' => true,
'count' => 5
// missing 'label' key
],
'item2' => [
'active' => false,
'count' => 10,
'label' => 'test'
],
'item3' => [
'active' => true, // same boolean as item1
'count' => 15,
'label' => 'another'
]
];
// Test with boolean
$result = ArrayHandler::findArraysMissingKey($array, true, 'label', 'active');
$this->assertCount(1, $result);
$this->assertEquals('item1', $result[0]['path']);
// Test with integer
$result = ArrayHandler::findArraysMissingKey($array, 5, 'label', 'count');
$this->assertCount(1, $result);
$this->assertEquals('item1', $result[0]['path']);
}
/**
* Test with empty array
*/
public function testEmptyArray()
{
$array = [];
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'key');
$this->assertEmpty($result);
}
/**
* Test with array containing non-array values
*/
public function testMixedArrayTypes()
{
$array = [
'string_value' => 'hello',
'numeric_value' => 123,
'array_value' => [
'name' => 'test',
// missing 'type' key
],
'another_array' => [
'name' => 'test',
'type' => 'example'
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'type');
$this->assertCount(1, $result);
$this->assertEquals($array['array_value'], $result[0]['content']);
$this->assertEquals('array_value', $result[0]['path']);
$this->assertEquals(['type'], $result[0]['missing_key']);
}
/**
* Test path building with deeper nesting
*/
public function testDeepNestingPathBuilding()
{
$array = [
'level1' => [
'level2' => [
'level3' => [
'items' => [
'target_item' => [
'name' => 'deep_test',
// missing 'required_field'
]
]
]
]
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'deep_test', 'required_field');
$this->assertCount(1, $result);
$this->assertEquals('level1:level2:level3:items:target_item', $result[0]['path']);
}
/**
* Test with custom path separator
*/
public function testCustomPathSeparator()
{
$array = [
'level1' => [
'level2' => [
'item' => [
'name' => 'test',
// missing 'type' key
]
]
]
];
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'type', null, '/');
$this->assertCount(1, $result);
$this->assertEquals('level1/level2/item', $result[0]['path']);
}
/**
* Test default path separator behavior
*/
public function testDefaultPathSeparator()
{
$array = [
'parent' => [
'child' => [
'name' => 'test',
// missing 'value' key
]
]
];
// Using default separator (should be ':')
$result = ArrayHandler::findArraysMissingKey($array, 'test', 'value');
$this->assertCount(1, $result);
$this->assertEquals('parent:child', $result[0]['path']);
}
/**
* Test different path separators don't affect search logic
*/
public function testPathSeparatorDoesNotAffectSearchLogic()
{
$array = [
'section' => [
'data' => [
'id' => 123,
'name' => 'item'
// missing 'status'
]
]
];
// Test with different separators - results should be identical except for path
$result1 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', ':');
$result2 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', '.');
$result3 = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id', '/');
$this->assertCount(1, $result1);
$this->assertCount(1, $result2);
$this->assertCount(1, $result3);
// Content and missing_key should be the same
$this->assertEquals($result1[0]['content'], $result2[0]['content']);
$this->assertEquals($result1[0]['content'], $result3[0]['content']);
$this->assertEquals($result1[0]['missing_key'], $result2[0]['missing_key']);
$this->assertEquals($result1[0]['missing_key'], $result3[0]['missing_key']);
// Paths should be different based on separator
$this->assertEquals('section:data', $result1[0]['path']);
$this->assertEquals('section.data', $result2[0]['path']);
$this->assertEquals('section/data', $result3[0]['path']);
}
/**
* test type checking
*/
public function testStrictTypeChecking()
{
$array = [
'item1' => [
'id' => '123', // string
'name' => 'test'
// missing 'status'
],
'item2' => [
'id' => 123, // integer
'name' => 'test2',
'status' => 'active'
]
];
// Search for integer 123 - should only match item2
$result = ArrayHandler::findArraysMissingKey($array, 123, 'status', 'id');
$this->assertEmpty($result); // item2 has the status key
// Search for string '123' - should only match item1
$result = ArrayHandler::findArraysMissingKey($array, '123', 'status', 'id');
$this->assertCount(1, $result);
$this->assertEquals('item1', $result[0]['path']);
}
}
// __END__

View File

@@ -0,0 +1,333 @@
<?php
// This code was created by Claude Sonnet 4
// modification for value checks with assertEqualsCanonicalizing
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Combined\ArrayHandler;
class CoreLibsCombinedArrayHandlerKsortArrayTest extends TestCase
{
/**
* Test basic ascending sort (default behavior)
*/
public function testKsortArrayBasicAscending(): void
{
$input = [
'zebra' => 'value1',
'apple' => 'value2',
'banana' => 'value3',
'cherry' => 'value4'
];
$expected = [
'apple' => 'value2',
'banana' => 'value3',
'cherry' => 'value4',
'zebra' => 'value1'
];
$result = ArrayHandler::ksortArray($input);
$this->assertEquals($expected, $result);
$this->assertEquals(array_keys($expected), array_keys($result));
}
/**
* Test descending sort with reverse=true
*/
public function testKsortArrayDescending(): void
{
$input = [
'zebra' => 'value1',
'apple' => 'value2',
'banana' => 'value3',
'cherry' => 'value4'
];
$expected = [
'zebra' => 'value1',
'cherry' => 'value4',
'banana' => 'value3',
'apple' => 'value2'
];
$result = ArrayHandler::ksortArray($input, false, true);
$this->assertEquals($expected, $result);
$this->assertEquals(array_keys($expected), array_keys($result));
}
/**
* Test case-insensitive ascending sort
*/
public function testKsortArrayCaseInsensitiveAscending(): void
{
$input = [
'Zebra' => 'value1',
'apple' => 'value2',
'Banana' => 'value3',
'cherry' => 'value4'
];
$expected = [
'apple' => 'value2',
'Banana' => 'value3',
'cherry' => 'value4',
'Zebra' => 'value1'
];
$result = ArrayHandler::ksortArray($input, true);
$this->assertEquals($expected, $result);
$this->assertEquals(array_keys($expected), array_keys($result));
}
/**
* Test case-insensitive descending sort
*/
public function testKsortArrayCaseInsensitiveDescending(): void
{
$input = [
'Zebra' => 'value1',
'apple' => 'value2',
'Banana' => 'value3',
'cherry' => 'value4'
];
$expected = [
'Zebra' => 'value1',
'cherry' => 'value4',
'Banana' => 'value3',
'apple' => 'value2'
];
$result = ArrayHandler::ksortArray($input, true, true);
$this->assertEquals($expected, $result);
$this->assertEquals(array_keys($expected), array_keys($result));
}
/**
* Test with mixed case keys to verify case sensitivity behavior
*/
public function testKsortArrayCaseSensitivityComparison(): void
{
$input = [
'B' => 'value1',
'a' => 'value2',
'C' => 'value3',
'b' => 'value4'
];
// Case-sensitive sort (uppercase comes before lowercase in ASCII)
$expectedCaseSensitive = [
'B' => 'value1',
'C' => 'value3',
'a' => 'value2',
'b' => 'value4'
];
// Case-insensitive sort
$expectedCaseInsensitive = [
'a' => 'value2',
'B' => 'value1',
'b' => 'value4',
'C' => 'value3'
];
$resultCaseSensitive = ArrayHandler::ksortArray($input, false);
$resultCaseInsensitive = ArrayHandler::ksortArray($input, true);
$this->assertEquals($expectedCaseSensitive, $resultCaseSensitive);
$this->assertEquals($expectedCaseInsensitive, $resultCaseInsensitive);
}
/**
* Test with numeric string keys
*/
public function testKsortArrayNumericStringKeys(): void
{
$input = [
'10' => 'value1',
'2' => 'value2',
'1' => 'value3',
'20' => 'value4'
];
// String comparison, not numeric
$expected = [
'1' => 'value3',
'10' => 'value1',
'2' => 'value2',
'20' => 'value4'
];
$result = ArrayHandler::ksortArray($input);
$this->assertEquals($expected, $result);
}
/**
* Test with special characters in keys
*/
public function testKsortArraySpecialCharacters(): void
{
$input = [
'key_with_underscore' => 'value1',
'key-with-dash' => 'value2',
'key.with.dot' => 'value3',
'key with space' => 'value4',
'keyWithCamelCase' => 'value5'
];
$result = ArrayHandler::ksortArray($input);
// Verify it doesn't throw an error and maintains all keys
$this->assertCount(5, $result);
$this->assertArrayHasKey('key_with_underscore', $result);
$this->assertArrayHasKey('key-with-dash', $result);
$this->assertArrayHasKey('key.with.dot', $result);
$this->assertArrayHasKey('key with space', $result);
$this->assertArrayHasKey('keyWithCamelCase', $result);
}
/**
* Test with empty array
*/
public function testKsortArrayEmpty(): void
{
$input = [];
$result = ArrayHandler::ksortArray($input);
$this->assertEquals([], $result);
$this->assertIsArray($result);
}
/**
* Test with single element array
*/
public function testKsortArraySingleElement(): void
{
$input = ['onlykey' => 'onlyvalue'];
$result = ArrayHandler::ksortArray($input);
$this->assertEquals($input, $result);
}
/**
* Test that original array is not modified (function returns new array)
*/
public function testKsortArrayDoesNotModifyOriginal(): void
{
$original = [
'zebra' => 'value1',
'apple' => 'value2',
'banana' => 'value3'
];
$originalCopy = $original; // Keep a copy for comparison
$result = ArrayHandler::ksortArray($original);
// Original array should remain unchanged
$this->assertEquals($originalCopy, $original);
$this->assertNotEquals(array_keys($original), array_keys($result));
}
/**
* Test with complex mixed data types as values
*/
public function testKsortArrayMixedValueTypes(): void
{
$input = [
'string_key' => 'string_value',
'array_key' => ['nested', 'array'],
'int_key' => 42,
'bool_key' => true,
'null_key' => null
];
$result = ArrayHandler::ksortArray($input);
// Check that all keys are preserved and sorted
$expectedKeys = ['array_key', 'bool_key', 'int_key', 'null_key', 'string_key'];
$this->assertEquals($expectedKeys, array_keys($result));
// Check that values are preserved correctly
$this->assertEquals('string_value', $result['string_key']);
$this->assertEquals(['nested', 'array'], $result['array_key']);
$this->assertEquals(42, $result['int_key']);
$this->assertTrue($result['bool_key']);
$this->assertNull($result['null_key']);
}
/**
* Test all parameter combinations
*/
public function testKsortArrayAllParameterCombinations(): void
{
$input = [
'Delta' => 'value1',
'alpha' => 'value2',
'Charlie' => 'value3',
'bravo' => 'value4'
];
// Test all 4 combinations
$result1 = ArrayHandler::ksortArray($input, false, false); // default
$result2 = ArrayHandler::ksortArray($input, false, true); // reverse only
$result3 = ArrayHandler::ksortArray($input, true, false); // lowercase only
$result4 = ArrayHandler::ksortArray($input, true, true); // both
// Each should produce different ordering
$this->assertNotEquals(array_keys($result1), array_keys($result2));
$this->assertNotEquals(array_keys($result1), array_keys($result3));
$this->assertNotEquals(array_keys($result1), array_keys($result4));
$this->assertNotEquals(array_keys($result2), array_keys($result3));
$this->assertNotEquals(array_keys($result2), array_keys($result4));
$this->assertNotEquals(array_keys($result3), array_keys($result4));
// But all should have same keys and values, just different order
$this->assertEqualsCanonicalizing(array_values($input), array_values($result1));
$this->assertEqualsCanonicalizing(array_values($input), array_values($result2));
$this->assertEqualsCanonicalizing(array_values($input), array_values($result3));
$this->assertEqualsCanonicalizing(array_values($input), array_values($result4));
}
/**
* Data provider for comprehensive testing
*/
public function sortingParametersProvider(): array
{
return [
'default' => [false, false],
'reverse' => [false, true],
'lowercase' => [true, false],
'lowercase_reverse' => [true, true],
];
}
/**
* Test that function works with all parameter combinations using data provider
*
* @dataProvider sortingParametersProvider
*/
public function testKsortArrayWithDataProvider(bool $lowerCase, bool $reverse): void
{
$input = [
'Zebra' => 'animal1',
'apple' => 'fruit1',
'Banana' => 'fruit2',
'cat' => 'animal2'
];
$result = ArrayHandler::ksortArray($input, $lowerCase, $reverse);
// Basic assertions that apply to all combinations
$this->assertIsArray($result);
$this->assertCount(4, $result);
$this->assertArrayHasKey('Zebra', $result);
$this->assertArrayHasKey('apple', $result);
$this->assertArrayHasKey('Banana', $result);
$this->assertArrayHasKey('cat', $result);
}
}
// __END__

View File

@@ -0,0 +1,383 @@
<?php
// created by Claude Sonnet 4
// testRecursiveSearchWithFlatResult had wrong retunr count
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Combined\ArrayHandler;
class CoreLibsCombinedArrayHandlerSelectArrayFromOptionTest extends TestCase
{
private array $testData;
private array $nestedTestData;
protected function setUp(): void
{
$this->testData = [
'item1' => [
'name' => 'John',
'age' => 25,
'status' => 'active',
'score' => 85.5
],
'item2' => [
'name' => 'jane',
'age' => 30,
'status' => 'inactive',
'score' => 92.0
],
'item3' => [
'name' => 'Bob',
'age' => 25,
'status' => 'active',
'score' => 78.3
],
'item4' => [
'name' => 'Alice',
'age' => 35,
'status' => 'pending',
'score' => 88.7
]
];
$this->nestedTestData = [
'level1_a' => [
'name' => 'Level1A',
'type' => 'parent',
'children' => [
'child1' => [
'name' => 'Child1',
'type' => 'child',
'active' => true
],
'child2' => [
'name' => 'Child2',
'type' => 'child',
'active' => false
]
]
],
'level1_b' => [
'name' => 'Level1B',
'type' => 'parent',
'children' => [
'child3' => [
'name' => 'Child3',
'type' => 'child',
'active' => true,
'nested' => [
'deep1' => [
'name' => 'Deep1',
'type' => 'deep',
'active' => true
]
]
]
]
],
'item5' => [
'name' => 'Direct',
'type' => 'child',
'active' => false
]
];
}
public function testEmptyArrayReturnsEmpty(): void
{
$result = ArrayHandler::selectArrayFromOption([], 'name', 'John');
$this->assertEmpty($result);
}
public function testBasicStringSearch(): void
{
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'John');
$this->assertCount(1, $result);
$this->assertArrayHasKey('item1', $result);
$this->assertEquals('John', $result['item1']['name']);
}
public function testBasicIntegerSearch(): void
{
$result = ArrayHandler::selectArrayFromOption($this->testData, 'age', 25);
$this->assertCount(2, $result);
$this->assertArrayHasKey('item1', $result);
$this->assertArrayHasKey('item3', $result);
}
public function testBasicFloatSearch(): void
{
$result = ArrayHandler::selectArrayFromOption($this->testData, 'score', 85.5);
$this->assertCount(1, $result);
$this->assertArrayHasKey('item1', $result);
$this->assertEquals(85.5, $result['item1']['score']);
}
public function testBasicBooleanSearch(): void
{
$data = [
'item1' => ['enabled' => true, 'name' => 'Test1'],
'item2' => ['enabled' => false, 'name' => 'Test2'],
'item3' => ['enabled' => true, 'name' => 'Test3']
];
$result = ArrayHandler::selectArrayFromOption($data, 'enabled', true);
$this->assertCount(2, $result);
$this->assertArrayHasKey('item1', $result);
$this->assertArrayHasKey('item3', $result);
}
public function testStrictComparison(): void
{
$data = [
'item1' => ['value' => '25', 'name' => 'String25'],
'item2' => ['value' => 25, 'name' => 'Int25'],
'item3' => ['value' => 25.0, 'name' => 'Float25']
];
// Non-strict should match all
$nonStrictResult = ArrayHandler::selectArrayFromOption($data, 'value', 25, false);
$this->assertCount(3, $nonStrictResult);
// Strict should only match exact type
$strictResult = ArrayHandler::selectArrayFromOption($data, 'value', 25, true);
$this->assertCount(1, $strictResult);
$this->assertArrayHasKey('item2', $strictResult);
}
public function testCaseInsensitiveSearch(): void
{
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'JANE', false, true);
$this->assertCount(1, $result);
$this->assertArrayHasKey('item2', $result);
$this->assertEquals('jane', $result['item2']['name']);
}
public function testCaseSensitiveSearch(): void
{
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'JANE', false, false);
$this->assertEmpty($result);
}
public function testRecursiveSearchWithFlatResult(): void
{
$result = ArrayHandler::selectArrayFromOption(
$this->nestedTestData,
'type',
'child',
false,
false,
true,
true,
':*'
);
$this->assertCount(4, $result);
$this->assertArrayHasKey('level1_a:*children:*child1', $result);
$this->assertArrayHasKey('level1_a:*children:*child2', $result);
$this->assertArrayHasKey('level1_b:*children:*child3', $result);
$this->assertArrayHasKey('item5', $result);
}
public function testRecursiveSearchWithNestedResult(): void
{
$result = ArrayHandler::selectArrayFromOption(
$this->nestedTestData,
'type',
'child',
false,
false,
true,
false
);
$this->assertCount(3, $result);
$this->assertArrayHasKey('level1_a', $result);
$this->assertArrayHasKey('level1_b', $result);
$this->assertArrayHasKey('item5', $result);
// Check nested structure is preserved
$this->assertArrayHasKey('children', $result['level1_a']);
$this->assertArrayHasKey('child1', $result['level1_a']['children']);
$this->assertArrayHasKey('child2', $result['level1_a']['children']);
}
public function testRecursiveSearchDeepNesting(): void
{
$result = ArrayHandler::selectArrayFromOption(
$this->nestedTestData,
'type',
'deep',
false,
false,
true,
true,
':*'
);
$this->assertCount(1, $result);
$this->assertArrayHasKey('level1_b:*children:*child3:*nested:*deep1', $result);
$this->assertEquals('Deep1', $result['level1_b:*children:*child3:*nested:*deep1']['name']);
}
public function testCustomFlatSeparator(): void
{
$result = ArrayHandler::selectArrayFromOption(
$this->nestedTestData,
'type',
'child',
false,
false,
true,
true,
'|'
);
$this->assertArrayHasKey('level1_a|children|child1', $result);
$this->assertArrayHasKey('level1_a|children|child2', $result);
$this->assertArrayHasKey('level1_b|children|child3', $result);
}
public function testNonRecursiveSearch(): void
{
$result = ArrayHandler::selectArrayFromOption(
$this->nestedTestData,
'type',
'child',
false,
false,
false
);
// Should only find direct matches, not nested ones
$this->assertCount(1, $result);
$this->assertArrayHasKey('item5', $result);
}
public function testNoMatchesFound(): void
{
$result = ArrayHandler::selectArrayFromOption($this->testData, 'name', 'NonExistent');
$this->assertEmpty($result);
}
public function testMissingLookupKey(): void
{
$result = ArrayHandler::selectArrayFromOption($this->testData, 'nonexistent_key', 'value');
$this->assertEmpty($result);
}
public function testCombinedStrictAndCaseInsensitive(): void
{
$data = [
'item1' => ['name' => 'Test', 'id' => '123'],
'item2' => ['name' => 'test', 'id' => 123],
'item3' => ['name' => 'TEST', 'id' => '123']
];
// Case insensitive but strict type matching
$result = ArrayHandler::selectArrayFromOption($data, 'id', '123', true, true);
$this->assertCount(2, $result);
$this->assertArrayHasKey('item1', $result);
$this->assertArrayHasKey('item3', $result);
}
public function testBooleanWithCaseInsensitive(): void
{
$data = [
'item1' => ['active' => true, 'name' => 'Test1'],
'item2' => ['active' => false, 'name' => 'Test2']
];
// Case insensitive flag should not affect boolean comparison
$result = ArrayHandler::selectArrayFromOption($data, 'active', true, false, true);
$this->assertCount(1, $result);
$this->assertArrayHasKey('item1', $result);
}
public function testArrayWithNumericKeys(): void
{
$data = [
0 => ['name' => 'First', 'type' => 'test'],
1 => ['name' => 'Second', 'type' => 'test'],
2 => ['name' => 'Third', 'type' => 'other']
];
$result = ArrayHandler::selectArrayFromOption($data, 'type', 'test');
$this->assertCount(2, $result);
$this->assertArrayHasKey(0, $result);
$this->assertArrayHasKey(1, $result);
}
public function testRecursiveWithMixedKeyTypes(): void
{
$data = [
'string_key' => [
'name' => 'Parent',
'type' => 'parent',
0 => [
'name' => 'Child0',
'type' => 'child'
],
'child_key' => [
'name' => 'ChildKey',
'type' => 'child'
]
]
];
$result = ArrayHandler::selectArrayFromOption($data, 'type', 'child', false, false, true, true, ':*');
$this->assertCount(2, $result);
$this->assertArrayHasKey('string_key:*0', $result);
$this->assertArrayHasKey('string_key:*child_key', $result);
}
public function testAllParametersCombined(): void
{
$data = [
'parent1' => [
'name' => 'Parent1',
'status' => 'ACTIVE',
'children' => [
'child1' => [
'name' => 'Child1',
'status' => 'active'
]
]
]
];
$result = ArrayHandler::selectArrayFromOption(
$data,
'status',
'active',
false, // not strict
true, // case insensitive
true, // recursive
true, // flat result
'|' // custom separator
);
$this->assertCount(2, $result);
$this->assertArrayHasKey('parent1', $result);
$this->assertArrayHasKey('parent1|children|child1', $result);
}
}
// __END__

View File

@@ -0,0 +1,328 @@
<?php
// This code was created by Claude Sonnet 4
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Combined\ArrayHandler;
class CoreLibsCombinedArrayHandlerSortArrayTest extends TestCase
{
/**
* Test basic ascending sort without maintaining keys
*/
public function testBasicAscendingSort()
{
$input = [3, 1, 4, 1, 5, 9];
$expected = [1, 1, 3, 4, 5, 9];
$result = ArrayHandler::sortArray($input);
$this->assertEquals($expected, $result);
$this->assertEquals(array_keys($expected), array_keys($result));
}
/**
* Test basic descending sort without maintaining keys
*/
public function testBasicDescendingSort()
{
$input = [3, 1, 4, 1, 5, 9];
$expected = [9, 5, 4, 3, 1, 1];
$result = ArrayHandler::sortArray($input, false, true);
$this->assertEquals($expected, $result);
$this->assertEquals(array_keys($expected), array_keys($result));
}
/**
* Test ascending sort with key maintenance
*/
public function testAscendingSortWithKeyMaintenance()
{
$input = ['c' => 3, 'a' => 1, 'd' => 4, 'b' => 1, 'e' => 5];
$expected = ['a' => 1, 'b' => 1, 'c' => 3, 'd' => 4, 'e' => 5];
$result = ArrayHandler::sortArray($input, false, false, true);
$this->assertEquals($expected, $result);
}
/**
* Test descending sort with key maintenance
*/
public function testDescendingSortWithKeyMaintenance()
{
$input = ['c' => 3, 'a' => 1, 'd' => 4, 'b' => 1, 'e' => 5];
$expected = ['e' => 5, 'd' => 4, 'c' => 3, 'a' => 1, 'b' => 1];
$result = ArrayHandler::sortArray($input, false, true, true);
$this->assertEquals($expected, $result);
}
/**
* Test string sorting with lowercase conversion
*/
public function testStringLowerCaseSort()
{
$input = ['Banana', 'apple', 'Cherry', 'date'];
$expected = ['apple', 'Banana', 'Cherry', 'date'];
$result = ArrayHandler::sortArray($input, true);
$this->assertEquals($expected, $result);
}
/**
* Test string sorting with lowercase conversion in reverse
*/
public function testStringLowerCaseSortReverse()
{
$input = ['Banana', 'apple', 'Cherry', 'date'];
$expected = ['date', 'Cherry', 'Banana', 'apple'];
$result = ArrayHandler::sortArray($input, true, true);
$this->assertEquals($expected, $result);
}
/**
* Test string sorting with lowercase conversion and key maintenance
*/
public function testStringLowerCaseSortWithKeys()
{
$input = ['b' => 'Banana', 'a' => 'apple', 'c' => 'Cherry', 'd' => 'date'];
$expected = ['a' => 'apple', 'b' => 'Banana', 'c' => 'Cherry', 'd' => 'date'];
$result = ArrayHandler::sortArray($input, true, false, true);
$this->assertEquals($expected, $result);
}
/**
* Test string sorting with lowercase conversion, reverse, and key maintenance
*/
public function testStringLowerCaseSortReverseWithKeys()
{
$input = ['b' => 'Banana', 'a' => 'apple', 'c' => 'Cherry', 'd' => 'date'];
$expected = ['d' => 'date', 'c' => 'Cherry', 'b' => 'Banana', 'a' => 'apple'];
$result = ArrayHandler::sortArray($input, true, true, true);
$this->assertEquals($expected, $result);
}
/**
* Test numeric string sorting with SORT_NUMERIC flag
*/
public function testNumericStringSorting()
{
$input = ['10', '2', '1', '20'];
$expected = ['1', '2', '10', '20'];
$result = ArrayHandler::sortArray($input, false, false, false, SORT_NUMERIC);
$this->assertEquals($expected, $result);
}
/**
* Test natural string sorting with SORT_NATURAL flag
*/
public function testNaturalStringSorting()
{
$input = ['img1.png', 'img10.png', 'img2.png', 'img20.png'];
$expected = ['img1.png', 'img2.png', 'img10.png', 'img20.png'];
$result = ArrayHandler::sortArray($input, false, false, false, SORT_NATURAL);
$this->assertEquals($expected, $result);
}
/**
* Test with empty array
*/
public function testEmptyArray()
{
$input = [];
$expected = [];
$result = ArrayHandler::sortArray($input);
$this->assertEquals($expected, $result);
}
/**
* Test with single element array
*/
public function testSingleElementArray()
{
$input = [42];
$expected = [42];
$result = ArrayHandler::sortArray($input);
$this->assertEquals($expected, $result);
}
/**
* Test with array containing null values
*/
public function testArrayWithNullValues()
{
$input = [3, null, 1, null, 2];
$expected = [null, null, 1, 2, 3];
$result = ArrayHandler::sortArray($input);
$this->assertEquals($expected, $result);
}
/**
* Test with mixed data types
*/
public function testMixedDataTypes()
{
$input = [3, '1', 4.5, '2', 1];
$result = ArrayHandler::sortArray($input);
// Should sort according to PHP's natural comparison rules
$this->assertIsArray($result);
$this->assertCount(5, $result);
}
/**
* Test that original array is not modified (immutability)
*/
public function testOriginalArrayNotModified()
{
$original = [3, 1, 4, 1, 5, 9];
$input = $original;
$result = ArrayHandler::sortArray($input);
$this->assertEquals($original, $input);
$this->assertNotEquals($input, $result);
}
/**
* Test case sensitivity without lowercase flag
*/
public function testCaseSensitivityWithoutLowercase()
{
$input = ['Banana', 'apple', 'Cherry'];
$result = ArrayHandler::sortArray($input);
// Capital letters should come before lowercase in ASCII sort
$this->assertEquals('Banana', $result[0]);
$this->assertEquals('Cherry', $result[1]);
$this->assertEquals('apple', $result[2]);
}
/**
* Test all parameters combination
*/
public function testAllParametersCombination()
{
$input = ['z' => 'Zebra', 'a' => 'apple', 'b' => 'Banana'];
$result = ArrayHandler::sortArray($input, true, true, true, SORT_REGULAR);
// Should be sorted by lowercase, reversed, with keys maintained
$keys = array_keys($result);
$values = array_values($result);
$this->assertEquals(['z', 'b', 'a'], $keys);
$this->assertEquals(['Zebra', 'Banana', 'apple'], $values);
}
/**
* Test floating point numbers
*/
public function testFloatingPointNumbers()
{
$input = [3.14, 2.71, 1.41, 1.73];
$expected = [1.41, 1.73, 2.71, 3.14];
$result = ArrayHandler::sortArray($input);
$this->assertEquals($expected, $result);
}
/**
* Test with duplicate values and key maintenance
*/
public function testDuplicateValuesWithKeyMaintenance()
{
$input = ['first' => 1, 'second' => 2, 'third' => 1, 'fourth' => 2];
$result = ArrayHandler::sortArray($input, false, false, true);
$this->assertCount(4, $result);
$this->assertEquals([1, 1, 2, 2], array_values($result));
// Keys should be preserved
$this->assertArrayHasKey('first', $result);
$this->assertArrayHasKey('second', $result);
$this->assertArrayHasKey('third', $result);
$this->assertArrayHasKey('fourth', $result);
}
/**
* Data provider for comprehensive parameter testing
*/
public function sortParameterProvider(): array
{
return [
'basic_ascending' => [
[3, 1, 4, 2],
false, false, false, SORT_REGULAR,
[1, 2, 3, 4]
],
'basic_descending' => [
[3, 1, 4, 2],
false, true, false, SORT_REGULAR,
[4, 3, 2, 1]
],
'lowercase_ascending' => [
['Banana', 'apple', 'Cherry'],
true, false, false, SORT_REGULAR,
['apple', 'Banana', 'Cherry']
],
'lowercase_descending' => [
['Banana', 'apple', 'Cherry'],
true, true, false, SORT_REGULAR,
['Cherry', 'Banana', 'apple']
]
];
}
/**
* Test various parameter combinations using data provider
*
* @dataProvider sortParameterProvider
*/
public function testSortParameterCombinations(
array $input,
bool $lowercase,
bool $reverse,
bool $maintainKeys,
int $params,
array $expected
) {
$result = ArrayHandler::sortArray($input, $lowercase, $reverse, $maintainKeys, $params);
if (!$maintainKeys) {
$this->assertEquals($expected, $result);
} else {
$this->assertEquals($expected, array_values($result));
}
}
}
// __END__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,6 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsConvertByteTest extends TestCase
{
/**
* Undocumented function
*
@@ -24,7 +23,31 @@ final class CoreLibsConvertByteTest extends TestCase
*/
public function byteProvider(): array
{
/*
* 0: input string
* 1: default flags
* 2: BYTE_FORMAT_SI
* 3: BYTE_FORMAT_NOSPACE
* 4: BYTE_FORMAT_ADJUST
* 5: BYTE_FORMAT_SI | BYTE_FORMAT_NOSPACE
*/
return [
'string number' => [
0 => '1024',
1 => '1 KB',
2 => '1.02 KiB',
3 => '1KB',
4 => '1.00 KB',
5 => '1.02KiB',
],
'invalid string number' => [
0 => '1024 MB',
1 => '1024 MB',
2 => '1024 MB',
3 => '1024 MB',
4 => '1024 MB',
5 => '1024 MB',
],
'negative number' => [
0 => -123123123,
1 => '-117.42 MB',
@@ -100,47 +123,6 @@ final class CoreLibsConvertByteTest extends TestCase
];
}
/**
* Undocumented function
*
* @return array
*/
public function byteStringProvider(): array
{
return [
'negative number' => [
0 => '-117.42 MB',
1 => -123123794,
2 => -117420000,
],
'megabyte' => [
0 => '242.98 MB',
1 => 254782996,
2 => 242980000
],
'megabyte si' => [
0 => '254.78 MiB',
1 => 267156193,
2 => 254780000
],
'petabyte' => [
0 => '1 EiB',
1 => 1152921504606846976,
2 => 1000000000000000000,
],
'max int' => [
0 => '8 EB',
1 => -9223372036854775807 - 1,
2 => 8000000000000000000,
],
'exabyte, overflow' => [
0 => '867.36EB',
1 => 3873816255479021568,
2 => 363028535651074048,
]
];
}
/**
* Undocumented function
*
@@ -157,7 +139,7 @@ final class CoreLibsConvertByteTest extends TestCase
* @return void
*/
public function testHumanReadableByteFormat(
$input,
string|int|float $input,
string $expected,
string $expected_si,
string $expected_no_space,
@@ -194,6 +176,73 @@ final class CoreLibsConvertByteTest extends TestCase
);
}
/**
* Undocumented function
*
* @return array
*/
public function byteStringProvider(): array
{
return [
'negative number' => [
0 => '-117.42 MB',
1 => -123123794,
2 => -117420000,
3 => "-123123793",
4 => "-117420000",
5 => null,
],
'megabyte' => [
0 => '242.98 MB',
1 => 254782996,
2 => 242980000,
3 => "254782996",
4 => "242980000",
5 => null,
],
'megabyte si' => [
0 => '254.78 MiB',
1 => 267156193,
2 => 254780000,
3 => "267156193",
4 => "254780000",
5 => null,
],
'petabyte' => [
0 => '1 EiB',
1 => 1152921504606846976,
2 => 1000000000000000000,
3 => "1152921504606846976",
4 => "1000000000000000000",
5 => null,
],
'max int' => [
0 => '8 EB',
1 => 0,
2 => 0,
3 => "9223372036854775808",
4 => "8000000000000000000",
5 => \LengthException::class,
],
'exabyte, overflow' => [
0 => '867.36EB',
1 => 0,
2 => 0,
3 => "999997996235794808832",
4 => "867360000000000000000",
5 => \LengthException::class,
],
'huge exabyte, overflow' => [
0 => '1000EB',
1 => 0,
2 => 0,
3 => "1152921504606846976000",
4 => "1000000000000000000000",
5 => \LengthException::class,
],
];
}
/**
* Undocumented function
*
@@ -204,10 +253,22 @@ final class CoreLibsConvertByteTest extends TestCase
* @param string|int|float $input
* @param string|int|float $expected
* @param string|int|float $expected_si
* @param string|int|float $expected_string
* @param string|int|float $expected_string_si
* @param ?string $exception
* @return void
*/
public function testStringByteFormat($input, $expected, $expected_si): void
{
public function testStringByteFormat(
string|int|float $input,
string|int|float $expected,
string|int|float $expected_si,
string|int|float $expected_string,
string|int|float $expected_string_si,
?string $exception
): void {
if ($exception !== null) {
$this->expectException($exception);
}
$this->assertEquals(
$expected,
\CoreLibs\Convert\Byte::stringByteFormat($input)
@@ -216,6 +277,54 @@ final class CoreLibsConvertByteTest extends TestCase
$expected_si,
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::BYTE_FORMAT_SI)
);
$this->assertEquals(
$expected_string,
\CoreLibs\Convert\Byte::stringByteFormat($input, \CoreLibs\Convert\Byte::RETURN_AS_STRING)
);
$this->assertEquals(
$expected_string_si,
\CoreLibs\Convert\Byte::stringByteFormat(
$input,
\CoreLibs\Convert\Byte::BYTE_FORMAT_SI | \CoreLibs\Convert\Byte::RETURN_AS_STRING
)
);
}
/**
* Exceptions tests
*
* @covers ::humanReadableByteFormat
* @testWith [99]
* @testdox Test exception for humanReadableByteFormat with flag $flag
*
* @param int $flag
* @return void
*/
public function testHumanReadableByteFormatException(int $flag): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionCode(1);
\CoreLibs\Convert\Byte::humanReadableByteFormat(12, $flag);
}
/**
* Exceptions tests
* can only be 4, try 1,2 and over
*
* @covers ::stringByteFormat
* @testWith [1]
* [2]
* [99]
* @testdox Test exception for stringByteFormat with flag $flag
*
* @param int $flag
* @return void
*/
public function testStringByteFormatException(int $flag): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionCode(1);
\CoreLibs\Convert\Byte::stringByteFormat(12, $flag);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase;
/**
* Test class for Convert\Colors
* @coversDefaultClass \CoreLibs\Convert\Colors
* @testdox \CoreLibs\Convert\Colors method tests
* @testdox \CoreLibs\Convert\Colors legacy method tests
*/
final class CoreLibsConvertColorsTest extends TestCase
{
@@ -21,7 +21,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function rgb2hexColorProvider(): array
public function providerRgb2hexColor(): array
{
return [
'color' => [
@@ -59,6 +59,27 @@ final class CoreLibsConvertColorsTest extends TestCase
3 => 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
],
];
}
@@ -67,7 +88,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function hex2rgbColorProvider(): array
public function providerHex2rgbColor(): array
{
return [
'color' => [
@@ -122,6 +143,8 @@ final class CoreLibsConvertColorsTest extends TestCase
*/
public function rgb2hslAndhsbList(): array
{
// if hsb_from or hsl_from is set, this will be used in hsb/hsl convert
// hsb_rgb is used for adjusted rgb valus due to round error to in
return [
'valid gray' => [
'rgb' => [12, 12, 12],
@@ -137,11 +160,51 @@ final class CoreLibsConvertColorsTest extends TestCase
'hsl' => [211.6, 90.5, 41.2],
'valid' => true,
],
// hsg/hsl with 360 which is seen as 0
'valid color hue 360' => [
'rgb' => [200, 10, 10],
'hsb' => [0, 95, 78.0],
'hsb_from' => [360, 95, 78.0],
'hsb_rgb' => [199, 10, 10], // should be rgb, but rounding error
'hsl' => [0.0, 90.5, 41.2],
'hsl_from' => [360.0, 90.5, 41.2],
'valid' => true,
],
// invalid values
'invalid color' => [
'rgb' => [-12, 300, 12],
'hsb' => [-12, 300, 12],
'hsl' => [-12, 300, 12],
'invalid color r/h/h low' => [
'rgb' => [-1, 12, 12],
'hsb' => [-1, 50, 50],
'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,
],
];
@@ -152,7 +215,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function rgb2hsbColorProvider(): array
public function providerRgb2hsbColor(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
@@ -171,14 +234,14 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function hsb2rgbColorProvider(): array
public function providerHsb2rgbColor(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
$list[$name . ', hsb to rgb'] = [
0 => $values['hsb'][0],
1 => $values['hsb'][1],
2 => $values['hsb'][2],
0 => $values['hsb_from'][0] ?? $values['hsb'][0],
1 => $values['hsb_from'][1] ?? $values['hsb'][1],
2 => $values['hsb_from'][2] ?? $values['hsb'][2],
3 => $values['valid'] ? $values['hsb_rgb'] : false
];
}
@@ -190,7 +253,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function rgb2hslColorProvider(): array
public function providerRgb2hslColor(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
@@ -209,14 +272,14 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function hsl2rgbColorProvider(): array
public function providerHsl2rgbColor(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
$list[$name . ', hsl to rgb'] = [
0 => $values['hsl'][0],
1 => $values['hsl'][1],
2 => $values['hsl'][2],
0 => $values['hsl_from'][0] ?? $values['hsl'][0],
1 => $values['hsl_from'][1] ?? $values['hsl'][1],
2 => $values['hsl_from'][2] ?? $values['hsl'][2],
3 => $values['valid'] ? $values['rgb'] : false
];
}
@@ -228,17 +291,28 @@ final class CoreLibsConvertColorsTest extends TestCase
* TODO: add cross convert check
*
* @covers ::rgb2hex
* @dataProvider rgb2hexColorProvider
* @dataProvider providerRgb2hexColor
* @testdox rgb2hex $input_r,$input_g,$input_b will be $expected [$_dataName]
*
* @param int $input_r
* @param int $input_g
* @param int $input_b
* @param string|bool $expected_hash
* @param string|bool $expected
* @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 #
$this->assertEquals(
$expected_hash,
@@ -268,7 +342,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::hex2rgb
* @dataProvider hex2rgbColorProvider
* @dataProvider providerHex2rgbColor
* @testdox hex2rgb $input will be $expected, $expected_str str[,], $expected_str_sep str[$separator] [$_dataName]
*
* @param string $input
@@ -280,11 +354,19 @@ final class CoreLibsConvertColorsTest extends TestCase
*/
public function testHex2rgb(
string $input,
$expected,
$expected_str,
array|bool $expected,
string|bool $expected_str,
string $separator,
$expected_str_sep
string|bool $expected_str_sep
): 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(
$expected,
\CoreLibs\Convert\Colors::hex2rgb($input)
@@ -303,7 +385,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::rgb2hsb
* @dataProvider rgb2hsbColorProvider
* @dataProvider providerRgb2hsbColor
* @testdox rgb2hsb $input_r,$input_g,$input_b will be $expected [$_dataName]
*
* @param integer $input_r
@@ -312,8 +394,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @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(
$expected,
\CoreLibs\Convert\Colors::rgb2hsb($input_r, $input_g, $input_b)
@@ -324,7 +409,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::hsb2rgb
* @dataProvider hsb2rgbColorProvider
* @dataProvider providerHsb2rgbColor
* @testdox hsb2rgb $input_h,$input_s,$input_b will be $expected [$_dataName]
*
* @param float $input_h
@@ -333,8 +418,12 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @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(
$expected,
\CoreLibs\Convert\Colors::hsb2rgb($input_h, $input_s, $input_b)
@@ -345,7 +434,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::rgb2hsl
* @dataProvider rgb2hslColorProvider
* @dataProvider providerRgb2hslColor
* @testdox rgb2hsl $input_r,$input_g,$input_b will be $expected [$_dataName]
*
* @param integer $input_r
@@ -354,8 +443,11 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @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(
$expected,
\CoreLibs\Convert\Colors::rgb2hsl($input_r, $input_g, $input_b)
@@ -366,7 +458,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::hsl2rgb
* @dataProvider hsl2rgbColorProvider
* @dataProvider providerHsl2rgbColor
* @testdox hsl2rgb $input_h,$input_s,$input_l will be $expected [$_dataName]
*
* @param integer|float $input_h
@@ -375,13 +467,37 @@ final class CoreLibsConvertColorsTest extends TestCase
* @param array|bool $expected
* @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(
$expected,
\CoreLibs\Convert\Colors::hsl2rgb($input_h, $input_s, $input_l)
);
}
/**
* edge case check hsl/hsb and hue 360 (= 0)
*
* @covers ::hsl2rgb
* @covers ::hsb2rgb
* @testdox hsl2rgb/hsb2rgb hue 360 valid check
*
* @return void
*/
public function testHslHsb360hue(): void
{
$this->assertIsArray(
\CoreLibs\Convert\Colors::hsl2rgb(360.0, 90.5, 41.2),
'HSL to RGB with 360 hue'
);
$this->assertIsArray(
\CoreLibs\Convert\Colors::hsb2rgb(360, 95, 78.0),
'HSB to RGB with 360 hue'
);
}
}
// __END__

View File

@@ -13,11 +13,10 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsConvertJsonTest extends TestCase
{
/**
* test list for json convert tests
*
* @return array
* @return array<mixed>
*/
public function jsonProvider(): array
{
@@ -55,10 +54,36 @@ final class CoreLibsConvertJsonTest extends TestCase
];
}
/**
* Undocumented function
*
* @return array<mixed>
*/
public function jsonArrayProvider(): array
{
return [
'valid json' => [
[
'm' => 2,
'f' => 'sub_2'
],
'{"m":2,"f":"sub_2"}',
],
'empty json array' => [
[],
'[]'
],
'empty json hash' => [
['' => ''],
'{"":""}'
]
];
}
/**
* json error list
*
* @return array JSON error list
* @return array<mixed> JSON error list
*/
public function jsonErrorProvider(): array
{
@@ -128,7 +153,7 @@ final class CoreLibsConvertJsonTest extends TestCase
*
* @param string|null $input
* @param bool $flag
* @param array $expected
* @param array<mixed> $expected
* @return void
*/
public function testJsonConvertToArray(?string $input, bool $flag, array $expected): void
@@ -139,6 +164,51 @@ final class CoreLibsConvertJsonTest extends TestCase
);
}
/**
* test with flags
*
* @covers ::jsonConvertToArray
* @testdox jsonConvertToArray flag test, if flag is used
*
* @return void
*/
public function testJsonConvertToArrayWithFlags(): void
{
$input = '{"valid":"json","invalid":"\xB1\x31"}';
/* $expected_without_flag = [
'valid' => 'json'
];
$expected_with_flag = [
'valid' => 'json',
'invalid' => "\xB1\x31"
]; */
// no idea why in both it throws an erro
$expected_without_flag = [];
$expected_with_flag = [];
$this->assertEquals(
$expected_without_flag,
\CoreLibs\Convert\Json::jsonConvertToArray($input)
);
$this->assertEquals(
$expected_with_flag,
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_INVALID_UTF8_IGNORE)
);
}
public function testJsonConvertToArrayRemoveThrowFlag(): void
{
$input = '{"valid":"json","invalid":"\xB1\x31"}';
// show NOT throw an exception
try {
$this->assertEquals(
[],
\CoreLibs\Convert\Json::jsonConvertToArray($input, flags:JSON_THROW_ON_ERROR)
);
} catch (\Exception $e) {
$this->fail('Exception was thrown despite flag removal');
}
}
/**
* test json error states
*
@@ -147,7 +217,8 @@ final class CoreLibsConvertJsonTest extends TestCase
* @testdox jsonGetLastError $input will be $expected_i/$expected_s [$_dataName]
*
* @param string|null $input
* @param string $expected
* @param int $expected_i
* @param string $expected_s
* @return void
*/
public function testJsonGetLastError(?string $input, int $expected_i, string $expected_s): void
@@ -162,6 +233,68 @@ final class CoreLibsConvertJsonTest extends TestCase
\CoreLibs\Convert\Json::jsonGetLastError(true)
);
}
/**
* test json error states
*
* @covers ::jsonValidate
* @dataProvider jsonErrorProvider
* @testdox jsonValidate $input will be $expected_i/$expected_s [$_dataName]
*
* @param string|null $input
* @param int $expected_i
* @param string $expected_s
* @return void
*/
public function testJsonValidateGetLastError(?string $input, int $expected_i, string $expected_s): void
{
\CoreLibs\Convert\Json::jsonValidate($input);
$this->assertEquals(
$expected_i,
\CoreLibs\Convert\Json::jsonGetLastError()
);
$this->assertEquals(
$expected_s,
\CoreLibs\Convert\Json::jsonGetLastError(true)
);
}
/**
* test json validation
*
* @covers ::jsonValidate
* @testdox jsonValidate test valid and invalid json
*
* @return void
*/
public function testJsonValidate(): void
{
$this->assertTrue(
\CoreLibs\Convert\Json::jsonValidate('{"valid": "json"}')
);
$this->assertFalse(
\CoreLibs\Convert\Json::jsonValidate('not valid json')
);
}
/**
* Undocumented function
*
* @covers ::jsonConvertArrayTo
* @dataProvider jsonArrayProvider
* @testdox jsonConvertArrayTo $input (Override: $flag) will be $expected [$_dataName]
*
* @param array<mixed> $input
* @param string $expected
* @return void
*/
public function testJsonConvertArrayto(array $input, string $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Json::jsonConvertArrayTo($input)
);
}
}
// __END__

View File

@@ -0,0 +1,504 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Undocumented class
* @coversDefaultClass \CoreLibs\Convert\Math
* @testdox \CoreLibs\Convert\Math method tests
*/
final class CoreLibsConvertMathTest extends TestCase
{
/**
* Undocumented function
*
* @return array<mixed>
*/
public function providerFceil(): array
{
return [
'5.5 must be 6' => [5.5, 6],
'5.1234567890 with 5 must be 6' => [5.1234567890, 6],
'6 must be 6' => [6, 6]
];
}
/**
* Undocumented function
*
* @covers ::fceil
* @dataProvider providerFceil
* @testdox fceil: Input $input must be $expected
*
* @param float $input
* @param int $expected
* @return void
*/
public function testMathFceilValue(float $input, int $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Math::fceil($input)
);
}
/**
* Undocumented function
*
* @return array<mixed>
*/
public function providerFloor(): array
{
return [
'5123456 with -3 must be 5123000' => [5123456, -3, 5123000],
'5123456 with -10 must be 5000000' => [5123456, -10, 5000000]
];
}
/**
* Undocumented function
*
* @covers ::floorp
* @dataProvider providerFloor
* @testdox floor: Input $input with cutoff $cutoff must be $expected
*
* @param int $input
* @param int $cutoff
* @param int $expected
* @return void
*/
public function testMathFloorValue(int $input, int $cutoff, int $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Math::floorp($input, $cutoff)
);
}
/**
* Undocumented function
*
* @return array<mixed>
*/
public function providerInitNumeric(): array
{
return [
'5 must be 5' => [5, 5, 'int'],
'5.123 must be 5.123' => [5.123, 5.123, 'float'],
"'5' must be 5" => ['5', 5, 'string'],
"'5.123' must be 5.123" => ['5.123', 5.123, 'string'],
];
}
/**
* Undocumented function
*
* @covers ::initNumeric
* @dataProvider providerInitNumeric
* @testdox initNumeric: Input $info $input must match $expected [$_dataName]
*
* @param int|float|string $input
* @param float $expected
* @param string $info
* @return void
*/
public function testMathInitNumericValue($input, float $expected, string $info): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Math::initNumeric($input)
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerCbrt(): array
{
return [
'cube root of 2' => [2, 1.25992, 5, null],
'cube root of 3' => [3, 1.44225, 5, null],
'cube root of -1' => [-1, 'NAN', 0, \InvalidArgumentException::class],
];
}
/**
* Undocumented function
*
* @covers ::cbrt
* @dataProvider providerCbrt
* @testdox initNumeric: Input $input must match $expected [$_dataName]
*
* @param float|int $number
* @param float $expected
* @param int $round_to
* @param ?string $exception
* @return void
*/
public function testCbrt(float|int $number, float|string $expected, int $round_to, ?string $exception): void
{
if ($exception !== null) {
$this->expectException($exception);
}
$this->assertEquals(
$expected,
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerMultiplyMatrices(): array
{
return [
'[3] x [3] => [3x1]' => [
[1, 2, 3],
[1, 2, 3],
[14]
],
'[3] x [3x1]' => [
[1, 2, 3],
[[1], [2], [3]],
[14]
],
'[3] x [3x1]' => [
[1, 2, 3],
[[1], [2], [3]],
[14]
],
'[1x3L] x [3x1]' => [
[[1, 2, 3]],
[[1], [2], [3]],
[14]
],
'[1x3] x [3x1]' => [
[[1], [2], [3]],
[[1], [2], [3]],
[1, 2, 3]
],
'[2x3] x [3] => [3x1]' => [
[
[1, 2, 3],
[1, 2, 3]
],
[1, 2, 3],
[
14,
14
]
],
'[2x3] x [3x1]' => [
[
[1, 2, 3],
[1, 2, 3]
],
[[1], [2], [3]],
[
14,
14
]
],
'[2x3] x [2x3] => [3x3]' => [
[
[1, 2, 3],
[1, 2, 3],
],
[
[1, 2, 3],
[1, 2, 3],
],
[
[3, 6, 9],
[3, 6, 9]
]
],
'[2x3] x [3x3]' => [
[
[1, 2, 3],
[1, 2, 3],
],
[
[1, 2, 3],
[1, 2, 3],
[0, 0, 0],
],
[
[3, 6, 9],
[3, 6, 9]
]
],
'[2x3] x [3x2]' => [
'a' => [
[1, 2, 3],
[1, 2, 3],
],
'b' => [
[1, 1],
[2, 2],
[3, 3],
],
'prod' => [
[14, 14],
[14, 14],
]
],
'[3x3] x [3] => [1x3]' => [
[
[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
],
[1, 2, 3],
[
14,
14,
14
]
],
'[3x3] x [2x3] => [3x3]' => [
[
[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
],
[
[1, 2, 3],
[1, 2, 3],
],
[
[3, 6, 9],
[3, 6, 9],
[3, 6, 9],
]
],
'[3x3] x [3x3]' => [
[
[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
],
[
[1, 2, 3],
[1, 2, 3],
// [0, 0, 0],
],
[
[3, 6, 9],
[3, 6, 9],
[3, 6, 9],
]
],
'[3] x [3x3]' => [
[1, 2, 3],
[
[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
],
[
[6, 12, 18],
]
],
'[2x3] x [3x3]' => [
[
[1, 2, 3],
[1, 2, 3],
],
[
[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
],
[
[6, 12, 18],
[6, 12, 18],
]
],
'inblanaced [2x2,3] x [3x2]' => [
'a' => [
[1, 2, 3],
[4, 5]
],
'b' => [
[6, 7],
[8, 9],
[10, 11]
],
'result' => [
[52, 58],
[64, 73],
]
],
'inblanaced [2x3] x [3x1,2]' => [
'a' => [
[1, 2, 3],
[4, 5, 7]
],
'b' => [
[7, 8],
[9, 10],
[11]
],
'result' => [
[58, 28],
[150, 82],
]
],
];
}
/**
* Undocumented function
*
* @covers ::multiplyMatrices
* @dataProvider providerMultiplyMatrices
* @testdox initNumeric: Input $input_a x $input_b must match $expected [$_dataName]
*
* @param array $input_a
* @param array $input_b
* @param array $expected
* @return void
*/
public function testMultiplyMatrices(array $input_a, array $input_b, array $expected): void
{
$this->assertEquals(
$expected,
\CoreLibs\Convert\Math::multiplyMatrices($input_a, $input_b)
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerEqualWithEpsilon(): array
{
return [
'equal' => [
'a' => 0.000000000000000222,
'b' => 0.000000000000000222,
'epsilon' => PHP_FLOAT_EPSILON,
'equal' => true,
],
'almost equal' => [
'a' => 0.000000000000000222,
'b' => 0.000000000000000232,
'epsilon' => PHP_FLOAT_EPSILON,
'equal' => true,
],
'not equal' => [
'a' => 0.000000000000000222,
'b' => 0.000000000000004222,
'epsilon' => PHP_FLOAT_EPSILON,
'equal' => false,
],
'equal, different epsilon' => [
'a' => 0.000000000000000222,
'b' => 0.000000000000004222,
'epsilon' => 0.0001,
'equal' => true,
],
'not equal, different epsilon' => [
'a' => 0.0001,
'b' => 0.0002,
'epsilon' => 0.0001,
'equal' => false,
]
];
}
/**
* Undocumented function
*
* @covers ::equalWithEpsilon
* @dataProvider providerEqualWithEpsilon
* @testdox equalWithEpsilon with $a and $b and Epsilon: $epsilon must be equal: $equal [$_dataName]
*
* @return void
*/
public function testEqualWithEpsilon(float $a, float $b, float $epsilon, bool $equal): void
{
$this->assertEquals(
$equal,
\CoreLibs\Convert\Math::equalWithEpsilon($a, $b, $epsilon)
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerCompareWithEpsilon(): array
{
return [
'smaller, true' => [
'value' => 0.0001,
'compare' => '<',
'limit' => 0.0002,
'epsilon' => 0.00001,
'match' => true,
],
'smaller, false' => [
'value' => 0.0001,
'compare' => '<',
'limit' => 0.0001,
'epsilon' => 0.00001,
'match' => false,
],
'bigger, true' => [
'value' => 0.0002,
'compare' => '>',
'limit' => 0.0001,
'epsilon' => 0.00001,
'match' => true,
],
'bigger, false' => [
'value' => 0.0001,
'compare' => '>',
'limit' => 0.0001,
'epsilon' => 0.00001,
'match' => false,
],
];
}
/**
* Undocumented function
*
* @covers ::compareWithEpsilon
* @dataProvider providerCompareWithEpsilon
* @testdox compareWithEpsilon $value $compare $limit with $epsilon must match: $match [$_dataName]
*
* @param float $value
* @param string $compare
* @param float $limit
* @param float $epslion
* @param bool $match
* @return void
*/
public function testCompareWithEpsilon(
float $value,
string $compare,
float $limit,
float $epsilon,
bool $match
): void {
$this->assertEquals(
$match,
\CoreLibs\Convert\Math::compareWithEpsilon($value, $compare, $limit, $epsilon)
);
}
}
// __END__

View File

@@ -33,15 +33,14 @@ final class CoreLibsConvertMimeEncodeTest extends TestCase
'The quick brown fox jumps over the lazy sheep that sleeps in the ravine '
. 'and has no idea what is going on here',
'UTF-8',
'The quick brown fox jumps over the lazy sheep that sleeps in the ravine '
. 'and has no idea what is going on here'
"The quick brown fox jumps over the lazy sheep that sleeps in the ravine and\r\n"
. ' has no idea what is going on here'
],
'standard with special chars UTF-8' => [
'This is ümläßtと漢字もカタカナ!^$%&',
'UTF-8',
'This is =?UTF-8?B?w7xtbMOkw59044Go5ryi5a2X44KC44Kr44K/44Kr44OK77yBIV4k?='
. "\r\n"
. ' =?UTF-8?B?JQ==?=&'
"This is =?UTF-8?B?w7xtbMOkw59044Go5ryi5a2X44KC44Kr44K/44Kr44OK77yBIV4k?=\r\n"
. ' =?UTF-8?B?JSY=?='
],
'35 chars and space at the end UTF-8' => [
'12345678901234567890123456789012345 '
@@ -62,9 +61,8 @@ final class CoreLibsConvertMimeEncodeTest extends TestCase
. 'is there a space?',
'UTF-8',
"=?UTF-8?B?44Kr44K/44Kr44OK44Kr44K/44Kr44OK44GL44Gq44Kr44K/44Kr44OK44Kr?=\r\n"
. " =?UTF-8?B?44K/44Kr44OK?=\r\n"
. " =?UTF-8?B?44GL44Gq44Kr44K/44Kr44OK44Kr44K/44Kr44OK44GL44Gq44Kr44K/44Kr?=\r\n"
. " =?UTF-8?B?44OK44Kr44K/?= is there a =?UTF-8?B?c3BhY2U/?="
. " =?UTF-8?B?44K/44Kr44OK44GL44Gq44Kr44K/44Kr44OK44Kr44K/44Kr44OK44GL44Gq?=\r\n"
. " =?UTF-8?B?44Kr44K/44Kr44OK44Kr44K/IGlzIHRoZXJlIGEgc3BhY2U/?="
]
];
}
@@ -85,16 +83,28 @@ final class CoreLibsConvertMimeEncodeTest extends TestCase
// print "MIME: -" . $encoded . "-\n";
$this->assertEquals(
$expected,
$encoded
$encoded,
"__mbMimeEncode"
);
$decoded = mb_decode_mimeheader($encoded);
// print "INPUT : " . $input . "\n";
// print "DECODED: " . $decoded . "\n";
// print "ENCODED: " . $encoded . "\n";
// print "INPUT : " . $input . " | " . mb_strlen($input) . "\n";
// print "DECODED: " . $decoded . " | " . mb_strlen($decoded) . "\n";
// $test_enc = mb_encode_mimeheader($input, $encoding);
// $test_dec = mb_decode_mimeheader($test_enc);
// print "TEST ENC: " . $test_enc . "\n";
// back compare decoded
$this->assertEquals(
$input,
$decoded
$decoded,
"mb_decode_mimeheader"
);
// $this->assertEquals(
// $input,
// $test_dec,
// 'mb_encode_to_decode'
// );
}
}

View File

@@ -0,0 +1,657 @@
<?php // phpcs:disable Generic.Files.LineLength
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
use CoreLibs\Convert\SetVarTypeNull;
/**
* Test class for Convert\Strings
* @coversDefaultClass \CoreLibs\Convert\SetVarTypeNull
* @testdox \CoreLibs\Convert\SetVarTypeNull method tests
*/
final class CoreLibsConvertSetVarTypeNullTest extends TestCase
{
/**
* Undocumented function
*
* @return array
*/
public function varSetTypeStringProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'empty string' => [
'',
null,
''
],
'filled string' => [
'string',
null,
'string'
],
'valid string, override set' => [
'string',
'override',
'string'
],
'int, no override' => [
1,
null,
'1'
],
'int, override set' => [
1,
'not int',
'1'
],
'array, override set' => [
[1, 2],
null,
null
]
];
}
/**
* Undocumented function
* @covers ::setStr
* @dataProvider varSetTypeStringProvider
* @testdox setStr $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param string|null $default
* @param string|null $expected
* @return void
*/
public function testSetString(mixed $input, ?string $default, ?string $expected): void
{
$set_var = SetVarTypeNull::setStr($input, $default);
if ($expected !== null) {
$this->assertIsString($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varMakeTypeStringProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'empty string' => [
'',
null,
''
],
'filled string' => [
'string',
null,
'string'
],
'valid string, override set' => [
'string',
'override',
'string'
],
'int, no override' => [
1,
null,
'1'
],
'int, override set' => [
1,
'not int',
'1'
],
'float, no override' => [
1.5,
null,
'1.5'
],
// all the strange things here
'function, override set' => [
$foo = function () {
return '';
},
'function',
'function'
],
'function, no override' => [
$foo = function () {
return '';
},
null,
null
],
'hex value, override set' => [
0x55,
'hex',
'85'
]
];
}
/**
* Undocumented function
* @covers ::makeStr
* @dataProvider varMakeTypeStringProvider
* @testdox makeStr $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param string|null $default
* @param string|null $expected
* @return void
*/
public function testMakeString(mixed $input, ?string $default, ?string $expected): void
{
$set_var = SetVarTypeNull::makeStr($input, $default);
if ($expected !== null) {
$this->assertIsString($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varSetTypeIntProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'int' => [
1,
null,
1
],
'int, override set' => [
1,
-1,
1
],
'string, no override' => [
'string',
null,
null
],
'string, override' => [
'string',
-1,
-1
],
'float' => [
1.5,
null,
1
]
];
}
/**
* Undocumented function
* @covers ::setInt
* @dataProvider varSetTypeIntProvider
* @testdox setInt $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param int|null $default
* @param int|null $expected
* @return void
*/
public function testSetInt(mixed $input, ?int $default, ?int $expected): void
{
$set_var = SetVarTypeNull::setInt($input, $default);
if ($expected !== null) {
$this->assertIsInt($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varMakeTypeIntProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'int' => [
1,
null,
1
],
'int, override set' => [
1,
-1,
1
],
'string, no override' => [
'string',
null,
0
],
'string, override' => [
'string',
-1,
0
],
'float' => [
1.5,
null,
1
],
// all the strange things here
'function, override set' => [
$foo = function () {
return '';
},
-1,
-1
],
'function, no override ' => [
$foo = function () {
return '';
},
null,
null
],
'hex value, override set' => [
0x55,
-1,
85
],
];
}
/**
* Undocumented function
* @covers ::makeInt
* @dataProvider varMakeTypeIntProvider
* @testdox makeInt $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param int|null $default
* @param int|null $expected
* @return void
*/
public function testMakeInt(mixed $input, ?int $default, ?int $expected): void
{
$set_var = SetVarTypeNull::makeInt($input, $default);
if ($expected !== null) {
$this->assertIsInt($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varSetTypeFloatProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'float' => [
1.5,
null,
1.5
],
'float, override set' => [
1.5,
-1.5,
1.5
],
'string, no override' => [
'string',
null,
null
],
'string, override' => [
'string',
1.5,
1.5
],
'int' => [
1,
null,
1.0
]
];
}
/**
* Undocumented function
* @covers ::setFloat
* @dataProvider varSetTypeFloatProvider
* @testdox setFloat $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param float|null $default
* @param float|null $expected
* @return void
*/
public function testSetFloat(mixed $input, ?float $default, ?float $expected): void
{
$set_var = SetVarTypeNull::setFloat($input, $default);
if ($expected !== null) {
$this->assertIsFloat($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varMakeTypeFloatProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'float' => [
1.5,
null,
1.5
],
'float, override set' => [
1.5,
-1.5,
1.5
],
'string, no override' => [
'string',
null,
0.0
],
'string, override' => [
'string',
1.5,
0.0
],
'int' => [
1,
null,
1.0
],
// all the strange things here
'function, override set' => [
$foo = function () {
return '';
},
-1.0,
-1.0
],
// all the strange things here
'function, no override' => [
$foo = function () {
return '';
},
null,
null
],
'hex value, override set' => [
0x55,
-1,
85.0
],
];
}
/**
* Undocumented function
* @covers ::makeFloat
* @dataProvider varMakeTypeFloatProvider
* @testdox makeFloat $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param float|null $default
* @param float|null $expected
* @return void
*/
public function testMakeFloat(mixed $input, ?float $default, ?float $expected): void
{
$set_var = SetVarTypeNull::makeFloat($input, $default);
if ($expected !== null) {
$this->assertIsFloat($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varSetTypeArrayProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'array, empty' => [
[],
null,
[]
],
'array, filled' => [
['array'],
null,
['array']
],
'string, no override' => [
'string',
null,
null
],
'string, override' => [
'string',
['string'],
['string']
]
];
}
/**
* Undocumented function
* @covers ::setArray
* @dataProvider varSetTypeArrayProvider
* @testdox setArray $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param array|null $default
* @param array|null $expected
* @return void
*/
public function testSetArray(mixed $input, ?array $default, ?array $expected): void
{
$set_var = SetVarTypeNull::setArray($input, $default);
if ($expected !== null) {
$this->assertIsArray($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varSetTypeBoolProvider(): array
{
// 0: input
// 1: default (null default)
// 2: expected
return [
'bool true' => [
true,
null,
true
],
'bool false' => [
false,
null,
false
],
'string, no override' => [
'string',
null,
null
],
'string, override' => [
'string',
true,
true
]
];
}
/**
* Undocumented function
* @covers ::setBool
* @dataProvider varSetTypeBoolProvider
* @testdox setBool $input with override $default will be $expected [$_dataName]
*
* @param mixed $input
* @param bool|null $default
* @param bool|null $expected
* @return void
*/
public function testSetBool(mixed $input, ?bool $default, ?bool $expected): void
{
$set_var = SetVarTypeNull::setBool($input, $default);
if ($expected !== null) {
$this->assertIsBool($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
/**
* Undocumented function
*
* @return array
*/
public function varMakeTypeBoolProvider(): array
{
// 0: input
// 2: expected
return [
'true' => [
true,
true
],
'false' => [
false,
false
],
'string on' => [
'on',
true
],
'string off' => [
'off',
false
],
'invalid string' => [
'sulzenbacher',
null,
],
'invalid string, override' => [
'sulzenbacher',
null,
],
'array to default' => [
[],
false
],
];
}
/**
* Undocumented function
* @covers ::setBool
* @dataProvider varMakeTypeBoolProvider
* @testdox setBool $input will be $expected [$_dataName]
*
* @param mixed $input
* @param bool|null $default
* @param bool|null $expected
* @return void
*/
public function testMakeBool(mixed $input, ?bool $expected): void
{
$set_var = SetVarTypeNull::makeBool($input);
if ($expected !== null) {
$this->assertIsBool($set_var);
} else {
$this->assertNull($set_var);
}
$this->assertEquals(
$expected,
$set_var
);
}
}
// __END__

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