Compare commits

..

1 Commits

Author SHA1 Message Date
Clemens Schwaighofer
1a027e5c7d DB SqLite interface class 2024-10-21 10:18:56 +09:00
36 changed files with 1136 additions and 8082 deletions

View File

@@ -1,65 +0,0 @@
<?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;
}
// 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;
}
print buildContent($http_headers, $file_get);
// __END__

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 legacy method tests
* @testdox \CoreLibs\Convert\Colors method tests
*/
final class CoreLibsConvertColorsTest extends TestCase
{
@@ -21,7 +21,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function providerRgb2hexColor(): array
public function rgb2hexColorProvider(): array
{
return [
'color' => [
@@ -88,7 +88,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function providerHex2rgbColor(): array
public function hex2rgbColorProvider(): array
{
return [
'color' => [
@@ -215,7 +215,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function providerRgb2hsbColor(): array
public function rgb2hsbColorProvider(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
@@ -234,7 +234,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function providerHsb2rgbColor(): array
public function hsb2rgbColorProvider(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
@@ -253,7 +253,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function providerRgb2hslColor(): array
public function rgb2hslColorProvider(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
@@ -272,7 +272,7 @@ final class CoreLibsConvertColorsTest extends TestCase
*
* @return array
*/
public function providerHsl2rgbColor(): array
public function hsl2rgbColorProvider(): array
{
$list = [];
foreach ($this->rgb2hslAndhsbList() as $name => $values) {
@@ -291,7 +291,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* TODO: add cross convert check
*
* @covers ::rgb2hex
* @dataProvider providerRgb2hexColor
* @dataProvider rgb2hexColorProvider
* @testdox rgb2hex $input_r,$input_g,$input_b will be $expected [$_dataName]
*
* @param int $input_r
@@ -342,7 +342,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::hex2rgb
* @dataProvider providerHex2rgbColor
* @dataProvider hex2rgbColorProvider
* @testdox hex2rgb $input will be $expected, $expected_str str[,], $expected_str_sep str[$separator] [$_dataName]
*
* @param string $input
@@ -385,7 +385,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::rgb2hsb
* @dataProvider providerRgb2hsbColor
* @dataProvider rgb2hsbColorProvider
* @testdox rgb2hsb $input_r,$input_g,$input_b will be $expected [$_dataName]
*
* @param integer $input_r
@@ -409,7 +409,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::hsb2rgb
* @dataProvider providerHsb2rgbColor
* @dataProvider hsb2rgbColorProvider
* @testdox hsb2rgb $input_h,$input_s,$input_b will be $expected [$_dataName]
*
* @param float $input_h
@@ -434,7 +434,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::rgb2hsl
* @dataProvider providerRgb2hslColor
* @dataProvider rgb2hslColorProvider
* @testdox rgb2hsl $input_r,$input_g,$input_b will be $expected [$_dataName]
*
* @param integer $input_r
@@ -458,7 +458,7 @@ final class CoreLibsConvertColorsTest extends TestCase
* Undocumented function
*
* @covers ::hsl2rgb
* @dataProvider providerHsl2rgbColor
* @dataProvider hsl2rgbColorProvider
* @testdox hsl2rgb $input_h,$input_s,$input_l will be $expected [$_dataName]
*
* @param integer|float $input_h

View File

@@ -18,7 +18,7 @@ final class CoreLibsConvertMathTest extends TestCase
*
* @return array<mixed>
*/
public function providerFceil(): array
public function fceilProvider(): array
{
return [
'5.5 must be 6' => [5.5, 6],
@@ -31,7 +31,7 @@ final class CoreLibsConvertMathTest extends TestCase
* Undocumented function
*
* @covers ::fceil
* @dataProvider providerFceil
* @dataProvider fceilProvider
* @testdox fceil: Input $input must be $expected
*
* @param float $input
@@ -51,7 +51,7 @@ final class CoreLibsConvertMathTest extends TestCase
*
* @return array<mixed>
*/
public function providerFloor(): array
public function floorProvider(): array
{
return [
'5123456 with -3 must be 5123000' => [5123456, -3, 5123000],
@@ -63,7 +63,7 @@ final class CoreLibsConvertMathTest extends TestCase
* Undocumented function
*
* @covers ::floorp
* @dataProvider providerFloor
* @dataProvider floorProvider
* @testdox floor: Input $input with cutoff $cutoff must be $expected
*
* @param int $input
@@ -84,7 +84,7 @@ final class CoreLibsConvertMathTest extends TestCase
*
* @return array<mixed>
*/
public function providerInitNumeric(): array
public function initNumericProvider(): array
{
return [
'5 must be 5' => [5, 5, 'int'],
@@ -98,7 +98,7 @@ final class CoreLibsConvertMathTest extends TestCase
* Undocumented function
*
* @covers ::initNumeric
* @dataProvider providerInitNumeric
* @dataProvider initNumericProvider
* @testdox initNumeric: Input $info $input must match $expected [$_dataName]
*
* @param int|float|string $input
@@ -113,358 +113,6 @@ final class CoreLibsConvertMathTest extends TestCase
\CoreLibs\Convert\Math::initNumeric($input)
);
}
/**
* Undocumented function
*
* @return array
*/
public function providerCbrt(): array
{
return [
'cube root of 2' => [2, 1.25992, 5],
'cube root of 3' => [3, 1.44225, 5],
'cube root of -1' => [-1, 'NAN', 0],
];
}
/**
* 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
* @return void
*/
public function testCbrt(float|int $number, float|string $expected, int $round_to): void
{
$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],
]
],
];
}
/**
* 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

@@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase;
* Test class for DB\Extended\ArrayIO
* This will only test the PgSQL parts
* @coversDefaultClass \CoreLibs\DB\Extended\ArrayIO
* @coversDefaultClass \CoreLibs\DB\Extended\ArrayIO
* @testdox \CoreLibs\Extended\ArrayIO method tests for extended DB interface
*/
final class CoreLibsDBExtendedArrayIOTest extends TestCase

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for DB\SqLite
* This will only test the SqLite parts
* @coversDefaultClass \CoreLibs\DB\SqLite
* @testdox \CoreLibs\SqLite method tests for extended DB interface
*/
final class CoreLibsDBESqLiteTest extends TestCase
{
/**
* Undocumented function
*
* @return void
*/
protected function setUp(): void
{
if (!extension_loaded('sqlite')) {
$this->markTestSkipped(
'The SqLite extension is not available.'
);
}
}
/**
* Undocumented function
*
* @testdox DB\SqLite Class tests
*
* @return void
*/
public function testSqLite()
{
$this->markTestIncomplete(
'DB\SqLite Tests have not yet been implemented'
);
}
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
"phpstan/phpstan": "^1.12",
"phan/phan": "^5.4",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan-strict-rules": "^1.6",
"phpunit/phpunit": "^9",
"phpstan/phpstan-deprecation-rules": "^1.2",
"yamadashy/phpstan-friendly-formatter": "^1.1"

View File

@@ -1,16 +1,16 @@
# PHP Stan Config
includes:
- phpstan-conditional.php
#- ./vendor/yamadashy/phpstan-friendly-formatter/extension.neon
- ./vendor/yamadashy/phpstan-friendly-formatter/extension.neon
parameters:
tmpDir: %currentWorkingDirectory%/tmp/phpstan-corelibs
#errorFormat: friendly
#friendly:
# lineBefore: 3
# lineAfter: 3
errorFormat: friendly
friendly:
lineBefore: 5
lineAfter: 3
level: 8 # max is now 9
# strictRules:
# allRules: false
# allRules: true
checkMissingCallableSignature: true
treatPhpDocTypesAsCertain: false
paths:
@@ -60,6 +60,6 @@ parameters:
# paths:
# - ...
# - ...
# -
# message: "#^Call to deprecated method #"
# path: www/admin/class_test*.php
-
message: "#^Call to deprecated method #"
path: www/admin/class_test*.php

View File

@@ -1,79 +0,0 @@
<?php // phpcs:ignore PSR1.Files.SideEffects
declare(strict_types=1);
// url requests target test
require 'config.php';
use CoreLibs\Convert\Json;
$LOG_FILE_ID = 'classTest-urlrequests-target';
$log = new CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
/**
* 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::jsonConvertToArray($body);
if (Json::jsonGetLastError()) {
$body = [$body];
} else {
$body = $_body;
}
} elseif (is_string($body)) {
$body = [];
}
return Json::jsonConvertArrayTo([
'HEADERS' => $http_headers,
"REQUEST_TYPE" => $_SERVER['REQUEST_METHOD'],
"PARAMS" => $_GET,
"BODY" => $body,
// "STRING_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;
}
// 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;
}
// str_replace('\"', '"', trim($file_get, '"'));
$log->debug('SERVER', $log->prAr($_SERVER));
$log->debug('HEADERS', $log->prAr($http_headers));
$log->debug('REQUEST TYPE', $_SERVER['REQUEST_METHOD']);
$log->debug('GET', $log->prAr($_GET));
$log->debug('POST', $log->prAr($_POST));
$log->debug('PHP-INPUT', $log->prAr($file_get));
print buildContent($http_headers, $file_get);
$log->debug('[END]', '=========================================>');
// __END__

View File

@@ -19,8 +19,6 @@ $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;
@@ -31,36 +29,6 @@ $log = new CoreLibs\Logging\Logging([
]);
$color_class = 'CoreLibs\Convert\Colors';
/**
* print out a color block with info
*
* @param string $color
* @param string $text
* @param string $text_add
* @return string
*/
function display(string $color, string $text, string $text_add): string
{
$css = 'margin:5px;padding:50px;'
. 'width:10%;'
. 'text-align:center;'
. 'color:white;text-shadow: 0 0 5px black;font-weight:bold;';
$template = <<<HTML
<div style="background-color:{COLOR};{CSS}">
{TEXT}
</div>
HTML;
return str_replace(
["{COLOR}", "{TEXT}", "{CSS}"],
[
$color,
$text . (!empty($text_add) ? '<br>' . $text_add : ''),
$css
],
$template
);
}
$PAGE_NAME = 'TEST CLASS: CONVERT COLORS';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
@@ -68,82 +36,32 @@ print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
// out of bounds test
// define a list of from to color sets for conversion test
$hwb = Color::hsbToHwb(new Coordinates\HSB([
160,
0,
50,
]));
print "HWB: " . DgS::printAr($hwb) . "<br>";
$hsb = Color::hwbToHsb($hwb);
print "HSB: " . DgS::printAr($hsb) . "<br>";
$oklch = Color::rgbToOkLch(Coordinates\RGB::create([
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::create([
250,
0,
0
]));
print "OkLab: " . DgS::printAr($oklab) . "<br>";
print display($oklab->toCssString(), $oklab->toCssString(), 'Oklab');
$rgb = Color::okLabToRgb($oklab);
print "OkLab -> RGB: " . DgS::printAr($rgb) . "<br>";
print display($rgb->toCssString(), $rgb->toCssString(), 'OkLab to RGB');
$rgb = Coordinates\RGB::create([250, 100, 10])->toLinear();
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
$rgb = Coordinates\RGB::create([0, 0, 0])->toLinear();
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
$cie_lab = Color::okLabToLab($oklab);
print "CieLab: " . DgS::printAr($cie_lab) . "<br>";
print display($cie_lab->toCssString(), $cie_lab->toCssString(), 'OkLab to Cie Lab');
$rgb = Coordinates\RGB::create([0, 0, 60]);
$hsb = Color::rgbToHsb($rgb);
$rgb_b = Color::hsbToRgb($hsb);
print "RGB: " . DgS::printAr($rgb) . "<br>";
print "RGB->HSB: " . DgS::printAr($hsb) . "<br>";
print "HSB->RGB: " . DgS::printAr($rgb_b) . "<br>";
$hsl = Coordinates\HSL::create([0, 20, 0]);
$hsb = Coordinates\HSB::create([0, 20, 0]);
$hsl_from_hsb = Color::hsbToHsl($hsb);
print "HSL from HSB: " . DgS::printAr($hsl_from_hsb) . "<br>";
print "<hr>";
// A(out of bounds)
try {
print "C::S/COLOR invalid rgb->hex (gray 125): -1, -1, -1: "
. (new Coordinates\RGB([-1, -1, -1]))->returnAsHex() . "<br>";
. CoreLibs\Convert\Colors::rgb2hex(-1, -1, -1) . "<br>";
} catch (\LengthException $e) {
print "*Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
print "*Exception: " . $e->getMessage() . "<br>" . $e . "<br>";
}
try {
print "\$C::S/COLOR invalid rgb->hex (gray 125): -1, -1, -1: "
. $color_class::rgb2hex(-1, -1, -1) . "<br>";
} catch (\LengthException $e) {
print "**Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
}
print "<hr>";
print "<h2>LEGACY</h2>";
// B(valid)
$rgb = [50, 20, 30];
$rgb = [10, 20, 30];
$hex = '#0a141e';
$hsb = [210, 67, 12];
$hsb_f = [210.5, 67.5, 12.5];
$hsb = [210, 50, 7.8];
$hsl = [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->hex: $hex: " . DgS::printAr(SetVarType::setArray(
print "C::S/COLOR rgb->hext: $hex: " . DgS::printAr(SetVarType::setArray(
CoreLibs\Convert\Colors::hex2rgb($hex)
)) . "<br>";
// C(to hsb/hsl)
@@ -164,9 +82,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: $hsb[0], $hsb[1], $hsb[2]: "
print "S::COLOR hsl->rgb: $hsl[0], $hsl[1], $hsl[2]: "
. DgS::printAr(SetVarType::setArray(
Colors::hsl2rgb($hsb[0], $hsb[1], $hsb[2])
Colors::hsl2rgb($hsl[0], $hsl[1], $hsl[2])
)) . "<br>";
$hsb = [0, 0, 5];
@@ -175,26 +93,16 @@ print "S::COLOR hsb->rgb: $hsb[0], $hsb[1], $hsb[2]: "
Colors::hsb2rgb($hsb[0], $hsb[1], $hsb[2])
)) . "<br>";
print "<hr>";
// Random text
$h = rand(0, 359);
$s = rand(15, 70);
$b = 100;
$l = 50;
print "RANDOM IN: H: " . $h . ", S: " . $s . ", B/L: " . $b . "/" . $l . "<br>";
print "RANDOM hsb->rgb: <pre>"
. DgS::printAr(SetVarType::setArray(Color::hsbToRgb(new Coordinates\HSB([$h, $s, $b])))) . "</pre><br>";
print "RANDOM hsl->rgb: <pre>"
. DgS::printAr(SetVarType::setArray(Color::hslToRgb(new Coordinates\HSL([$h, $s, $l])))) . "</pre><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>";
print "<hr>";
$rgb = [0, 0, 0];
print "rgb 0,0,0: " . Dgs::printAr($rgb) . " => "
. Dgs::printAr(Color::rgbToHsb(new Coordinates\RGB([$rgb[0], $rgb[1], $rgb[2]]))) . "<br>";
print "<hr>";
// TODO: run compare check input must match output
print "</body></html>";

View File

@@ -0,0 +1,157 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
// turn on all error reporting
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
// basic class test file
define('USE_DATABASE', true);
define('DATABASE', 'sqlite' . DIRECTORY_SEPARATOR);
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-db';
ob_end_flush();
$sql_file = BASE . MEDIA . DATABASE . "class_test.db.sqlite.sq3";
use CoreLibs\DB\SqLite;
use CoreLibs\Debug\Support;
use CoreLibs\Convert\SetVarType;
$log = new CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
// db connection and attach logger
$db = new CoreLibs\DB\SqLite($log, "sqlite:" . $sql_file);
$db->log->debug('START', '=============================>');
$PAGE_NAME = 'TEST CLASS: DB: SqLite';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print "<hr>";
echo "Create Tables on demand<br>";
$query = <<<SQL
CREATE TABLE IF NOT EXISTS test (
test_id INTEGER PRIMARY KEY,
c_text TEXT,
c_integer INTEGER,
c_integer_default INTEGER DEFAULT -1,
c_bool BOOLEAN,
c_datetime TEXT,
c_datetime_microseconds TEXT,
c_datetime_default TEXT DEFAULT CURRENT_TIMESTAMP,
c_date TEXT,
c_julian REAL,
c_unixtime DATETIME,
c_unixtime_alt DATETIME,
c_numeric NUMERIC,
c_real REAL,
c_blob
)
SQL;
$db->dbExec($query);
// **********************
$query = <<<SQL
CREATE TABLE IF NOT EXISTS test_no_pk (
c_text TEXT,
c_integer INTEGER
)
SQL;
$db->dbExec($query);
print "<hr>";
$table = 'test';
echo "Table info for: " . $table . "<br>";
if (($table_info = $db->dbShowTableMetaData($table)) === false) {
print "Read problem for: $table<br>";
} else {
print "TABLE INFO: <pre>" . print_r($table_info, true) . "</pre><br>";
}
print "<hr>";
echo "Insert into 'test'<br>";
$query = <<<SQL
INSERT INTO test (
c_text, c_integer, c_bool,
c_datetime, c_datetime_microseconds, c_date,
c_julian, c_unixtime, c_unixtime_alt,
c_numeric, c_real, c_blob
) VALUES (
?, ?, ?,
?, ?, ?,
julianday(?), ?, unixepoch(?),
?, ?, ?
)
SQL;
$db->dbExecParams($query, [
'test', rand(1, 100), true,
date('Y-m-d H:i:s'), date_format(date_create("now"), 'Y-m-d H:i:s.u'), date('Y-m-d'),
// julianday pass through
date('Y-m-d H:i:s'),
// use "U" if no unixepoch in query
date('U'), date('Y-m-d H:i:s'),
1.5, 10.5, 'Anything'
]);
print "<hr>";
echo "Insert into 'test_no_pk'<br>";
$query = <<<SQL
INSERT INTO test_no_pk (
c_text, c_integer
) VALUES (
?, ?
)
SQL;
$db->dbExecParams($query, ['test no pk', rand(100, 200)]);
print "<hr>";
$query = <<<SQL
SELECT test_id, c_text, c_integer, c_integer_default, c_datetime_default
FROM test
SQL;
while (is_array($row = $db->dbReturnArray($query))) {
print "ROW: PK(test_id): " . $row["test_id"]
. ", Text: " . $row["c_text"] . ", Int: " . $row["c_integer"]
. ", Int Default: " . $row["c_integer_default"]
. ", Date Default: " . $row["c_datetime_default"]
. "<br>";
}
echo "<hr>";
$query = <<<SQL
SELECT rowid, c_text, c_integer
FROM test_no_pk
SQL;
while (is_array($row = $db->dbReturnArray($query))) {
print "ROW[CURSOR]: PK(rowid): " . $row["rowid"]
. ", Text: " . $row["c_text"] . ", Int: " . $row["c_integer"]
. "<br>";
}
print "</body></html>";
// __END__

View File

@@ -23,6 +23,7 @@ $log = new CoreLibs\Logging\Logging([
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
$_math = new CoreLibs\Convert\Math();
$math_class = 'CoreLibs\Convert\Math';
// define a list of from to color sets for conversion test
@@ -34,9 +35,13 @@ print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
print "FCEIL: " . $_math->fceil(5.1234567890, 5) . "<br>";
print "FLOORP: " . $_math->floorp(5123456, -3) . "<br>";
print "FLOORP: " . $_math->floorp(5123456, -10) . "<br>";
print "INITNUMERIC: " . $_math->initNumeric('123') . "<br>";
print "S-FCEIL: " . $math_class::fceil(5.1234567890, 5) . "<br>";
print "S-FLOORP: " . $math_class::floorp(5123456, -3) . "<br>";
print "S-FLOORP: " . $math_class::floorp(5123456, -10) . "<br>";
print "S-INITNUMERIC: " . $math_class::initNumeric(123) . "<br>";
print "S-INITNUMERIC: " . $math_class::initNumeric(123.456) . "<br>";
print "S-INITNUMERIC: " . $math_class::initNumeric('123') . "<br>";

View File

@@ -73,6 +73,7 @@ $test_files = [
'class_test.db.query-placeholder.php' => 'Class Test: DB query placeholder convert',
'class_test.db.dbReturn.php' => 'Class Test: DB dbReturn',
'class_test.db.single.php' => 'Class Test: DB single query tests',
'class_test.db.sqlite.php' => 'Class Test: DB: SqLite',
'class_test.convert.colors.php' => 'Class Test: CONVERT COLORS',
'class_test.check.colors.php' => 'Class Test: CHECK COLORS',
'class_test.mime.php' => 'Class Test: MIME',
@@ -117,7 +118,6 @@ $test_files = [
'class_test.config.direct.php' => 'Class Test: CONFIG DIRECT',
'class_test.class-calls.php' => 'Class Test: CLASS CALLS',
'class_test.error_msg.php' => 'Class Test: ERROR MSG',
'class_test.url-requests.curl.php' => 'Class Test: URL REQUESTS: CURL',
'subfolder/class_test.config.direct.php' => 'Class Test: CONFIG DIRECT SUB',
];
@@ -134,7 +134,7 @@ print "<div>READ _ENV ARRAY:</div>";
print Support::dumpVar(array_map('htmlentities', $_ENV));
// set + check edit access id
$edit_access_id = 3;
if (isset($login->loginGetAcl()['unit'])) {
if (is_object($login) && isset($login->loginGetAcl()['unit'])) {
print "ACL UNIT: " . print_r(array_keys($login->loginGetAcl()['unit']), true) . "<br>";
print "ACCESS CHECK: " . (string)$login->loginCheckEditAccess($edit_access_id) . "<br>";
if ($login->loginCheckEditAccess($edit_access_id)) {
@@ -177,23 +177,25 @@ $log->debug('SOME MARK', 'Some error output');
// INTERNAL SET
print "EDIT ACCESS ID: " . $backend->edit_access_id . "<br>";
// print "ACL: <br>".$backend->print_ar($login->loginGetAcl())."<br>";
// $log->debug('ACL', "ACL: " . \CoreLibs\Debug\Support::dumpVar($login->loginGetAcl()));
// print "DEFAULT ACL: <br>".$backend->print_ar($login->default_acl_list)."<br>";
// print "DEFAULT ACL: <br>".$backend->print_ar($login->default_acl_list)."<br>";
// $result = array_flip(
// array_filter(
// array_flip($login->default_acl_list),
// function ($key) {
// if (is_numeric($key)) {
// return $key;
// }
// }
// )
// );
// print "DEFAULT ACL: <br>".$backend->print_ar($result)."<br>";
// DEPRICATED CALL
// $backend->adbSetACL($login->loginGetAcl());
if (is_object($login)) {
// print "ACL: <br>".$backend->print_ar($login->loginGetAcl())."<br>";
// $log->debug('ACL', "ACL: " . \CoreLibs\Debug\Support::dumpVar($login->loginGetAcl()));
// print "DEFAULT ACL: <br>".$backend->print_ar($login->default_acl_list)."<br>";
// print "DEFAULT ACL: <br>".$backend->print_ar($login->default_acl_list)."<br>";
// $result = array_flip(
// array_filter(
// array_flip($login->default_acl_list),
// function ($key) {
// if (is_numeric($key)) {
// return $key;
// }
// }
// )
// );
// print "DEFAULT ACL: <br>".$backend->print_ar($result)."<br>";
// DEPRICATED CALL
// $backend->adbSetACL($login->loginGetAcl());
}
print "THIS HOST: " . HOST_NAME . ", with PROTOCOL: " . HOST_PROTOCOL . " is running SSL: " . HOST_SSL . "<br>";
print "DIR: " . DIR . "<br>";

View File

@@ -1,361 +0,0 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
ob_start();
// basic class test file
define('USE_DATABASE', false);
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-urlrequests';
ob_end_flush();
use CoreLibs\UrlRequests\Curl;
$log = new CoreLibs\Logging\Logging([
'log_folder' => BASE . LOG,
'log_file_id' => $LOG_FILE_ID,
'log_per_date' => true,
]);
$PAGE_NAME = 'TEST CLASS: URL REQUESTS CURL';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$client = new Curl();
print "<hr>";
$data = $client->get(
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=get_a',
[
'headers' => [
'test-header' => 'ABC',
'info-request-type' => '_GET',
'Funk-pop' => 'Semlly god'
],
'query' => ['foo' => 'BAR']
]
);
print "_GET RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->request(
'get',
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=get_a',
);
print "_GET RESPONSE, nothing set: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
try {
$data = $client->request(
'get',
'soba54.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=get_a',
);
print "_GET RESPONSE, nothing set, invalid URL: <pre>" . print_r($data, true) . "</pre>";
} catch (Exception $e) {
print "Exception: <pre>" . print_r($e, true) . "</pre><br>";
}
print "<hr>";
$data = $client->request(
"get",
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/'
. 'trunk/www/admin/UrlRequests.target.php'
. '?other=get_a',
[
"headers" => [
'test-header' => 'ABC',
'info-request-type' => '_GET',
'Funk-pop' => 'Semlly god'
],
"query" => ['foo' => 'BAR'],
],
);
print "[request] _GET RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->post(
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=post_a',
[
'body' => ['payload' => 'data post'],
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'test-header' => 'ABC',
'info-request-type' => '_POST',
],
'query' => ['foo' => 'BAR post'],
]
);
print "_POST RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->request(
"post",
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=post_a',
[
"body" => ['payload' => 'data post', 'request' => 'I am the request body'],
"headers" => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'test-header' => 'ABC',
'info-request-type' => '_POST',
],
"query" => ['foo' => 'BAR post'],
]
);
print "[request] _POST RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->request(
"post",
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=post_a',
[
"body" => 'string body here',
"headers" => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'test-header' => 'ABC',
'info-request-type' => '_POST',
],
"query" => ['foo' => 'BAR post'],
]
);
print "[request|string body] _POST RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->put(
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=put_a',
[
"body" => ['payload' => 'data put'],
"headers" => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'test-header' => 'ABC',
'info-request-type' => '_PUT',
],
'query' => ['foo' => 'BAR put'],
]
);
print "_PUT RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->patch(
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=patch_a',
[
"body" => ['payload' => 'data patch'],
"headers" => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'test-header' => 'ABC',
'info-request-type' => '_PATCH',
],
'query' => ['foo' => 'BAR patch'],
]
);
print "_PATCH RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->delete(
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=delete_no_body_a',
[
"body" => null,
"headers" => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'test-header' => 'ABC',
'info-request-type' => '_DELETE',
],
"query" => ['foo' => 'BAR delete'],
]
);
print "_DELETE RESPONSE: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
$data = $client->delete(
'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/UrlRequests.target.php'
. '?other=delete_body_a',
[
"body" => ['payload' => 'data delete'],
"headers" => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'test-header' => 'ABC',
'info-request-type' => '_DELETE',
],
"query" => ['foo' => 'BAR delete'],
]
);
print "_DELETE RESPONSE BODY: <pre>" . print_r($data, true) . "</pre>";
print "<hr>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/foo',
"headers" => [
'DEFAULT-master' => 'master-header',
'default-header' => 'uc-get',
'default-remove' => 'will be removed',
'default-remove-array' => ['a', 'b'],
'default-remove-array-part' => ['c', 'd'],
'default-remove-array-part-alt' => ['c', 'd', 'e'],
'default-overwrite' => 'will be overwritten',
'default-add' => 'will be added',
],
'query' => [
'global-p' => 'glob'
]
]);
print "CONFIG: <pre>" . print_r($uc->getConfig(), true) . "</pre>";
$uc->removeHeaders(['default-remove' => '']);
$uc->removeHeaders(['default-remove-array' => ['a', 'b']]);
$uc->removeHeaders(['default-remove-array-part' => 'c']);
$uc->removeHeaders(['default-remove-array-part-alt' => ['c', 'd']]);
$uc->setHeaders(['default-new' => 'Something new']);
$uc->setHeaders(['default-overwrite' => 'Something Overwritten']);
$uc->setHeaders(['default-add' => 'Something Added'], true);
print "CONFIG: <pre>" . print_r($uc->getConfig(), true) . "</pre>";
$data = $uc->request(
'get',
'UrlRequests.target.php',
[
'headers' => [
'call-header' => 'call-get',
'default-header' => 'overwrite-uc-get',
'X-Foo' => ['bar', 'baz'],
],
'query' => [
'other' => 'get_a',
],
]
);
print "[uc] _GET RESPONSE, nothing set: <pre>" . print_r($data, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
} catch (Exception $e) {
print "Exception: <pre>" . print_r(json_decode($e->getMessage(), true), true) . "</pre><br>";
}
print "<hr>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"http_errors" => false,
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
]
]);
$response = $uc->get('UrlRequests.target.php');
print "AUTH REQUEST: <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
} catch (Exception $e) {
print "Exception: <pre>" . print_r(json_decode($e->getMessage(), true), true) . "</pre><br>";
}
print "AUTH REQUEST WITH EXCEPTION:<br>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"http_errors" => true,
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
]
]);
$response = $uc->get('UrlRequests.target.php');
print "AUTH REQUEST: <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
} catch (Exception $e) {
print "Exception: <pre>" . print_r(json_decode($e->getMessage(), true), true) . "</pre><br>";
}
print "AUTH REQUEST WITH EXCEPTION (UNSET):<br>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"http_errors" => true,
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
]
]);
$response = $uc->get('UrlRequests.target.php', ['http_errors' => false]);
print "AUTH REQUEST (UNSET): <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
} catch (Exception $e) {
print "Exception: <pre>" . print_r(json_decode($e->getMessage(), true), true) . "</pre><br>";
}
print "AUTH REQUEST HEADER SET:<br>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"auth" => ["user", "pass", "basic"],
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
]
]);
$response = $uc->get('UrlRequests.target.php');
print "AUTH REQUEST (HEADER): <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
} catch (Exception $e) {
print "Exception: <pre>" . print_r(json_decode($e->getMessage(), true), true) . "</pre><br>";
}
print "<hr>";
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"headers" => [
"header-one" => "one"
]
]);
$response = $uc->get('UrlRequests.target.php', ["headers" => null, "query" => ["test" => "one-test"]]);
print "HEADER RESET REQUEST: <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
print "<hr>";
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"headers" => [
'bar' => 'foo:bar'
]
]);
$response = $uc->get('UrlRequests.target.php');
print "HEADER SET TEST REQUEST: <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
print "</body></html>";
// __END__

View File

@@ -51,7 +51,6 @@ $SITE_CONFIG = [
'soba.tequila.jp' => $__LOCAL_CONFIG,
'soba.teq.jp' => $__LOCAL_CONFIG,
'soba-local.tokyo.tequila.jp' => $__LOCAL_CONFIG,
'localhost' => $__LOCAL_CONFIG,
];
// __END__

View File

@@ -1139,6 +1139,118 @@ class Basic
// *** BETTER PASSWORD OPTIONS END ***
// *** COLORS ***
// [!!! DEPRECATED !!!]
// moved to \CoreLibs\Convert\Colors
/**
* converts a hex RGB color to the int numbers
* @param string $hexStr RGB hexstring
* @param bool $returnAsString flag to return as string
* @param string $seperator string seperator: default: ","
* @return string|array<mixed>|bool false on error or array with RGB or
* a string with the seperator
* @deprecated use \CoreLibs\Convert\Colors::hex2rgb() instead
*/
public static function hex2rgb(string $hexStr, bool $returnAsString = false, string $seperator = ',')
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Colors::hex2rgb()', E_USER_DEPRECATED);
return \CoreLibs\Convert\Colors::hex2rgb($hexStr, $returnAsString, $seperator);
}
/**
* converts the rgb values from int data to the valid rgb html hex string
* optional can turn of leading #
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @param bool $hex_prefix default true, prefix with "#"
* @return string|bool rgb in hex values with leading # if set
* @deprecated use \CoreLibs\Convert\Colors::rgb2hex() instead
*/
public static function rgb2hex(int $red, int $green, int $blue, bool $hex_prefix = true)
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Colors::rgb2hex()', E_USER_DEPRECATED);
return \CoreLibs\Convert\Colors::rgb2hex($red, $green, $blue, $hex_prefix);
}
/**
* converts and int RGB to the HTML color string in hex format
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @return string|bool hex rgb string
* @deprecated use rgb2hex instead
*/
public static function rgb2html(int $red, int $green, int $blue)
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Colors::rgb2hex()', E_USER_DEPRECATED);
// check that each color is between 0 and 255
return \CoreLibs\Convert\Colors::rgb2hex($red, $green, $blue, true);
}
/**
* converts RGB to HSB/V values
* returns:
* array with hue (0-360), sat (0-100%), brightness/value (0-100%)
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @return array<mixed>|bool Hue, Sat, Brightness/Value
* @deprecated use \CoreLibs\Convert\Colors::rgb2hsb() instead
*/
public static function rgb2hsb(int $red, int $green, int $blue)
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Colors::rgb2hsb()', E_USER_DEPRECATED);
return \CoreLibs\Convert\Colors::rgb2hsb($red, $green, $blue);
}
/**
* converts HSB/V to RGB values RGB is full INT
* @param int $H hue 0-360
* @param float $S saturation 0-1 (float)
* @param float $V brightness/value 0-1 (float)
* @return array<mixed>|bool 0 red/1 green/2 blue array
* @deprecated use \CoreLibs\Convert\Colors::hsb2rgb() instead
*/
public static function hsb2rgb(int $H, float $S, float $V)
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Colors::hsb2rgb()', E_USER_DEPRECATED);
return \CoreLibs\Convert\Colors::hsb2rgb($H, (int)round($S * 100), (int)round($V * 100));
}
/**
* converts a RGB (0-255) to HSL
* return:
* array with hue (0-360), saturation (0-100%) and luminance (0-100%)
* @param int $r red 0-255
* @param int $g green 0-255
* @param int $b blue 0-255
* @return array<mixed>|bool hue/sat/luminance
* @deprecated use \CoreLibs\Convert\Colors::rgb2hsl() instead
*/
public static function rgb2hsl(int $r, int $g, int $b)
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Colors::rgb2hsl()', E_USER_DEPRECATED);
return \CoreLibs\Convert\Colors::rgb2hsb($r, $g, $b);
}
/**
* converts an HSL to RGB
* @param int $h hue: 0-360 (degrees)
* @param float $s saturation: 0-1
* @param float $l luminance: 0-1
* @return array<mixed>|bool red/blue/green 0-255 each
* @deprecated use \CoreLibs\Convert\Colors::hsl2rgb() instead
*/
public static function hsl2rgb(int $h, float $s, float $l)
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Convert\Colors::hsl2rgb()', E_USER_DEPRECATED);
return \CoreLibs\Convert\Colors::hsl2rgb($h, $s * 100, $l * 100);
}
// *** COLORS END ***
// *** EMAIL FUNCTIONS ***
// [!!! DEPRECATED !!!]
// Moved to \CoreLibs\Check\Email

View File

@@ -1,359 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/12
* DESCRIPTION:
* CIE XYZ color space conversion
* This for various interims work
* none public
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color;
use CoreLibs\Convert\Math;
use CoreLibs\Convert\Color\Coordinates\RGB;
use CoreLibs\Convert\Color\Coordinates\Lab;
use CoreLibs\Convert\Color\Coordinates\XYZ;
class CieXyz
{
// MARK: public wrapper functions
/**
* Convert from RGB to OkLab
* via xyz D65
*
* @param RGB $rgb
* @return Lab
*/
public static function rgbViaXyzD65ToOkLab(RGB $rgb): Lab
{
return self::xyzD65ToOkLab(
self::linRgbToXyzD65($rgb)
);
}
/**
* Convet from OkLab to RGB
* via xyz D65
*
* @param Lab $lab
* @return RGB
*/
public static function okLabViaXyzD65ToRgb(Lab $lab): RGB
{
return self::xyzD65ToLinRgb(
self::okLabToXyzD65($lab)
)->fromLinear();
}
/**
* Convert RGB to CIE Lab
* via xyz D65 to xyz D50
*
* @param RGB $rgb
* @return Lab
*/
public static function rgbViaXyzD65ViaXyzD50ToLab(RGB $rgb): Lab
{
return self::xyzD50ToLab(
self::xyzD65ToXyzD50(
self::linRgbToXyzD65($rgb)
)
);
}
/**
* Convert CIE Lab to RGB
* via xyz D50 to xyz D65
*
* @param Lab $lab
* @return RGB
*/
public static function labViaXyzD50ViaXyzD65ToRgb(Lab $lab): RGB
{
return self::xyzD65ToLinRgb(
self::xyzD50ToXyxD65(
self::labToXyzD50($lab)
)
)->fromLinear();
}
/**
* Convert from oklab to cie lab
*
* @param Lab $lab
* @return Lab
*/
public static function okLabViaXyzD65ViaXyzD50ToLab(Lab $lab): Lab
{
return self::xyzD50ToLab(
self::xyzD65ToXyzD50(
self::okLabToXyzD65($lab)
)
);
}
/**
* Convert from cie lab to oklab
*
* @param Lab $lab
* @return Lab
*/
public static function labViaXyzD50ViaXyzD65ToOkLab(Lab $lab): Lab
{
return self::xyzD65ToOkLab(
self::xyzD50ToXyxD65(
self::labToXyzD50($lab)
)
);
}
// MARK: helper convert any array to array{float, float, float}
/**
* This is a hack for phpstan until we write a proper matrix to class
* conversion wrapper function
*
* @param array<array<float|int>|float|int> $_array
* @return array{0:float,1:float,2:float}
*/
private static function convertArray(array $_array): array
{
/** @var array{0:float,1:float,2:float} */
return [$_array[0], $_array[1], $_array[2]];
}
// MARK: xyzD65 <-> xyzD50
/**
* xyzD65 to xyzD50 whitepoint
*
* @param XYZ $xyz
* @return XYZ
*/
private static function xyzD65ToXyzD50(XYZ $xyz): XYZ
{
return new XYZ(self::convertArray(Math::multiplyMatrices(
a: [
[1.0479298208405488, 0.022946793341019088, -0.05019222954313557],
[0.029627815688159344, 0.990434484573249, -0.01707382502938514],
[-0.009243058152591178, 0.015055144896577895, 0.7518742899580008],
],
b: $xyz->returnAsArray(),
)), options: ["whitepoint" => 'D50']);
}
/**
* xyzD50 to xyzD65 whitepoint
*
* @param XYZ $xyz
* @return XYZ
*/
private static function xyzD50ToXyxD65(XYZ $xyz): XYZ
{
return new XYZ(self::convertArray(Math::multiplyMatrices(
a: [
[0.9554734527042182, -0.023098536874261423, 0.0632593086610217],
[-0.028369706963208136, 1.0099954580058226, 0.021041398966943008],
[0.012314001688319899, -0.020507696433477912, 1.3303659366080753],
],
b: $xyz->returnAsArray()
)), options: ["whitepoint" => 'D65']);
}
// MARK: xyzD50 <-> Lab
/**
* Convert xyzD50 to Lab (Cie)
*
* @param XYZ $xyz
* @return Lab
*/
private static function xyzD50ToLab(XYZ $xyz): Lab
{
$_xyz = $xyz->returnAsArray();
$d50 = [
0.3457 / 0.3585,
1.00000,
(1.0 - 0.3457 - 0.3585) / 0.3585,
];
$a = 216 / 24389;
$b = 24389 / 27;
$_xyz = array_map(
fn ($k, $v) => $v / $d50[$k],
array_keys($_xyz),
array_values($_xyz),
);
$f = array_map(
fn ($v) => (($v > $a) ?
pow($v, 1 / 3) :
(($b * $v + 16) / 116)
),
$_xyz,
);
return new Lab([
(116 * $f[1]) - 16,
500 * ($f[0] - $f[1]),
200 * ($f[1] - $f[2]),
], colorspace: 'CIELab');
}
/**
* Convert Lab (Cie) to xyz D50
*
* @param Lab $lab
* @return XYZ
*/
private static function labToXyzD50(Lab $lab): XYZ
{
$_lab = $lab->returnAsArray();
$a = 24389 / 27;
$b = 216 / 24389;
$f = [];
$f[1] = ($_lab[0] + 16) / 116;
$f[0] = $_lab[1] / 500 + $f[1];
$f[2] = $f[1] - $_lab[2] / 200;
$xyz = [
// x
pow($f[0], 3) > $b ?
pow($f[0], 3) :
(116 * $f[0] - 16) / $a,
// y
$_lab[0] > $a * $b ?
pow(($_lab[0] + 16) / 116, 3) :
$_lab[0] / $a,
// z
pow($f[2], 3) > $b ?
pow($f[2], 3) :
(116 * $f[2] - 16) / $a,
];
$d50 = [
0.3457 / 0.3585,
1.00000,
(1.0 - 0.3457 - 0.3585) / 0.3585,
];
return new XYZ(
self::convertArray(array_map(
fn ($k, $v) => $v * $d50[$k],
array_keys($xyz),
array_values($xyz),
)),
options: ["whitepoint" => 'D50']
);
}
// MARK: xyzD65 <-> (linear)RGB
/**
* convert linear RGB to xyz D65
* if rgb is not flagged linear, it will be auto converted
*
* @param RGB $rgb
* @return XYZ
*/
private static function linRgbToXyzD65(RGB $rgb): XYZ
{
// if not linear, convert to linear
if (!(bool)$rgb->get('linear')) {
$rgb = (new RGB($rgb->returnAsArray()))->toLinear();
}
return new XYZ(self::convertArray(Math::multiplyMatrices(
[
[0.41239079926595934, 0.357584339383878, 0.1804807884018343],
[0.21263900587151027, 0.715168678767756, 0.07219231536073371],
[0.01933081871559182, 0.11919477979462598, 0.9505321522496607],
],
$rgb->returnAsArray()
)), options: ["whitepoint" => 'D65']);
}
/**
* Convert xyz D65 to linear RGB
*
* @param XYZ $xyz
* @return RGB
*/
private static function xyzD65ToLinRgb(XYZ $xyz): RGB
{
// xyz D65 to linrgb
return new RGB(self::convertArray(Math::multiplyMatrices(
a : [
[ 3.2409699419045226, -1.537383177570094, -0.4986107602930034 ],
[ -0.9692436362808796, 1.8759675015077202, 0.04155505740717559 ],
[ 0.05563007969699366, -0.20397695888897652, 1.0569715142428786 ],
],
b : $xyz->returnAsArray()
)), options: ["linear" => true]);
}
// MARK: xyzD65 <-> OkLab
/**
* xyz D65 to OkLab
*
* @param XYZ $xyz
* @return Lab
*/
private static function xyzD65ToOkLab(XYZ $xyz): Lab
{
return new Lab(self::convertArray(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((float)$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: $xyz->returnAsArray(),
),
)
)), colorspace: 'OkLab');
}
/**
* xyz D65 to OkLab
*
* @param Lab $lab
* @return XYZ
*/
private static function okLabToXyzD65(Lab $lab): XYZ
{
return new XYZ(self::convertArray(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(),
),
),
)), options: ["whitepoint" => 'D65']);
}
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -1,190 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/11
* DESCRIPTION:
* Color Coordinate: HSB/HSV
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Utils;
class HSB implements Interface\CoordinatesInterface
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @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;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/**
* HSB (HSV) color coordinates
* Hue/Saturation/Brightness or Value
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @param array<string,string> $options [default=[]]
* @throws \InvalidArgumentException only array colors allowed
*/
public function __construct(string|array $colors, string $colorspace = 'sRGB', array $options = [])
{
if (!is_array($colors)) {
throw new \InvalidArgumentException('Only array colors allowed', 0);
}
$this->setColorspace($colorspace)->parseOptions($options)->setFromArray($colors);
}
/**
* set from array
* where 0: Hue, 1: Saturation, 2: Brightness
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @param array<string,string> $options [default=[]]
* @return self
*/
public static function create(string|array $colors, string $colorspace = 'sRGB', array $options = []): self
{
return new HSB($colors, $colorspace, $options);
}
/**
* parse options
*
* @param array<string,string> $options
* @return self
*/
private function parseOptions(array $options): self
{
return $this;
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
private 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.0) {
$value = 0;
}
// if ($value < 0 || $value > 360) {
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
1
);
}
break;
case 'S':
// if ($value < 0 || $value > 100) {
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
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) {
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
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|string|bool
{
$name = strtoupper($name);
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: 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} $colors
* @return self
*/
private function setFromArray(array $colors): self
{
$this->set('H', $colors[0]);
$this->set('S', $colors[1]);
$this->set('B', $colors[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__

View File

@@ -1,195 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/11
* DESCRIPTION:
* Color Coordinate: HSL
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Utils;
class HSL implements Interface\CoordinatesInterface
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @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;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/**
* Color Coordinate HSL
* Hue/Saturation/Lightness
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @param array<string,string> $options [default=[]]
* @throws \InvalidArgumentException only array colors allowed
*/
public function __construct(string|array $colors, string $colorspace = 'sRGB', array $options = [])
{
if (!is_array($colors)) {
throw new \InvalidArgumentException('Only array colors allowed', 0);
}
$this->setColorspace($colorspace)->parseOptions($options)->setFromArray($colors);
}
/**
* set from array
* where 0: Hue, 1: Saturation, 2: Lightness
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @param array<string,string> $options [default=[]]
* @return self
*/
public static function create(string|array $colors, string $colorspace = 'sRGB', array $options = []): self
{
return new HSL($colors, $colorspace, $options);
}
/**
* parse options
*
* @param array<string,string> $options
* @return self
*/
private function parseOptions(array $options): self
{
return $this;
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
private 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.0) {
$value = 0;
}
// if ($value < 0 || $value > 360) {
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
1
);
}
break;
case 'S':
// if ($value < 0 || $value > 100) {
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
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) {
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for lightness 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|string|bool
{
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: 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} $colors
* @return self
*/
private function setFromArray(array $colors): self
{
$this->set('H', $colors[0]);
$this->set('S', $colors[1]);
$this->set('L', $colors[2]);
return $this;
}
/**
* convert to css string with optional opacityt
*
* @param float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = 'hsl('
. $this->H
. ' '
. $this->S
. ' '
. $this->L
. Utils::setOpacity($opacity)
. ')';
return $string;
}
}
// __END__

View File

@@ -1,195 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/11
* DESCRIPTION:
* Color Coordinate: HWB
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Utils;
class HWB implements Interface\CoordinatesInterface
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @var float Hue */
private float $H = 0.0;
/** @var float Whiteness */
private float $W = 0.0;
/** @var float Blackness */
private float $B = 0.0;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/**
* Color Coordinate: HWB
* Hue/Whiteness/Blackness
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @param array<string,string> $options [default=[]]
* @throws \InvalidArgumentException only array colors allowed
*/
public function __construct(string|array $colors, string $colorspace = 'sRGB', array $options = [])
{
if (!is_array($colors)) {
throw new \InvalidArgumentException('Only array colors allowed', 0);
}
$this->setColorspace($colorspace)->parseOptions($options)->setFromArray($colors);
}
/**
* set from array
* where 0: Hue, 1: Whiteness, 2: Blackness
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @param array<string,string> $options [default=[]]
* @return self
*/
public static function create(string|array $colors, string $colorspace = 'sRGB', array $options = []): self
{
return new HWB($colors, $colorspace, $options);
}
/**
* parse options
*
* @param array<string,string> $options
* @return self
*/
private function parseOptions(array $options): self
{
return $this;
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
private 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.0) {
$value = 0;
}
// if ($value < 0 || $value > 360) {
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
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) {
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for whiteness is not in the range of 0 to 100',
2
);
}
break;
case 'B':
// if ($value < 0 || $value > 100) {
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for blackness 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|string|bool
{
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: 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} $colors
* @return self
*/
private function setFromArray(array $colors): self
{
$this->set('H', $colors[0]);
$this->set('W', $colors[1]);
$this->set('B', $colors[2]);
return $this;
}
/**
* convert to css string with optional opacityt
*
* @param float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = 'hwb('
. $this->H
. ' '
. $this->W
. ' '
. $this->B
. Utils::setOpacity($opacity)
. ')';
return $string;
}
}
// __END__

View File

@@ -1,53 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: Ymd
* DESCRIPTION:
* DescriptionHere
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates\Interface;
interface CoordinatesInterface
{
/**
* create class via "Class::create()" call
* was used for multiple create interfaces
* no longer needed, use "new Class()" instead
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default='']
* @param array<string,string|bool|int> $options [default=[]]
* @return self
*/
public static function create(string|array $colors, string $colorspace = '', array $options = []): self;
/**
* get color
*
* @param string $name
* @return float
*/
public function get(string $name): float|string|bool;
/**
* Returns the color as array
* where 0: Lightness, 1: a, 2: b
*
* @return array{0:float,1:float,2:float}
*/
public function returnAsArray(): array;
/**
* Convert into css string with optional opacity
*
* @param null|float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string;
}
// __END__

View File

@@ -1,227 +0,0 @@
<?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;
use CoreLibs\Convert\Color\Utils;
class LCH implements Interface\CoordinatesInterface
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['OkLab', 'CIELab'];
/** @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
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default='']
* @param array<string,string> $options [default=[]]
* @throws \InvalidArgumentException only array colors allowed
*/
public function __construct(string|array $colors, string $colorspace = '', array $options = [])
{
if (!is_array($colors)) {
throw new \InvalidArgumentException('Only array colors allowed', 0);
}
$this->setColorspace($colorspace)->parseOptions($options)->setFromArray($colors);
}
/**
* set from array
* where 0: Lightness, 1: Chroma, 2: Hue
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default='']
* @param array<string,string> $options [default=[]]
* @return self
*/
public static function create(string|array $colors, string $colorspace = '', array $options = []): self
{
return new LCH($colors, $colorspace, $options);
}
/**
* parse options
*
* @param array<string,string> $options
* @return self
*/
private function parseOptions(array $options): self
{
return $this;
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
private 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 == 'CIELab' && ($value < 0 || $value > 100)) {
if ($this->colorspace == 'CIELab' && Utils::compare(0.0, $value, 100.0, Utils::ESPILON_BIG)) {
throw new \LengthException(
'Argument value ' . $value . ' for lightness is not in the range of 0 to 100 for CIE Lab',
1
);
// } elseif ($this->colorspace == 'OkLab' && ($value < 0 || $value > 1)) {
} elseif ($this->colorspace == 'OkLab' && Utils::compare(0.0, $value, 1.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for lightness is not in the range of 0.0 to 1.0 for OkLab',
1
);
}
break;
case 'C':
// if ($this->colorspace == 'CIELab' && ($value < 0 || $value > 230)) {
if ($this->colorspace == 'CIELab' && Utils::compare(0.0, $value, 230.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for chroma is not in the range of '
. '0 to 150 and a maximum of 230 for CIE Lab',
1
);
// } elseif ($this->colorspace == 'OkLab' && ($value < 0 || $value > 0.55)) {
} elseif ($this->colorspace == 'OkLab' && Utils::compare(0.0, $value, 0.55, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for lightness is not in the range of '
. '0.0 to 0.4 and a maximum of 0.5 for OkLab',
1
);
}
break;
case 'H':
// if ($value < 0 || $value > 360) {
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for hue is not in the range of 0.0 to 360.0',
1
);
}
break;
}
$this->$name = $value;
}
/**
* get color
*
* @param string $name
* @return float
*/
public function get(string $name): float|string|bool
{
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: 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} $colors
* @return self
*/
private function setFromArray(array $colors): self
{
$this->set('L', $colors[0]);
$this->set('C', $colors[1]);
$this->set('H', $colors[2]);
return $this;
}
/**
* Convert into css string with optional opacity
*
* @param null|float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = '';
switch ($this->colorspace) {
case 'CIELab':
$string = 'lch';
break;
case 'OkLab':
$string = 'oklch';
break;
}
$string .= '('
. $this->L
. ' '
. $this->C
. ' '
. $this->H
. Utils::setOpacity($opacity)
. ');';
return $string;
}
}
// __END__

View File

@@ -1,233 +0,0 @@
<?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;
use CoreLibs\Convert\Color\Utils;
class Lab implements Interface\CoordinatesInterface
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['OkLab', 'CIELab'];
/** @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
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default='']
* @param array<string,string> $options [default=[]]
* @throws \InvalidArgumentException only array colors allowed
*/
public function __construct(string|array $colors, string $colorspace = '', array $options = [])
{
if (!is_array($colors)) {
throw new \InvalidArgumentException('Only array colors allowed', 0);
}
$this->setColorspace($colorspace)->parseOptions($options)->setFromArray($colors);
}
/**
* set from array
* where 0: Lightness, 1: a, 2: b
*
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace [default='']
* @param array<string,string> $options [default=[]]
* @return self
*/
public static function create(string|array $colors, string $colorspace = '', array $options = []): self
{
return new Lab($colors, $colorspace, $options);
}
/**
* parse options
*
* @param array<string,string> $options
* @return self
*/
private function parseOptions(array $options): self
{
return $this;
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
private 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 == 'CIELab' && ($value < 0 || $value > 100)) {
if ($this->colorspace == 'CIELab' && Utils::compare(0.0, $value, 100.0, Utils::ESPILON_BIG)) {
throw new \LengthException(
'Argument value ' . $value . ' for lightness is not in the range of 0 to 100 for CIE Lab',
1
);
// } elseif ($this->colorspace == 'OkLab' && ($value < 0 || $value > 1)) {
} elseif ($this->colorspace == 'OkLab' && Utils::compare(0.0, $value, 1.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for lightness is not in the range of 0.0 to 1.0 for OkLab',
1
);
}
break;
case 'a':
// if ($this->colorspace == 'CIELab' && ($value < -125 || $value > 125)) {
if ($this->colorspace == 'CIELab' && Utils::compare(-125.0, $value, 125.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for a is not in the range of -125 to 125 for CIE Lab',
2
);
// } elseif ($this->colorspace == 'OkLab' && ($value < -0.55 || $value > 0.55)) {
} elseif ($this->colorspace == 'OkLab' && Utils::compare(-0.55, $value, 0.55, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for a is not in the range of -0.5 to 0.5 for OkLab',
2
);
}
break;
case 'b':
// if ($this->colorspace == 'CIELab' && ($value < -125 || $value > 125)) {
if ($this->colorspace == 'CIELab' && Utils::compare(-125.0, $value, 125.0, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for b is not in the range of -125 to 125 for CIE Lab',
3
);
// } elseif ($this->colorspace == 'OkLab' && ($value < -0.55 || $value > 0.55)) {
} elseif ($this->colorspace == 'OkLab' && Utils::compare(-0.55, $value, 0.55, Utils::EPSILON_SMALL)) {
throw new \LengthException(
'Argument value ' . $value . ' for b is not in the range of -0.5 to 0.5 for OkLab',
3
);
}
break;
}
$this->$name = $value;
}
/**
* get color
*
* @param string $name
* @return float
*/
public function get(string $name): float|string|bool
{
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} $colors
* @return self
*/
private function setFromArray(array $colors): self
{
$this->set('L', $colors[0]);
$this->set('a', $colors[1]);
$this->set('b', $colors[2]);
return $this;
}
/**
* Convert into css string with optional opacity
*
* @param null|float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = '';
switch ($this->colorspace) {
case 'CIELab':
$string = 'lab';
break;
case 'OkLab':
$string = 'oklab';
break;
}
$string .= '('
. $this->L
. ' '
. $this->a
. ' '
. $this->b
. Utils::setOpacity($opacity)
. ');';
return $string;
}
}
// __END__

View File

@@ -1,329 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/11
* DESCRIPTION:
* Color Coordinate: RGB
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Utils;
class RGB implements Interface\CoordinatesInterface
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @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 string color space: either ok or cie */
private string $colorspace = '';
/** @var bool set if this is linear */
private bool $linear = false;
/**
* Color Coordinate RGB
* @param array{0:float,1:float,2:float}|string $colors RGB color array or hex string
* @param string $colorspace [default=sRGB]
* @param array<string,bool> $options [default=[]] only "linear" allowed at the moment
*/
public function __construct(string|array $colors, string $colorspace = 'sRGB', array $options = [])
{
$this->setColorspace($colorspace)->parseOptions($options);
if (is_array($colors)) {
$this->setFromArray($colors);
} else {
$this->setFromHex($colors);
}
}
/**
* set from array or string
* where 0: Red, 1: Green, 2: Blue
* OR #ffffff or ffffff
*
* @param array{0:float,1:float,2:float}|string $colors RGB color array or hex string
* @param string $colorspace [default=sRGB]
* @param array<string,bool> $options [default=[]] only "linear" allowed at the moment
* @return self
*/
public static function create(string|array $colors, string $colorspace = 'sRGB', array $options = []): self
{
return new RGB($colors, $colorspace, $options);
}
/**
* parse options
*
* @param array<string,bool> $options
* @return self
*/
private function parseOptions(array $options): self
{
$this->flagLinear($options['linear'] ?? false);
return $this;
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
private 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 && ((int)$value < 0 || (int)$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 < 0.0 || $value > 1.0)
$this->linear && Utils::compare(0.0, $value, 1.0, 0.000001)
) {
throw new \LengthException('Argument value ' . $value . ' for color ' . $name
. ' is not in the range of 0 to 1 for linear rgb', 2);
}
$this->$name = $value;
}
/**
* get color
*
* @param string $name
* @return float|bool
*/
public function get(string $name): float|string|bool
{
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: 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} $colors
* @return self
*/
private function setFromArray(array $colors): self
{
$this->set('R', $colors[0]);
$this->set('G', $colors[1]);
$this->set('B', $colors[2]);
return $this;
}
/**
* Return current set RGB as hex string. with or without # prefix
*
* @param bool $hex_prefix
* @return string
*/
public function returnAsHex(bool $hex_prefix = true): string
{
// prefix
$hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
}
// convert if in linear
if ($this->linear) {
$this->fromLinear();
}
foreach ($this->returnAsArray() as $color) {
$hex_color .= str_pad(dechex((int)$color), 2, '0', STR_PAD_LEFT);
}
return $hex_color;
}
/**
* set colors RGB from hex string
*
* @param string $hex_string
* @return self
*/
private function setFromHex(string $hex_string): self
{
$hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $hex_string); // Gets a proper hex string
if (empty($hex_string) || !is_string($hex_string)) {
throw new \InvalidArgumentException('hex_string argument cannot be empty', 3);
}
$rgbArray = [];
if (strlen($hex_string) == 6) {
// If a proper hex code, convert using bitwise operation.
// No overhead... faster
$colorVal = hexdec($hex_string);
$rgbArray = [
0xFF & ($colorVal >> 0x10),
0xFF & ($colorVal >> 0x8),
0xFF & $colorVal
];
} elseif (strlen($hex_string) == 3) {
// If shorthand notation, need some string manipulations
$rgbArray = [
hexdec(str_repeat(substr($hex_string, 0, 1), 2)),
hexdec(str_repeat(substr($hex_string, 1, 1), 2)),
hexdec(str_repeat(substr($hex_string, 2, 1), 2))
];
} else {
// Invalid hex color code
throw new \UnexpectedValueException('Invalid hex_string: ' . $hex_string, 4);
}
return $this->setFromArray($rgbArray);
}
/**
* 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
{
// if linear, as is
if ($this->linear) {
return $this;
}
$this->flagLinear(true)->setFromArray(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
{
// if not linear, as is
if (!$this->linear) {
return $this;
}
$this->flagLinear(false)->setFromArray(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(),
));
return $this;
}
/**
* convert to css string with optional opacity
* Note: if this is a linear RGB, the data will converted during this operation and the converted back
*
* @param float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
// if we are in linear mode, convert to normal mode temporary
$was_linear = false;
if ($this->linear) {
$this->fromLinear();
$was_linear = true;
}
$string = 'rgb('
. (int)round($this->R, 0)
. ' '
. (int)round($this->G, 0)
. ' '
. (int)round($this->B, 0)
. Utils::setOpacity($opacity)
. ')';
if ($was_linear) {
$this->toLinear();
}
return $string;
}
}
// __END__

View File

@@ -1,202 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/11
* DESCRIPTION:
* Color Coordinate: XYZ (Cie) (colorspace CIEXYZ)
* Note, this is only for the D50 & D65 whitepoint conversion
* 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#Illuminant_series_D
* https://en.wikipedia.org/wiki/Standard_illuminant#D65_values
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
// use CoreLibs\Convert\Color\Utils;
class XYZ implements Interface\CoordinatesInterface
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['CIEXYZ'];
/** @var array<string> allowed whitepoints
* D50: ICC profile PCS (horizon light) <-> CieLab
* D65: RGB color space (noon) <-> linear RGB
*/
private const ILLUMINANT = ['D50', 'D65'];
/** @var float X coordinate */
private float $X = 0.0;
/** @var float Y coordinate (Luminance) */
private float $Y = 0.0;
/** @var float Z coordinate (blue) */
private float $Z = 0.0;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/** @var string illuminat white point: only D50 and D65 are allowed */
private string $whitepoint = '';
/**
* Color Coordinate Lch
* for oklch conversion
*
* @param string|array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=CIEXYZ]
* @param array<string,string> $options [default=[]] Only "whitepoint" option allowed
* @throws \InvalidArgumentException only array colors allowed
*/
public function __construct(
string|array $colors,
string $colorspace = 'CIEXYZ',
array $options = [],
) {
if (!is_array($colors)) {
throw new \InvalidArgumentException('Only array colors allowed', 0);
}
$this->setColorspace($colorspace)
->parseOptions($options)
->setFromArray($colors);
}
/**
* set from array
* where 0: X, 1: Y, 2: Z
*
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=CIEXYZ]
* @param array<string,string> $options [default=[]] Only "whitepoint" option allowed
* @return self
*/
public static function create(
string|array $colors,
string $colorspace = 'CIEXYZ',
array $options = [],
): self {
return new XYZ($colors, $colorspace, $options);
}
/**
* parse options
*
* @param array<string,string> $options
* @return self
*/
private function parseOptions(array $options): self
{
$this->setWhitepoint($options['whitepoint'] ?? '');
return $this;
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
private function set(string $name, float $value): void
{
if (!property_exists($this, $name)) {
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
}
// TODO: setup XYZ value limits
// X: 0 to 95.047, Y: 0 to 100, Z: 0 to 108.88
// if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL))) {
// throw new \LengthException('Argument value ' . $value . ' for color ' . $name
// . ' is not in the range of 0 to 100.0', 1);
// }
$this->$name = $value;
}
/**
* get color
*
* @param string $name
* @return float
*/
public function get(string $name): float|string|bool
{
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;
}
/**
* set the whitepoint flag
*
* @param string $whitepoint
* @return self
*/
private function setWhitepoint(string $whitepoint): self
{
if (empty($whitepoint)) {
$this->whitepoint = '';
return $this;
}
if (!in_array($whitepoint, $this::ILLUMINANT)) {
throw new \InvalidArgumentException('Not allowed whitepoint', 0);
}
$this->whitepoint = $whitepoint;
return $this;
}
/**
* 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} $colors
* @return self
*/
private function setFromArray(array $colors): self
{
$this->set('X', $colors[0]);
$this->set('Y', $colors[1]);
$this->set('Z', $colors[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('XYZ is not available as CSS color string', 0);
}
}
// __END__

View File

@@ -1,35 +0,0 @@
<?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
{
/**
* return the CSS string including optional opacity
*
* @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__

View File

@@ -1,56 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/14
* DESCRIPTION:
* Utils for color
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color;
use CoreLibs\Convert\Math;
class Utils
{
/** @var float deviation allowed for valid data checks, small */
public const EPSILON_SMALL = 0.000000000001;
/** @var float deviation allowed for valid data checks, medium */
public const EPSILON_MEDIUM = 0.0000001;
/** @var float deviation allowed for valid data checks, big */
public const ESPILON_BIG = 0.0001;
public static function compare(float $lower, float $value, float $upper, float $epslion): bool
{
if (
Math::compareWithEpsilon($value, '<', $lower, $epslion) ||
Math::compareWithEpsilon($value, '>', $upper, $epslion)
) {
return true;
}
return false;
}
/**
* Build the opactiy sub string part and return it
*
* @param null|float|string|null $opacity
* @return string
*/
public static function setOpacity(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 $opacity;
}
}
// __END__

View File

@@ -17,9 +17,6 @@ declare(strict_types=1);
namespace CoreLibs\Convert;
use CoreLibs\Convert\Color\Color;
use CoreLibs\Convert\Color\Coordinates;
class Colors
{
/**
@@ -33,7 +30,6 @@ class Colors
* @param bool $hex_prefix default true, prefix with "#"
* @return string rgb in hex values with leading # if set,
* @throws \LengthException If any argument is not in the range of 0~255
* @deprecated v9.20.0 use: new Coordinates\RGB([$red, $green, $blue]))->returnAsHex(true/false for #)
*/
public static function rgb2hex(
int $red,
@@ -41,7 +37,20 @@ class Colors
int $blue,
bool $hex_prefix = true
): string {
return (new Coordinates\RGB([$red, $green, $blue]))->returnAsHex($hex_prefix);
$hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
}
foreach (['red', 'green', 'blue'] as $color) {
// if not valid, abort
if ($$color < 0 || $$color > 255) {
throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
. ' is not in the range of 0 to 255', 1);
}
// pad left with 0
$hex_color .= str_pad(dechex($$color), 2, '0', STR_PAD_LEFT);
}
return $hex_color;
}
/**
@@ -54,29 +63,32 @@ class Colors
* or a string with the seperator
* @throws \InvalidArgumentException if hex string is empty
* @throws \UnexpectedValueException if the hex string value is not valid
* @deprecated v9.20.0 use: new Coordinates\RGB($hex_string) (build string/array from return data)
*/
public static function hex2rgb(
string $hex_string,
bool $return_as_string = false,
string $seperator = ','
): string|array {
$hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $hex_string); // Gets a proper hex string
if (!is_string($hex_string)) {
throw new \InvalidArgumentException('hex_string argument cannot be empty', 1);
}
$rgbArray = [];
// rewrite to previous r/g/b key output
foreach ((new Coordinates\RGB($hex_string))->returnAsArray() as $p => $el) {
$k = '';
switch ($p) {
case 0:
$k = 'r';
break;
case 1:
$k = 'g';
break;
case 2:
$k = 'b';
break;
}
$rgbArray[$k] = (int)round($el);
if (strlen($hex_string) == 6) {
// If a proper hex code, convert using bitwise operation.
// No overhead... faster
$colorVal = hexdec($hex_string);
$rgbArray['r'] = 0xFF & ($colorVal >> 0x10);
$rgbArray['g'] = 0xFF & ($colorVal >> 0x8);
$rgbArray['b'] = 0xFF & $colorVal;
} elseif (strlen($hex_string) == 3) {
// If shorthand notation, need some string manipulations
$rgbArray['r'] = hexdec(str_repeat(substr($hex_string, 0, 1), 2));
$rgbArray['g'] = hexdec(str_repeat(substr($hex_string, 1, 1), 2));
$rgbArray['b'] = hexdec(str_repeat(substr($hex_string, 2, 1), 2));
} else {
// Invalid hex color code
throw new \UnexpectedValueException('Invalid hex_string: ' . $hex_string, 2);
}
// returns the rgb string or the associative array
return $return_as_string ? implode($seperator, $rgbArray) : $rgbArray;
@@ -93,16 +105,42 @@ class Colors
* @param int $blue blue 0-255
* @return array<int|float> Hue, Sat, Brightness/Value
* @throws \LengthException If any argument is not in the range of 0~255
* @deprecated v9.20.0 use: Color::rgbToHsb(...)->returnAsArray() will return float unrounded
*/
public static function rgb2hsb(int $red, int $green, int $blue): array
{
return array_map(
fn ($v) => (int)round($v),
Color::rgbToHsb(
new Coordinates\RGB([$red, $green, $blue])
)->returnAsArray()
);
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $color) {
if ($$color < 0 || $$color > 255) {
throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
. ' is not in the range of 0 to 255', 1);
}
$$color = $$color / 255;
}
$MAX = max($red, $green, $blue);
$MIN = min($red, $green, $blue);
$HUE = 0;
if ($MAX == $MIN) {
return [0, 0, round($MAX * 100)];
}
if ($red == $MAX) {
$HUE = ($green - $blue) / ($MAX - $MIN);
} elseif ($green == $MAX) {
$HUE = 2 + (($blue - $red) / ($MAX - $MIN));
} elseif ($blue == $MAX) {
$HUE = 4 + (($red - $green) / ($MAX - $MIN));
}
$HUE *= 60;
if ($HUE < 0) {
$HUE += 360;
}
return [
(int)round($HUE),
(int)round((($MAX - $MIN) / $MAX) * 100),
(int)round($MAX * 100)
];
}
/**
@@ -115,16 +153,80 @@ class Colors
* @param float $V brightness/value 0-100 (int)
* @return array<int> 0 red/1 green/2 blue array as 0-255
* @throws \LengthException If any argument is not in the valid range
* @deprecated v9.20.0 use: Color::hsbToRgb(...)->returnAsArray() will return float unrounded
*/
public static function hsb2rgb(float $H, float $S, float $V): array
{
return array_map(
fn ($v) => (int)round($v),
Color::hsbToRgb(
new Coordinates\HSB([$H, $S, $V])
)->returnAsArray()
);
// check that H is 0 to 359, 360 = 0
// and S and V are 0 to 1
if ($H == 360) {
$H = 0;
}
if ($H < 0 || $H > 359) {
throw new \LengthException('Argument value ' . $H . ' for hue is not in the range of 0 to 359', 1);
}
if ($S < 0 || $S > 100) {
throw new \LengthException('Argument value ' . $S . ' for saturation is not in the range of 0 to 100', 2);
}
if ($V < 0 || $V > 100) {
throw new \LengthException('Argument value ' . $V . ' for brightness is not in the range of 0 to 100', 3);
}
// convert to internal 0-1 format
$S /= 100;
$V /= 100;
if ($S == 0) {
$V = (int)round($V * 255);
return [$V, $V, $V];
}
$Hi = floor($H / 60);
$f = ($H / 60) - $Hi;
$p = $V * (1 - $S);
$q = $V * (1 - ($S * $f));
$t = $V * (1 - ($S * (1 - $f)));
switch ($Hi) {
case 0:
$red = $V;
$green = $t;
$blue = $p;
break;
case 1:
$red = $q;
$green = $V;
$blue = $p;
break;
case 2:
$red = $p;
$green = $V;
$blue = $t;
break;
case 3:
$red = $p;
$green = $q;
$blue = $V;
break;
case 4:
$red = $t;
$green = $p;
$blue = $V;
break;
case 5:
$red = $V;
$green = $p;
$blue = $q;
break;
default:
$red = 0;
$green = 0;
$blue = 0;
}
return [
(int)round($red * 255),
(int)round($green * 255),
(int)round($blue * 255)
];
}
/**
@@ -137,16 +239,50 @@ class Colors
* @param int $blue blue 0-255
* @return array<float> hue/sat/luminance
* @throws \LengthException If any argument is not in the range of 0~255
* @deprecated v9.20.0 use: Color::rgbToHsl(...)->returnAsArray() will return float unrounded
*/
public static function rgb2hsl(int $red, int $green, int $blue): array
{
return array_map(
fn ($v) => round($v, 1),
Color::rgbToHsl(
new Coordinates\RGB([$red, $green, $blue])
)->returnAsArray()
);
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $color) {
if ($$color < 0 || $$color > 255) {
throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
. ' is not in the range of 0 to 255', 1);
}
$$color = $$color / 255;
}
$min = min($red, $green, $blue);
$max = max($red, $green, $blue);
$chroma = $max - $min;
$sat = 0;
$hue = 0;
// luminance
$lum = ($max + $min) / 2;
// achromatic
if ($chroma == 0) {
// H, S, L
return [0.0, 0.0, round($lum * 100, 1)];
} else {
$sat = $chroma / (1 - abs(2 * $lum - 1));
if ($max == $red) {
$hue = fmod((($green - $blue) / $chroma), 6);
if ($hue < 0) {
$hue = (6 - fmod(abs($hue), 6));
}
} elseif ($max == $green) {
$hue = ($blue - $red) / $chroma + 2;
} elseif ($max == $blue) {
$hue = ($red - $green) / $chroma + 4;
}
$hue = $hue * 60;
// $sat = 1 - abs(2 * $lum - 1);
return [
round($hue, 1),
round($sat * 100, 1),
round($lum * 100, 1)
];
}
}
/**
@@ -158,16 +294,57 @@ class Colors
* @param float $lum luminance: 0-100
* @return array<int,float|int> red/blue/green 0-255 each
* @throws \LengthException If any argument is not in the valid range
* @deprecated v9.20.0 use: Color::hslToRgb(...)->returnAsArray() will return float unrounded
*/
public static function hsl2rgb(float $hue, float $sat, float $lum): array
{
return array_map(
fn ($v) => round($v),
Color::hslToRgb(
new Coordinates\HSL([$hue, $sat, $lum])
)->returnAsArray()
);
if ($hue == 360) {
$hue = 0;
}
if ($hue < 0 || $hue > 359) {
throw new \LengthException('Argument value ' . $hue . ' for hue is not in the range of 0 to 359', 1);
}
if ($sat < 0 || $sat > 100) {
throw new \LengthException('Argument value ' . $sat . ' for saturation is not in the range of 0 to 100', 2);
}
if ($lum < 0 || $lum > 100) {
throw new \LengthException('Argument value ' . $lum . ' for luminance is not in the range of 0 to 100', 3);
}
// calc to internal convert value for hue
$hue = (1 / 360) * $hue;
// convert to internal 0-1 format
$sat /= 100;
$lum /= 100;
// if saturation is 0
if ($sat == 0) {
$lum = (int)round($lum * 255);
return [$lum, $lum, $lum];
} else {
$m2 = $lum < 0.5 ? $lum * ($sat + 1) : ($lum + $sat) - ($lum * $sat);
$m1 = $lum * 2 - $m2;
$hueue = function ($base) use ($m1, $m2) {
// base = hue, hue > 360 (1) - 360 (1), else < 0 + 360 (1)
$base = $base < 0 ? $base + 1 : ($base > 1 ? $base - 1 : $base);
// 6: 60, 2: 180, 3: 240
// 2/3 = 240
// 1/3 = 120 (all from 360)
if ($base * 6 < 1) {
return $m1 + ($m2 - $m1) * $base * 6;
}
if ($base * 2 < 1) {
return $m2;
}
if ($base * 3 < 2) {
return $m1 + ($m2 - $m1) * ((2 / 3) - $base) * 6;
}
return $m1;
};
return [
(int)round(255 * $hueue($hue + (1 / 3))),
(int)round(255 * $hueue($hue)),
(int)round(255 * $hueue($hue - (1 / 3)))
];
}
}
}

View File

@@ -56,178 +56,6 @@ class Math
return (float)$number;
}
}
/**
* calc cube root
*
* @param float $number Number to cubic root
* @return float Calculated value
*/
public static function cbrt(float|int $number): float
{
return pow((float)$number, 1.0 / 3);
}
/**
* use PHP_FLOAT_EPSILON to compare if two float numbers are matching
*
* @param float $x
* @param float $y
* @param float $epsilon [default=PHP_FLOAT_EPSILON]
* @return bool True equal
*/
public static function equalWithEpsilon(float $x, float $y, float $epsilon = PHP_FLOAT_EPSILON): bool
{
if (abs($x - $y) < $epsilon) {
return true;
}
return false;
}
/**
* Compare two value base on direction given
* The default delta is PHP_FLOAT_EPSILON
*
* @param float $value
* @param string $compare
* @param float $limit
* @param float $epsilon [default=PHP_FLOAT_EPSILON]
* @return bool True on smaller/large or equal
*/
public static function compareWithEpsilon(
float $value,
string $compare,
float $limit,
float $epsilon = PHP_FLOAT_EPSILON
): bool {
switch ($compare) {
case '<':
if ($value < ($limit - $epsilon)) {
return true;
}
break;
case '<=':
if ($value <= ($limit - $epsilon)) {
return true;
}
break;
case '==':
return self::equalWithEpsilon($value, $limit, $epsilon);
case '>':
if ($value > ($limit + $epsilon)) {
return true;
}
break;
case '>=':
if ($value >= ($limit + $epsilon)) {
return true;
}
break;
}
return false;
}
/**
* 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:
* https://github.com/matthieumastadenis/couleur/blob/3842cf51c9517e77afaa0a36ec78643a0c258e0b/src/utils/utils.php#L507
*
* It returns an array which is the product of the two number matrices passed as parameters.
*
* NOTE:
* if the right side (B matrix) has a missing row, this row will be fillwed with 0 instead of
* throwing an error:
* A:
* [
* [1, 2, 3],
* [4, 5, 6],
* ]
* B:
* [
* [7, 8, 9],
* [10, 11, 12],
* ]
* The B will get a third row with [0, 0, 0] added to make the multiplication work as it will be
* rewritten as
* B-rewrite:
* [
* [7, 10, 0],
* [8, 11, 12],
* [0, 0, 0] <- automatically added
* ]
*
* @param array<float|int|array<int|float>> $a m x n matrice
* @param array<float|int|array<int|float>> $b n x p matrice
*
* @return array<float|int|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:
// so that we can multiply row by row
$bCols = array_map(
callback: fn ($k) => array_map(
(fn ($i) => is_array($i) ? $i[$k] : 0),
$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 * (
// if last entry missing for full copy add a 0 to it
$col[$i ?? array_search($v, $row, true)] ?? 0 /** @phpstan-ignore-line */
),
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, ...]]:
return $product[0];
}
if ($p === 1) {
// Avoid [[a], [b], [c], ...]]:
return array_map(
callback: fn ($v) => $v[0] ?? 0,
array: $product,
);
}
return $product;
}
}
// __END__

View File

@@ -0,0 +1,90 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: Ymd
* DESCRIPTION:
* DescriptionHere
*/
declare(strict_types=1);
namespace CoreLibs\DB\Interface;
interface DatabaseInterface
{
/**
* Table meta data
* Note that if columns have multi
*
* @param string $table
* @return array<array<string,mixed>>|false
*/
public function dbShowTableMetaData(string $table): array|false;
/**
* for reading or simple execution, no return data
*
* @param string $query
* @return int|false
*/
public function dbExec(string $query): int|false;
/**
* Run a simple query and return its statement
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbQuery(string $query): \PDOStatement|false;
/**
* Execute one query with params
*
* @param string $query
* @param array<mixed> $params
* @return \PDOStatement|false
*/
public function dbExecParams(string $query, array $params): \PDOStatement|false;
/**
* Prepare query
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbPrepare(string $query): \PDOStatement|false;
/**
* execute a cursor
*
* @param \PDOStatement $cursor
* @param array<mixed> $params
* @return bool
*/
public function dbCursorExecute(\PDOStatement $cursor, array $params): bool;
/**
* return array with data, when finshed return false
* also returns false on error
*
* TODO: This is currently a one time run
* if the same query needs to be run again, the cursor_ext must be reest
* with dbCacheReset
*
* @param string $query
* @param array<mixed> $params
* @return array<mixed>|false
*/
public function dbReturnArray(string $query, array $params = []): array|false;
/**
* get current db handler
* this is for raw access
*
* @return \PDO
*/
public function getDbh(): \PDO;
}
// __END__

View File

@@ -0,0 +1,432 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/8/21
* DESCRIPTION:
* SQL Lite interface
* Note: This is a very simple library and in future should perhaps merge with the master
* CoreLibs SQL interface
*
* TODO: This should move to the CoreLibs\DB\IO class as a sub type for "sqlite" next to "pgsql"
*/
declare(strict_types=1);
namespace CoreLibs\DB;
use CoreLibs\Create\Hash;
class SqLite implements Interface\DatabaseInterface
{
/** @var \CoreLibs\Logging\Logging logging */
public \CoreLibs\Logging\Logging $log;
/** @var string database connection string */
private string $dsn;
/** @var \PDO database handler */
private \PDO $dbh;
/** @var PDOStatement|false one cursor, for internal handling */
// private \PDOStatement|false $cursor;
/** @var array<string,mixed> extended cursoers string index with content */
private array $cursor_ext = [];
/**
* init database system
*
* @param \CoreLibs\Logging\Logging $log
* @param string $dsn
*/
public function __construct(
\CoreLibs\Logging\Logging $log,
string $dsn
) {
$this->log = $log;
// open new connection
if ($this->__connectToDB($dsn) === false) {
throw new \ErrorException("Cannot load database: " . $dsn, 1);
}
}
// *********************************************************************
// MARK: PRIVATE METHODS
// *********************************************************************
/**
* Get a cursor dump with all info
*
* @param \PDOStatement $cursor
* @return string|false
*/
private function __dbGetCursorDump(\PDOStatement $cursor): string|false
{
// get the cursor info
ob_start();
$cursor->debugDumpParams();
$cursor_dump = ob_get_contents();
ob_end_clean();
return $cursor_dump;
}
/**
* fetch rows from a cursor (post execute)
*
* @param \PDOStatement $cursor
* @return array<mixed>|false
*/
private function __dbFetchArray(\PDOStatement $cursor): array|false
{
try {
// on empty array return false
// TODO make that more elegant?
return empty($row = $cursor->fetch(mode:\PDO::FETCH_NAMED)) ? false : $row;
} catch (\PDOException $e) {
$this->log->error(
"Cannot fetch from cursor",
[
"dsn" => $this->dsn,
"DumpParams" => $this->__dbGetCursorDump($cursor),
"PDOException" => $e
]
);
return false;
}
}
// MARK: open database
/**
* Open database
* reports errors for wrong DSN or failed connection
*
* @param string $dsn
* @return bool
*/
private function __connectToDB(string $dsn): bool
{
// check if dsn starts with ":"
if (!str_starts_with($dsn, "sqlite:")) {
$this->log->error(
"Invalid dsn string",
[
"dsn" => $dsn
]
);
return false;
}
// TODO: if not ":memory:" check if path to file is writeable by system
// avoid double open
if (!empty($this->dsn) && $dsn == $this->dsn && $this->dbh instanceof \PDO) {
$this->log->info(
"Connection already establisehd with this dsn",
[
"dsn" => $dsn,
]
);
return true;
}
// TODO: check that folder is writeable
// set DSN and open connection
$this->dsn = $dsn;
try {
$this->dbh = new \PDO($this->dsn);
} catch (\PDOException $e) {
$this->log->error(
"Cannot open database",
[
"dsn" => $this->dsn,
"PDOException" => $e
]
);
return false;
}
return true;
}
// *********************************************************************
// MARK: PUBLIC METHODS
// *********************************************************************
// MARK: db meta data (table info)
/**
* Table meta data
* Note that if columns have multi
*
* @param string $table
* @return array<array<string,mixed>>|false
*/
public function dbShowTableMetaData(string $table): array|false
{
$table_info = [];
$query = <<<SQL
SELECT
ti.cid, ti.name, ti.type, ti.'notnull', ti.dflt_value, ti.pk,
il_ii.idx_name, il_ii.idx_unique, il_ii.idx_origin, il_ii.idx_partial
FROM
sqlite_schema AS m,
pragma_table_info(m.name) AS ti
LEFT JOIN (
SELECT
il.name AS idx_name, il.'unique' AS idx_unique, il.origin AS idx_origin, il.partial AS idx_partial,
ii.cid AS tbl_cid
FROM
sqlite_schema AS m,
pragma_index_list(m.name) AS il,
pragma_index_info(il.name) AS ii
WHERE m.name = ?1
) AS il_ii ON (ti.cid = il_ii.tbl_cid)
WHERE
m.name = ?1
SQL;
while (is_array($row = $this->dbReturnArray($query, [$table]))) {
$table_info[] = [
'cid' => $row['cid'],
'name' => $row['name'],
'type' => $row['type'],
'notnull' => $row['notnull'],
'dflt_value' => $row['dflt_value'],
'pk' => $row['pk'],
'idx_name' => $row['idx_name'],
'idx_unique' => $row['idx_unique'],
'idx_origin' => $row['idx_origin'],
'idx_partial' => $row['idx_partial'],
];
}
if (!$table_info) {
return false;
}
return $table_info;
}
// MARK: db exec
/**
* for reading or simple execution, no return data
*
* @param string $query
* @return int|false
*/
public function dbExec(string $query): int|false
{
try {
return $this->dbh->exec($query);
} catch (\PDOException $e) {
$this->log->error(
"Cannot execute query",
[
"dsn" => $this->dsn,
"query" => $query,
"PDOException" => $e
]
);
return false;
}
}
// MARK: db query
/**
* Run a simple query and return its statement
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbQuery(string $query): \PDOStatement|false
{
try {
return $this->dbh->query($query, \PDO::FETCH_NAMED);
} catch (\PDOException $e) {
$this->log->error(
"Cannot run query",
[
"dsn" => $this->dsn,
"query" => $query,
"PDOException" => $e
]
);
return false;
}
}
// MARK: db prepare & execute calls
/**
* Execute one query with params
*
* @param string $query
* @param array<mixed> $params
* @return \PDOStatement|false
*/
public function dbExecParams(string $query, array $params): \PDOStatement|false
{
// prepare query
if (($cursor = $this->dbPrepare($query)) === false) {
return false;
}
// execute the query, on failure return false
if ($this->dbCursorExecute($cursor, $params) === false) {
return false;
}
return $cursor;
}
/**
* Prepare query
*
* @param string $query
* @return \PDOStatement|false
*/
public function dbPrepare(string $query): \PDOStatement|false
{
try {
// store query with cursor so we can reference?
return $this->dbh->prepare($query);
} catch (\PDOException $e) {
$this->log->error(
"Cannot open cursor",
[
"dsn" => $this->dsn,
"query" => $query,
"PDOException" => $e
]
);
return false;
}
}
/**
* execute a cursor
*
* @param \PDOStatement $cursor
* @param array<mixed> $params
* @return bool
*/
public function dbCursorExecute(\PDOStatement $cursor, array $params): bool
{
try {
return $cursor->execute($params);
} catch (\PDOException $e) {
// write error log
$this->log->error(
"Cannot execute prepared query",
[
"dsn" => $this->dsn,
"params" => $params,
"DumpParams" => $this->__dbGetCursorDump($cursor),
"PDOException" => $e
]
);
return false;
}
}
// MARK: db return array
/**
* Returns hash for query
* Hash is used in all internal storage systems for return data
*
* @param string $query The query to create the hash from
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return string Hash, as set by hash long
*/
public function dbGetQueryHash(string $query, array $params = []): string
{
return Hash::__hashLong(
$query . (
$params !== [] ?
'#' . json_encode($params) : ''
)
);
}
/**
* resets all data stored to this query
* @param string $query The Query whose cache should be cleaned
* @param array<mixed> $params If the query is params type we need params
* data to create a unique call one, optional
* @return bool False if query not found, true if success
*/
public function dbCacheReset(string $query, array $params = []): bool
{
$query_hash = $this->dbGetQueryHash($query, $params);
// clears cache for this query
if (empty($this->cursor_ext[$query_hash]['query'])) {
$this->log->error('Cannot reset cursor_ext with given query and params', [
"query" => $query,
"params" => $params,
]);
return false;
}
unset($this->cursor_ext[$query_hash]);
return true;
}
/**
* return array with data, when finshed return false
* also returns false on error
*
* TODO: This is currently a one time run
* if the same query needs to be run again, the cursor_ext must be reest
* with dbCacheReset
*
* @param string $query
* @param array<mixed> $params
* @return array<mixed>|false
*/
public function dbReturnArray(string $query, array $params = []): array|false
{
$query_hash = $this->dbGetQueryHash($query, $params);
if (!isset($this->cursor_ext[$query_hash])) {
$this->cursor_ext[$query_hash] = [
// cursor null: unset, if set \PDOStatement
'cursor' => null,
// the query used in this call
'query' => $query,
// parameter
'params' => $params,
// how many rows have been read from db
'read_rows' => 0,
// when fetch array or cache read returns false
// in loop read that means dbReturn retuns false without error
'finished' => false,
];
if (!empty($params)) {
if (($cursor = $this->dbExecParams($query, $params)) === false) {
return false;
}
} else {
if (($cursor = $this->dbQuery($query)) === false) {
return false;
}
}
$this->cursor_ext[$query_hash]['cursor'] = $cursor;
}
// flag finished if row is false
$row = $this->__dbFetchArray($this->cursor_ext[$query_hash]['cursor']);
if ($row === false) {
$this->cursor_ext[$query_hash]['finished'] = true;
} else {
$this->cursor_ext[$query_hash]['read_rows']++;
}
return $row;
}
// MARK other interface
/**
* get current db handler
* this is for raw access
*
* @return \PDO
*/
public function getDbh(): \PDO
{
return $this->dbh;
}
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -1,128 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/10/29
* DESCRIPTION:
* Curl Client Trait for get/post/put/delete requests through the php curl inteface
*
* For anything more complex use guzzlehttp/http
* https://docs.guzzlephp.org/en/stable/index.html
*/
// phpcs:disable Generic.Files.LineLength
declare(strict_types=1);
namespace CoreLibs\UrlRequests;
trait CurlTrait
{
/**
* combined set call for any type of request with options type parameters
* The following options can be set:
* header: as array string:string
* query as string or array string:string
* body as string or array of any type
*
* @param string $type What type of request we send, will throw exception if not a valid one
* @param string $url The url to send
* @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Request options
* @return array{code:string,headers:array<string,array<string>>,content:string} [default=[]] Result code, headers and content as array, content is json
* @throws \UnexpectedValueException on missing body data when body data is needed
*/
abstract public function request(string $type, string $url, array $options = []): array;
/**
* Makes an request to the target url via curl: GET
* Returns result as string (json)
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} [default=[]] Result code, headers and content as array, content is json
*/
public function get(string $url, array $options = []): array
{
return $this->request(
"get",
$url,
$options,
);
}
/**
* Makes an request to the target url via curl: POST
* Returns result as string (json)
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
*/
public function post(string $url, array $options): array
{
return $this->request(
"post",
$url,
$options,
);
}
/**
* Makes an request to the target url via curl: PUT
* Returns result as string (json)
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
*/
public function put(string $url, array $options): array
{
return $this->request(
"put",
$url,
$options,
);
}
/**
* Makes an request to the target url via curl: PATCH
* Returns result as string (json)
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
*/
public function patch(string $url, array $options): array
{
return $this->request(
"patch",
$url,
$options,
);
}
/**
* Makes an request to the target url via curl: DELETE
* Returns result as string (json)
* Note that DELETE body is optional
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} [default=[]] Result code, headers and content as array, content is json
*/
public function delete(string $url, array $options = []): array
{
return $this->request(
"delete",
$url,
$options,
);
}
}
// __END__

View File

@@ -1,83 +0,0 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/9/20
* DESCRIPTION:
* URL Requests client interface
*/
namespace CoreLibs\UrlRequests\Interface;
interface RequestsInterface
{
/**
* get the config array with all settings
*
* @return array<string,mixed> all current config settings
*/
public function getConfig(): array;
/**
* Return the full url as it was sent
*
* @return string url sent
*/
public function getUrlSent(): string;
/**
* get the parsed url
*
* @return array{scheme?:string,user?:string,host?:string,port?:string,path?:string,query?:string,fragment?:string,pass?:string}
*/
public function getUrlParsedSent(): array;
/**
* Return the full headers as they where sent
*
* @return array<string,string>
*/
public function getHeadersSent(): array;
/**
* set, add or overwrite header
* On default this will overwrite header, and not set
*
* @param array<string,string|array<string>> $header
* @param bool $add [default=false] if set will add header to existing value
* @return void
*/
public function setHeaders(array $header, bool $add = false): void;
/**
* remove header entry
* if key is only set then match only key, if both are set both sides must match
*
* @param array<string,string> $remove_headers
* @return void
*/
public function removeHeaders(array $remove_headers): void;
/**
* Update the base url set, if empty will unset the base url
*
* @param string $base_uri
* @return void
*/
public function setBaseUri(string $base_uri): void;
/**
* combined set call for any type of request with options type parameters
*
* phpcs:disable Generic.Files.LineLength
* @param string $type
* @param string $url
* @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
* @throws \UnexpectedValueException on missing body data when body data is needed
* phpcs:enable Generic.Files.LineLength
*/
public function request(string $type, string $url, array $options = []): array;
}
// __END__