Compare commits

..

259 Commits

Author SHA1 Message Date
Clemens Schwaighofer
35cc6dbf91 Minor fixes for some calls 2024-11-18 14:52:36 +09:00
Clemens Schwaighofer
cb3d5e1f27 Matrix multiplication fixes 2024-11-18 14:44:18 +09:00
Clemens Schwaighofer
0a45300c21 fix the deprecation version for Colors class calls 2024-11-18 10:12:48 +09:00
Clemens Schwaighofer
54ce378ae2 Text fix for deprecation message 2024-11-18 10:10:39 +09:00
Clemens Schwaighofer
4ac659f7d9 Colors deprecation messages and remove Class Basic color convert calls 2024-11-18 09:50:24 +09:00
Clemens Schwaighofer
497833ca71 phpunit test updated to removal of __get 2024-11-15 19:45:36 +09:00
Clemens Schwaighofer
e5a9b149b1 phpstan fixes with move away from __get to dedicated get 2024-11-15 19:43:30 +09:00
Clemens Schwaighofer
5213805a58 phan updates 2024-11-15 18:18:45 +09:00
Clemens Schwaighofer
a9f1d878f7 Math: add epsilon compare for float, update Color Coordinate calls
Math has a compare with epsilon for float numbers.

Use this for fixing sligth color conversion issues.

NOTE: this might need some adjustment over time

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

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

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

Test phpunit not yet finished (exceptions, etc)

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

Test all and set phpstan deprecated messages

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Logic clean up for return flow

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Update all calls in connected classes.

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

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

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

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

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

instead of returning bool: false

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

update unit tests for this

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

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

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

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

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

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

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

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

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

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

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

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

Convert flags can be chagned with dbSetConvertFlag and dbUnsetConvertFlag

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

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

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

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

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

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

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

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

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

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

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

Add new SymmetricEncryption and CreateKey

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

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

Above $key must be stored in some secure location (.env file)
2023-05-24 15:47:02 +09:00
Clemens Schwaighofer
0250b86b3f Merge branch 'development' of git.tokyo.tequila.jp:E-GRAPHICS_Communications_Japan/PHP_Core_Libraries into development 2023-05-18 15:14:40 +09:00
Clemens Schwaighofer
e45acc412b Add better error reporting to DB\IO for query with params
On error with query with params the query was sent to the server and
if ther query itself is ok but there is a problem with the parameters
a wrong error message ($1 not found) will be returned

Add pg_last_error reporting to catch this too.

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

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

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

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

Other:
ACL\Login: replace EOM with HTML
config.master: replace list() with []
Add single DB tester where we can test single db calls without adding
more to the general test run
2023-05-18 15:14:29 +09:00
Clemens Schwaighofer
854206bc70 Add better error reporting to DB\IO for query with params
On error with query with params the query was sent to the server and
if ther query itself is ok but there is a problem with the parameters
a wrong error message ($1 not found) will be returned

Add pg_last_error reporting to catch this too.

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

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

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

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

Other:
ACL\Login: replace EOM with HTML
config.master: replace list() with []
Add single DB tester where we can test single db calls without adding
more to the general test run
2023-05-18 15:08:45 +09:00
Clemens Schwaighofer
b192e98a8a edit.jq.js docstring fix
not needed [ in param part
2023-05-10 08:26:40 +09:00
Clemens Schwaighofer
c4e2c781c6 Bug fix in arraySearchKey
Path was fully reset, reven if we went up just one level.
Now splice the reset off but keep everything before
2023-04-26 15:38:48 +09:00
Clemens Schwaighofer
e80b3b8dfd arraySearchKey: search for key in array and return value or path set
search in an array in any format and returns value of key if found.
Either as set with path to key, or as flat with only values.
Optional possible to prefix with the search keys to group found entries
2023-04-26 14:40:39 +09:00
Clemens Schwaighofer
2b079ff836 DB\IO: add missing debug query, clean up not needed code
in dbReturn with params on not matching param the system exited on fail
without printing the query making it hard to find where the error is.
Added debug output in case the params count is not matching.
Same move in the dbExecute call

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

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

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

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

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

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

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

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

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

Override table arrays have to be set on load
2023-02-17 13:19:59 +09:00
1860 changed files with 10411 additions and 170515 deletions

43
.eslintrc.js Normal file
View File

@@ -0,0 +1,43 @@
module.exports = {
'env': {
'browser': true,
'es6': true,
'commonjs': true,
'jquery': true
},
'extends': 'eslint:recommended',
'parserOptions': {
'ecmaVersion': 6
},
'rules': {
'indent': [
'error',
'tab',
{
'SwitchCase': 1
}
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
],
'no-console': 'off',
'no-unused-vars': [
'error', {
'vars': 'all',
'args': 'after-used',
'ignoreRestSiblings': false
}
],
// Requires eslint >= v8.14.0
'no-constant-binary-expression': 'error'
}
};

6
.gitignore vendored
View File

@@ -1,3 +1,7 @@
.libs
node_modules/
composer.lock
vendor/
.env
tools/
www/composer.lock
www/vendor

View File

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

12
.phive/phars.xml Normal file
View File

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

2
.shellcheckrc Normal file
View File

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

View File

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

View File

@@ -2,16 +2,18 @@
# read source mo files and writes target js files in object form
# check for ARG 1 is "mv"
# then move the files directly and don't do manual check (don't create temp files)
FILE_MOVE=0;
if [ "${1}" = "mv" ]; then
# check for ARG 1 is "no-move"
# then do not move the files directly for manual check
FILE_MOVE=1;
if [ "${1}" = "no-move" ]; then
echo "+++ CREATE TEMPORARY FILES +++";
FILE_MOVE=0;
else
echo "*** Direct write ***";
FILE_MOVE=1;
fi;
target='';
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
# Assume script is in 4dev/bin
base_folder="${BASE_FOLDER}../../www/";
po_folder='../4dev/locale/'
@@ -26,9 +28,9 @@ if [ "${target}" == '' ]; then
echo "*** Non smarty ***";
TEXTDOMAINDIR=${base_folder}${mo_folder}.
# default is admin
TEXTDOMAIN=admin;
TEXTDOMAIN="admin";
fi;
js_folder="layout/${TEXTDOMAIN}/javascript/";
js_folder="${TEXTDOMAIN}/layout/javascript/";
error=0;
# this checks if the TEXTDOMAIN target actually exists
@@ -44,15 +46,16 @@ if [ ${error} -eq 1 ]; then
fi;
# locale gettext po to mo translator master
for file in $(ls -1 ${base_folder}../4dev/locale/*.po); do
file=$(basename $file .po);
echo "Translate language ${file}";
for file in "${base_folder}"../4dev/locale/*.po; do
[[ -e "$file" ]] || break
file=$(basename "$file" .po);
locale=$(echo "${file}" | cut -d "-" -f 1);
domain=$(echo "${file}" | cut -d "-" -f 2);
echo "- Translate language file '${file}' for locale '${locale}' and domain '${domain}':";
if [ ! -d "${base_folder}/includes/locale/${locale}/LC_MESSAGES/" ]; then
mkdir -p "${base_folder}/includes/locale/${locale}/LC_MESSAGES/";
fi;
msgfmt -o ${base_folder}/includes/locale/${locale}/LC_MESSAGES/${domain}.mo ${base_folder}${po_folder}${locale}-${domain}.po;
msgfmt -o "${base_folder}/includes/locale/${locale}/LC_MESSAGES/${domain}.mo" "${base_folder}${po_folder}${locale}-${domain}.po";
done;
rx_msgid_empty="^msgid \"\"";
@@ -62,7 +65,7 @@ rx_msgstr="^msgstr \""
# quick copy string at the end
quick_copy='';
for language in ${language_list[*]}; do
for language in "${language_list[@]}"; do
# I don't know which one must be set, but I think at least LANGUAGE
case ${language} in
ja)
@@ -79,7 +82,8 @@ for language in ${language_list[*]}; do
esac;
# write only one for language and then symlink files
template_file=$(echo ${template_file_stump} | sed -e "s/##SUFFIX##//" | sed -e "s/##LANGUAGE##/${LANG}/");
original_file=$(echo ${template_file} | sed -e 's/\.TMP//g');
# original_file=$(echo ${template_file} | sed -e 's/\.TMP//g');
original_file=${template_file//.TMP/};
if [ "${FILE_MOVE}" -eq 0 ]; then
file=${target_folder}${template_file};
else
@@ -88,16 +92,18 @@ for language in ${language_list[*]}; do
echo "===> Write translation file ${file}";
echo ". = normal, : = escape, x = skip";
# init line [aka don't touch this file]
echo "// AUTO FILL, changes will be overwritten" > $file;
echo "// source: ${suffix}, language: ${language}" >> $file;
echo "// Translation strings in the format" >> $file;
echo "// \"Original\":\"Translated\""$'\n' >> $file;
echo "var i18n = {" >> $file;
echo "// AUTO FILL, changes will be overwritten" > "$file";
{
echo "// source: ${suffix}, language: ${language}";
echo "// Translation strings in the format";
echo "// \"Original\":\"Translated\""$'\n'
echo "var i18n = {"
} >> "$file"
# translations stuff
# read the po file
pos=0; # do we add a , for the next line
cat "${base_folder}${po_folder}${language}-${TEXTDOMAIN}.po" |
while read str; do
while read -r str; do
# echo "S: ${str}";
# skip empty
if [[ "${str}" =~ ${rx_msgid_empty} ]]; then
@@ -112,12 +118,13 @@ for language in ${language_list[*]}; do
str_source=$(echo "${str}" | sed -e "s/^msgid \"//" | sed -e "s/\"$//");
# close right side, if not last add ,
if [ "${pos}" -eq 1 ]; then
echo -n "," >> $file;
echo -n "," >> "$file";
fi;
# all " inside string need to be escaped
str_source=$(echo "${str_source}" | sed -e 's/"/\\"/g');
# str_source=$(echo "${str_source}" | sed -e 's/"/\\"/g');
str_source=${str_source//\"/\\\"}
# fix with proper layout
echo -n "\"$str_source\":\"$(TEXTDOMAINDIR=${TEXTDOMAINDIR} LANGUAGE=${language} LANG=${LANG} gettext ${TEXTDOMAIN} "${str_source}")\"" >> $file;
echo -n "\"$str_source\":\"$(TEXTDOMAINDIR=${TEXTDOMAINDIR} LANGUAGE=${language} LANG=${LANG} gettext "${TEXTDOMAIN}" "${str_source}")\"" >> "$file";
pos=1;
elif [[ "${str}" =~ ${rx_msgstr} ]]; then
# open right side (ignore)
@@ -128,8 +135,8 @@ for language in ${language_list[*]}; do
fi;
done;
echo "" >> $file;
echo "};" >> $file;
echo "" >> "$file";
echo "};" >> "$file";
echo " [DONE]";
# on no move
@@ -140,19 +147,19 @@ for language in ${language_list[*]}; do
fi;
# symlink to master file
for suffix in ${source_list[*]}; do
for suffix in "${source_list[@]}"; do
# symlink with full lang name
symlink_file[0]=$(echo ${template_file_stump} | sed -e "s/##SUFFIX##/${suffix}_/" | sed -e "s/##LANGUAGE##/${LANG}/" | sed -e 's/\.TMP//g');
# create second one with lang (no country) + encoding
symlink_file[1]=$(echo ${template_file_stump} | sed -e "s/##SUFFIX##/${suffix}_/" | sed -e "s/##LANGUAGE##/${LANGUAGE}\.${ENCODING}/" | sed -e 's/\.TMP//g');
for template_file in ${symlink_file[@]}; do
for template_file in "${symlink_file[@]}"; do
# if this is not symlink, create them
if [ ! -h "${template_file}" ]; then
echo "Create symlink: ${template_file}";
# symlik to original
cd "${target_folder}";
cd "${target_folder}" || exit;
ln -sf "${original_file}" "${template_file}";
cd - >/dev/null;
cd - >/dev/null || exit;
fi;
done;
done;

View File

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

View File

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

View File

@@ -1,37 +1,42 @@
#!/bin/env bash
base="/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/";
# -c phpunit.xml
# --testdox
# call with "t" to give verbose testdox output
# SUPPORTED: https://www.php.net/supported-versions.php
# call with 7.4, 8.0, 8.1 to force a certain php version
# call with php version number to force a certain php version
opt_testdox="";
if [ "${1}" = "t" ] || [ "${2}" = "t" ]; then
opt_testdox="--testdox";
fi;
php_bin="";
if [ ! -z "${1}" ]; then
if [ -n "${1}" ]; then
case "${1}" in
# "7.3") php_bin="/usr/bin/php7.3 "; ;;
"7.4") php_bin="/usr/bin/php7.4 "; ;;
"8.0") php_bin="/usr/bin/php8.0 "; ;;
"8.1") php_bin="/usr/bin/php8.1 "; ;;
# "7.4") php_bin="/usr/bin/php7.4 "; ;;
# "8.0") php_bin="/usr/bin/php8.0 "; ;;
# "8.1") php_bin="/usr/bin/php8.1 "; ;;
"8.2") php_bin="/usr/bin/php8.2 "; ;;
"8.3") php_bin="/usr/bin/php8.4 "; ;;
*) echo "Not support PHP: ${1}"; exit; ;;
esac;
fi;
if [ ! -z "${2}" ] && [ -z "${php_bin}" ]; then
if [ -n "${2}" ] && [ -z "${php_bin}" ]; then
case "${2}" in
# "7.3") php_bin="/usr/bin/php7.3 "; ;;
"7.4") php_bin="/usr/bin/php7.4 "; ;;
"8.0") php_bin="/usr/bin/php8.0 "; ;;
"8.1") php_bin="/usr/bin/php8.1 "; ;;
# "7.4") php_bin="/usr/bin/php7.4 "; ;;
# "8.0") php_bin="/usr/bin/php8.0 "; ;;
# "8.1") php_bin="/usr/bin/php8.1 "; ;;
"8.2") php_bin="/usr/bin/php8.2 "; ;;
"8.3") php_bin="/usr/bin/php8.3 "; ;;
*) echo "Not support PHP: ${1}"; exit; ;;
esac;
fi;
phpunit_call="${php_bin}${base}www/vendor/bin/phpunit ${opt_testdox} -c ${base}phpunit.xml ${base}4dev/tests/";
# Note 4dev/tests/bootstrap.php has to be set as bootstrap file in phpunit.xml
phpunit_call="${php_bin}${base}tools/phpunit ${opt_testdox} -c ${base}phpunit.xml ${base}4dev/tests/";
${phpunit_call};

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,622 +0,0 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2006/08/15
* VERSION: 1.0.0
* RELEASED LICENSE: GNU GPL 3
* DESCRIPTION
* Basic Admin interface backend
* - sets action flags
* - menu creation
* - array vars for smarty
*
* CHANGE PLAN:
* loads DB\IO + Logger and returns one group object
* also checks all missing CONFIG vars from Basic class
*
* PUBLIC VARIABLES
*
* PRIVATE VARIABLES
*
* PUBLIC METHODS
*
* PRIVATE METHODS
*
* HISTORY:
*
*********************************************************************/
declare(strict_types=1);
namespace CoreLibs\Admin;
class Backend
{
// page name
/** @var array<mixed> */
public $menu = [];
/** @var int|string */
public $menu_show_flag = 0; // top menu flag (mostly string)
// action ids
/** @var array<string> */
public $action_list = [
'action', 'action_id', 'action_sub_id', 'action_yes', 'action_flag',
'action_menu', 'action_value', 'action_error', 'action_loaded'
];
/** @var string */
public $action;
/** @var string|int */
public $action_id;
/** @var string|int */
public $action_sub_id;
/** @var string|int|bool */
public $action_yes;
/** @var string */
public $action_flag;
/** @var string */
public $action_menu;
/** @var string */
public $action_loaded;
/** @var string */
public $action_value;
/** @var string */
public $action_error;
// ACL array variable if we want to set acl data from outisde
/** @var array<mixed> */
public $acl = [];
/** @var int */
public $default_acl;
// queue key
/** @var string */
public $queue_key;
// the current active edit access id
/** @var int */
public $edit_access_id;
/** @var string */
public $page_name;
// error/warning/info messages
/** @var array<mixed> */
public $messages = [];
/** @var bool */
public $error = false;
/** @var bool */
public $warning = false;
/** @var bool */
public $info = false;
// internal lang & encoding vars
/** @var string */
public $lang_dir = '';
/** @var string */
public $lang;
/** @var string */
public $lang_short;
/** @var string */
public $domain;
/** @var string */
public $encoding;
/** @var \CoreLibs\Debug\Logging logger */
public $log;
/** @var \CoreLibs\DB\IO database */
public $db;
/** @var \CoreLibs\Language\L10n language */
public $l;
/** @var \CoreLibs\Create\Session session class */
public $session;
// smarty publics [end processing in smarty class]
/** @var array<mixed> */
public $DATA;
/** @var array<mixed> */
public $HEADER;
/** @var array<mixed> */
public $DEBUG_DATA;
/** @var array<mixed> */
public $CONTENT_DATA;
// CONSTRUCTOR / DECONSTRUCTOR |====================================>
/**
* main class constructor
*
* @param \CoreLibs\DB\IO $db Database connection class
* @param \CoreLibs\Debug\Logging $log Logging class
* @param \CoreLibs\Create\Session $session Session interface class
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,string> $locale locale data read from setLocale
*/
public function __construct(
\CoreLibs\DB\IO $db,
\CoreLibs\Debug\Logging $log,
\CoreLibs\Create\Session $session,
\CoreLibs\Language\L10n $l10n,
array $locale
) {
// attach db class
$this->db = $db;
// set to log not per class
$log->setLogPer('class', false);
// attach logger
$this->log = $log;
// attach session class
$this->session = $session;
// get the language sub class & init it
$this->l = $l10n;
// parse and read, legacy stuff
$this->encoding = $locale['encoding'];
$this->lang = $locale['lang'];
// get first part from lang
$this->lang_short = explode('_', $locale['lang'])[0];
$this->domain = $this->l->getDomain();
$this->lang_dir = $this->l->getBaseLocalePath();
// set the page name
$this->page_name = \CoreLibs\Get\System::getPageName();
// set the action ids
foreach ($this->action_list as $_action) {
$this->$_action = $_POST[$_action] ?? '';
}
$this->default_acl = DEFAULT_ACL_LEVEL;
// queue key
if (preg_match("/^(add|save|delete|remove|move|up|down|push_live)$/", $this->action)) {
$this->queue_key = \CoreLibs\Create\RandomKey::randomKeyGen(3);
}
}
/**
* class deconstructor
*/
public function __destruct()
{
// NO OP
}
// PUBLIC METHODS |=================================================>
/**
* set internal ACL from login ACL
*
* @param array<mixed> $acl login acl array
*/
public function setACL(array $acl): void
{
$this->acl = $acl;
}
/**
* writes all action vars plus other info into edit_log table
*
* @param string $event any kind of event description,
* @param string|array<mixed> $data any kind of data related to that event
* @param string $write_type write type can bei STRING or BINARY
* @return void
*/
public function adbEditLog(
string $event = '',
$data = '',
string $write_type = 'STRING'
): void {
$data_binary = '';
if ($write_type == 'BINARY') {
$data_binary = $this->db->dbEscapeBytea((string)bzcompress(serialize($data)));
$data = 'see bzip compressed data_binary field';
}
if ($write_type == 'STRING') {
$data_binary = '';
$data = $this->db->dbEscapeString(serialize($data));
}
// check schema
$SCHEMA = 'public';
/** @phpstan-ignore-next-line */
if (defined('LOGIN_DB_SCHEMA') && !empty(LOGIN_DB_SCHEMA)) {
$SCHEMA = LOGIN_DB_SCHEMA;
} elseif ($this->db->dbGetSchema()) {
$SCHEMA = $this->db->dbGetSchema();
} elseif (defined('PUBLIC_SCHEMA')) {
$SCHEMA = PUBLIC_SCHEMA;
}
/** @phpstan-ignore-next-line for whatever reason $SCHEMA is seen as possible array */
$q = "INSERT INTO " . $SCHEMA . ".edit_log "
. "(euid, event_date, event, data, data_binary, page, "
. "ip, user_agent, referer, script_name, query_string, server_name, http_host, "
. "http_accept, http_accept_charset, http_accept_encoding, session_id, "
. "action, action_id, action_yes, action_flag, action_menu, action_loaded, action_value, action_error) "
. "VALUES "
. "(" . $this->db->dbEscapeString(isset($_SESSION['EUID']) && is_numeric($_SESSION['EUID']) ?
$_SESSION['EUID'] :
'NULL')
. ", "
. "NOW(), "
. "'" . $this->db->dbEscapeString((string)$event) . "', '" . $data . "', "
. "'" . $data_binary . "', '" . $this->db->dbEscapeString((string)$this->page_name) . "', "
. "'" . @$_SERVER["REMOTE_ADDR"] . "', "
. "'" . $this->db->dbEscapeString(@$_SERVER['HTTP_USER_AGENT']) . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_REFERER'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['SCRIPT_FILENAME'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['QUERY_STRING'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['SERVER_NAME'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_HOST'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_ACCEPT'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_ACCEPT_CHARSET'] ?? '') . "', "
. "'" . $this->db->dbEscapeString($_SERVER['HTTP_ACCEPT_ENCODING'] ?? '') . "', "
. ($this->session->getSessionId() === false ?
"NULL" :
"'" . $this->session->getSessionId() . "'")
. ", "
. "'" . $this->db->dbEscapeString($this->action) . "', "
. "'" . $this->db->dbEscapeString($this->action_id) . "', "
. "'" . $this->db->dbEscapeString($this->action_yes) . "', "
. "'" . $this->db->dbEscapeString($this->action_flag) . "', "
. "'" . $this->db->dbEscapeString($this->action_menu) . "', "
. "'" . $this->db->dbEscapeString($this->action_loaded) . "', "
. "'" . $this->db->dbEscapeString($this->action_value) . "', "
. "'" . $this->db->dbEscapeString($this->action_error) . "')";
$this->db->dbExec($q, 'NULL');
}
/**
* Set the menu show flag
*
* @param string|int $menu_show_flag
* @return string|int
*/
public function adbSetMenuShowFlag($menu_show_flag)
{
// must be string or int
$this->menu_show_flag = $menu_show_flag;
return $this->menu_show_flag;
}
/**
* Return the menu show flag
*
* @return string|int
*/
public function adbGetMenuShowFlag()
{
return $this->menu_show_flag;
}
/**
* menu creater (from login menu session pages)
*
* @param int $flag visible flag trigger
* @return array<mixed> menu array for output on page (smarty)
*/
public function adbTopMenu(int $flag = 0): array
{
if ($this->menu_show_flag) {
$flag = $this->menu_show_flag;
}
// get the session pages array
$PAGES = $_SESSION['PAGES'] ?? null;
if (!isset($PAGES) || !is_array($PAGES)) {
$PAGES = [];
}
$pages = [];
foreach ($PAGES as $PAGE_DATA) {
$pages[] = $PAGE_DATA;
}
// $this->debug('pages', $this->print_ar($pages));
// if flag is 0, then we show all, else, we show only the matching flagges array points
// array is already sorted after correct order
reset($pages);
foreach ($pages as $data) {
// for ($i = 0, $iMax = count($pages); $i < $iMax; $i ++) {
$show = 0;
// is it visible in the menu & is it online
if ($data['menu'] && $data['online']) {
// check if it falls into our flag if we have a flag
if ($flag) {
foreach ($data['visible'] as $name => $key) {
if ($key == $flag) {
$show = 1;
}
}
} else {
// if no flag given, show all menu points
$show = 1;
}
if ($show) {
// if it is popup, write popup arrayound
if (isset($data['popup']) && $data['popup']) {
$type = 'popup';
} else {
$type = 'normal';
$data['popup'] = 0;
}
$query_string = '';
if (
isset($data['query']) &&
is_array($data['query']) &&
count($data['query'])
) {
// for ($j = 0, $jMax = count($pages[$i]['query']); $j < $jMax; $j ++) {
foreach ($data['query'] as $j => $query) {
if (
!empty($query['name']) &&
!empty($query['value'])
) {
if (strlen($query_string)) {
$query_string .= '&';
}
$query_string .= $query['name'] . '=';
if (
isset($query['dynamic']) &&
$query['dynamic']
) {
if (isset($_GET[$query['value']])) {
$query_string .= urlencode($_GET[$query['value']]);
} elseif (isset($_POST[$query['value']])) {
$query_string .= urlencode($_POST[$query['value']]);
}
} else {
$query_string .= urlencode($query['value']);
}
}
}
}
$url = '';
if (isset($data['hostname']) && $data['hostname']) {
$url .= $data['hostname'];
}
$url .= $data['filename'] ?? '';
if (strlen($query_string)) {
$url .= '?' . $query_string;
}
$name = $data['page_name'] ?? '';
// if page name matchs -> set selected flag
$selected = 0;
if (
isset($data['filename']) &&
\CoreLibs\Get\System::getPageName() == $data['filename'] &&
(!isset($data['hostname']) || (
isset($data['hostname']) &&
(!$data['hostname'] || strstr($data['hostname'], CONTENT_PATH) !== false)
))
) {
$selected = 1;
$this->page_name = $name;
}
// last check, is this menu point okay to show
$enabled = 0;
if (
isset($data['filename']) &&
$this->adbShowMenuPoint($data['filename'])
) {
$enabled = 1;
}
// write in to view menu array
array_push($this->menu, [
'name' => $this->l->__($name),
'url' => $url,
'selected' => $selected,
'enabled' => $enabled,
'popup' => $type == 'popup' ? 1 : 0,
'type' => $type
]);
} // show page
} // online and in menu
} // for each page
return $this->menu;
}
/**
* ONLY USED IN adbTopMenu
* checks if this filename is in the current situation (user id, etc) available
*
* @param string|null $filename filename
* @return bool true for visible/accessable menu point, false for not
*/
private function adbShowMenuPoint(?string $filename): bool
{
$enabled = false;
if ($filename === null) {
return $enabled;
}
/** @phan-suppress-next-line PhanNoopSwitchCases */
switch ($filename) {
default:
$enabled = true;
break;
};
return $enabled;
}
/**
* creates out of a normal db_return array an assoc array
*
* @param array<mixed> $db_array input array
* @param string|int|bool $key key
* @param string|int|bool $value value
* @return array<mixed> associative array
* @deprecated \CoreLibs\Combined\ArrayHandler::genAssocArray()
*/
public function adbAssocArray(array $db_array, $key, $value): array
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Combined\ArrayHandler::genAssocArray',
E_USER_DEPRECATED
);
return \CoreLibs\Combined\ArrayHandler::genAssocArray($db_array, $key, $value);
}
/**
* converts bytes into formated string with KB, MB, etc
*
* @param string|int|float $number string or int or number
* @return string formatted string
* @deprecated \CoreLibs\Convert\Byte::humanReadableByteFormat()
*/
public function adbByteStringFormat($number): string
{
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Convert\Byte::humanReadableByteFormat()',
E_USER_DEPRECATED
);
return \CoreLibs\Convert\Byte::humanReadableByteFormat($number);
}
/**
* converts picture to a thumbnail with max x and max y size
*
* @param string $pic source image file with or without path
* @param int $size_x maximum size width
* @param int $size_y maximum size height
* @param string $dummy empty, or file_type to show an icon
* instead of nothing if file is not found
* @param string $path if source start is not ROOT path
* if empty ROOT is choosen
* @return string|bool thumbnail name, or false for error
* @deprecated \CoreLibs\Output\Image::createThumbnail()
*/
public function adbCreateThumbnail(
string $pic,
int $size_x,
int $size_y,
string $dummy = '',
string $path = '',
string $cache = ''
) {
trigger_error(
'Method ' . __METHOD__ . ' is deprecated: \CoreLibs\Output\Image::createThumbnail()',
E_USER_DEPRECATED
);
return \CoreLibs\Output\Image::createThumbnail($pic, $size_x, $size_y, $dummy, $path, $cache);
}
/**
* wrapper function to fill up the mssages array
*
* @param string $level info/warning/error
* @param string $msg string, can be printf formated
* @param array<mixed> $vars optional data for a possible printf formated msg
* @return void has no return
*/
public function adbMsg(string $level, string $msg, array $vars = []): void
{
if (!preg_match("/^info|warning|error$/", $level)) {
$level = "info";
}
$this->messages[] = [
'msg' => vsprintf($this->l->__($msg), $vars),
'class' => $level
];
switch ($level) {
case 'info':
$this->info = true;
break;
case 'warning':
$this->warning = true;
break;
case 'error':
$this->error = true;
break;
}
}
/**
* writes live queue
*
* @param string $queue_key string to identfy the queue
* @param string $type [description]
* @param string $target [description]
* @param string $data [description]
* @param string $key_name [description]
* @param string $key_value [description]
* @param ?string $associate [description]
* @param ?string $file [description]
* @return void has no return
*/
public function adbLiveQueue(
string $queue_key,
string $type,
string $target,
string $data,
string $key_name,
string $key_value,
string $associate = null,
string $file = null
): void {
/** @phpstan-ignore-next-line */
if (defined('GLOBAL_DB_SCHEMA') && !empty(GLOBAL_DB_SCHEMA)) {
$SCHEMA = GLOBAL_DB_SCHEMA;
} elseif ($this->db->dbGetSchema()) {
$SCHEMA = $this->db->dbGetSchema();
} elseif (defined('PUBLIC_SCHEMA')) {
$SCHEMA = PUBLIC_SCHEMA;
} else {
$SCHEMA = 'public';
}
$q = "INSERT INTO " . $SCHEMA . ".live_queue ("
. "queue_key, key_value, key_name, type, target, data, group_key, action, associate, file"
. ") VALUES ("
. "'" . $this->db->dbEscapeString($queue_key) . "', '" . $this->db->dbEscapeString($key_value) . "', "
. "'" . $this->db->dbEscapeString($key_name) . "', '" . $this->db->dbEscapeString($type) . "', "
. "'" . $this->db->dbEscapeString($target) . "', '" . $this->db->dbEscapeString($data) . "', "
. "'" . $this->queue_key . "', '" . $this->action . "', "
. "'" . $this->db->dbEscapeString((string)$associate) . "', "
. "'" . $this->db->dbEscapeString((string)$file) . "')";
$this->db->dbExec($q);
}
/**
* Basic class holds exact the same, except the Year/Month/Day/etc strings
* are translated in this call
*
* @param int $year year YYYY
* @param int $month month m
* @param int $day day d
* @param int $hour hour H
* @param int $min min i
* @param string $suffix additional info printed after the date time
* variable in the drop down
* also used for ID in the on change JS call
* @param int $min_steps default is 1 (minute), can set to anything,
* is used as sum up from 0
* @param bool $name_pos_back default false, if set to true,
* the name will be printend
* after the drop down and not before the drop down
* @return string HTML formated strings for drop down lists
* of date and time
*/
public function adbPrintDateTime(
$year,
$month,
$day,
$hour,
$min,
string $suffix = '',
int $min_steps = 1,
bool $name_pos_back = false
) {
// get the build layout
$html_time = \CoreLibs\Output\Form\Elements::printDateTime(
$year,
$month,
$day,
$hour,
$min,
$suffix,
$min_steps,
$name_pos_back
);
// translate the strings inside
foreach (['Year ', 'Month ', 'Day ', 'Hour ', 'Minute '] as $_time) {
$html_time = str_replace($_time, $this->l->__(str_replace(' ', '', $_time)) . ' ', $html_time);
}
// replace week days in short
foreach (['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as $_date) {
$html_time = str_replace('(' . $_date . ')', '(' . $this->l->__($_date) . ')', $html_time);
}
// return the datetime select string with strings translated
return $html_time;
}
}
// __END__

View File

@@ -1,588 +0,0 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2023/1/6
* DESCRIPTION:
* Original created: 2003/06/10
* This is the edit_base.php data as is moved into a class so we can
* more easy update this and also move to a different AJAX style more
* easy
*********************************************************************/
declare(strict_types=1);
namespace CoreLibs\Admin;
use Exception;
use SmartyException;
class EditBase
{
/** @var array<mixed> */
private $HEADER = [];
/** @var array<mixed> */
private $DATA = [];
/** @var array<mixed> */
private $DEBUG_DATA = [];
/** @var string the template name */
private $EDIT_TEMPLATE = '';
/** @var \CoreLibs\Template\SmartyExtend smarty system */
private $smarty;
/** @var \CoreLibs\Output\Form\Generate form generate system */
private $form;
/** @var \CoreLibs\Debug\Logging */
public $log;
/**
* construct form generator
*
* @param array<mixed> $db_config db config array, mandatory
* @param \CoreLibs\Debug\Logging $log Logging class, null auto set
* @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set
* @param array<string,string> $locale locale array from ::setLocale,
* null auto set
*/
public function __construct(
array $db_config,
\CoreLibs\Debug\Logging $log,
\CoreLibs\Language\L10n $l10n,
array $locale
) {
$this->log = $log;
// smarty template engine (extended Translation version)
$this->smarty = new \CoreLibs\Template\SmartyExtend($l10n, $locale);
// turn off set log per class
$log->setLogPer('class', false);
// create form class
$this->form = new \CoreLibs\Output\Form\Generate(
$db_config,
$log,
$l10n,
$locale
);
if ($this->form->mobile_phone) {
echo "I am sorry, but this page cannot be viewed by a mobile phone";
exit;
}
// $this->form->log->debug('POST', $this->form->log->prAr($_POST));
}
/**
* edit order page
*
* @return void
*/
private function editOrderPage(): void
{
// get is for "table_name" and "where" only
$table_name = $_GET['table_name'] ?? $_POST['table_name'] ?? '';
// not in use
// $where_string = $_GET['where'] ?? $_POST['where'] ?? '';
// order name is _always_ order_number for the edit interface
// follwing arrays do exist here:
// $position ... has the positions of the [0..max], cause in a <select>
// I can't put an number into the array field, in this array,
// there are the POSITION stored,
// that should CHANGE there order (up/down)
// $row_data_id ... has ALL ids from the sorting part
// $row_data_order ... has ALL order positions from the soirting part
$position = $_POST['position'] ?? [];
$row_data_id = $_POST['row_data_id'] ?? [];
$original_id = $row_data_id;
$row_data_order = $_POST['row_data_order'] ?? [];
// direction
$up = $_POST['up'] ?? '';
$down = $_POST['down'] ?? '';
if (count($position)) {
// FIRST u have to put right sort, then read again ...
// hast to be >0 or the first one is selected and then there is no move
if (!empty($up) && isset($position[0]) && $position[0] > 0) {
for ($i = 0; $i < count($position); $i++) {
// change position order
// this gets temp, id before that, gets actual (moves one "down")
// this gets the old before (moves one "up")
// is done for every element in row
// echo "A: ".$row_data_id[$position[$i]]
// ." (".$row_data_order[$position[$i]].") -- ".$row_data_id[$position[$i]-1]
// ." (".$row_data_order[$position[$i]-1].")<br>";
$temp_id = $row_data_id[$position[$i]] ?? null;
$row_data_id[$position[$i]] = $row_data_id[(int)$position[$i] - 1] ?? null;
$row_data_id[(int)$position[$i] - 1] = $temp_id;
// echo "A: ".$row_data_id[$position[$i]]
// ." (".$row_data_order[$position[$i]].") -- "
// .$row_data_id[$position[$i]-1]." (".$row_data_order[$position[$i]-1].")<br>";
} // for
} // if up
// the last position id from position array is not to be the count - 1 of
// row_data_id array, or it is the last element
if (!empty($down) && ($position[count($position) - 1] != (count($row_data_id) - 1))) {
for ($i = count($position) - 1; $i >= 0; $i--) {
// same as up, just up in other way, starts from bottom (last element) and moves "up"
// element before actuel gets temp, this element, becomes element after this,
// element after this, gets this
$temp_id = $row_data_id[(int)$position[$i] + 1] ?? null;
$row_data_id[(int)$position[$i] + 1] = $row_data_id[$position[$i]] ?? null;
$row_data_id[$position[$i]] = $temp_id;
} // for
} // if down
// write data ... (which has to be abstrackt ...)
if (
(!empty($up) && $position[0] > 0) ||
(!empty($down) && ($position[count($position) - 1] != (count($row_data_id) - 1)))
) {
for ($i = 0; $i < count($row_data_id); $i++) {
if (isset($row_data_order[$i]) && isset($row_data_id[$i])) {
$q = "UPDATE " . $table_name
. " SET order_number = " . $row_data_order[$i]
. " WHERE " . $table_name . "_id = " . $row_data_id[$i];
$q = $this->form->dbExec($q);
}
} // for all article ids ...
} // if write
} // if there is something to move
// get ...
$q = "SELECT " . $table_name . "_id, name, order_number FROM " . $table_name . " ";
// /* if (!empty($where_string)) {
// $q .= "WHERE $where_string ";
// } */
$q .= "ORDER BY order_number";
// init arrays
$row_data = [];
$options_id = [];
$options_name = [];
$options_selected = [];
// DB read data for menu
while (is_array($res = $this->form->dbReturn($q))) {
$row_data[] = [
"id" => $res[$table_name . "_id"],
"name" => $res["name"],
"order" => $res["order_number"]
];
} // while read data ...
// html title
$this->HEADER['HTML_TITLE'] = $this->form->l->__('Edit Order');
$messages = [];
$error = $_POST['error'] ?? 0;
// error msg
if (!empty($error)) {
$msg = $_POST['msg'] ?? [];
if (!is_array($msg)) {
$msg = [];
}
$messages[] = [
'msg' => $msg,
'class' => 'error',
'width' => '100%'
];
}
$this->DATA['form_error_msg'] = $messages;
// all the row data
for ($i = 0; $i < count($row_data); $i++) {
$options_id[] = $i;
$options_name[] = $row_data[$i]['name'];
// list of points to order
for ($j = 0; $j < count($position); $j++) {
// if matches, put into select array
if (
isset($original_id[$position[$j]]) && isset($row_data[$i]['id']) &&
$original_id[$position[$j]] == $row_data[$i]['id']
) {
$options_selected[] = $i;
}
}
}
$this->DATA['options_id'] = $options_id;
$this->DATA['options_name'] = $options_name;
$this->DATA['options_selected'] = $options_selected;
// hidden list for the data (id, order number)
$row_data_id = [];
$row_data_order = [];
for ($i = 0; $i < count($row_data); $i++) {
$row_data_id[] = $row_data[$i]['id'];
$row_data_order[] = $row_data[$i]['order'];
}
$this->DATA['row_data_id'] = $row_data_id;
$this->DATA['row_data_order'] = $row_data_order;
// hidden names for the table & where string
$this->DATA['table_name'] = $table_name;
$this->DATA['where_string'] = '';
// $this->DATA['where_string'] = $where_string ?? '';
$this->EDIT_TEMPLATE = 'edit_order.tpl';
}
/**
* all edit pages
*
* @return void
*/
private function editPageFlow(): void
{
// set table width
$table_width = '100%';
// load call only if id is set
if (!empty($_POST[$this->form->archive_pk_name])) {
$this->form->formProcedureLoad($_POST[$this->form->archive_pk_name]);
}
$this->form->formProcedureNew();
$this->form->formProcedureSave();
$this->form->formProcedureDelete();
// delete call only if those two are set
// and we are not in new/save/master delete
if (
!$this->form->new &&
!$this->form->save &&
!$this->form->delete &&
!empty($_POST['element_list']) &&
!empty($_POST['remove_name'])
) {
$this->form->formProcedureDeleteFromElementList(
$_POST['element_list'],
$_POST['remove_name']
);
// run a load post element delete to not end up with empty page
$this->form->formLoadTableArray($_POST[$this->form->archive_pk_name]);
$this->form->yes = 1;
}
$this->DATA['table_width'] = $table_width;
$messages = [];
// write out error / status messages
$messages[] = $this->form->formPrintMsg();
$this->DATA['form_error_msg'] = $messages;
// MENU START
// request some session vars
if (empty($_SESSION['HEADER_COLOR'])) {
$this->DATA['HEADER_COLOR'] = '#E0E2FF';
} else {
$this->DATA['HEADER_COLOR'] = $_SESSION['HEADER_COLOR'];
}
$this->DATA['USER_NAME'] = $_SESSION['USER_NAME'];
$this->DATA['EUID'] = $_SESSION['EUID'];
$this->DATA['GROUP_NAME'] = $_SESSION['GROUP_NAME'];
$this->DATA['GROUP_LEVEL'] = $_SESSION['GROUP_ACL_LEVEL'];
$PAGES = $_SESSION['PAGES'];
//$this->form->log->debug('menu', $this->form->log->prAr($PAGES));
// build nav from $PAGES ...
if (!isset($PAGES) || !is_array($PAGES)) {
$PAGES = [];
}
$menuarray = [];
foreach ($PAGES as $PAGE_CUID => $PAGE_DATA) {
if ($PAGE_DATA['menu'] && $PAGE_DATA['online']) {
$menuarray[] = $PAGE_DATA;
}
}
// split point for nav points
$COUNT_NAV_POINTS = count($menuarray);
$SPLIT_FACTOR = 3;
$START_SPLIT_COUNT = 3;
// WTF ?? I dunno what I am doing here ...
for ($i = 9; $i < $COUNT_NAV_POINTS; $i += $START_SPLIT_COUNT) {
if ($COUNT_NAV_POINTS > $i) {
$SPLIT_FACTOR += 1;
}
}
$position = 0;
$menu_data = [];
// for ($i = 1; $i <= count($menuarray); $i ++) {
foreach ($menuarray as $i => $menu_element) {
// do that for new array
$j = $i + 1;
$menu_data[$i]['pagename'] = htmlentities($menu_element['page_name']);
$menu_data[$i]['filename'] =
// prefix folder or host name
(isset($menu_element['hostname']) && $menu_element['hostname'] ?
$menu_element['hostname'] :
''
)
// filename
. ($menu_element['filename'] ?? '')
// query string
. (isset($menu_element['query_string']) && $menu_element['query_string'] ?
$menu_element['query_string'] :
''
);
if ($j == 1 || !($i % $SPLIT_FACTOR)) {
$menu_data[$i]['splitfactor_in'] = 1;
} else {
$menu_data[$i]['splitfactor_in'] = 0;
}
// on matching, we also need to check if we are in the same folder
if (
isset($menu_element['filename']) &&
$menu_element['filename'] == \CoreLibs\Get\System::getPageName() &&
(!isset($menu_element['hostname']) || (
isset($menu_element['hostname']) &&
(!$menu_element['hostname'] || strstr($menu_element['hostname'], CONTENT_PATH) !== false)
))
) {
$position = $i;
$menu_data[$i]['position'] = 1;
$menu_data[$i]['popup'] = 0;
} else {
// add query stuff
// HAS TO DONE LATER ... set urlencode, etc ...
// check if popup needed
if (isset($menu_element['popup']) && $menu_element['popup'] == 1) {
$menu_data[$i]['popup'] = 1;
$menu_data[$i]['rand'] = uniqid((string)rand());
$menu_data[$i]['width'] = $menu_element['popup_x'];
$menu_data[$i]['height'] = $menu_element['popup_y'];
} else {
$menu_data[$i]['popup'] = 0;
}
$menu_data[$i]['position'] = 0;
} // highlight or not
if (!($j % $SPLIT_FACTOR) || (($j + 1) > count($menuarray))) {
$menu_data[$i]['splitfactor_out'] = 1;
} else {
$menu_data[$i]['splitfactor_out'] = 0;
}
} // for
// $this->form->log->debug('MENU ARRAY', $this->form->log->prAr($menu_data));
$this->DATA['menu_data'] = $menu_data;
$this->DATA['page_name'] = $menuarray[$position]['page_name'] ?? '-Undefined [' . $position . '] -';
$L_TITLE = $this->DATA['page_name'];
// html title
$this->HEADER['HTML_TITLE'] = $this->form->l->__($L_TITLE);
// END MENU
// LOAD AND NEW
$this->DATA['load'] = $this->form->formCreateLoad();
$this->DATA['new'] = $this->form->formCreateNew();
// SHOW DATA PART
if ($this->form->yes) {
$this->DATA['form_yes'] = $this->form->yes;
$this->DATA['form_my_page_name'] = $this->form->my_page_name;
$this->DATA['filename_exist'] = 0;
$this->DATA['drop_down_input'] = 0;
$elements = [];
// depending on the "getPageName()" I show different stuff
switch ($this->form->my_page_name) {
case 'edit_users':
$elements[] = $this->form->formCreateElement('login_error_count');
$elements[] = $this->form->formCreateElement('login_error_date_last');
$elements[] = $this->form->formCreateElement('login_error_date_first');
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('deleted');
$elements[] = $this->form->formCreateElement('protected');
$elements[] = $this->form->formCreateElement('username');
$elements[] = $this->form->formCreateElement('password');
$elements[] = $this->form->formCreateElement('password_change_interval');
$elements[] = $this->form->formCreateElement('login_user_id');
$elements[] = $this->form->formCreateElement('login_user_id_set_date');
$elements[] = $this->form->formCreateElement('login_user_id_last_revalidate');
$elements[] = $this->form->formCreateElement('login_user_id_locked');
$elements[] = $this->form->formCreateElement('login_user_id_revalidate_after');
$elements[] = $this->form->formCreateElement('login_user_id_valid_from');
$elements[] = $this->form->formCreateElement('login_user_id_valid_until');
$elements[] = $this->form->formCreateElement('email');
$elements[] = $this->form->formCreateElement('last_name');
$elements[] = $this->form->formCreateElement('first_name');
$elements[] = $this->form->formCreateElement('edit_group_id');
$elements[] = $this->form->formCreateElement('edit_access_right_id');
$elements[] = $this->form->formCreateElement('strict');
$elements[] = $this->form->formCreateElement('locked');
$elements[] = $this->form->formCreateElement('lock_until');
$elements[] = $this->form->formCreateElement('lock_after');
$elements[] = $this->form->formCreateElement('admin');
$elements[] = $this->form->formCreateElement('debug');
$elements[] = $this->form->formCreateElement('db_debug');
$elements[] = $this->form->formCreateElement('edit_language_id');
$elements[] = $this->form->formCreateElement('edit_scheme_id');
$elements[] = $this->form->formCreateElementListTable('edit_access_user');
$elements[] = $this->form->formCreateElement('additional_acl');
break;
case 'edit_schemes':
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('header_color');
$elements[] = $this->form->formCreateElement('template');
break;
case 'edit_pages':
if (!isset($this->form->table_array['edit_page_id']['value'])) {
$q = "DELETE FROM temp_files";
$this->form->dbExec($q);
// gets all files in the current dir and dirs given ending with .php
$folders = ['../admin/', '../frontend/'];
$files = ['*.php'];
$search_glob = [];
foreach ($folders as $folder) {
// make sure this folder actually exists
if (is_dir(ROOT . $folder)) {
foreach ($files as $file) {
$search_glob[] = $folder . $file;
}
}
}
$crap = exec('ls ' . join(' ', $search_glob), $output, $status);
// now get all that are NOT in de DB
$q = "INSERT INTO temp_files (folder, filename) VALUES ";
$t_q = '';
foreach ($output as $output_file) {
// split the ouput into folder and file
$pathinfo = pathinfo($output_file);
if (!empty($pathinfo['dirname'])) {
$pathinfo['dirname'] .= DIRECTORY_SEPARATOR;
} else {
$pathinfo['dirname'] = '';
}
if ($t_q) {
$t_q .= ', ';
}
$t_q .= "('" . $this->form->dbEscapeString($pathinfo['dirname']) . "', '"
. $this->form->dbEscapeString($pathinfo['basename']) . "')";
}
$this->form->dbExec($q . $t_q, 'NULL');
$elements[] = $this->form->formCreateElement('filename');
} else {
// show file menu
// just show name of file ...
$this->DATA['filename_exist'] = 1;
$this->DATA['filename'] = $this->form->table_array['filename']['value'];
} // File Name View IF
$elements[] = $this->form->formCreateElement('hostname');
$elements[] = $this->form->formCreateElement('name');
// $elements[] = $this->form->formCreateElement('tag');
// $elements[] = $this->form->formCreateElement('min_acl');
$elements[] = $this->form->formCreateElement('order_number');
$elements[] = $this->form->formCreateElement('online');
$elements[] = $this->form->formCreateElement('menu');
$elements[] = $this->form->formCreateElementListTable('edit_query_string');
$elements[] = $this->form->formCreateElement('content_alias_edit_page_id');
$elements[] = $this->form->formCreateElementListTable('edit_page_content');
$elements[] = $this->form->formCreateElement('popup');
$elements[] = $this->form->formCreateElement('popup_x');
$elements[] = $this->form->formCreateElement('popup_y');
$elements[] = $this->form->formCreateElementReferenceTable('edit_visible_group');
$elements[] = $this->form->formCreateElementReferenceTable('edit_menu_group');
break;
case 'edit_languages':
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('short_name');
$elements[] = $this->form->formCreateElement('long_name');
$elements[] = $this->form->formCreateElement('iso_name');
break;
case 'edit_groups':
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('edit_access_right_id');
$elements[] = $this->form->formCreateElement('edit_scheme_id');
$elements[] = $this->form->formCreateElementListTable('edit_page_access');
$elements[] = $this->form->formCreateElement('additional_acl');
break;
case 'edit_visible_group':
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('flag');
break;
case 'edit_menu_group':
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('flag');
$elements[] = $this->form->formCreateElement('order_number');
break;
case 'edit_access':
$elements[] = $this->form->formCreateElement('name');
$elements[] = $this->form->formCreateElement('enabled');
$elements[] = $this->form->formCreateElement('protected');
$elements[] = $this->form->formCreateElement('color');
$elements[] = $this->form->formCreateElement('description');
// add name/value list here
$elements[] = $this->form->formCreateElementListTable('edit_access_data');
$elements[] = $this->form->formCreateElement('additional_acl');
break;
default:
print '[No valid page definition given]';
break;
}
// $this->form->log->debug('edit', "Elements: <pre>".$this->form->log->prAr($elements));
$this->DATA['elements'] = $elements;
$this->DATA['hidden'] = $this->form->formCreateHiddenFields();
$this->DATA['save_delete'] = $this->form->formCreateSaveDelete();
} else {
$this->DATA['form_yes'] = 0;
}
$this->EDIT_TEMPLATE = 'edit_body.tpl';
}
/**
* main method that either calls edit order page method or general page
* builds the smarty content and runs smarty display output
*
* @return void
* @throws Exception
* @throws SmartyException
*/
public function editBaseRun()
{
// set the template dir
// WARNING: this has a special check for the mailing tool layout (old layout)
if (defined('LAYOUT')) {
$this->smarty->setTemplateDir(BASE . INCLUDES . TEMPLATES . CONTENT_PATH);
$this->DATA['css'] = LAYOUT . CSS;
$this->DATA['js'] = LAYOUT . JS;
} else {
$this->smarty->setTemplateDir(TEMPLATES);
$this->DATA['css'] = CSS;
$this->DATA['js'] = JS;
}
$ADMIN_STYLESHEET = 'edit.css';
// define all needed smarty stuff for the general HTML/page building
$this->HEADER['CSS'] = CSS;
$this->HEADER['DEFAULT_ENCODING'] = DEFAULT_ENCODING;
/** @phpstan-ignore-next-line because ADMIN_STYLESHEET can be null */
$this->HEADER['STYLESHEET'] = $ADMIN_STYLESHEET ?? ADMIN_STYLESHEET;
// main run
if ($this->form->my_page_name == 'edit_order') {
$this->editOrderPage();
} else {
$this->editPageFlow();
}
// debug data, if DEBUG flag is on, this data is print out
// $this->DEBUG_DATA['DEBUG'] = $DEBUG_TMPL ?? '';
$this->DEBUG_DATA['DEBUG'] = '';
// create main data array
$CONTENT_DATA = array_merge($this->HEADER, $this->DATA, $this->DEBUG_DATA);
// data is 1:1 mapping (all vars, values, etc)
foreach ($CONTENT_DATA as $key => $value) {
$this->smarty->assign($key, $value);
}
if (is_dir(BASE . TEMPLATES_C)) {
$this->smarty->setCompileDir(BASE . TEMPLATES_C);
}
if (is_dir(BASE . CACHE)) {
$this->smarty->setCacheDir(BASE . CACHE);
}
$this->smarty->display(
$this->EDIT_TEMPLATE,
'editAdmin_' . $this->smarty->lang,
'editAdmin_' . $this->smarty->lang
);
$this->form->log->debug('DEBUGEND', '==================================== [Form END]');
}
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -1,187 +0,0 @@
<?php
/*
* valid checks for css/html based colors
* # hex
* # hex + alpha
* rgb
* rgba
* hsl
* hsla
*/
declare(strict_types=1);
namespace CoreLibs\Check;
use Exception;
class Colors
{
/** @var int 1 for HEX rgb */
public const HEX_RGB = 1;
/** @var int 2 for HEX rgb with alpha */
public const HEX_RGBA = 2;
/** @var int 4 for rgb() */
public const RGB = 4;
/** @var int 8 for rgba() */
public const RGBA = 8;
/** @var int 16 for hsl() */
public const HSL = 16;
/** @var int 32 for hsla() */
public const HSLA = 32;
/** @var int 63 for all bits set (sum of above) */
public const ALL = 63;
/**
* check rgb/hsl content values in detail
* will abort and return false on first error found
*
* @param string $color html/css tring to check
* @param int|false $rgb_flag flag to check for rgb
* @param int|false $hsl_flag flag to check for hsl type
* @return bool True if no error, False if error
*/
private static function rgbHslContentCheck(string $color, $rgb_flag, $hsl_flag): bool
{
// extract string between () and split into elements
preg_match("/\((.*)\)/", $color, $matches);
if (
!is_array($color_list = preg_split("/,\s*/", $matches[1] ?? ''))
) {
throw new \Exception("Could not extract color list from rgg/hsl", 3);
}
// based on rgb/hsl settings check that entries are valid
// rgb: either 0-255 OR 0-100%
// hsl: first: 0-360
foreach ($color_list as $pos => $color_check) {
if (empty($color_check)) {
return false;
}
$percent_check = false;
if (strrpos($color_check, '%', -1) !== false) {
$percent_check = true;
$color_check = str_replace('%', '', $color_check);
}
// first three normal percent or valid number
if ($rgb_flag !== false) {
if ($percent_check === true) {
// for ALL pos
if ($color_check < 0 || $color_check > 100) {
return false;
}
} elseif (
$pos < 3 &&
($color_check < 0 || $color_check > 255)
) {
return false;
} elseif (
// RGBA set pos 3 if not percent
$pos == 3 &&
($color_check < 0 || $color_check > 1)
) {
return false;
}
} elseif ($hsl_flag !== false) {
// pos 0: 0-360
// pos 1,2: %
// pos 3: % or 0-1 (float)
if (
$pos == 0 &&
($color_check < 0 || $color_check > 360)
) {
return false;
} elseif (
// if pos 1/2 are not percent
($pos == 1 || $pos == 2) &&
($percent_check != true ||
($color_check < 0 || $color_check > 100))
) {
return false;
} elseif (
// 3 is either percent or 0~1
$pos == 3 &&
(
($percent_check == false &&
($color_check < 0 || $color_check > 1)) ||
($percent_check === true &&
($color_check < 0 || $color_check > 100))
)
) {
return false;
}
}
}
return true;
}
/**
* check if html/css color string is valid
* @param string $color A color string of any format
* @param int $flags defaults to ALL, else use | to combined from
* HEX_RGB, HEX_RGBA, RGB, RGBA, HSL, HSLA
* @return bool True if valid, False if not
* @throws Exception 1: no valid flag set
*/
public static function validateColor(string $color, int $flags = self::ALL): bool
{
// blocks for each check
$regex_blocks = [];
// set what to check
if ($flags & self::HEX_RGB) {
$regex_blocks[] = '#[\dA-Fa-f]{6}';
}
if ($flags & self::HEX_RGBA) {
$regex_blocks[] = '#[\dA-Fa-f]{8}';
}
if ($flags & self::RGB) {
$regex_blocks[] = 'rgb\(\d{1,3}%?,\s*\d{1,3}%?,\s*\d{1,3}%?\)';
}
if ($flags & self::RGBA) {
$regex_blocks[] = 'rgba\(\d{1,3}%?,\s*\d{1,3}%?,\s*\d{1,3}%?(,\s*(0\.\d{1,2}|1(\.0)?|\d{1,3}%))?\)';
}
if ($flags & self::HSL) {
$regex_blocks[] = 'hsl\(\d{1,3},\s*\d{1,3}(\.\d{1})?%,\s*\d{1,3}(\.\d{1})?%\)';
}
if ($flags & self::HSLA) {
$regex_blocks[] = 'hsla\(\d{1,3},\s*\d{1,3}(\.\d{1})?%,\s*\d{1,3}'
. '(\.\d{1})?%(,\s*(0\.\d{1,2}|1(\.0)?|\d{1,3}%))?\)';
}
// wrong flag set
if ($flags > self::ALL) {
throw new \Exception("Invalid flags parameter: $flags", 1);
}
if (!count($regex_blocks)) {
throw new \Exception("No regex blocks set: $flags", 2);
}
// build regex
$regex = '^('
. join('|', $regex_blocks)
// close regex
. ')$';
// print "C: $color, F: $flags, R: $regex\n";
if (preg_match("/$regex/", $color)) {
// if valid regex, we now need to check if the content is actually valid
// only for rgb/hsl type
/** @var int|false */
$rgb_flag = strpos($color, 'rgb');
/** @var int|false */
$hsl_flag = strpos($color, 'hsl');
// if both not match, return true
if (
$rgb_flag === false &&
$hsl_flag === false
) {
return true;
}
// run detaul rgb/hsl content check
return self::rgbHslContentCheck($color, $rgb_flag, $hsl_flag);
} else {
return false;
}
}
}
// __END__

View File

@@ -1,265 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Check;
class Email
{
// this is for error check parts in where the email regex failed
/** @var array<int,string> */
private static $email_regex_check = [
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
1 => "@(.*)@(.*)", // double @
2 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@", // wrong part before @
3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
5 => "\.([a-zA-Z]{2,6}){1}$", // wrong top level part
6 => "@(.*)\.{2,}", // double .. in domain name part
7 => "@.*\.$" // ends with a dot, top level, domain missing
];
// for above position, description string below
/** @var array<int,string> */
private static $email_regex_check_message = [
0 => 'Invalid email address',
1 => 'Double @ mark in email address',
2 => 'Invalid email part before @ sign',
3 => 'Invalid domain part after @ sign',
4 => 'Invalid domain name part',
5 => 'Wrong domain top level part',
6 => 'Double consecutive dots in domain name (..)',
7 => 'Domain ends with a dot or is missing top level part'
];
// the array with the mobile types that are valid
/** @var array<string,string> */
private static $mobile_email_type = [
'.*@docomo\.ne\.jp$' => 'keitai_docomo',
// correct are a[2-4], b2, c[1-9], e[2-9], h[2-4], t[1-9]
'.*@([a-z0-9]{2}\.)?ezweb\.ne\.jp$' => 'keitai_kddi_ezweb',
// ez[a-j] or nothing
'.*@(ez[a-j]{1}\.)?ido\.ne\.jp$' => 'keitai_kddi_ido',
// (sky group)
'.*@([a-z]{2}\.)?sky\.tu-ka\.ne\.jp$' => 'keitai_kddi_tu-ka',
// (sky group) [tkk,tkc only]
'.*@([a-z]{2}\.)?sky\.tk[kc]{1}\.ne\.jp$' => 'keitai_kddi_sky',
// dtg (sky group)
'.*@([a-z]{2}\.)?sky\.dtg\.ne\.jp$' => 'keitai_kddi_dtg',
// old vodafone [t,k,d,h,c,r,n,s,q]
'.*@[tkdhcrnsq]{1}\.vodafone\.ne\.jp$' => 'keitai_softbank_vodafone',
// very old j-phone (pre vodafone) [d,h,t,k,r,s,n,q,c]
'.*@jp-[dhtkrsnqc]{1}\.ne\.jp$' => 'keitai_softbank_j-phone',
// add i for iphone also as keitai, others similar to the vodafone group
'.*@([dhtcrknsq]{1}\.)?softbank\.ne\.jp$' => 'keitai_softbank',
// add iPhone also as keitai and not as pc
'.*@i{1}\.softbank(\.ne)?\.jp$' => 'smartphone_softbank_iphone',
'.*@disney\.ne\.jp$' => 'keitai_softbank_disney', // (kids)
'.*@willcom\.ne\.jp$' => 'keitai_willcom',
'.*@willcom\.com$' => 'keitai_willcom', // new for pdx.ne.jp address
'.*@wcm\.ne\.jp$' => 'keitai_willcom', // old willcom wcm.ne.jp
'.*@pdx\.ne\.jp$' => 'keitai_willcom_pdx', // old pdx address for willcom
'.*@bandai\.jp$' => 'keitai_willcom_bandai', // willcom paipo! (kids)
'.*@pipopa\.ne\.jp$' => 'keitai_willcom_pipopa', // willcom paipo! (kids)
// actually only di,dj,dk,wm -> all others are "wrong", but none also allowed?
'.*@([a-z0-9]{2,4}\.)?pdx\.ne\.jp$' => 'keitai_willcom_pdx',
// ymobile, ymobile1 techincally not willcom, but I group them there (softbank sub)
'.*@ymobile([1]{1})?\.ne\.jp$' => 'keitai_willcom_ymobile',
// y-mobile techincally not willcom, but I group them there (softbank sub)
'.*@y-mobile\.ne\.jp$' => 'keitai_willcom_ymobile',
'.*@emnet\.ne\.jp$' => 'keitai_willcom_emnet', // e-mobile, group will willcom
'.*@emobile\.ne\.jp$' => 'keitai_willcom_emnet', // e-mobile, group will willcom
'.*@emobile-s\.ne\.jp$' => 'keitai_willcom_emnet' # e-mobile, group will willcom
];
// short list for mobile email types
/** @var array<string,string> */
private static $mobile_email_type_short = [
'keitai_docomo' => 'docomo',
'keitai_kddi_ezweb' => 'kddi',
'keitai_kddi' => 'kddi',
'keitai_kddi_tu-ka' => 'kddi',
'keitai_kddi_sky' => 'kddi',
'keitai_softbank' => 'softbank',
'smartphone_softbank_iphone' => 'iphone',
'keitai_softbank_disney' => 'softbank',
'keitai_softbank_vodafone' => 'softbank',
'keitai_softbank_j-phone' => 'softbank',
'keitai_willcom' => 'willcom',
'keitai_willcom_pdx' => 'willcom',
'keitai_willcom_bandai' => 'willcom',
'keitai_willcom_pipopa' => 'willcom',
'keitai_willcom_ymobile' => 'willcom',
'keitai_willcom_emnet' => 'willcom',
'pc_html' => 'pc',
// old sets -> to be removed later
'docomo' => 'docomo',
'kddi_ezweb' => 'kddi',
'kddi' => 'kddi',
'kddi_tu-ka' => 'kddi',
'kddi_sky' => 'kddi',
'softbank' => 'softbank',
'keitai_softbank_iphone' => 'iphone',
'softbank_iphone' => 'iphone',
'softbank_disney' => 'softbank',
'softbank_vodafone' => 'softbank',
'softbank_j-phone' => 'softbank',
'willcom' => 'willcom',
'willcom_pdx' => 'willcom',
'willcom_bandai' => 'willcom',
'willcom_pipopa' => 'willcom',
'willcom_ymobile' => 'willcom',
'willcom_emnet' => 'willcom',
'pc' => 'pc'
];
/**
* get one position from the regex check list
*
* @param int $type Which position in the regex list to get
* if not set or not valid get default pos 0
* @return string
*/
public static function getEmailRegex(int $type = 0): string
{
// make sure type is in valid range
if ($type < 0 || $type >= count(self::$email_regex_check)) {
$type = 0;
}
return self::$email_regex_check[$type];
}
/**
* get the full check array, except position 0, but preserve keys
* Currently used to add per error level type from
* getEmailRegex to error reporting
* Might be deprecated at some point
*
* @return array<mixed>
*/
public static function getEmailRegexCheck(): array
{
// return all but the first
return array_slice(
self::$email_regex_check,
1,
count(self::$email_regex_check) - 1,
true
);
}
/**
* Returns error message for email ergex error, or empty string if not set
*
* @param int $error
* @return array<string,string|int> Error message and regex
*/
public static function getEmailRegexErrorMessage(int $error): array
{
// return error message and regex
return [
'error' => $error,
'message' => self::$email_regex_check_message[$error] ?? '',
'regex' => self::$email_regex_check[$error] ?? '',
];
}
/**
* guesses the email type (mostly for mobile) from the domain
* if second is set to true, it will return short naming scheme (only provider)
*
* @param string $email email string
* @param bool $short default false, if true,
* returns only short type (pc instead of pc_html)
* @return string|bool email type, eg "pc", "docomo", etc,
* false for invalid short type
*/
public static function getEmailType(string $email, bool $short = false)
{
// trip if there is no email address
if (!$email) {
return 'invalid';
}
// loop until we match a mobile type, return this first found type
foreach (self::$mobile_email_type as $email_regex => $email_type) {
if (preg_match("/$email_regex/", $email)) {
if ($short) {
return self::getShortEmailType($email_type);
} else {
return $email_type;
}
}
}
// if no previous return we assume this is a pc address
if ($short) {
return 'pc';
} else {
return 'pc_html';
}
}
/**
* gets the short email type from a long email type
*
* @param string $email_type email string
* @return string|bool short string or false for invalid
*/
public static function getShortEmailType(string $email_type)
{
// check if the short email type exists
if (isset(self::$mobile_email_type_short[$email_type])) {
return self::$mobile_email_type_short[$email_type];
} else {
// return false on not found
return false;
}
}
/**
* simple email check with the basic email eregex
*
* @param string $email Email address, will be checkd as lower
* @return bool True if email is ok, or false if regex failed
*/
public static function checkEmail(string $email): bool
{
$email_regex = self::getEmailRegex();
if (!preg_match("/$email_regex/", strtolower($email))) {
return false;
}
return true;
}
/**
* check email with all regex checks possible and return errors as array
*
* @param string $email Email address, will be checkd as lower
* @param bool $error_code_only If this is set to true it will only return
* the error pos, instead of detailed array
* @return array<mixed> Errors as array with message and regex
*/
public static function checkEmailFull(string $email, bool $error_code_only = false): array
{
$errors = [];
foreach (self::$email_regex_check as $pos => $email_regex) {
$match = preg_match("/$email_regex/", strtolower($email));
// if the first does not fail, quit as ok
if ($pos == 0 && $match) {
break;
}
// else do error storage
// not that for 1, 6, 7 the regex is matching
if (
(!$match && in_array($pos, [0, 2, 3, 4, 5])) ||
($match && in_array($pos, [1, 6, 7]))
) {
if ($error_code_only === true) {
$errors[] = $pos;
} else {
$errors[] = self::getEmailRegexErrorMessage($pos);
}
}
}
return $errors;
}
}
// __END__

View File

@@ -1,117 +0,0 @@
<?php
/*
* check if string is valid in target encoding
*/
declare(strict_types=1);
namespace CoreLibs\Check;
class Encoding
{
/** @var int<min, -1>|int<1, max>|string */
private static $mb_error_char = '';
/**
* set error char
*
* @param string|int|null $string The character to use to represent
* error chars
* "long" for long, "none" for none
* or a valid code point in int
* like 0x2234 (8756, ∴)
* default character is ? (63)
* if null is set then "none"
* @return void
*/
public static function setErrorChar($string): void
{
if (empty($string)) {
$string = 'none';
}
// if not special string or char but code point
if (in_array($string, ['none', 'long', 'entity'])) {
self::$mb_error_char = $string;
} else {
// always convert to char for internal use
self::$mb_error_char = \IntlChar::chr($string);
// if string convert to code point
if (is_string($string)) {
$string = \IntlChar::ord($string);
}
}
mb_substitute_character($string);
}
/**
* get the current set error character
*
* @param bool $return_substitute_func if set to true return the set
* character from the php function
* directly
* @return string|int Set error character
*/
public static function getErrorChar(bool $return_substitute_func = false)
{
// return mb_substitute_character();
if ($return_substitute_func === true) {
return mb_substitute_character();
} else {
return self::$mb_error_char;
}
}
/**
* test if a string can be safely convert between encodings.
* mostly utf8 to shift jis
* the default compare has a possibility of failure, especially with windows
* it is recommended to the following in the script which uses this method:
* mb_substitute_character(0x2234);
* $class->mb_error_char = '∴';
* if check to Shift JIS
* if check to ISO-2022-JP
* if check to ISO-2022-JP-MS
* set three dots (∴) as wrong character for correct convert error detect
* (this char is used, because it is one of the least used ones)
*
* @param string $string string to test
* @param string $from_encoding encoding of string to test
* @param string $to_encoding target encoding
* @return bool|array<string> false if no error or
* array with failed characters
*/
public static function checkConvertEncoding(
string $string,
string $from_encoding,
string $to_encoding
) {
// convert to target encoding and convert back
$temp = mb_convert_encoding($string, $to_encoding, $from_encoding);
$compare = mb_convert_encoding($temp, $from_encoding, $to_encoding);
// if string does not match anymore we have a convert problem
if ($string != $compare) {
$failed = [];
// go through each character and find the ones that do not match
for ($i = 0, $iMax = mb_strlen($string, $from_encoding); $i < $iMax; $i++) {
$char = mb_substr($string, $i, 1, $from_encoding);
$r_char = mb_substr($compare, $i, 1, $from_encoding);
// the ord 194 is a hack to fix the IE7/IE8
// bug with line break and illegal character
if (
(($char != $r_char && (!self::$mb_error_char ||
in_array(self::$mb_error_char, ['none', 'long', 'entity']))) ||
($char != $r_char && $r_char == self::$mb_error_char && self::$mb_error_char)) &&
ord($char) != 194
) {
$failed[] = $char;
}
}
return $failed;
} else {
return false;
}
}
}
// __END__

View File

@@ -1,56 +0,0 @@
<?php
/*
* various file/file name functions
*/
declare(strict_types=1);
namespace CoreLibs\Check;
class File
{
/**
* quick return the extension of the given file name
*
* @param string $filename file name
* @return string extension of the file name
*/
public static function getFilenameEnding(string $filename): string
{
$page_temp = pathinfo($filename);
return $page_temp['extension'] ?? '';
}
/**
* get lines in a file
*
* @param string $file file for line count read
* @return int number of lines or -1 for non readable file
*/
public static function getLinesFromFile(string $file): int
{
if (
is_file($file) &&
file_exists($file) &&
is_readable($file)
) {
$f = fopen($file, 'rb');
if (!is_resource($f)) {
return 0;
}
$lines = 0;
while (!feof($f)) {
$lines += substr_count(fread($f, 8192) ?: '', "\n");
}
fclose($f);
} else {
// if file does not exist or is not readable, return -1
$lines = -1;
}
// return lines in file
return $lines;
}
}
// __END__

View File

@@ -1,41 +0,0 @@
<?php
/*
* DEPRECATED: Use correct Convert\Json:: instead
*/
declare(strict_types=1);
namespace CoreLibs\Check;
use CoreLibs\Convert\Json;
class Jason
{
/**
* @param string|null $json a json string, or null data
* @param bool $override if set to true, then on json error
* set original value as array
* @return array<mixed> returns an array from the json values
* @deprecated Use Json::jsonConvertToArray()
*/
public static function jsonConvertToArray(?string $json, bool $override = false): array
{
return Json::jsonConvertToArray($json, $override);
}
/**
* @param bool|boolean $return_string [default=false] if set to true
* it will return the message string and not
* the error number
* @return int|string Either error number (0 for no error)
* or error string ('' for no error)
* @deprecated Use Json::jsonGetLastError()
*/
public static function jsonGetLastError(bool $return_string = false)
{
return Json::jsonGetLastError($return_string);
}
}
// __END__

View File

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

View File

@@ -1,412 +0,0 @@
<?php
/*
* array search and transform functions
*/
declare(strict_types=1);
namespace CoreLibs\Combined;
class ArrayHandler
{
/**
* searches key = value in an array / array
* only returns the first one found
*
* @param string|int $needle needle (search for)
* @param array<mixed> $haystack haystack (search in)
* @param string|null $key_search_for the key to look out for, default empty
* @return array<mixed> array with the elements where
* the needle can be found in the
* haystack array
*/
public static function arraySearchRecursive($needle, array $haystack, ?string $key_search_for = null): array
{
$path = [];
if (!is_array($haystack)) {
$haystack = [];
}
if (
$key_search_for != null &&
array_key_exists($key_search_for, $haystack) &&
$needle === $haystack[$key_search_for]
) {
$path[] = $key_search_for;
} else {
foreach ($haystack as $key => $val) {
if (
is_scalar($val) &&
$val === $needle &&
empty($key_search_for)
) {
$path[] = $key;
break;
} elseif (
is_scalar($val) &&
!empty($key_search_for) &&
$key === $key_search_for &&
$val === $needle
) {
$path[] = $key;
break;
} elseif (
is_array($val) &&
$path = self::arraySearchRecursive(
$needle,
(array)$val,
// to avoid PhanTypeMismatchArgumentNullable
($key_search_for === null ? $key_search_for : (string)$key_search_for)
)
) {
array_unshift($path, $key);
break;
}
}
}
return $path;
}
/**
* recursive array search function, which returns all found not only the first one
*
* @param string|int $needle needle (search for)
* @param array<mixed> $haystack haystack (search in)
* @param string|int $key_search_for the key to look for in
* @param bool $old [true], if set to false will
* return new flat layout
* @param array<mixed>|null $path recursive call for previous path
* @return array<mixed>|null all array elements paths where
* the element was found
*/
public static function arraySearchRecursiveAll(
$needle,
array $haystack,
$key_search_for,
bool $old = true,
?array $path = null
): ?array {
// init if not set on null
if ($path === null) {
$path = [
'level' => 0,
'work' => []
];
} else {
// init sub sets if not set
if (!isset($path['level'])) {
$path['level'] = 0;
}
if (!isset($path['work'])) {
$path['work'] = [];
}
}
// should not be needed because it would trigger a php mehtod error
if (!is_array($haystack)) {
$haystack = [];
}
// go through the array,
foreach ($haystack as $_key => $_value) {
if (
is_scalar($_value) &&
$_value === $needle &&
empty($key_search_for)
) {
// only value matches
$path['work'][$path['level'] ?? 0] = $_key;
$path['found'][] = $path['work'];
} elseif (
is_scalar($_value) &&
!empty($key_search_for) &&
$_key === $key_search_for &&
$_value === $needle
) {
// key and value matches
$path['work'][$path['level'] ?? 0] = $_key;
$path['found'][] = $path['work'];
} elseif (is_array($_value)) {
// add position to working
$path['work'][$path['level'] ?? 0] = $_key;
// we will up a level
$path['level'] += 1;
// call recursive
$path = self::arraySearchRecursiveAll($needle, $_value, $key_search_for, $old, $path);
}
}
// be 100% sure the array elements are set
$path['level'] = $path['level'] ?? 0;
$path['work'] = $path['work'] ?? [];
// cut all that is >= level
array_splice($path['work'], $path['level']);
// step back a level
$path['level'] -= 1;
if ($old === false && $path['level'] == -1) {
return $path['found'] ?? [];
} else {
return $path;
}
}
/**
* array search simple. looks for key, value combination, if found, returns true
* on default does not strict check, so string '4' will match int 4 and vica versa
*
* @param array<mixed> $array search in as array
* @param string|int $key key (key to search in)
* @param string|int $value value (what to find)
* @param bool $strict [false], if set to true, will strict check key/value
* @return bool true on found, false on not found
*/
public static function arraySearchSimple(array $array, $key, $value, bool $strict = false): bool
{
if (!is_array($array)) {
$array = [];
}
foreach ($array as $_key => $_value) {
// if value is an array, we search
if (is_array($_value)) {
// call recursive, and return result if it is true, else continue
if (($result = self::arraySearchSimple($_value, $key, $value, $strict)) !== false) {
return $result;
}
} elseif ($strict === false && $_key == $key && $_value == $value) {
return true;
} elseif ($strict === true && $_key === $key && $_value === $value) {
return true;
}
}
// no true returned, not found
return false;
}
/**
* correctly recursive merges as an array as array_merge_recursive
* just glues things together
* array first array to merge
* array second array to merge
* ... etc
* bool key flag: true: handle keys as string or int
* default false: all keys are string
*
* @return array<mixed>|bool merged array
*/
public static function arrayMergeRecursive()
{
// croak on not enough arguemnts (we need at least two)
if (func_num_args() < 2) {
trigger_error(__FUNCTION__ . ' needs two or more array arguments', E_USER_WARNING);
return false;
}
// default key is not string
$key_is_string = false;
$arrays = func_get_args();
// if last is not array, then assume it is trigger for key is always string
if (!is_array(end($arrays))) {
if (array_pop($arrays)) {
$key_is_string = true;
}
}
// check that arrays count is at least two, else we don't have enough to do anything
if (count($arrays) < 2) {
trigger_error(__FUNCTION__ . ' needs two or more array arguments', E_USER_WARNING);
return false;
}
$merged = [];
while ($arrays) {
$array = array_shift($arrays);
if (!is_array($array)) {
trigger_error(__FUNCTION__ . ' encountered a non array argument', E_USER_WARNING);
return false;
}
if (!$array) {
continue;
}
foreach ($array as $key => $value) {
// if string or if key is assumed to be string do key match
// else add new entry
if (is_string($key) || $key_is_string === false) {
if (is_array($value) && array_key_exists($key, $merged) && is_array($merged[$key])) {
// $merged[$key] = call_user_func(__METHOD__, $merged[$key], $value, $key_is_string);
$merged[$key] = self::arrayMergeRecursive($merged[$key], $value, $key_is_string);
} else {
$merged[$key] = $value;
}
} else {
$merged[] = $value;
}
}
}
return $merged;
}
/**
* correct array_diff that does an actualy difference between two arrays.
* array_diff only checks elements from A that are not in B, but not the
* other way around.
* Note that like array_diff this only checks first level values not keys
*
* @param array<mixed> $a array to compare a
* @param array<mixed> $b array to compare b
* @return array<mixed> array with missing elements from a & b
*/
public static function arrayDiff(array $a, array $b): array
{
$intersect = array_intersect($a, $b);
return array_merge(array_diff($a, $intersect), array_diff($b, $intersect));
}
/**
* search for the needle array elements in haystack and
* return the ones found as an array,
* is there nothing found, it returns FALSE (boolean)
*
* @param array<mixed> $needle elements to search for
* @param array<mixed> $haystack array where the $needle elements should
* be searched int
* @return array<mixed>|bool either the found elements or
* false for nothing found or error
*/
public static function inArrayAny(array $needle, array $haystack)
{
$found = [];
foreach ($needle as $element) {
if (in_array($element, $haystack)) {
$found[] = $element;
}
}
if (count($found) == 0) {
return false;
} else {
return $found;
}
}
/**
* creates out of a normal db_return array an assoc array
*
* @param array<mixed> $db_array return array from the database
* @param string|int|bool $key key set, false for not set
* @param string|int|bool $value value set, false for not set
* @param bool $set_only flag to return all (default), or set only
* @return array<mixed> associative array
*/
public static function genAssocArray(array $db_array, $key, $value, bool $set_only = false): array
{
$ret_array = [];
// do this to only run count once
for ($i = 0, $iMax = count($db_array); $i < $iMax; $i++) {
// if no key then we make an order reference
if (
$key !== false &&
$value !== false &&
(($set_only && !empty($db_array[$i][$value])) ||
(!$set_only && isset($db_array[$i][$value]))) &&
!empty($db_array[$i][$key])
) {
$ret_array[$db_array[$i][$key]] = $db_array[$i][$value];
} elseif (
$key === false && $value !== false &&
isset($db_array[$i][$value])
) {
$ret_array[] = $db_array[$i][$value];
} elseif (
$key !== false && $value === false &&
!empty($db_array[$i][$key])
) {
$ret_array[$db_array[$i][$key]] = $i;
}
}
return $ret_array;
}
/**
* converts multi dimensional array to a flat array
* does NOT preserve keys
*
* @param array<mixed> $array multi dimensionial array
* @return array<mixed> flattened array
*/
public static function flattenArray(array $array): array
{
$return = [];
array_walk_recursive(
$array,
function ($value) use (&$return) {
$return[] = $value;
}
);
return $return;
}
/**
* will loop through an array recursivly and write the array keys back
*
* @param array<mixed> $array multidemnsional array to flatten
* @param array<mixed> $return recoursive pass on array of keys
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKey(array $array, array $return = []): array
{
foreach ($array as $key => $sub) {
$return[] = $key;
if (is_array($sub) && count($sub) > 0) {
$return = self::flattenArrayKey($sub, $return);
}
}
return $return;
}
/**
* as above will flatten an array, but in this case only the outmost
* leave nodes, all other keyswill be skipped
*
* @param array<mixed> $array multidemnsional array to flatten
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKeyLeavesOnly(array $array): array
{
$return = [];
array_walk_recursive(
$array,
function ($value, $key) use (&$return) {
$return[] = $key;
}
);
return $return;
}
/**
* searches for key -> value in an array tree and writes the value one level up
* this will remove this leaf will all other values
*
* @param array<mixed> $array nested array
* @param string|int $search key to find that has no sub leaf
* and will be pushed up
* @return array<mixed> modified, flattened array
*/
public static function arrayFlatForKey(array $array, $search): array
{
if (!is_array($array)) {
$array = [];
}
foreach ($array as $key => $value) {
// if it is not an array do just nothing
if (!is_array($value)) {
continue;
}
// probe it has search key
if (isset($value[$search])) {
// set as current
$array[$key] = $value[$search];
} else {
// call up next node down
// $array[$key] = call_user_func(__METHOD__, $value, $search);
$array[$key] = self::arrayFlatForKey($value, $search);
}
}
return $array;
}
}
// __END__

View File

@@ -1,452 +0,0 @@
<?php
/*
* date convert and check functions
*/
declare(strict_types=1);
namespace CoreLibs\Combined;
use Exception;
class DateTime
{
/** @var array<int,string> */
public const DAY_SHORT = [
1 => 'Mon',
2 => 'Tue',
3 => 'Wed',
4 => 'Thu',
5 => 'Fri',
6 => 'Sat',
7 => 'Sun',
];
/** @var array<int,string> */
public const DAY_LONG = [
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
4 => 'Thursday',
5 => 'Friday',
6 => 'Saturday',
7 => 'Sunday',
];
/** @var array<int,string> */
public const MONTH_LONG = [
1 => 'January',
2 => 'February',
3 => 'March',
4 => 'April',
5 => 'May',
6 => 'June',
7 => 'July',
8 => 'August',
9 => 'September',
10 => 'October',
11 => 'November',
12 => 'December',
];
/** @var array<int,string> */
public const MONTH_SHORT = [
1 => 'Jan',
2 => 'Feb',
3 => 'Mar',
4 => 'Apr',
5 => 'May',
6 => 'Jun',
7 => 'Jul',
8 => 'Aug',
9 => 'Sep',
10 => 'Oct',
11 => 'Nov',
12 => 'Dec',
];
/**
* a simple wrapper for the date format
* if an invalid timestamp is give zero timestamp unix time is used
*
* @param int|float $timestamp unix timestamp
* @param bool $show_micro show the micro time (default false)
* @param bool $micro_as_float Add the micro time with . instead
* of ms (default false)
* @return string formated date+time in Y-M-D h:m:s ms
*/
public static function dateStringFormat(
$timestamp,
bool $show_micro = false,
bool $micro_as_float = false
): string {
// split up the timestamp, assume . in timestamp
// array pad $ms if no microtime
list ($timestamp, $ms) = array_pad(explode('.', (string)round($timestamp, 4)), 2, null);
$string = date("Y-m-d H:i:s", (int)$timestamp);
if ($show_micro && $ms) {
if ($micro_as_float == false) {
$string .= ' ' . $ms . 'ms';
} else {
$string .= '.' . $ms;
}
}
return $string;
}
/**
* formats a timestamp into interval, not into a date
*
* @param string|int|float $timestamp interval in seconds and optional
* float micro seconds
* @param bool $show_micro show micro seconds, default true
* @return string interval formatted string or string as is
*/
public static function timeStringFormat($timestamp, bool $show_micro = true): string
{
// check if the timestamp has any h/m/s/ms inside, if yes skip
if (!preg_match("/(h|m|s|ms)/", (string)$timestamp)) {
list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 4)), 2, null);
// if negative remember
$negative = false;
if ((int)$timestamp < 0) {
$negative = true;
}
$timestamp = abs((float)$timestamp);
$timegroups = [86400, 3600, 60, 1];
$labels = ['d', 'h', 'm', 's'];
$time_string = '';
// if timestamp is zero, return zero string
if ($timestamp == 0) {
$time_string = '0s';
} else {
for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) {
$output = floor((float)$timestamp / $timegroups[$i]);
$timestamp = (float)$timestamp % $timegroups[$i];
// output has days|hours|min|sec
if ($output || $time_string) {
$time_string .= $output . $labels[$i] . (($i + 1) != count($timegroups) ? ' ' : '');
}
}
}
// only add ms if we have an ms value
if ($ms !== null) {
// if we have ms and it has leading zeros, remove them, but only if it is nut just 0
$ms = preg_replace("/^0+(\d+)$/", '${1}', $ms);
if (!is_string($ms) || empty($ms)) {
$ms = '0';
}
// add ms if there
if ($show_micro) {
$time_string .= ' ' . $ms . 'ms';
} elseif (!$time_string) {
$time_string .= $ms . 'ms';
}
}
if ($negative) {
$time_string = '-' . $time_string;
}
} else {
$time_string = $timestamp;
}
return (string)$time_string;
}
/**
* does a reverse of the timeStringFormat and converts the string from
* xd xh xm xs xms to a timestamp.microtime format
*
* @param string|int|float $timestring formatted interval
* @return string|int|float converted float interval, or string as is
*/
public static function stringToTime($timestring)
{
$timestamp = 0;
if (preg_match("/(d|h|m|s|ms)/", (string)$timestring)) {
$timestring = (string)$timestring;
// pos for preg match read + multiply factor
$timegroups = [2 => 86400, 4 => 3600, 6 => 60, 8 => 1];
$matches = [];
// if start with -, strip and set negative
$negative = false;
if (preg_match("/^-/", $timestring)) {
$negative = true;
$timestring = substr($timestring, 1);
}
// preg match: 0: full string
// 2, 4, 6, 8 are the to need values
preg_match("/^((\d+)d ?)?((\d+)h ?)?((\d+)m ?)?((\d+)s ?)?((\d+)ms)?$/", $timestring, $matches);
// multiply the returned matches and sum them up. the last one (ms) is added with .
foreach ($timegroups as $i => $time_multiply) {
if (isset($matches[$i]) && is_numeric($matches[$i])) {
$timestamp += (float)$matches[$i] * $time_multiply;
}
}
if (isset($matches[10]) && is_numeric($matches[10])) {
$timestamp .= '.' . $matches[10];
}
if ($negative) {
// cast to flaot so we can do a negative multiplication
$timestamp = (float)$timestamp * -1;
}
return $timestamp;
} else {
return $timestring;
}
}
/**
* Returns long or short day of week name based on ISO day of week number
* 1: Monday
* ...
* 7: Sunday
*
* @param int $isodow 1: Monday, 7: Sunday
* @param bool $long Default false 'Mon', if true 'Monday'
* @return string Day of week string either short 'Mon' or long 'Monday'
*/
public static function setWeekdayNameFromIsoDow(int $isodow, bool $long = false): string
{
// if not valid, set to invalid
if ($isodow < 1 || $isodow > 7) {
return $long ? 'Invalid' : 'Inv';
}
return date($long ? 'l' : 'D', strtotime("Sunday +{$isodow} days") ?: null);
}
/**
* Get the day of week Name from date
*
* @param string $date Any valid date
* @param bool $long Default false 'Mon', if true 'Monday'
* @return string Day of week string either short 'Mon' or long 'Monday'
*/
public static function setWeekdayNameFromDate(string $date, bool $long = false): string
{
if (!self::checkDate($date)) {
return $long ? 'Invalid' : 'Inv';
}
return date($long ? 'l' : 'D', strtotime($date) ?: null);
}
/**
* Get the day of week Name from date
*
* @param string $date Any valid date
* @return int ISO Weekday number 1: Monday, 7: Sunday, -1 for invalid date
*/
public static function setWeekdayNumberFromDate(string $date): int
{
if (!self::checkDate($date)) {
return -1;
}
return (int)date('N', strtotime($date) ?: null);
}
/**
* splits & checks date, wrap around for check_date function
*
* @param string $date a date string in the format YYYY-MM-DD
* @return bool true if valid date, false if date not valid
*/
public static function checkDate($date): bool
{
if (!$date) {
return false;
}
list ($year, $month, $day) = array_pad(
preg_split("/[\/-]/", $date) ?: [],
3,
null
);
if (!$year || !$month || !$day) {
return false;
}
if (!checkdate((int)$month, (int)$day, (int)$year)) {
return false;
}
return true;
}
/**
* splits & checks date, wrap around for check_date function
*
* @param string $datetime date (YYYY-MM-DD) + time (HH:MM:SS), SS can be dropped
* @return bool true if valid date, false if date not valid
*/
public static function checkDateTime(string $datetime): bool
{
if (!$datetime) {
return false;
}
// catch last overflow if sec has - in front
list ($year, $month, $day, $hour, $min, $sec, $sec_overflow) = array_pad(
preg_split("/[\/\- :]/", $datetime) ?: [],
7,
null
);
if (!$year || !$month || !$day) {
return false;
}
if (!checkdate((int)$month, (int)$day, (int)$year)) {
return false;
}
if (!is_numeric($hour) || !is_numeric($min)) {
return false;
}
if (!empty($sec) && !is_numeric($sec)) {
return false;
}
if (!empty($sec) && ($sec < 0 || $sec > 60)) {
return false;
};
// in case we have - for seconds
if (!empty($sec_overflow)) {
return false;
}
if (
($hour < 0 || $hour > 24) ||
($min < 0 || $min > 60)
) {
return false;
}
return true;
}
/**
* compares two dates, tries to convert them via strtotime to timestamps
* returns int/bool in:
* -1 if the first date is smaller the last
* 0 if both are equal
* 1 if the first date is bigger than the last
* false if date validation/conversion failed
*
* @param string $start_date start date string in YYYY-MM-DD
* @param string $end_date end date string in YYYY-MM-DD
* @return int|bool false on error
* or int -1 (s<e)/0 (s=e)/1 (s>e) as difference
*/
public static function compareDate($start_date, $end_date)
{
// pre check for empty or wrong
if ($start_date == '--' || $end_date == '--' || !$start_date || !$end_date) {
return false;
}
// if invalid, quit
if (($start_timestamp = strtotime($start_date)) === false) {
return false;
}
if (($end_timestamp = strtotime($end_date)) === false) {
return false;
}
// convert anything to Y-m-d and then to timestamp
// this is to remove any time parts
$start_timestamp = strtotime(date('Y-m-d', $start_timestamp));
$end_timestamp = strtotime(date('Y-m-d', $end_timestamp));
// compare, or end with false
if ($start_timestamp < $end_timestamp) {
return -1;
} elseif ($start_timestamp == $end_timestamp) {
return 0;
} elseif ($start_timestamp > $end_timestamp) {
return 1;
} else {
return false;
}
}
/**
* compares the two dates + times. if seconds missing in one set,
* adds :00, converts date + times via strtotime to timestamps
* returns int/bool in:
* -1 if the first date is smaller the last
* 0 if both are equal
* 1 if the first date is bigger than the last
* false if date/times validation/conversion failed
*
* @param string $start_datetime start date/time in YYYY-MM-DD HH:mm:ss
* @param string $end_datetime end date/time in YYYY-MM-DD HH:mm:ss
* @return int|bool false for error
* or -1 (s<e)/0 (s=e)/1 (s>e) as difference
*/
public static function compareDateTime($start_datetime, $end_datetime)
{
// pre check for empty or wrong
if ($start_datetime == '--' || $end_datetime == '--' || !$start_datetime || !$end_datetime) {
return false;
}
// quit if invalid timestamp
if (($start_timestamp = strtotime($start_datetime)) === false) {
return false;
}
if (($end_timestamp = strtotime($end_datetime)) === false) {
return false;
}
// compare, or return false
if ($start_timestamp < $end_timestamp) {
return -1;
} elseif ($start_timestamp == $end_timestamp) {
return 0;
} elseif ($start_timestamp > $end_timestamp) {
return 1;
} else {
return false;
}
}
/**
* calculates the days between two dates
* return: overall days, week days, weekend days as array 0...2 or named
* as overall, weekday and weekend
*
* @param string $start_date valid start date (y/m/d)
* @param string $end_date valid end date (y/m/d)
* @param bool $return_named return array type, false (default), true for named
* @return array<mixed> 0/overall, 1/weekday, 2/weekend
*/
public static function calcDaysInterval($start_date, $end_date, bool $return_named = false): array
{
// pos 0 all, pos 1 weekday, pos 2 weekend
$days = [];
// if anything invalid, return 0,0,0
try {
$start = new \DateTime($start_date);
$end = new \DateTime($end_date);
} catch (Exception $e) {
if ($return_named === true) {
return [
'overall' => 0,
'weekday' => 0,
'weekend' => 0,
];
} else {
return [0, 0, 0];
}
}
// so we include the last day too, we need to add +1 second in the time
$end->setTime(0, 0, 1);
// if end date before start date, only this will be filled
$days[0] = $end->diff($start)->days;
$days[1] = 0;
$days[2] = 0;
// get period for weekends/weekdays
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end);
foreach ($period as $dt) {
$curr = $dt->format('D');
if ($curr == 'Sat' || $curr == 'Sun') {
$days[2] ++;
} else {
$days[1] ++;
}
}
if ($return_named === true) {
return [
'overall' => $days[0],
'weekday' => $days[1],
'weekend' => $days[2],
];
} else {
return $days;
}
}
}
// __END__

View File

@@ -1,167 +0,0 @@
<?php
/*
* byte conversion from and to human readable
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Byte
{
// define byteFormat
public const BYTE_FORMAT_NOSPACE = 1;
public const BYTE_FORMAT_ADJUST = 2;
public const BYTE_FORMAT_SI = 4;
/**
* This function replaces the old byteStringFormat
*
* Converts any number string to human readable byte format
* Maxium is Exobytes and above that the Exobytes suffix is used for all
* If more are needed only the correct short name for the suffix has to be
* added to the labels array
* On no number string it returns string as is
* Source Idea: SOURCE: https://programming.guide/worlds-most-copied-so-snippet.html
*
* The class itself hast the following defined
* BYTE_FORMAT_NOSPACE [1] turn off spaces between number and suffix
* BYTE_FORMAT_ADJUST [2] use sprintf to always print two decimals
* BYTE_FORMAT_SI [4] use si standard 1000 instead of bytes 1024
* To use the constant from outside use class::CONSTANT
*
* @param string|int|float $bytes bytes as string int or pure int
* @param int $flags bitwise flag with use space turned on
* BYTE_FORMAT_NOSPACE: no space between number and suffix
* BYTE_FORMAT_ADJUST: sprintf adjusted two 2 decimals
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string converted byte number (float) with suffix
* @throws \Exception 1: no valid flag set
*/
public static function humanReadableByteFormat($bytes, int $flags = 0): string
{
// if not numeric, return as is
if (is_numeric($bytes)) {
// flags bit wise check
// remove space between number and suffix
if ($flags & self::BYTE_FORMAT_NOSPACE) {
$space = false;
} else {
$space = true;
}
// use sprintf instead of round
if ($flags & self::BYTE_FORMAT_ADJUST) {
$adjust = true;
} else {
$adjust = false;
}
// use SI 1000 mod and not 1024 mod
if ($flags & self::BYTE_FORMAT_SI) {
$si = true;
} else {
$si = false;
}
if ($flags > 7) {
throw new \Exception("Invalid flags parameter: $flags", 1);
}
// si or normal
$unit = $si ? 1000 : 1024;
// always positive
$abs_bytes = $bytes == PHP_INT_MIN ? PHP_INT_MAX : abs((float)$bytes);
// smaller than unit is always B
if ($abs_bytes < $unit) {
return $bytes . 'B';
}
// labels in order of size [Y, Z]
$labels = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
// exp position calculation
$exp = floor(log($abs_bytes, $unit));
// avoid printing out anything larger than max labels
if ($exp >= count($labels)) {
$exp = count($labels) - 1;
}
// deviation calculation
$dev = pow($unit, $exp) * ($unit - 0.05);
// shift the exp +1 for on the border units
if (
$exp < 6 &&
$abs_bytes > ($dev - (((int)$dev & 0xfff) == 0xd00 ? 52 : 0))
) {
$exp++;
}
// label name, including leading space if flagged
$pre = ($space ? ' ' : '') . ($labels[$exp] ?? '>E') . ($si ? 'i' : '') . 'B';
$bytes_calc = $abs_bytes / pow($unit, $exp);
// if original is negative, reverse
if ($bytes < 0) {
$bytes_calc *= -1;
}
if ($adjust) {
return sprintf("%.2f%s", $bytes_calc, $pre);
} else {
return round($bytes_calc, 2) . $pre;
}
} else {
// if anything other return as string
return (string)$bytes;
}
}
/**
* calculates the bytes based on a string with nnG, nnGB, nnM, etc
* NOTE: large exabyte numbers will overflow
* flag allowed:
* BYTE_FORMAT_SI [4] use si standard 1000 instead of bytes 1024
*
* @param string|int|float $number any string or number to convert
* @param int $flags bitwise flag with use space turned on
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string|int|float converted value or original value
* @throws \Exception 1: no valid flag set
*/
public static function stringByteFormat($number, int $flags = 0)
{
// use SI 1000 mod and not 1024 mod
if ($flags & self::BYTE_FORMAT_SI) {
$si = true;
} else {
$si = false;
}
if ($flags != 0 && $flags != 4) {
throw new \Exception("Invalid flags parameter: $flags", 1);
}
// matches in regex
$matches = [];
// all valid units
$valid_units_ = 'bkmgtpezy';
// detects up to exo bytes
preg_match(
"/(-)?([\d.,]*)\s?(eib|pib|tib|gib|mib|kib|eb|pb|tb|gb|mb|kb|e|p|t|g|m|k|b)$/i",
strtolower((string)$number),
$matches
);
if (isset($matches[2]) && isset($matches[3])) {
// remove all non valid characters from the number
$number = preg_replace('/[^0-9\.]/', '', $matches[2]);
// final clean up and convert to float
$number = (float)trim((string)$number);
// convert any mb/gb/etc to single m/b
$unit = preg_replace('/[^bkmgtpezy]/i', '', $matches[3]);
if ($unit) {
$number = $number * pow($si ? 1000 : 1024, stripos($valid_units_, $unit[0]) ?: 0);
}
// convert to INT to avoid +E output
$number = (int)round($number);
// if negative input, keep nnegative
if (!empty($matches[1])) {
$number *= -1;
}
}
// if not matching return as is
return $number;
}
}
// __END__

View File

@@ -1,344 +0,0 @@
<?php
/*
* Convert color spaces
* rgb to hex
* hex to rgb
* rgb to hsb
* hsb to rgb
* rgb to hsl
* hsl to rgb
*/
// TODO: use oklab as base for converting colors
// https://bottosson.github.io/posts/oklab/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Colors
{
/**
* converts the rgb values from int data to the valid rgb html hex string
* optional can turn of leading #
* if one value is invalid, will return false
*
* @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,
* false for invalid color
*/
public static function rgb2hex(int $red, int $green, int $blue, bool $hex_prefix = true)
{
$hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
}
foreach (['red', 'green', 'blue'] as $color) {
// if not valid, abort
if ($$color < 0 || $$color > 255) {
return false;
}
// pad left with 0
$hex_color .= str_pad(dechex($$color), 2, '0', STR_PAD_LEFT);
}
return $hex_color;
}
/**
* converts a hex RGB color to the int numbers
*
* @param string $hexStr RGB hexstring
* @param bool $return_as_string flag to return as string
* @param string $seperator string seperator: default: ","
* @return string|array<string,float|int>|bool false on error or array with RGB
* or a string with the seperator
*/
public static function hex2rgb(
string $hexStr,
bool $return_as_string = false,
string $seperator = ','
) {
$hexStr = preg_replace("/[^0-9A-Fa-f]/", '', $hexStr); // Gets a proper hex string
if (!is_string($hexStr)) {
return false;
}
$rgbArray = [];
if (strlen($hexStr) == 6) {
// If a proper hex code, convert using bitwise operation.
// No overhead... faster
$colorVal = hexdec($hexStr);
$rgbArray['r'] = 0xFF & ($colorVal >> 0x10);
$rgbArray['g'] = 0xFF & ($colorVal >> 0x8);
$rgbArray['b'] = 0xFF & $colorVal;
} elseif (strlen($hexStr) == 3) {
// If shorthand notation, need some string manipulations
$rgbArray['r'] = hexdec(str_repeat(substr($hexStr, 0, 1), 2));
$rgbArray['g'] = hexdec(str_repeat(substr($hexStr, 1, 1), 2));
$rgbArray['b'] = hexdec(str_repeat(substr($hexStr, 2, 1), 2));
} else {
// Invalid hex color code
return false;
}
// returns the rgb string or the associative array
return $return_as_string ? implode($seperator, $rgbArray) : $rgbArray;
}
/**
* rgb2hsb does not clean convert back to rgb in a round trip
* 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<int|float>|bool Hue, Sat, Brightness/Value
* false for input value error
*/
public static function rgb2hsb(int $red, int $green, int $blue)
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
if ($$c < 0 || $$c > 255) {
return false;
}
$$c = $$c / 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)
];
}
/**
* hsb2rgb does not clean convert back to hsb in a round trip
* converts HSB/V to RGB values RGB is full INT
* if HSB/V value is invalid, sets this value to 0
*
* @param float $H hue 0-360 (int)
* @param float $S saturation 0-100 (int)
* @param float $V brightness/value 0-100 (int)
* @return array<int>|bool 0 red/1 green/2 blue array as 0-255
* false for input value error
*/
public static function hsb2rgb(float $H, float $S, float $V)
{
// 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) {
return false;
}
if ($S < 0 || $S > 100) {
return false;
}
if ($V < 0 || $V > 100) {
return false;
}
// 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)
];
}
/**
* converts a RGB (0-255) to HSL
* return:
* array with hue (0-360), saturation (0-100%) and luminance (0-100%)
*
* @param int $red red 0-255
* @param int $green green 0-255
* @param int $blue blue 0-255
* @return array<float>|bool hue/sat/luminance
* false for input value error
*/
public static function rgb2hsl(int $red, int $green, int $blue)
{
// check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $c) {
if ($$c < 0 || $$c > 255) {
return false;
}
$$c = $$c / 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)
];
}
}
/**
* converts an HSL to RGB
* if HSL value is invalid, set this value to 0
*
* @param float $hue hue: 0-360 (degrees)
* @param float $sat saturation: 0-100
* @param float $lum luminance: 0-100
* @return array<int,float|int>|bool red/blue/green 0-255 each
*/
public static function hsl2rgb(float $hue, float $sat, float $lum)
{
if (!is_numeric($hue)) {
return false;
}
if ($hue == 360) {
$hue = 0;
}
if ($hue < 0 || $hue > 359) {
return false;
}
if ($sat < 0 || $sat > 100) {
return false;
}
if ($lum < 0 || $lum > 100) {
return false;
}
// 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)))
];
}
}
}
// __END__

View File

@@ -1,256 +0,0 @@
<?php
/*
* Run is_<type> checks and return default value if not this type
* This will return default null on invalid entries
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Extends;
class VarSetTypeMain
{
/**
* If input variable is string then returns it, else returns default set
* if not null is true, then null as return is allowed, else return is
* converted to string
*
* @param mixed $val Input variable
* @param string|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return string|null Input var or default value
*/
protected static function setStrMain(
mixed $val,
?string $default = null,
bool $to_null = false
): ?string {
if (is_string($val)) {
return $val;
}
if ($to_null === false) {
return (string)$default;
}
return $default;
}
/**
* Will convert input data to string if possible.
* Runs for string/int/float/bool/null
* Will skip array/object/resource/callable/etc and use default for that
*
* @param mixed $val Input variable
* @param string|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return string|null Converted input data to string/null
*/
protected static function makeStrMain(
mixed $val,
string $default = null,
bool $to_null = false
): ?string {
// int/float/string/bool/null, everything else is ignored
// no: array/object/resource/callable
if (
is_int($val) ||
is_float($val) ||
is_string($val) ||
is_bool($val) ||
is_null($val)
) {
return (string)$val;
}
if ($to_null === false) {
return (string)$default;
}
return $default;
}
/**
* If input variable is int, return it, else return default value. If to_null
* is true then null as return is allowed, else only int is returned
*
* @param mixed $val Input variable
* @param int|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return int|null Input var or default value
*/
protected static function setIntMain(
mixed $val,
?int $default = null,
bool $to_null = false
): ?int {
if (is_int($val)) {
return $val;
}
if ($to_null === false) {
return (int)$default;
}
return $default;
}
/**
* Convert input to int via filter_var. If not convertable return default value.
* If to_null is set to true null return is allowed
* NOTE: this is only a drastic fallback and not recommned for special use.
* It will try to check via filter_var if we can get an int value and then use
* intval to convert it.
* Reason is that filter_var will convert eg 1.5 to 15 instead 1
* One is very wrong, the other is at least better, but not perfect
*
* @param mixed $val Input variable
* @param int|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return int|null Converted input data to int/null
*/
protected static function makeIntMain(
mixed $val,
int $default = null,
bool $to_null = false
): ?int {
// if we can filter it to a valid int, we can convert it
// we so avoid object, array, etc
if (
filter_var(
$val,
FILTER_SANITIZE_NUMBER_INT
) !== false
) {
return intval($val);
}
if ($to_null === false) {
return (int)$default;
}
return $default;
}
/**
* If input is float return it, else set to default value. If to_null is set
* to true, allow null return
*
* @param mixed $val Input variable
* @param float|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return float|null Input var or default value
*/
protected static function setFloatMain(
mixed $val,
?float $default = null,
bool $to_null = false
): ?float {
if (is_float($val)) {
return $val;
}
if ($to_null === false) {
return (float)$default;
}
return $default;
}
/**
* Convert intput var to float via filter_var. If failed to so return default.
* If to_null is set to true allow null return
*
* @param mixed $val Input variable
* @param float|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return float|null Converted intput data to float/null
*/
protected static function makeFloatMain(
mixed $val,
float $default = null,
bool $to_null = false
): ?float {
if (
(
$val = filter_var(
$val,
FILTER_SANITIZE_NUMBER_FLOAT,
FILTER_FLAG_ALLOW_FRACTION
)
) !== false
) {
return (float)$val;
}
if ($to_null === false) {
return (float)$default;
}
return $default;
}
/**
* If input var is array return it, else return default value. If to_null is
* set to true, allow null return
*
* @param mixed $val Input variable
* @param array<mixed>|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return array<mixed>|null Input var or default value
*/
protected static function setArrayMain(
mixed $val,
?array $default = null,
bool $to_null = false
): ?array {
if (is_array($val)) {
return $val;
}
if ($to_null === false) {
return (array)$default;
}
return $default;
}
/**
* If input var is bool return it, else return default value. If to_null is
* set to true will allow null return.
*
* @param mixed $val Input variable
* @param bool|null $default Default value
* @param bool $to_null Convert to null (default no)
* @return bool|null Input var or default value
*/
protected static function setBoolMain(
mixed $val,
?bool $default = null,
bool $to_null = false
): ?bool {
if (is_bool($val)) {
return $val;
}
if ($to_null === false) {
return (bool)$default;
}
return $default;
}
/**
* Convert anything to bool. If it is a string it will try to use the filter_var
* to convert know true/false strings.
* Else it uses (bool) to convert the rest
* If null is allowed, will return null
*
* @param mixed $val Input variable
* @param bool $default Default value if to_null if false
* @param bool $to_null Convert to null (default no)
* @return bool|null Converted input data to bool/ null
*/
protected static function makeBoolMain(
mixed $val,
bool $default = false,
bool $to_null = false
): ?bool {
$boolvar = is_string($val) ?
filter_var(
$val,
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE
) :
(bool)$val;
return $boolvar === null && !$to_null ? $default : $boolvar;
}
}
// __END__

View File

@@ -1,70 +0,0 @@
<?php
/*
* html convert functions
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Html
{
public const SELECTED = 0;
public const CHECKED = 1;
/**
* full wrapper for html entities
*
* @param mixed $string string to html encode
* @return mixed if string, encoded, else as is (eg null)
*/
public static function htmlent($string)
{
if (is_string($string)) {
return htmlentities($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
} else {
return $string;
}
}
/**
* strips out all line breaks or replaced with given string
* @param string $string string
* @param string $replace replace character, default ' '
* @return string cleaned string without any line breaks
*/
public static function removeLB(string $string, string $replace = ' '): string
{
return str_replace(["\n\r", "\r", "\n"], $replace, $string);
}
/**
* returns 'checked' or 'selected' if okay
* $needle is a var, $haystack an array or a string
* **** THE RETURN: VALUE WILL CHANGE TO A DEFAULT NULL IF NOT FOUND ****
*
* @param array<mixed>|string $haystack (search in) haystack can be
* an array or a string
* @param string $needle needle (search for)
* @param int $type type: 0: returns selected, 1,
* returns checked
* @return ?string returns checked or selected,
* else returns null
*/
public static function checked($haystack, string $needle, int $type = 0): ?string
{
if (is_array($haystack)) {
if (in_array($needle, $haystack)) {
return $type ? 'checked' : 'selected';
}
} else {
if ($haystack == $needle) {
return $type ? 'checked' : 'selected';
}
}
return null;
}
}
// __END__

View File

@@ -1,97 +0,0 @@
<?php
/*
* Converts a json string to array and stores error for later checking
* can also return empty array on demand
* and self set json as is on error as array
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Json
{
/** @var int */
private static $json_last_error;
/**
* converts a json string to an array
* or inits an empty array on null string
* or failed convert to array
* In ANY case it will ALWAYS return array.
* Does not throw errors
*
* @param string|null $json a json string, or null data
* @param bool $override if set to true, then on json error
* set original value as array
* @return array<mixed> returns an array from the json values
*/
public static function jsonConvertToArray(?string $json, bool $override = false): array
{
if ($json !== null) {
$_json = json_decode($json, true);
if (self::$json_last_error = json_last_error()) {
if ($override == true) {
// init return as array with original as element
$json = [$json];
} else {
$json = [];
}
} else {
$json = $_json;
}
} else {
$json = [];
}
// be sure that we return an array
return (array)$json;
}
/**
* returns human readable string for json errors thrown in jsonConvertToArray
*
* @param bool|boolean $return_string [default=false] if set to true
* it will return the message string and not
* the error number
* @return int|string Either error number (0 for no error)
* or error string ('' for no error)
*/
public static function jsonGetLastError(bool $return_string = false)
{
$json_error_string = '';
// valid errors as of php 8.0
switch (self::$json_last_error) {
case JSON_ERROR_NONE:
$json_error_string = '';
break;
case JSON_ERROR_DEPTH:
$json_error_string = 'Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$json_error_string = 'Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$json_error_string = 'Unexpected control character found';
break;
case JSON_ERROR_SYNTAX:
$json_error_string = 'Syntax error, malformed JSON';
break;
case JSON_ERROR_UTF8:
$json_error_string = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
case JSON_ERROR_INVALID_PROPERTY_NAME:
$json_error_string = 'A key starting with \u0000 character was in the string';
break;
case JSON_ERROR_UTF16:
$json_error_string = 'Single unpaired UTF-16 surrogate in unicode escape';
break;
default:
$json_error_string = 'Unknown error';
break;
}
return $return_string === true ? $json_error_string : self::$json_last_error;
}
}
// __END__

View File

@@ -1,61 +0,0 @@
<?php
/*
* various math related function wrappers
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Math
{
/**
* some float numbers will be rounded up even if they have no decimal entries
* this function fixes this by pre-rounding before calling ceil
*
* @param float $number number to round
* @param int|integer $precision intermediat round up decimals (default 10)
* @return float correct ceil number
*/
public static function fceil(float $number, int $precision = 10): float
{
return ceil(round($number, $precision));
}
/**
* round inside an a number, not the decimal part only
* eg 48767 with -2 -> 48700
*
* @param float $number number to round
* @param int $precision negative number for position in number (default -2)
* @return float rounded number
*/
public static function floorp(float $number, int $precision = -2): float
{
// if precision is requal or larger than the number length,
// set precision to length -1
if (abs($precision) >= strlen((string)$number)) {
$precision = (strlen((string)$number) - 1) * -1;
}
$mult = pow(10, $precision); // Can be cached in lookup table
return floor($number * $mult) / $mult;
}
/**
* inits input to 0, if value is not numeric
*
* @param string|int|float $number string or number to check
* @return float if not number, then returns 0, else original input
*/
public static function initNumeric($number): float
{
if (!is_numeric($number)) {
return 0;
} else {
return (float)$number;
}
}
}
// __END__

View File

@@ -1,85 +0,0 @@
<?php
/*
* Translates a mime id string into the actual application or file name
* for example 'text/plain' will output 'Text file'
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class MimeAppName
{
/** @var array<string,string> */
private static $mime_apps = [];
/**
* constructor: init mime list
*/
public function __construct()
{
self::$mime_apps = [
// zip
'application/zip' => 'Zip File',
// Powerpoint
'application/vnd.ms-powerpoint' => 'Microsoft Powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'Microsoft Powerpoint',
// PDF
'pplication/pdf' => 'PDF',
// JPEG
'image/jpeg' => 'JPEG',
// PNG
'image/png' => 'PNG',
// Indesign
'application/x-indesign' => 'Adobe InDesign',
// Photoshop
'image/vnd.adobe.photoshop' => 'Adobe Photoshop',
'application/photoshop' => 'Adobe Photoshop',
// Illustrator
'application/illustrator' => 'Adobe Illustrator',
// Word
'application/vnd.ms-word' => 'Microsoft Word',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'Microsoft Word',
// Excel
'application/vnd.ms-excel' => 'Microsoft Excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'Microsoft Excel',
// plain text
'text/plain' => 'Text file',
// html
'text/html' => 'HTML',
// mp4 (max 45MB each)
'video/mpeg' => 'MPEG Video'
];
}
/**
* Sets or updates a mime type
*
* @param string $mime MIME Name, no validiation
* @param string $app Applicaiton name
* @return void
*/
public static function mimeSetAppName(string $mime, string $app): void
{
// if empty, don't set
if (empty($mime) || empty($app)) {
return;
}
self::$mime_apps[$mime] = $app;
}
/**
* get the application name from mime type
* if not set returns "Other file"
*
* @param string $mime MIME Name
* @return string Application name matching
*/
public static function mimeGetAppName(string $mime): string
{
return self::$mime_apps[$mime] ?? 'Other file';
}
}
// __END__

View File

@@ -1,71 +0,0 @@
<?php
/*
* alternate for header mime encode to void problems with long strings and
* spaces/strange encoding problems.
* Orignal issues during PHP5/7
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class MimeEncode
{
/**
* wrapper function for mb mime convert
* for correct conversion with long strings
*
* @param string $string string to encode
* @param string $encoding target encoding
* @param string $line_break default line break is \r\n
* @return string encoded string
*/
public static function __mbMimeEncode(
string $string,
string $encoding,
string $line_break = "\r\n"
): string {
$current_internal_encoding = mb_internal_encoding();
// set internal encoding, so the mimeheader encode works correctly
mb_internal_encoding($encoding);
// if a subject, make a work around for the broken mb_mimencode
$pos = 0;
// after 36 single bytes characters,
// if then comes MB, it is broken
// has to 2 x 36 < 74 so the mb_encode_mimeheader
// 74 hardcoded split does not get triggered
$split = 36;
$_string = '';
while ($pos < mb_strlen($string, $encoding)) {
$output = mb_strimwidth($string, $pos, $split, "", $encoding);
$pos += mb_strlen($output, $encoding);
// if the strinlen is 0 here, get out of the loop
if (!mb_strlen($output, $encoding)) {
$pos += mb_strlen($string, $encoding);
}
$_string_encoded = mb_encode_mimeheader($output, $encoding);
// only make linebreaks if we have mime encoded code inside
// the space only belongs in the second line
if ($_string && preg_match("/^=\?/", $_string_encoded)) {
$_string .= $line_break . " ";
} elseif (
// hack for plain text with space at the end
mb_strlen($output, $encoding) == $split &&
mb_substr($output, -1, 1, $encoding) == " "
) {
// if output ends with space, add one more
$_string_encoded .= " ";
}
$_string .= $_string_encoded;
}
// strip out any spaces BEFORE a line break
$string = str_replace(" " . $line_break, $line_break, $_string);
// before we end, reset internal encoding
mb_internal_encoding($current_internal_encoding);
// return mime encoded string
return $string;
}
}
// __END__

View File

@@ -1,123 +0,0 @@
<?php
/*
* string convert and transform functions
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Strings
{
/**
* return the number of elements in the split list
* 0 if nothing / invalid split
* 1 if no split character found
* n for the numbers in the split list
*
* @param string $split_format
* @param string $split_characters
* @return int
*/
public static function countSplitParts(
string $split_format,
string $split_characters = '-'
): int {
if (
empty($split_format) ||
// non valid characters inside, abort
!preg_match("/^[0-9" . $split_characters . "]/", $split_format) ||
preg_match('/[^\x20-\x7e]/', $split_characters)
) {
return 0;
}
$split_list = preg_split(
// allowed split characters
"/([" . $split_characters . "]{1})/",
$split_format
);
if (!is_array($split_list)) {
return 0;
}
return count(array_filter($split_list));
}
/**
* split format a string base on a split format string
* split format string is eg
* 4-4-4 that means 4 characters DASH 4 characters DASH 4 characters
* So a string in the format of
* ABCD1234EFGH will be ABCD-1234-EFGH
* Note a string LONGER then the maxium will be attached with the LAST
* split character. In above exmaple
* ABCD1234EFGHTOOLONG will be ABCD-1234-EFGH-TOOLONG
*
* @param string $value string value to split
* @param string $split_format split format
* @param string $split_characters list of charcters with which we split
* if not set uses dash ('-')
* @return string split formatted string or original value if not chnaged
*/
public static function splitFormatString(
string $value,
string $split_format,
string $split_characters = '-'
): string {
if (
// abort if split format is empty
empty($split_format) ||
// if not in the valid ASCII character range for any of the strings
preg_match('/[^\x20-\x7e]/', $value) ||
// preg_match('/[^\x20-\x7e]/', $split_format) ||
preg_match('/[^\x20-\x7e]/', $split_characters) ||
// only numbers and split characters in split_format
!preg_match("/[0-9" . $split_characters . "]/", $split_format)
) {
return $value;
}
// split format list
$split_list = preg_split(
// allowed split characters
"/([" . $split_characters . "]{1})/",
$split_format,
-1,
PREG_SPLIT_DELIM_CAPTURE
);
// if this is false, or only one array, abort split
if (!is_array($split_list) || count($split_list) == 1) {
return $value;
}
$out = '';
$pos = 0;
$last_split = '';
foreach ($split_list as $offset) {
if (is_numeric($offset)) {
$_part = substr($value, $pos, (int)$offset);
if (empty($_part)) {
break;
}
$out .= $_part;
$pos += (int)$offset;
} elseif ($pos) { // if first, do not add
$out .= $offset;
$last_split = $offset;
}
}
if (!empty($out) && $pos < strlen($value)) {
$out .= $last_split . substr($value, $pos);
}
// if last is not alphanumeric remove, remove
if (!strcspn(substr($out, -1, 1), $split_characters)) {
$out = substr($out, 0, -1);
}
// overwrite only if out is set
if (!empty($out)) {
return $out;
} else {
return $value;
}
}
}
// __END__

View File

@@ -1,136 +0,0 @@
<?php
/*
* Run is_<type> checks and return default value if not this type
* This will return a default value as always what is expected and never null
* Use this for santize output from multi return functions where we know what
* will come back
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
use CoreLibs\Convert\Extends\VarSetTypeMain;
class VarSetType extends Extends\VarSetTypeMain
{
/**
* Check is input is string, if not return default string.
* Will always return string
*
* @param mixed $val Input value
* @param string $default Default override value
* @return string Input value or default as string
*/
public static function setStr(mixed $val, string $default = ''): string
{
return (string)VarSetTypeMain::setStrMain($val, $default, false);
}
/**
* Convert input to string if possible.
* Will only work on string/int/float/bool/null types
* Will always return string
*
* @param mixed $val Input value
* @param string $default Default override value
* @return string Input value as string or default as string
*/
public static function makeStr(mixed $val, string $default = ''): string
{
return (string)VarSetTypeMain::makeStrMain($val, $default, false);
}
/**
* Check if input is int, if not return default int value 0.
* Will always return int.
*
* @param mixed $val Input value
* @param int $default Default override value
* @return int Input value or default as int
*/
public static function setInt(mixed $val, int $default = 0): int
{
return (int)VarSetTypeMain::setIntMain($val, $default, false);
}
/**
* Convert intput to int if possible, if not return default value 0.
* Will always return int.
*
* @param mixed $val Input value
* @param int $default Default override value
* @return int Input value as int or default as int
*/
public static function makeInt(mixed $val, int $default = 0): int
{
return (int)VarSetTypeMain::makeIntMain($val, $default, false);
}
/**
* Check if input is float, if not return default value value 0.0.
* Will always return float
*
* @param mixed $val Input value
* @param float $default Default override value
* @return float Input value or default as float
*/
public static function setFloat(mixed $val, float $default = 0.0): float
{
return (float)VarSetTypeMain::setFloatMain($val, $default, false);
}
/**
* Convert input to float, if not possible return default value 0.0.
* Will always return float
*
* @param mixed $val Input value
* @param float $default Default override value
* @return float Input value as float or default as float
*/
public static function makeFloat(mixed $val, float $default = 0.0): float
{
return (float)VarSetTypeMain::makeFloatMain($val, $default, false);
}
/**
* Check if input is array, if not return default empty array.
* Will always return array.
*
* @param mixed $val Input value
* @param array<mixed> $default Default override value
* @return array<mixed> Input value or default as array
*/
public static function setArray(mixed $val, array $default = []): array
{
return (array)VarSetTypeMain::setArrayMain($val, $default, false);
}
/**
* Check if input is bool, if not will return default value false.
* Will aways return bool.
*
* @param mixed $val Input value
* @param bool $default Default override value
* @return bool Input value or default as bool
*/
public static function setBool(mixed $val, bool $default = false): bool
{
return (bool)VarSetTypeMain::setBoolMain($val, $default, false);
}
/**
* Convert anything to bool
*
* @param mixed $val Input value
* @param bool $default Default override value
* @return bool Input value as bool or default as bool
*/
public static function makeBool(mixed $val, bool $default = false): bool
{
return (bool)VarSetTypeMain::makeBoolMain($val, $default, false);
}
}
// __END__

View File

@@ -1,130 +0,0 @@
<?php
/*
* Run is_<type> checks and return default value if not this type
* This will return default null on invalid entries
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
use CoreLibs\Convert\Extends\VarSetTypeMain;
class VarSetTypeNull extends Extends\VarSetTypeMain
{
/**
* Check is input is string, if not return default string.
* Will return null if no string as default.
*
* @param mixed $val Input value
* @param string|null $default Default override value
* @return string|null Input value or default as string/null
*/
public static function setStr(mixed $val, ?string $default = null): ?string
{
return VarSetTypeMain::setStrMain($val, $default, true);
}
/**
* Convert input to string if possible.
* Will only work on string/int/float/bool/null types.
* Will return null if convert failed as default.
*
* @param mixed $val Input value
* @param string|null $default Default override value
* @return string|null Input value as string or default as string/null
*/
public static function makeStr(mixed $val, string $default = null): ?string
{
return VarSetTypeMain::makeStrMain($val, $default, true);
}
/**
* Check if input is int, if not return default value null.
*
* @param mixed $val Input value
* @param int|null $default Default override value
* @return int|null Input value or default as int/null
*/
public static function setInt(mixed $val, ?int $default = null): ?int
{
return VarSetTypeMain::setIntMain($val, $default, true);
}
/**
* Convert intput to int if possible, if not return default value value null.
*
* @param mixed $val Input value $val
* @param int|null $default Default override value
* @return int|null Input value as int or default as int/null
*/
public static function makeInt(mixed $val, int $default = null): ?int
{
return VarSetTypeMain::makeIntMain($val, $default, true);
}
/**
* Check if input is float, if not return default value value null.
*
* @param mixed $val Input value $val
* @param float|null $default Default override value
* @return float|null Input value or default as float/null
*/
public static function setFloat(mixed $val, ?float $default = null): ?float
{
return VarSetTypeMain::setFloatMain($val, $default, true);
}
/**
* Convert input to float, if not possible return default value null.
*
* @param mixed $val Input value $val
* @param float|null $default Default override value
* @return float|null Input value as float or default as float/null
*/
public static function makeFloat(mixed $val, float $default = null): ?float
{
return VarSetTypeMain::makeFloatMain($val, $default, true);
}
/**
* Check if input is array, if not return default value null.
*
* @param mixed $val Input value $val
* @param array<mixed>|null $default Default override value
* @return array<mixed>|null Input value or default as array/null
*/
public static function setArray(mixed $val, ?array $default = null): ?array
{
return VarSetTypeMain::setArrayMain($val, $default, true);
}
/**
* Check if input is bool, if not will return default value null.
*
* @param mixed $val Input value $val
* @param bool|null $default Default override value
* @return bool|null Input value or default as bool/null
*/
public static function setBool(mixed $val, ?bool $default = null): ?bool
{
return VarSetTypeMain::setBoolMain($val, $default, true);
}
/**
* Convert anything to bool
*
* @param mixed $val Input value $val
* @return bool|null Input value as bool or default as bool/null
*/
public static function makeBool(mixed $val): ?bool
{
// note that the default value here is irrelevant, we return null
// on unsetable string var
return VarSetTypeMain::makeBoolMain($val, false, true);
}
}
// __END__

View File

@@ -1,122 +0,0 @@
<?php
/*
* hash wrapper functions for old problem fixes
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class Hash
{
public const STANDARD_HASH_LONG = 'ripemd160';
public const STANDARD_HASH_SHORT = 'adler32';
/**
* checks php version and if >=5.2.7 it will flip the string
* can return empty string if none of string sets work
* hash returns false
* preg_replace fails for older php version
* Use __hash with crc32b or hash('crc32b', ...) for correct output
*
* @param string $string string to crc
* @return string crc32b hash (old type)
*/
public static function __crc32b(string $string): string
{
// do normal hash crc32b
$string = hash('crc32b', $string);
// if bigger than 5.2.7, we need to "unfix" the fix
if (\CoreLibs\Check\PhpVersion::checkPHPVersion('5.2.7')) {
// flip it back to old (two char groups)
$string = preg_replace("/^([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})$/", "$4$3$2$1", $string);
}
if (!is_string($string)) {
$string = '';
}
return $string;
}
/**
* replacement for __crc32b call
*
* @param string $string string to hash
* @param bool $use_sha use sha instead of crc32b (default false)
* @return string hash of the string
*/
public static function __sha1Short(string $string, bool $use_sha = false): string
{
if ($use_sha) {
// return only the first 9 characters
return substr(hash('sha1', $string), 0, 9);
} else {
return self::__crc32b($string);
}
}
/**
* replacemend for __crc32b call (alternate)
* defaults to adler 32
* allowed crc32b, adler32, fnv132, fnv1a32, joaat
* all that create 8 char long hashes
*
* @param string $string string to hash
* @param string $hash_type hash type (default adler32)
* @return string hash of the string
*/
public static function __hash(
string $string,
string $hash_type = self::STANDARD_HASH_SHORT
): string {
if (
!in_array(
$hash_type,
['crc32b', 'adler32', 'fnv132', 'fnv1a32', 'joaat']
)
) {
$hash_type = 'adler32';
}
return hash($hash_type, $string);
}
/**
* Wrapper function for standard long hashd
*
* @param string $string String to be hashed
* @return string Hashed string
*/
public static function __hashLong(string $string): string
{
return hash(self::STANDARD_HASH_LONG, $string);
}
/**
* create a unique id with the standard hash type defined in __hash
*
* @return string Unique ID with fixed length of 8 characters
* @deprecated Use \CoreLibs\Create\Uids::uniqIdShort() instead
*/
public static function __uniqId(): string
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, '
. '\CoreLibs\Create\Uids::uniqIdShort() class', E_USER_DEPRECATED);
return \CoreLibs\Create\Uids::uniqIdShort();
}
/**
* create a unique id with the standard long hash type
* defined in __hashLong
*
* @return string Unique ID with length of current default long hash
* @deprecated Use \CoreLibs\Create\Uids::uniqIdLong() instead
*/
public static function __uniqIdLong(): string
{
trigger_error('Method ' . __METHOD__ . ' is deprecated, '
. '\CoreLibs\Create\Uids::uniqIdLong() class', E_USER_DEPRECATED);
return \CoreLibs\Create\Uids::uniqIdLong();
}
}
// __END__

View File

@@ -1,121 +0,0 @@
<?php
/*
* random key functions
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class RandomKey
{
// key generation
/** @var string */
private static $key_range = '';
/** @var int */
private static $one_key_length;
/** @var int */
private static $key_length = 4; // default key length
/** @var int */
private static $max_key_length = 256; // max allowed length
/**
* if launched as class, init random key data first
*/
public function __construct()
{
$this->initRandomKeyData();
}
/**
* sets the random key range with the default values
*
* @return void has no return
*/
private static function initRandomKeyData()
{
// random key generation base string
self::$key_range = join('', array_merge(
range('A', 'Z'),
range('a', 'z'),
range('0', '9')
));
self::$one_key_length = strlen(self::$key_range);
}
/**
* validates they key length for random string generation
*
* @param int $key_length key length
* @return bool true for valid, false for invalid length
*/
private static function validateRandomKeyLenght(int $key_length): bool
{
if (
is_numeric($key_length) &&
$key_length > 0 &&
$key_length <= self::$max_key_length
) {
return true;
} else {
return false;
}
}
/**
* sets the key length and checks that they key given is valid
* if failed it will not change the default key length and return false
*
* @param int $key_length key length
* @return bool true/false for set status
*/
public static function setRandomKeyLength(int $key_length): bool
{
// only if valid int key with valid length
if (self::validateRandomKeyLenght($key_length) === true) {
self::$key_length = $key_length;
return true;
} else {
return false;
}
}
/**
* get the current set random key length
*
* @return int Current set key length
*/
public static function getRandomKeyLength(): int
{
return self::$key_length;
}
/**
* creates a random key based on the key_range with key_length
* if override key length is set, it will check on valid key and use this
* this will not set the class key length variable
*
* @param int $key_length key length override, -1 for use default
* @return string random key
*/
public static function randomKeyGen(int $key_length = -1): string
{
// init random key strings if not set
if (!is_numeric(self::$one_key_length)) {
self::initRandomKeyData();
}
$use_key_length = 0;
// only if valid int key with valid length
if (self::validateRandomKeyLenght($key_length) === true) {
$use_key_length = $key_length;
} else {
$use_key_length = self::$key_length;
}
// create random string
$random_string = '';
for ($i = 1; $i <= $use_key_length; $i++) {
$random_string .= self::$key_range[random_int(0, self::$one_key_length - 1)];
}
return $random_string;
}
}

View File

@@ -1,371 +0,0 @@
<?php
/*
* AUTHOR: Clemens Schwaighofer
* DESCRIPTION:
* start a php sesseion
* name can be given via startSession parameter
* if not set tries to read $SET_SESSION_NAME from global
* else will use default set in php.ini
*/
declare(strict_types=1);
namespace CoreLibs\Create;
class Session
{
/** @var string list for errors */
private $session_intern_error_str = '';
/**
* Start session
* startSession should be called for complete check
* If this is called without any name set before the php.ini name is
* used.
*
* @return void
*/
protected function startSessionCall(): void
{
session_start();
}
/**
* init a session, if array is empty or array does not have session_name set
* then no auto init is run
*
* @param string $session_name if set and not empty, will start session
*/
public function __construct(string $session_name = '')
{
if (!empty($session_name)) {
$this->startSession($session_name);
}
}
/**
* check if we are in CLI, we set this, so we can mock this
* Not this is just a wrapper for the static System::checkCLI call
*
* @return bool True if we are in a CLI enviroment, or false for everything else
*/
public function checkCliStatus(): bool
{
return \CoreLibs\Get\System::checkCLI();
}
/**
* Set session name call. If not valid session name, will return false
*
* @param string $session_name A valid string for session name
* @return bool True if session name is valid,
* False if not
*/
public function setSessionName(string $session_name): bool
{
if (!$this->checkValidSessionName($session_name)) {
return false;
}
session_name($session_name);
return true;
}
/**
* Return set error string, empty if none set
* Error strings are only set in the startSession method
*
* @return string Last error string
*/
public function getErrorStr(): string
{
return $this->session_intern_error_str;
}
/**
* check if session name is valid
*
* As from PHP 8.1/8.0/7.4 error
* INVALID CHARS: =,; \t\r\n\013\014
* NOTE: using . will fail even thought valid
* we allow only alphanumeric with - (dash) and 1 to 128 characters
*
* @param string $session_name any string, not null
* @return bool True for valid, False for invalid
*/
public static function checkValidSessionName(string $session_name): bool
{
// check
if (
// must only have those
!preg_match('/^[-a-zA-Z0-9]{1,128}$/', $session_name) ||
// cannot be only numbers
preg_match('/^[0-9]+$/', $session_name)
) {
return false;
}
return true;
}
/**
* start session with given session name if set
* aborts on command line or if sessions are not enabled
* also aborts if session cannot be started
* On sucess returns the session id
*
* @param string|null $session_name
* @return string|bool
*/
public function startSession(?string $session_name = null)
{
// we can't start sessions on command line
if ($this->checkCliStatus()) {
$this->session_intern_error_str = '[SESSION] No sessions in php cli';
return false;
}
// if session are OFF
if ($this->getSessionStatus() === PHP_SESSION_DISABLED) {
$this->session_intern_error_str = '[SESSION] Sessions are disabled';
return false;
}
// session_status
// initial the session if there is no session running already
if (!$this->checkActiveSession()) {
// if session name is emtpy, check if there is a global set
// this is a deprecated fallback
$session_name = $session_name ?? $GLOBALS['SET_SESSION_NAME'] ?? '';
// DEPRECTED: constant SET_SESSION_NAME is no longer used
// if set, set special session name
if (!empty($session_name)) {
// invalid session name, abort
if (!$this->checkValidSessionName($session_name)) {
$this->session_intern_error_str = '[SESSION] Invalid session name: ' . $session_name;
return false;
}
$this->setSessionName($session_name);
}
// start session
$this->startSessionCall();
}
// if we still have no active session
if (!$this->checkActiveSession()) {
$this->session_intern_error_str = '[SESSION] Failed to activate session';
return false;
}
if (false === ($session_id = $this->getSessionId())) {
$this->session_intern_error_str = '[SESSION] getSessionId did not return a session id';
}
return $session_id;
}
/**
* get current set session id or false if none started
*
* @return string|bool
*/
public function getSessionId()
{
return session_id();
}
/**
* get set session name or false if none started
*
* @return string|bool
*/
public function getSessionName()
{
return session_name();
}
/**
* Checks if there is an active session.
* Does not check if we can have a session
*
* @return bool True if there is an active session, else false
*/
public function checkActiveSession(): bool
{
if ($this->getSessionStatus() === PHP_SESSION_ACTIVE) {
return true;
} else {
return false;
}
}
/**
* unlock the session file, so concurrent AJAX requests can be done
* NOTE: after this has been called, no changes in _SESSION will be stored
* NOTE: a new session with a different name can be started after this one is called
* if problem, run ob_flush() and flush() too
*
* @return bool True und sucess, false on failure
*/
public function writeClose(): bool
{
return session_write_close();
}
/**
* Proper destroy a session
* - unset the _SESSION array
* - unset cookie if cookie on and we have not strict mode
* - destroy session
*
* @return bool
*/
public function sessionDestroy(): bool
{
$_SESSION = [];
if (
ini_get('session.use_cookies') &&
!ini_get('session.use_strict_mode')
) {
$session_name = $this->getSessionName();
if ($session_name === false) {
$session_name = '';
}
$params = session_get_cookie_params();
setcookie(
(string)$session_name,
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}
return session_destroy();
}
/**
* get session status
* PHP_SESSION_DISABLED if sessions are disabled.
* PHP_SESSION_NONE if sessions are enabled, but none exists.
* PHP_SESSION_ACTIVE if sessions are enabled, and one exists.
*
* https://www.php.net/manual/en/function.session-status.php
*
* @return int See possible return int values above
*/
public function getSessionStatus(): int
{
return session_status();
}
// _SESSION set/unset methods
/**
* unset all _SESSION entries
*
* @return void
*/
public function unsetAllS(): void
{
foreach (array_keys($_SESSION ?? []) as $name) {
unset($_SESSION[$name]);
}
}
/**
* set _SESSION entry 'name' with any value
*
* @param string|int $name array name in _SESSION
* @param mixed $value value to set (can be anything)
* @return void
*/
public function setS($name, $value): void
{
$_SESSION[$name] = $value;
}
/**
* get _SESSION 'name' entry or empty string if not set
*
* @param string|int $name value key to get from _SESSION
* @return mixed value stored in _SESSION
*/
public function getS($name)
{
return $_SESSION[$name] ?? '';
}
/**
* Check if a name is set in the _SESSION array
*
* @param string|int $name Name to check for
* @return bool True for set, False fornot set
*/
public function issetS($name): bool
{
return isset($_SESSION[$name]);
}
/**
* unset one _SESSION entry 'name' if exists
*
* @param string|int $name _SESSION key name to remove
* @return void
*/
public function unsetS($name): void
{
if (isset($_SESSION[$name])) {
unset($_SESSION[$name]);
}
}
// set/get below
// ->var = value;
/**
* Undocumented function
*
* @param string|int $name
* @param mixed $value
* @return void
*/
public function __set($name, $value): void
{
$_SESSION[$name] = $value;
}
/**
* Undocumented function
*
* @param string|int $name
* @return mixed
*/
public function __get($name)
{
if (isset($_SESSION[$name])) {
return $_SESSION[$name];
}
}
/**
* Undocumented function
*
* @param string|int $name
* @return bool
*/
public function __isset($name): bool
{
return isset($_SESSION[$name]);
}
/**
* Undocumented function
*
* @param string|int $name
* @return void
*/
public function __unset($name): void
{
if (isset($_SESSION[$name])) {
unset($_SESSION[$name]);
}
}
}
// __END__

View File

@@ -1,108 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Create;
class Uids
{
// what to use as a default hash if non ise set and no DEFAULT_HASH is defined
public const DEFAULT_HASH = 'sha256';
public const STANDARD_HASH_LONG = 'ripemd160';
public const STANDARD_HASH_SHORT = 'adler32';
/**
* creates psuedo random uuid v4
* Code take from class here:
* https://www.php.net/manual/en/function.uniqid.php#94959
*
* @return string pseudo random uuid v4
*/
public static function uuidv4(): string
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
}
/**
* TODO: make this a proper uniq ID creation
* add uuidv4 subcall to the uuid function too
* creates a uniq id
*
* @param string $type uniq id type, currently md5 or sha256 allowed
* if not set will use DEFAULT_HASH if set
* @return string uniq id
*/
public static function uniqId(string $type = ''): string
{
$uniq_id = '';
switch ($type) {
case 'md5':
$uniq_id = md5(uniqid((string)rand(), true));
break;
case self::DEFAULT_HASH:
$uniq_id = hash(self::DEFAULT_HASH, uniqid((string)rand(), true));
break;
case self::STANDARD_HASH_LONG:
$uniq_id = hash(self::STANDARD_HASH_LONG, uniqid((string)rand(), true));
break;
case self::STANDARD_HASH_SHORT:
$uniq_id = hash(self::STANDARD_HASH_SHORT, uniqid((string)rand(), true));
break;
default:
// if not empty, check if in valid list
if (
!empty($type) &&
in_array($type, hash_algos())
) {
$hash = $type;
} else {
// fallback to default hash type if none set or invalid
$hash = self::DEFAULT_HASH;
}
$uniq_id = hash($hash, uniqid((string)rand(), true));
break;
}
return $uniq_id;
}
/**
* create a unique id with the standard hash type defined in __hash
*
* @return string Unique ID with fixed length of 8 characters
*/
public static function uniqIdShort(): string
{
return self::uniqId(self::STANDARD_HASH_SHORT);
}
/**
* create a unique id with the standard long hash type
* defined in __hashLong
*
* @return string Unique ID with length of current default long hash
*/
public static function uniqIdLong(): string
{
return self::uniqId(self::STANDARD_HASH_LONG);
}
}
// __END__

View File

@@ -1,655 +0,0 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2002/12/17
* VERSION: 1.0.0
* RELEASED LICENSE: GNU GPL 3
* SHORT DESC :RIPTION:
* DB Array IO Class:
* writes, reads or deletes a complete array (one data set) in/out a
* table from the connected DB.
* you don't have to write any SQL queries, worry over update/insert
*
* HISTORY:
* 2019/9/11 (cs) error string 21->1021, 22->1022 for not overlapping with IO
* 2005/07/07 (cs) updated array class for postgres: set 0 & NULL if int field given, insert uses () values () syntax
* 2005/03/31 (cs) fixed the class call with all debug vars
* 2003-03-10: error_ids where still wrong chagned 11->21 and 12->22
* 2003-02-26: db_array_io is no longer single class but extens db_io,
* as it needs it anyway
* moved the class info vars into class_info array into
* the constructor, removed info function
* 2003-02-24: in db_delete moved query build to top, or pk_name/value
* will be reset before delete is done
* 2002-12-20: just added info() method
* 2002-12-17: splitted the class from other file (with main db wrapper)
*********************************************************************/
// picture upload should be taken out from here and out in media_class
// as it actually has nothing to do with this one here ? (or at least
// put into separete function in this class)
declare(strict_types=1);
namespace CoreLibs\DB\Extended;
// subclass for one array handling
class ArrayIO extends \CoreLibs\DB\IO
{
// main calss variables
/** @var array<mixed> */
public $table_array; // the array from the table to work on
/** @var string */
public $table_name; // the table_name
/** @var string */
public $pk_name = ''; // the primary key from this table
/** @var int|string|null */
public $pk_id; // the PK id
// security values
/** @var int base acl for current page */
private $base_acl_level = 0;
/**
* constructor for the array io class, set the
* primary key name automatically (from array)
*
* @param array<mixed> $db_config db connection config
* @param array<mixed> $table_array table array config
* @param string $table_name table name string
* @param \CoreLibs\Debug\Logging|null $log Logging class, default set if not set
* @param int $base_acl_level Set base acl level, if needed
* @param int $acl_admin Flag if this is an admin ACL access level
*/
public function __construct(
array $db_config,
array $table_array,
string $table_name,
\CoreLibs\Debug\Logging $log = null,
int $base_acl_level = 0,
int $acl_admin = 0
) {
// instance db_io class
parent::__construct($db_config, $log ?? new \CoreLibs\Debug\Logging());
// more error vars for this class
$this->error_string['1999'] = 'No table array or table name set';
$this->error_string['1021'] = 'No Primary Key given';
$this->error_string['1022'] = 'Could not run Array Query';
$this->table_array = $table_array;
$this->table_name = $table_name;
// error abort if no table array or no table name
if (empty($table_array) || empty($table_name)) {
$this->__dbError(1999, false, 'MAJOR ERROR: Core settings missing');
}
// set primary key for given table_array
foreach ($this->table_array as $key => $value) {
if (!empty($value['pk'])) {
$this->pk_name = $key;
}
}
$this->dbArrayIOSetAcl($base_acl_level, $acl_admin);
}
/**
* class deconstructor
*/
public function __destruct()
{
parent::__destruct();
}
/**
* set the base acl level and admin acl flag
* This is needed for table array ACL checks
* if not set I assume 0 (non write/non read/non admin)
*
* @param int $base_acl_level ACL Level from 0 to 100, -1 is not allowed
* Will sett 0 if invalid
* @param int $acl_admin 0 for non admin, 1 for admin (base acl is 100)
* @return void
*/
public function dbArrayIOSetAcl(int $base_acl_level, int $acl_admin): void
{
// default not allowed, must be 0 at least
if ($base_acl_level < 0) {
$base_acl_level = 0;
}
// only 0 or 1 allowed
if (!in_array($acl_admin, [0, 1])) {
$acl_admin = 0;
}
// if the user is admin flagged, auto set to 100, if not already set to 100
if ($acl_admin == 1) {
$base_acl_level = 100;
}
$this->base_acl_level = $base_acl_level;
}
/**
* changes all previously alterd HTML code into visible one,
* works for <b>,<i>, and <a> (thought <a> can be / or should
* be handled with the magic links functions
* used with the read function
*
* @param string $text any html encoded string
* @return string decoded html string
*/
public function convertData($text): string
{
$text = str_replace('&lt;b&gt;', '<b>', $text);
$text = str_replace('&lt;/b&gt;', '</b>', $text);
$text = str_replace('&lt;i&gt;', '<i>', $text);
$text = str_replace('&lt;/i&gt;', '</i>', $text);
// my need a change
$text = str_replace('&lt;a href=&quot;', '<a target="_blank" href="', $text);
$text = str_replace('&quot;&gt;', '">', $text);
$text = str_replace('&lt;/a&gt;', '</a>', $text);
return $text;
}
/**
* changeds all HTML entities into non HTML ones
*
* @param string $text encoded html string
* @return string decoded html string
*/
public function convertEntities($text): string
{
$text = str_replace('&lt;', '<', $text);
$text = str_replace('&gt;', '>', $text);
$text = str_replace('&amp;', '&', $text);
$text = str_replace('&quot;', '"', $text);
$text = str_replace('&#039;', "'", $text);
return $text;
}
/**
* dumps the current data
*
* @param bool $write write to error message, default false
* @return string the array data as html string entry
*/
public function dbDumpArray($write = false): string
{
reset($this->table_array);
$string = '';
foreach ($this->table_array as $column => $data_array) {
$string .= '<b>' . $column . '</b> -> ' . $data_array['value'] . '<br>';
}
// add output to internal error_msg
if ($write === true) {
$this->__dbDebug('dbArray', $string);
}
return $string;
}
/**
* checks if pk is set and if not, set from pk_id and
* if this also not set return 0
*
* @return bool true if pk value is set, else false
*/
public function dbCheckPkSet()
{
// if pk_id is set, overrule ...
if ($this->pk_id) {
$this->table_array[$this->pk_name]['value'] = $this->pk_id;
}
// if not set ... produce error
if (!$this->table_array[$this->pk_name]['value']) {
// if no PK found, error ...
$this->__dbError(1021);
return false;
} else {
return true;
}
}
/**
* resets the whole array values
* @param boolean $reset_pk true if we want to reset the pk too
* @return void has no return
*/
public function dbResetArray($reset_pk = false): void
{
reset($this->table_array);
foreach (array_keys($this->table_array) as $column) {
if (!$this->table_array[$column]['pk']) {
unset($this->table_array[$column]['value']);
} elseif ($reset_pk) {
unset($this->table_array[$column]['value']);
}
}
}
/**
* deletes one dataset
*
* @param array<mixed> $table_array optional override for table array set
* set this as new table array too
* @param boolean $acl_limit [false], if set to true, well do ACL limit check
* @return array<mixed> returns the table array that was deleted
*/
public function dbDelete($table_array = [], $acl_limit = false)
{
// is array and has values, override set and set new
if (is_array($table_array) && count($table_array)) {
$this->table_array = $table_array;
}
if (!$this->dbCheckPkSet()) {
return $this->table_array;
}
if ($acl_limit === true && $this->base_acl_level < 100) {
$this->log->debug('DB DELETE ERROR', 'ACL Limit on, Delete, '
. 'but base ACL level of 100 not met: ' . $this->base_acl_level);
return $this->table_array;
}
// delete query
$q = 'DELETE FROM ' . $this->table_name . ' WHERE ';
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
// delete files and build FK query
reset($this->table_array);
$q_where = '';
foreach (array_keys($this->table_array) as $column) {
// suchen nach bildern und lschen ...
if (
!empty($this->table_array[$column]['file']) &&
file_exists($this->table_array[$column]['url'] . $this->table_array[$column]['value'])
) {
if (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
unlink($this->table_array[$column]['path'] . $this->table_array[$column]['value']);
}
$file_name = str_replace('_tn', '', $this->table_array[$column]['value']);
if (file_exists($this->table_array[$column]['path'] . $file_name)) {
unlink($this->table_array[$column]['path'] . $file_name);
}
}
// if we have a foreign key
if (!empty($this->table_array[$column]['fk'])) {
// create FK constraint checks
if ($q_where) {
$q_where .= ' AND ';
}
$q_where .= $column . ' = ' . $this->table_array[$column]['value'];
}
// allgemeines zurcksetzen des arrays
unset($this->table_array[$column]['value']);
}
// attach fk row if there ...
if ($q_where) {
$q .= ' AND ' . $q_where;
}
// if 0, error
$this->pk_id = null;
if (!$this->dbExec($q)) {
$this->__dbError(1022);
}
return $this->table_array;
}
/**
* reads one row into the array
*
* @param boolean $edit on true convert data, else as is
* @param array<mixed> $table_array optional table array, overwrites
* internal set array
* @return array<mixed> set table array with values
*/
public function dbRead($edit = false, $table_array = [])
{
// if array give, overrules internal array
if (is_array($table_array) && count($table_array)) {
$this->table_array = $table_array;
}
if (!$this->dbCheckPkSet()) {
return $this->table_array;
}
reset($this->table_array);
$q_select = '';
$q_where = '';
// create select part & addition FK part
foreach ($this->table_array as $column => $data_array) {
if ($q_select) {
$q_select .= ', ';
}
if (
!empty($data_array['type']) && $data_array['type'] == 'datetime' &&
!empty($data_array['sql_read'])
) {
// convert tom different timestamp type
$q_select .= "TO_CHAR($column, '" . $data_array['sql_read'] . "') AS $column";
} else {
$q_select .= $column;
}
// check FK ...
if (
isset($this->table_array[$column]['fk']) &&
isset($this->table_array[$column]['value'])
) {
if (!empty($q_where)) {
$q_where .= ' AND ';
}
$q_where .= $column .= ' = ' . $this->table_array[$column]['value'];
}
}
$q = 'SELECT ';
$q .= $q_select;
$q .= ' FROM ' . $this->table_name . ' WHERE ';
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
if ($q_where) {
$q .= ' AND ' . $q_where;
}
// if query was executed okay, else set error
if ($this->dbExec($q)) {
if (is_array($res = $this->dbFetchArray())) {
reset($this->table_array);
foreach ($this->table_array as $column => $data_array) {
// wenn "edit" dann gib daten wie in DB zurück, ansonten aufbereiten fr ausgabe
// ?? sollte das nicht drauen ??? man weis ja net was da drin steht --> is noch zu berlegen
// $this->log->debug('DB READ', 'EDIT: $edit | Spalte: $column | type: '
// .$this->table_array[$column]['type'].' | Res: '.$res[$column]);
if ($edit) {
$this->table_array[$column]['value'] = $res[$column];
// if password, also write to hidden
if (
isset($this->table_array[$column]['type']) &&
$this->table_array[$column]['type'] == 'password'
) {
$this->table_array[$column]['HIDDEN_value'] = $res[$column];
}
} else {
$this->table_array[$column]['value'] = $this->convertData(nl2br($res[$column]));
// had to put out the htmlentities from the line above as it breaks japanese characters
}
}
}
// possible dbFetchArray errors ...
$this->pk_id = $this->table_array[$this->pk_name]['value'];
} else {
$this->__dbError(1022);
}
return $this->table_array;
}
/**
* writes one set into DB or updates one set (if PK exists)
*
* @param boolean $addslashes old convert entities and set set escape
* @param array<mixed> $table_array optional table array, overwrites internal one
* @param boolean $acl_limit [false], if set to true, well do ACL limit check
* @return array<mixed> table array or null
*/
public function dbWrite(
bool $addslashes = false,
array $table_array = [],
bool $acl_limit = false
): array {
if (is_array($table_array) && count($table_array)) {
$this->table_array = $table_array;
}
// PK ID check
// if ($this->pk_id && !$this->table_array[$this->pk_name]["value"]) {
// $this->table_array[$this->pk_name]["value"]=$this->pk_id;
// }
// checken ob PKs gesetzt, wenn alle -> update, wenn keiner -> insert, wenn ein paar -> ERROR!
if (!$this->table_array[$this->pk_name]['value']) {
$insert = 1;
} else {
$insert = 0;
}
// early abort for new write with not enough ACL level
if ($insert && $acl_limit === true && $this->base_acl_level < 100) {
$this->log->debug('DB WRITE ERROR', 'ACL Limit on, Insert, '
. 'but base ACL level of 100 not met: ' . $this->base_acl_level);
return $this->table_array;
}
reset($this->table_array);
$q_data = '';
$q_vars = '';
$q_where = '';
foreach ($this->table_array as $column => $data_array) {
/********************************* START FILE *************************************/
// file upload
if (isset($this->table_array[$column]['file'])) {
// falls was im tmp drinnen, sprich ein upload, datei kopieren, Dateinamen in db schreiben
// falls datei schon am server (physischer pfad), dann einfach url in db schreiben (update)
// falls in 'delete' 'ja' dann loeschen (und gibts eh nur beim update)
if ($this->table_array[$column]['delete']) {
unset($this->table_array[$column]['delete']);
if (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
unlink($this->table_array[$column]['path'] . $this->table_array[$column]['value']);
}
$file_name = str_replace('_tn', '', $this->table_array[$column]['value']);
if (file_exists($this->table_array[$column]['path'] . $file_name)) {
unlink($this->table_array[$column]['path'] . $file_name);
}
$this->table_array[$column]['value'] = '';
} else {
if ($this->table_array[$column]['tmp'] != 'none' && $this->table_array[$column]['tmp']) {
// mozilla, patch
$fn_name = explode('/', $this->table_array[$column]['dn']);
$this->table_array[$column]['dn'] = $fn_name[count($fn_name) - 1];
$filename_parts = explode('.', $this->table_array[$column]['dn']);
$ext = end($filename_parts);
array_splice($filename_parts, -1, 1);
$name = str_replace(' ', '_', implode('.', $filename_parts));
$file_name = $name . '.' . $ext;
//echo 'Dn: $file_name';
copy($this->table_array[$column]['tmp'], $this->table_array[$column]['path'] . $file_name);
// automatisch thumbnail generieren, geht nur mit convert (ImageMagic!!!), aber nur bei bild ..
if (in_array(strtolower($ext), ['jpeg', 'jpg', 'gif', 'png'])) {
$file_name_tn = $name . '_tn.' . $ext;
$input = $this->table_array[$column]['path'] . $file_name;
$output = $this->table_array[$column]['path'] . $file_name_tn;
$com = 'convert -geometry 115 ' . $input . ' ' . $output;
exec($com);
$this->table_array[$column]['value'] = $file_name_tn;
} else {
$this->table_array[$column]['value'] = $file_name;
}
} elseif (file_exists($this->table_array[$column]['path'] . $this->table_array[$column]['value'])) {
// mach gar nix, wenn bild schon da ???
}
} // delete or upload
} // file IF
/********************************* END FILE **************************************/
// do not write 'pk' (primary key) or 'view' values
// also do not write UPDATE for elements that are
// acl flagged, not if we have an ACL limiter, don't insert
// $this->log->debug('DB WRITE', 'C: ' . $column . ', '
// . 'ACL Level ' . $this->log->prBl($acl_limit) . ', '
// . 'TA ACL: ' . ($this->table_array[$column]['min_edit_acl'] ?? 100) . ', '
// . 'Base ACL: ' . $this->base_acl_level);
if (
!isset($this->table_array[$column]['pk']) &&
isset($this->table_array[$column]['type']) &&
$this->table_array[$column]['type'] != 'view' &&
strlen($column) > 0 &&
// no acl limiter
($acl_limit === false ||
(
// acl limit is true, min edit must be at larger than set
$acl_limit === true &&
$this->base_acl_level >=
($this->table_array[$column]['min_edit_acl'] ?? 100)
))
) {
// for password use hidden value if main is not set
if (
isset($this->table_array[$column]['type']) &&
$this->table_array[$column]['type'] == 'password' &&
empty($this->table_array[$column]['value'])
) {
$this->table_array[$column]['value'] = $this->table_array[$column]['HIDDEN_value'];
}
if (!$insert) {
if (strlen($q_data)) {
$q_data .= ', ';
}
$q_data .= $column . ' = ';
} else {
// this is insert
if (strlen($q_data)) {
$q_data .= ', ';
}
if (strlen($q_vars)) {
$q_vars .= ', ';
}
$q_vars .= $column;
}
// integer is different
if (isset($this->table_array[$column]['int']) || isset($this->table_array[$column]['int_null'])) {
$this->log->debug('WRITE CHECK', '[' . $column . '][' . $this->table_array[$column]['value'] . ']'
. '[' . $this->table_array[$column]['type'] . '] '
. 'VALUE SET: ' . (string)isset($this->table_array[$column]['value'])
. ' | INT NULL: ' . (string)isset($this->table_array[$column]['int_null']));
if (
isset($this->table_array[$column]['value']) &&
!$this->table_array[$column]['value'] &&
isset($this->table_array[$column]['int_null'])
) {
$_value = 'NULL';
} elseif (
!isset($this->table_array[$column]['value']) ||
(isset($this->table_array[$column]['value']) && !$this->table_array[$column]['value'])
) {
$_value = 0;
} else {
$_value = $this->table_array[$column]['value'];
}
$q_data .= $_value;
} elseif (isset($this->table_array[$column]['bool'])) {
// boolean storeage (reverse check on ifset)
$q_data .= "'" . $this->dbBoolean($this->table_array[$column]['value'], true) . "'";
} elseif (
isset($this->table_array[$column]['interval']) ||
isset($this->table_array[$column]['date']) ||
isset($this->table_array[$column]['datetime']) ||
isset($this->table_array[$column]['emptynull'])
) {
// for interval we check if no value, then we set null
if (
!isset($this->table_array[$column]['value']) ||
(isset($this->table_array[$column]['value']) && !$this->table_array[$column]['value'])
) {
$_value = 'NULL';
} elseif (isset($this->table_array[$column]['value'])) {
$_value = $this->dbEscapeLiteral($this->table_array[$column]['value']);
} else {
// fallback
$_value = 'NULL';
}
$q_data .= $_value;
} else {
// if the error check is json, we set field to null if NOT set
// else normal string write
if (
isset($this->table_array[$column]['error_check']) &&
$this->table_array[$column]['error_check'] == 'json' &&
(
!isset($this->table_array[$column]['value']) ||
(isset($this->table_array[$column]['value']) &&
!$this->table_array[$column]['value'])
)
) {
$q_data .= 'NULL';
} else {
// normal string
$q_data .= "'";
// if add slashes do convert & add slashes else write AS is
if ($addslashes) {
$q_data .= $this->dbEscapeString(
$this->convertEntities($this->table_array[$column]['value'])
);
} else {
$q_data .= $this->dbEscapeString($this->table_array[$column]['value']);
}
$q_data .= "'";
}
}
}
} // while ...
if (empty($q_data)) {
$this->log->debug('DB WRITE ERROR', 'No data to write, possible through ACL');
return $this->table_array;
}
// NOW get PK, and FK settings (FK only for update query)
// get it at the end, cause now we can be more sure of no double IDs, etc
reset($this->table_array);
// create select part & addition FK part
foreach ($this->table_array as $column => $data_array) {
// check FK ...
if (
isset($this->table_array[$column]['fk']) &&
isset($this->table_array[$column]['value'])
) {
if (!empty($q_where)) {
$q_where .= ' AND ';
}
$q_where .= $column .= ' = ' . $this->table_array[$column]['value'];
}
}
// if no PK set, then get max ID from DB
if (!$this->table_array[$this->pk_name]['value']) {
// max id, falls INSERT
$q = 'SELECT MAX(' . $this->pk_name . ') + 1 AS pk_id FROM ' . $this->table_name;
if (is_array($res = $this->dbReturnRow($q))) {
$pk_id = $res['pk_id'];
} else {
$pk_id = 1;
}
$this->table_array[$this->pk_name]['value'] = $pk_id;
}
if (!$insert) {
$q = 'UPDATE ' . $this->table_name . ' SET ';
$q .= $q_data;
$q .= ' WHERE ';
$q .= $this->pk_name . ' = ' . $this->table_array[$this->pk_name]['value'] . ' ';
if (!empty($q_where)) {
$q .= ' AND ' . $q_where;
}
// set pk_id ... if it has changed or so
$this->pk_id = $this->table_array[$this->pk_name]['value'];
} else {
$q = 'INSERT INTO ' . $this->table_name . ' ';
$q .= '(' . $q_vars . ') ';
$q .= 'VALUES (' . $q_data . ')';
// write primary key too
// if ($q_data)
// $q .= ", ";
// $q .= $this->pk_name." = ".$this->table_array[$this->pk_name]['value']." ";
// $this->pk_id = $this->table_array[$this->pk_name]['value'];
}
// return success or not
if (!$this->dbExec($q)) {
$this->__dbError(1022);
}
// set primary key
if ($insert) {
$insert_id = $this->dbGetInsertPK();
if (is_array($insert_id)) {
$insert_id = 0;
}
$this->table_array[$this->pk_name]['value'] = $insert_id;
$this->pk_id = $insert_id;
}
// return the table if needed
return $this->table_array;
}
// end of class
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -1,883 +0,0 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2003/04/09
* SHORT DESCRIPTION:
* 2018/3/23, the whole class system is transformed to namespaces
* also all internal class calls are converted to camel case
*
* pgsql wrapper calls
*
* HISTORY:
* 2008/04/16 (cs) wrapper for pg escape string
* 2007/01/11 (cs) add prepare/execute for postgres
* 2006/09/12 (cs) in case db_query retuns false, save the query and
* run the query through the send/get procedure to get
* correct error data from the db
* 2006/06/26 (cs) added port for db connection
* 2006/04/03 (cs) added meta data for table
* 2005/07/25 (cs) removed the plural s remove, not needed and not 100% working
* 2005/07/07 (cs) the default it is table_name _ id
* 2005/01/19 (cs) changed the pgsql connect, so it dies if it can't connect to the DB
* 2004/09/30 (cs) layout cleanup
*
*
* collection of PostgreSQL wrappers
*
* pg_prepare
* pg_execute
* pg_num_rows
* pg_num_fields
* pg_field_name
* pg_affected_rows (*)
* pg_fetch_array
* pg_query
* pg_send_query
* pg_get_result
* pg_connection_busy
* pg_close
* pg_connect (*)
* pg_meta_data
* pg_escape_string
*
*/
declare(strict_types=1);
namespace CoreLibs\DB\SQL;
// below no ignore is needed if we want to use PgSql interface checks with PHP 8.0
// as main system. Currently all @var sets are written as object
/** @#phan-file-suppress PhanUndeclaredTypeProperty,PhanUndeclaredTypeParameter,PhanUndeclaredTypeReturnType */
class PgSQL implements \CoreLibs\DB\SQL\SqlInterface\SqlFunctions
{
/** @var string */
private $last_error_query;
// NOTE for PHP 8.1 this is no longer a resource
/** @var object|resource|bool */ // replace object with PgSql\Connection
private $dbh;
/**
* queries last error query and returns true or false if error was set
*
* @return bool true/false if last error is set
*/
public function __dbLastErrorQuery(): bool
{
if ($this->last_error_query) {
return true;
} else {
return false;
}
}
/**
* wrapper for pg_query, catches error and stores it in class var
*
* @param string $query Query string
* @return object|resource|bool query result (PgSql\Result)
*/
public function __dbQuery(string $query)
{
$this->last_error_query = '';
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
// read out the query status and save the query if needed
$result = pg_query($this->dbh, $query);
if ($result === false) {
$this->last_error_query = $query;
}
return $result;
}
/**
* Proposed
* wrapperf or pg_query_params for queries in the style of
* SELECT foo FROM bar WHERE foobar = $1
*
* @param string $query Query string with placeholders $1, ..
* @param array<mixed> $params Matching parameters for each placerhold
* @return object|resource|bool Query result (PgSql\Result)
*/
public function __dbQueryParams(string $query, array $params)
{
$this->last_error_query = '';
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
// parse query and get all $n entries
// TODO count of $n must match params
// read out the query status and save the query if needed
$result = pg_query_params($this->dbh, $query, $params);
if ($result === false) {
$this->last_error_query = $query;
}
return $result;
}
/**
* sends an async query to the server
*
* @param string $query query string
* @return bool true/false if query was sent successful
*/
public function __dbSendQuery(string $query): bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
$result = pg_send_query($this->dbh, $query);
return $result ? true : false;
}
/**
* wrapper for pg_get_result
*
* @return object|resource|bool resource handler or false for error (PgSql\Result)
*/
public function __dbGetResult()
{
$this->last_error_query = '';
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
$result = pg_get_result($this->dbh);
if ($result === false) {
return false;
}
if ($error = pg_result_error($result)) {
$this->last_error_query = $error;
}
return $result;
}
/**
* wrapper for pg_close
*
* @return void has no return
*/
public function __dbClose(): void
{
if ($this->dbh === false || is_bool($this->dbh)) {
return;
}
if (pg_connection_status($this->dbh) === PGSQL_CONNECTION_OK) {
// in 8.1 this throws an error, and we don't need that anyway
// pg_close($this->dbh);
}
}
/**
* wrapper for pg_prepare
*
* @param string $name statement name
* @param string $query query string
* @return object|resource|bool prepare statement handler or
* false for error (PgSql\Result)
*/
public function __dbPrepare(string $name, string $query)
{
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
$result = pg_prepare($this->dbh, $name, $query);
if (!$result) {
$this->last_error_query = $query;
}
return $result;
}
/**
* wrapper for pg_execute for running a prepared statement
*
* @param string $name statement name
* @param array<mixed> $data data array
* @return object|resource|bool returns status or false for error (PgSql\Result)
*/
public function __dbExecute(string $name, array $data)
{
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
$result = pg_execute($this->dbh, $name, $data);
if (!$result) {
$this->last_error_query = $name;
}
return $result;
}
/**
* wrapper for pg_num_rows
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return int number of rows, -1 on error
*/
public function __dbNumRows($cursor): int
{
if ($cursor === false || is_bool($cursor)) {
return -1;
}
return pg_num_rows($cursor);
}
/**
* wrapper for pg_num_fields
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return int number for fields in result, -1 on error
*/
public function __dbNumFields($cursor): int
{
if ($cursor === false || is_bool($cursor)) {
return -1;
}
return pg_num_fields($cursor);
}
/**
* wrapper for pg_field_name
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @param int $i field position
* @return string|bool name or false on error
*/
public function __dbFieldName($cursor, int $i)
{
if ($cursor === false || is_bool($cursor)) {
return false;
}
return pg_field_name($cursor, $i);
}
/**
* wrapper for pg_fetch_array
* if through/true false, use __dbResultType(true)
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @param int $result_type result type as int number
* @return array<mixed>|bool array result data or false on end/error
*/
public function __dbFetchArray($cursor, int $result_type = PGSQL_BOTH)
{
if ($cursor === false || is_bool($cursor)) {
return false;
}
// result type is passed on as is [should be checked]
return pg_fetch_array($cursor, null, $result_type);
}
/**
* simple match up between assoc true/false
*
* @param bool $assoc_type true (default) for PGSQL_ASSOC, false for PGSQL_BOTH
* @return int valid result type for fetch array
*/
public function __dbResultType(bool $assoc_type = true): int
{
if ($assoc_type == true) {
return PGSQL_ASSOC;
}
// fallback to default
return PGSQL_BOTH;
}
/**
* wrapper for pg_fetch_all
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return array<mixed>|bool data array or false for end/error
*/
public function __dbFetchAll($cursor)
{
if ($cursor === false || is_bool($cursor)) {
return false;
}
return pg_fetch_all($cursor);
}
/**
* wrapper for pg_affected_rows
*
* @param object|resource|bool $cursor cursor PgSql\Result (former resource)
* @return int affected rows, 0 for none, -1 for error
*/
public function __dbAffectedRows($cursor): int
{
if ($cursor === false || is_bool($cursor)) {
return -1;
}
return pg_affected_rows($cursor);
}
/**
* reads the last inserted primary key for the query
* if there is no pk_name tries to auto built it from the table name
* this only works if db schema is after no plural names
* and pk name is table name + _id
*
* detects schema prefix in table name
* @param string $query query string
* @param string|null $pk_name primary key name, if '' then auto detect
* @return string|int|false primary key value
*/
public function __dbInsertId(string $query, ?string $pk_name)
{
// only if an insert has been done
if (preg_match("/^insert /i", $query)) {
$schema = '';
// get table name from insert
$array = explode(' ', $query);
$_table = $array[2];
// if there is a dot inside, we need to split
if (strstr($_table, '.')) {
list($schema, $table) = explode('.', $_table);
} else {
$table = $_table;
}
// no PK name given at all
if (empty($pk_name)) {
// if name is plurar, make it singular
// if (preg_match("/.*s$/i", $table))
// $table = substr($table, 0, -1);
// set pk_name to "id"
$pk_name = $table . "_id";
}
$seq = ($schema ? $schema . '.' : '') . $table . "_" . $pk_name . "_seq";
$q = "SELECT CURRVAL('$seq') AS insert_id";
// I have to do manually or I overwrite the original insert internal vars ...
if ($q = $this->__dbQuery($q)) {
if (is_array($res = $this->__dbFetchArray($q))) {
list($id) = $res;
} else {
return false;
}
} else {
$id = [-1, $q];
}
return $id;
} else {
//if not insert, return false
return false;
}
}
/**
* queries database for the primary key name to this table in the selected schema
*
* @param string $table table name
* @param string $schema optional schema name, '' for default
* @return string|bool primary key name or false if not found
*/
public function __dbPrimaryKey(string $table, string $schema = '')
{
if ($table) {
// check if schema set is different from schema given,
// only needed if schema is not empty
$table_prefix = '';
if ($schema) {
$search_path = $this->__dbGetSchema();
if ($search_path != $schema) {
$table_prefix = $schema . '.';
}
}
// read from table the PK name
// faster primary key get
$q = "SELECT pg_attribute.attname AS column_name, "
. "format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS type "
. "FROM pg_index, pg_class, pg_attribute ";
if ($schema) {
$q .= ", pg_namespace ";
}
$q .= "WHERE "
// regclass translates the OID to the name
. "pg_class.oid = '" . $table_prefix . $table . "'::regclass AND "
. "indrelid = pg_class.oid AND ";
if ($schema) {
$q .= "nspname = '" . $schema . "' AND "
. "pg_class.relnamespace = pg_namespace.oid AND ";
}
$q .= "pg_attribute.attrelid = pg_class.oid AND "
. "pg_attribute.attnum = any(pg_index.indkey) "
. "AND indisprimary";
$cursor = $this->__dbQuery($q);
if ($cursor !== false) {
$__db_fetch_array = $this->__dbFetchArray($cursor);
if (!is_array($__db_fetch_array)) {
return false;
}
return $__db_fetch_array['column_name'] ?? false;
} else {
return false;
}
} else {
return false;
}
}
/**
* wrapper for pg_connect, writes out failure to screen if error occurs (hidden var)
*
* @param string $db_host host name
* @param string $db_user user name
* @param string $db_pass password
* @param string $db_name databse name
* @param integer $db_port port (int, 5432 is default)
* @param string $db_ssl SSL (allow is default)
* @return object|resource|bool db handler PgSql\Connection or false on error
*/
public function __dbConnect(
string $db_host,
string $db_user,
string $db_pass,
string $db_name,
int $db_port,
string $db_ssl = 'allow'
) {
if (empty($db_name)) {
return false;
}
// if there is no host, leave it empty, this will try default unix path
// same for port (defaults to 5432 if not set)
// must set is db name
// if no user name, db name is used
$connection_string = [];
if (!empty($db_host)) {
$connection_string[] = 'host=' . $db_host;
}
if (!empty($db_port)) {
$connection_string[] = 'port=' . $db_port;
}
if (!empty($db_user)) {
$connection_string[] = 'user=' . $db_user;
}
if (!empty($db_pass)) {
$connection_string[] = 'password=' . $db_pass;
}
// we must have at least db name set
$connection_string[] = 'dbname=' . $db_name;
if (!empty($db_ssl)) {
$connection_string[] = 'sslmode=' . $db_ssl;
}
// connect
$this->dbh = pg_connect(join(' ', $connection_string));
return $this->dbh;
}
/**
* reads the last error for this cursor and returns
* html formatted string with error name
*
* @param bool|object|resource $cursor cursor PgSql\Result (former resource)
* or null
* @return string error string
*/
public function __dbPrintError($cursor = false): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
return '';
}
// run the query again for the error result here
if (($cursor === false || is_bool($cursor)) && $this->last_error_query) {
pg_send_query($this->dbh, $this->last_error_query);
$this->last_error_query = '';
$cursor = pg_get_result($this->dbh);
}
if ($cursor && !is_bool($cursor) && $error_str = pg_result_error($cursor)) {
return '-PostgreSQL-Error- '
. $error_str;
} else {
return '';
}
}
/**
* wrapper for pg_meta_data
*
* @param string $table table name
* @param bool $extended show extended info (default true)
* @return array<mixed>|bool array data for the table info or false on error
*/
public function __dbMetaData(string $table, $extended = true)
{
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
// needs to prefixed with @ or it throws a warning on not existing table
return @pg_meta_data($this->dbh, $table, $extended);
}
/**
* wrapper for pg_escape_string
*
* @param string|int|float|bool $string any string/int/float/bool
* @return string excaped string
*/
public function __dbEscapeString($string): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
return '';
}
return pg_escape_string($this->dbh, (string)$string);
}
/**
* wrapper for pg_escape_literal
* difference to escape string is that this one adds quotes ('') around
* the string too
*
* @param string|int|float|bool $string any string/int/float/bool
* @return string excaped string including quites
*/
public function __dbEscapeLiteral($string): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
return (string)'';
}
// for phpstan, thinks this is string|false?
return (string)pg_escape_literal($this->dbh, (string)$string);
}
/**
* wrapper for pg_escape_identifier
* Only used for table names, column names
*
* @param string $string any string
* @return string escaped string
*/
public function __dbEscapeIdentifier(string $string): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
return '';
}
// for phpstan, thinks this is string|false?
return (string)pg_escape_identifier($this->dbh, (string)$string);
}
/**
* wrapper for pg_escape_byte
*
* @param string $data data stream
* @return string escaped bytea string
*/
public function __dbEscapeBytea(string $data): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
return '';
}
return pg_escape_bytea($this->dbh, $data);
}
/**
* unescape bytea data from postgesql
*
* @param string $bytea Bytea data stream
* @return string Unescaped bytea data
*/
public function __dbUnescapeBytea(string $bytea): string
{
return pg_unescape_bytea($bytea);
}
/**
* wrapper for pg_connection_busy
*
* @return bool True if connection is busy, False if not or no db connection at all
*/
public function __dbConnectionBusy(): bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
return pg_connection_busy($this->dbh);
}
/**
* Experimental wrapper with scoket timetout
*
* @param integer $timeout_seconds Wait how many seconds on timeout
* @return boolean True if connection is busy, or false on
* not busy or no db connection at all
*/
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool
{
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
$busy = pg_connection_busy($this->dbh);
/** @var array<resource>|null */
$socket = [pg_socket($this->dbh)];
$null = null;
while ($busy) {
// Will wait on that socket until that happens or the timeout is reached
stream_select($socket, $null, $null, $timeout_seconds);
$busy = pg_connection_busy($this->dbh);
}
return $busy;
}
/**
* extended wrapper for pg_version
* can return any setting in this array block
* If no connection, return empty string,
* if not in array return empty string
* On default 'version' will be stripped of any space attached info
* eg 13.5 (other info) will return only 13.5
*
* @param string $parameter Parameter string to extract from array
* @param boolean $strip If parameter is server strip out on default
* Set to false to get original string AS is
* @return string The parameter value
*/
public function __dbVersionInfo(string $parameter, bool $strip = true): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
return '';
}
// extract element
$return_string = (string)(pg_version($this->dbh)[$parameter] ?? '');
// for version, strip if requested
if (
in_array($parameter, ['server']) &&
$strip === true
) {
$return_string = explode(' ', $return_string, 2)[0] ?? '';
}
return $return_string;
}
/**
* Returns all parameters that are possible from the db_version
*
* @return array<mixed> Parameter key names from pg_version
*/
public function __dbVersionInfoParameterList(): array
{
if ($this->dbh === false || is_bool($this->dbh)) {
return [];
}
return array_keys(pg_version($this->dbh));
}
/**
* wrapper for pg_version
* Note: this only returns server version
* not connection version OR client version
*
* @return string version string
*/
public function __dbVersion(): string
{
if ($this->dbh === false || is_bool($this->dbh)) {
return '';
}
// array has client, protocol, server, we just return server stripped
return $this->__dbVersionInfo('server', true);
}
/**
* Returns a numeric version eg 90506 or 130006, etc
* Note that this calls a show command on the server
* Note:
* Old version is 9.5.6 where 9.5 is the major version
* Newer Postgresql (10 on) have only one major version so eg 13.5
* is returned as 130005
*
* @return integer Server version
*/
public function __dbVersionNumeric(): int
{
return (int)$this->__dbShow('server_version_num');
}
/**
* NOTE: it is recommended to convert arrays to json and parse the json in
* PHP itself, array_to_json(array)
* This is a fallback for old PostgreSQL versions
* postgresql array to php array
* https://stackoverflow.com/a/27964420
*
* @param string $array_text Array text from PostgreSQL
* @param int $start Start string position
* @param int|null $end End string position from recursive call
* @return array<mixed>|null PHP type array
*/
public function __dbArrayParse(
string $array_text,
int $start = 0,
?int &$end = null
): ?array {
if (empty($array_text) || $array_text[0] != '{') {
return null;
}
$return = [];
$string = false;
$quote = '';
$len = strlen($array_text);
$v = '';
// start from offset
for ($array_pos = $start + 1; $array_pos < $len; $array_pos += 1) {
$ch = $array_text[$array_pos];
// check wher ein the string are we
// end, one down
if (!$string && $ch == '}') {
if ($v !== '' || !empty($return)) {
$return[] = $v;
}
$end = $array_pos;
break;
// open new array, jump recusrive up
} elseif (!$string && $ch == '{') {
// full string + poff set and end
$v = $this->__dbArrayParse($array_text, $array_pos, $array_pos);
// next array element
} elseif (!$string && $ch == ',') {
$return[] = $v;
$v = '';
// flag that this is a string
} elseif (!$string && ($ch == '"' || $ch == "'")) {
$string = true;
$quote = $ch;
// quoted string
} elseif ($string && $ch == $quote && $array_text[$array_pos - 1] == "\\") {
$v = substr($v, 0, -1) . $ch;
} elseif ($string && $ch == $quote && $array_text[$array_pos - 1] != "\\") {
$string = false;
} else {
// build string
$v .= $ch;
}
}
return $return;
}
/**
* Returns any server setting
* if no connection or empty parameter or other error returns false
* else returns a string
*
* @param string $parameter Parameter to query
* @return string|bool Settings value as string
*/
public function __dbParameter(string $parameter)
{
if ($this->dbh === false || is_bool($this->dbh)) {
return false;
}
if (empty($parameter)) {
return false;
}
return pg_parameter_status($this->dbh, $parameter);
}
/**
* wrapper for any SHOW data blocks
* eg search_path or client_encoding
*
* @param string $show_string Part to show, if invalid will return empty string
* @return string Found part as is
*/
public function __dbShow(string $show_string): string
{
// get search path
$cursor = $this->__dbQuery("SHOW " . $this->__dbEscapeIdentifier($show_string));
// abort on failure and return empty
if ($cursor === false) {
return '';
}
// get result
$db_schema = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
return $db_schema[$show_string] ?? '';
}
/**
* Sets a new database schema/search_path
* Checks if schema exits and if not aborts with error code 2
*
* @param string $db_schema Schema to set
* @return int Returns 0 if no error
* 1 for check query failed
* 2 for schema not found in database
*/
public function __dbSetSchema(string $db_schema): int
{
// check if schema actually exists
$query = "SELECT EXISTS("
. "SELECT 1 FROM information_schema.schemata "
. "WHERE schema_name = " . $this->__dbEscapeLiteral($db_schema)
. ")";
$cursor = $this->__dbQuery($query);
// abort if execution fails
if ($cursor === false) {
return 1;
}
// check if schema does not exists
$row = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
if (empty($row['exists']) || $row['exists'] == 'f') {
return 2;
}
$query = "SET search_path TO " . $this->__dbEscapeLiteral($db_schema);
$this->__dbQuery($query);
return 0;
}
/**
* Returns current set schema/search_path
*
* @return string Search Path as currently set in DB
*/
public function __dbGetSchema(): string
{
return $this->__dbShow('search_path');
}
/**
* set the client encoding
* Returns 0 on set ok, or 3 if the client encoding could not be set
*
* @param string $db_encoding
* @return int Returns 0 for no error
* [not used] 1 for check query failed
* [not used] 2 for invalid client encoding
* 3 client encoding could not be set
*/
public function __dbSetEncoding(string $db_encoding): int
{
// check if ecnoding is valid first
// does not take into account aliases
// TODO lookup with alisaes so eg ShiftJIS does not get a false
// $query = "SELECT EXISTS("
// . "SELECT pg_catalog.pg_encoding_to_char(conforencoding) "
// . "FROM pg_catalog.pg_conversion "
// . "WHERE pg_catalog.pg_encoding_to_char(conforencoding) = "
// . $this->dbEscapeLiteral($db_encoding)
// . ")";
// $cursor = $this->__dbQuery($query);
// if ($cursor === false) {
// return 1;
// }
// $row = $this->__dbFetchArray($cursor, PGSQL_ASSOC);
// if ($row['exists'] == 'f') {
// return 2;
// }
$query = "SET client_encoding TO " . $this->__dbEscapeLiteral($db_encoding);
if ($this->__dbQuery($query) === false) {
return 3;
}
return 0;
}
/**
* Returns current set client encoding
* @return string Client encoding string, empty if not set
*/
public function __dbGetEncoding(): string
{
return $this->__dbShow('client_encoding');
}
}
// __END__

View File

@@ -1,305 +0,0 @@
<?php
/**
* Intrface for all SQL\* functions
*/
declare(strict_types=1);
namespace CoreLibs\DB\SQL\SqlInterface;
interface SqlFunctions
{
/**
* Undocumented function
*
* @return bool
*/
public function __dbLastErrorQuery(): bool;
/**
* Undocumented function
*
* @param string $query
* @return object|resource|bool
*/
public function __dbQuery(string $query);
/**
* Undocumented function
*
* @param string $query
* @param array<mixed> $params
* @return object|resource|bool
*/
public function __dbQueryParams(string $query, array $params);
/**
* Undocumented function
*
* @param string $query
* @return boolean
*/
public function __dbSendQuery(string $query): bool;
/**
* Undocumented function
*
* @return object|resource|bool
*/
public function __dbGetResult();
/**
* Undocumented function
*
* @return void
*/
public function __dbClose(): void;
/**
* Undocumented function
*
* @param string $name
* @param string $query
* @return object|resource|bool
*/
public function __dbPrepare(string $name, string $query);
/**
* Undocumented function
*
* @param string $name
* @param array<mixed> $data
* @return object|resource|bool
*/
public function __dbExecute(string $name, array $data);
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return integer
*/
public function __dbNumRows($cursor): int;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return integer
*/
public function __dbNumFields($cursor): int;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @param int $i
* @return string|bool
*/
public function __dbFieldName($cursor, int $i);
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @param int $result_type
* @return array<mixed>|bool
*/
public function __dbFetchArray($cursor, int $result_type = PGSQL_BOTH);
/**
* Undocumented function
*
* @param boolean $assoc_type
* @return integer
*/
public function __dbResultType(bool $assoc_type = true): int;
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return array<mixed>|bool
*/
public function __dbFetchAll($cursor);
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return integer
*/
public function __dbAffectedRows($cursor): int;
/**
* Undocumented function
*
* @param string $query
* @param string|null $pk_name
* @return string|integer|false
*/
public function __dbInsertId(string $query, ?string $pk_name);
/**
* Undocumented function
*
* @param string $table
* @param string $schema
* @return string|bool
*/
public function __dbPrimaryKey(string $table, string $schema = '');
/**
* Undocumented function
*
* @param string $db_host
* @param string $db_user
* @param string $db_pass
* @param string $db_name
* @param integer $db_port
* @param string $db_ssl
* @return object|resource|bool
*/
public function __dbConnect(
string $db_host,
string $db_user,
string $db_pass,
string $db_name,
int $db_port,
string $db_ssl = 'allow'
);
/**
* Undocumented function
*
* @param object|resource|bool $cursor
* @return string
*/
public function __dbPrintError($cursor = false): string;
/**
* Undocumented function
*
* @param string $table
* @param boolean $extended
* @return array<mixed>|bool
*/
public function __dbMetaData(string $table, $extended = true);
/**
* Undocumented function
*
* @param string|int|float|bool $string
* @return string
*/
public function __dbEscapeString($string): string;
/**
* Undocumented function
*
* @param string|int|float|bool $string
* @return string
*/
public function __dbEscapeLiteral($string): string;
/**
* Undocumented function
*
* @param string $string
* @return string
*/
public function __dbEscapeIdentifier(string $string): string;
/**
* Undocumented function
*
* @param string $data
* @return string
*/
public function __dbEscapeBytea(string $data): string;
/**
* Undocumented function
*
* @param string $bytea
* @return string
*/
public function __dbUnescapeBytea(string $bytea): string;
/**
* Undocumented function
*
* @return boolean
*/
public function __dbConnectionBusy(): bool;
/**
* Undocumented function
*
* @param integer $timeout_seconds
* @return boolean
*/
public function __dbConnectionBusySocketWait(int $timeout_seconds = 3): bool;
/**
* Undocumented function
*
* @return string
*/
public function __dbVersion(): string;
/**
* Undocumented function
*
* @param string $array_text
* @param integer $start
* @param integer|null $end
* @return array<mixed>|null
*/
public function __dbArrayParse(
string $array_text,
int $start = 0,
?int &$end = null
): ?array;
/**
* Undocumented function
*
* @param string $show_string
* @return string
*/
public function __dbShow(string $show_string): string;
/**
* Undocumented function
*
* @param string $db_schema
* @return integer
*/
public function __dbSetSchema(string $db_schema): int;
/**
* Undocumented function
*
* @return string
*/
public function __dbGetSchema(): string;
/**
* Undocumented function
*
* @param string $db_encoding
* @return integer
*/
public function __dbSetEncoding(string $db_encoding): int;
/**
* Undocumented function
*
* @return string
*/
public function __dbGetEncoding(): string;
}
// __END__

View File

@@ -1,99 +0,0 @@
<?php
/*
* direct write to log file
* must have BASE folder and LOG foder defined
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
class FileWriter
{
/** @var string */
private static $debug_filename = 'debug_file.log'; // where to write output
/** @var string */
private static $debug_folder;
/**
* Set a debug log folder, if not set BASE+LOG folders are set
* if they are defined
* This folder name must exist and must be writeable
*
* @param string $folder Folder name to where the log file will be written
* @return boolean True for valid folder name, False for invalid
*/
public static function fsetFolder(string $folder): bool
{
if (!preg_match("/^[\w\-\/]+/", $folder)) {
return false;
}
if (!is_writeable($folder)) {
return false;
}
// if last is not / then add
if (substr($folder, -1, 1) != DIRECTORY_SEPARATOR) {
$folder .= DIRECTORY_SEPARATOR;
}
self::$debug_folder = $folder;
return true;
}
/**
* set new debug file name
* Must be alphanumeric and underscore only.
* Must end with .log
*
* @param string $filename File name to set
* @return bool True for valid file name, False if invalid
*/
public static function fsetFilename(string $filename): bool
{
// valid file. must be only ascii & _, must end with .log
if (!preg_match("/^[\w\-]+\.log$/", $filename)) {
return false;
}
self::$debug_filename = $filename;
return true;
}
/**
* writes a string to a file immediatly, for fast debug output
*
* @param string $string string to write to the file
* @param boolean $enter default true, if set adds a linebreak \n at the end
* @return bool True for log written, false for not wirrten
*/
public static function fdebug(string $string, bool $enter = true): bool
{
if (empty(self::$debug_filename)) {
return false;
}
// if empty try to set base log folder
if (
empty(self::$debug_folder) &&
defined('BASE') && defined('LOG')
) {
self::$debug_folder = BASE . LOG;
}
if (!is_writeable(self::$debug_folder)) {
return false;
}
$filename = self::$debug_folder . self::$debug_filename;
$fh = fopen($filename, 'a');
if ($fh === false) {
return false;
}
if ($enter === true) {
$string .= "\n";
}
$string = "[" . \CoreLibs\Debug\Support::printTime() . "] "
. "[" . \CoreLibs\Get\System::getPageName(2) . "] - " . $string;
fwrite($fh, $string);
fclose($fh);
return true;
}
}
// __END__

View File

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

View File

@@ -1,129 +0,0 @@
<?php
/*
* dump memory usage
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
use CoreLibs\Convert\Byte;
class MemoryUsage
{
/** @var int */
private static $start_memory = 0;
/** @var int */
private static $set_memory = 0;
/** @var int */
private static $previous_memory = 0;
/** @var bool */
private static $debug_memory = false;
/**
* set memory flag, or return set memory flag
*
* @param bool|null $set_debug
* @return bool
*/
public static function debugMemoryFlag(?bool $set_debug = null): bool
{
if ($set_debug === null) {
return self::$debug_memory;
}
self::$debug_memory = $set_debug;
return self::$debug_memory;
}
/**
* Reset all memory variables to 0
*
* @return void
*/
public static function resetMemory(): void
{
self::$start_memory = 0;
self::$set_memory = 0;
self::$previous_memory = 0;
}
/**
* set the start memory velue, or reset to a new start value
*
* @return void
*/
public static function setStartMemory(): void
{
self::$start_memory = memory_get_usage();
}
/**
* set the and independent memory set for a sub tracking outside main
*
* @return void
*/
public static function setMemory(): void
{
self::$set_memory = memory_get_usage();
}
/**
* calculate and set memory usage values
* this will return an array with all the data that can be used in
* printMemoryUsage for human readable output
*
* @param string $prefix A prefix tag
* @return array<string,int|string> return array
*/
public static function memoryUsage(string $prefix): array
{
// skip if DEBUG is off
if (self::$debug_memory === false) {
return [];
}
if (empty(self::$start_memory)) {
self::$start_memory = memory_get_usage();
}
$memory_usage = memory_get_usage();
$data = [
'prefix' => $prefix,
'peak' => memory_get_peak_usage(),
'usage' => $memory_usage,
'start' => $memory_usage - self::$start_memory,
'last' => $memory_usage - self::$previous_memory,
'set' => $memory_usage - self::$set_memory
];
self::$previous_memory = $memory_usage;
return $data;
}
/**
* returns a human readable output from the memoryUsage function
* can be used for logging purpose
*
* @param array<string,int|string> $data Data array from memoryUsage
* @param bool $raw Flag to shaw unconverted memory numbers
* @return string Return debug string with memory usage
*/
public static function printMemoryUsage(array $data, bool $raw = false): string
{
return
'[' . $data['prefix'] . '] Peak/Curr/Change: '
. Byte::humanReadableByteFormat($data['peak'])
. '/'
. Byte::humanReadableByteFormat($data['usage'])
// . ($raw === true ? ' [' . $data['usage'] . ']' : '')
. '/Since Start: '
. Byte::humanReadableByteFormat($data['start'])
. ($raw === true ? ' [' . $data['start'] . ']' : '')
. ' | Since Last: '
. Byte::humanReadableByteFormat($data['last'])
. ($raw === true ? ' [' . $data['last'] . ']' : '')
. ' | Since Set: '
. Byte::humanReadableByteFormat($data['set'])
. ($raw === true ? ' [' . $data['set'] . ']' : '');
}
}
// __END__

View File

@@ -1,173 +0,0 @@
<?php
/*
* various running time checkers
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
class RunningTime
{
// hr
/** @var float */
private static $hr_start_time;
/** @var float */
private static $hr_end_time;
/** @var float */
private static $hr_last_time;
// normal
/** @var float */
private static $start_time;
/** @var float */
private static $end_time;
/** @var string */
private static $running_time_string;
/**
* sub calculation for running time based on out time.
* If no running time set, return 0
*
* @param string $out_time
* @return float
*/
private static function hrRunningTimeCalc(
float $run_time,
string $out_time = 'ms'
): float {
// init divisor, just in case
$divisor = 1;
// check through valid out time, if nothing matches default to ms
switch ($out_time) {
case 'n':
case 'ns':
$divisor = 1;
break;
case 'y':
case 'ys':
$divisor = 1000;
break;
case 'm':
case 'ms':
$divisor = 1000000;
break;
case 's':
$divisor = 1000000000;
break;
// default is ms
default:
$divisor = 1000000;
break;
}
// return the run time in converted format
return $run_time /= $divisor;
}
/**
* for messure run time between two calls for this method
* uses the hrtime() for running time
* first call sets start time and returns 0,
* every other call sets end time and returns the run time since start
* the out_time parameter can be:
* n/ns (nano), y/ys (micro), m/ms (milli), s (seconds)
* default is milliseconds
*
* @param string $out_time set return time adjustment calculation
* @return float running time without out_time suffix
*/
public static function hrRunningTime(string $out_time = 'ms'): float
{
// if start time not set, set start time
if (!self::$hr_start_time) {
self::$hr_start_time = hrtime(true);
self::$hr_last_time = self::$hr_start_time;
$run_time = 0;
} else {
self::$hr_end_time = hrtime(true);
$run_time = self::$hr_end_time - self::$hr_last_time;
self::$hr_last_time = self::$hr_end_time;
}
return self::hrRunningTimeCalc($run_time, $out_time);
}
/**
* print overall end time , can only be called after hrRunningtime
* see $out_time parameter description in hrRunningtime.
* Does not record a new timestamp, only prints different between start and
* last recoreded timestamp
*
* @param string $out_time set return time adjustment calculation
* @return float overall running time without out_time suffix
*/
public static function hrRunningTimeFromStart(string $out_time = 'ms'): float
{
if (!self::$hr_start_time) {
return (float)0;
}
$time = self::hrRunningTimeCalc(
self::$hr_end_time - self::$hr_start_time,
$out_time
);
return $time;
}
/**
* reset hr running time internal variables (start, end, last)
*
* @return void
*/
public static function hrRunningTimeReset(): void
{
self::$hr_start_time = 0;
self::$hr_end_time = 0;
self::$hr_last_time = 0;
}
/**
* prints start or end time in text format. On first call sets start time
* on second call it sends the end time and then also prints the running time
* Sets the internal runningtime_string variable with Start/End/Run time string
* NOTE: for pure running time check it is recommended to use hrRunningTime method
* @param bool $simple if true prints HTML strings, default text only
* @return float running time as float number
*/
public static function runningTime(bool $simple = false): float
{
list($micro, $timestamp) = explode(' ', microtime());
$running_time = 0;
// set start & end time
if (!self::$start_time) {
// always reset running time string on first call
self::$running_time_string = '';
self::$start_time = ((float)$micro + (float)$timestamp);
self::$running_time_string .= $simple ? 'Start: ' : "<b>Started at</b>: ";
} else {
self::$end_time = ((float)$micro + (float)$timestamp);
self::$running_time_string .= $simple ? 'End: ' : "<b>Stopped at</b>: ";
}
self::$running_time_string .= date('Y-m-d H:i:s', (int)$timestamp);
self::$running_time_string .= ' ' . $micro . ($simple ? ', ' : '<br>');
// if both are set
if (self::$start_time && self::$end_time) {
$running_time = self::$end_time - self::$start_time;
self::$running_time_string .= ($simple ? 'Run: ' : "<b>Script running time</b>: ") . $running_time . " s";
// reset start & end time after run
self::$start_time = 0;
self::$end_time = 0;
}
return $running_time;
}
/**
* get the runningTime string (for debug visual)
*
* @return string
*/
public static function runningTimeString(): string
{
return self::$running_time_string;
}
}
// __END__

View File

@@ -1,219 +0,0 @@
<?php
/*
* Debug support functions
*/
declare(strict_types=1);
namespace CoreLibs\Debug;
use CoreLibs\Convert\Html;
class Support
{
/**
* wrapper around microtime function to print out y-m-d h:i:s.ms
*
* @param int $set_microtime -1 to set micro time, 0 for none,
* positive for rounding
* @return string formated datetime string with microtime
*/
public static function printTime(int $set_microtime = -1): string
{
list($microtime, $timestamp) = explode(' ', microtime());
$string = date("Y-m-d H:i:s", (int)$timestamp);
// if microtime flag is -1 no round, if 0, no microtime, if >= 1, round that size
if ($set_microtime == -1) {
$string .= substr($microtime, 1);
} elseif ($set_microtime >= 1) {
// in round case we run this through number format to always get the same amount of digits
$string .= substr(number_format(round((float)$microtime, $set_microtime), $set_microtime), 1);
}
return $string;
}
/**
* prints a html formatted (pre) array
*
* @param array<mixed> $array any array
* @param bool $no_html set to true to use ##HTMLPRE##
* @return string formatted array for output with <pre> tag added
*/
public static function printAr(array $array, bool $no_html = false): string
{
if ($no_html === false) {
return "<pre>" . print_r($array, true) . "</pre>";
} else {
return '##HTMLPRE##' . print_r($array, true) . '##/HTMLPRE##';
}
}
/**
* alternate name for printAr function
*
* @param array<mixed> $array any array
* @param bool $no_html set to true to use ##HTMLPRE##
* @return string formatted array for output with <pre> tag added
*/
public static function printArray(array $array, bool $no_html = false): string
{
return self::printAr($array, $no_html);
}
/**
* convert bool value to string
* if $name is set prefix with nae
* default true: true, false: false
*
* @param bool $bool Variable to convert
* @param string $name [default: ''] Prefix name
* @param string $true [default: true] True string
* @param string $false [default: false] False string
* @return string String with converted bool text for debug
*/
public static function printBool(
bool $bool,
string $name = '',
string $true = 'true',
string $false = 'false'
): string {
$string = (!empty($name) ? '<b>' . $name . '</b>: ' : '')
. ($bool ? $true : $false);
return $string;
}
/**
* print out any data as string
* will convert boolean to TRUE/FALSE
* if object return get_class
* for array use printAr function, can be controlled with no_html for
* Debug\Logging compatible output
*
* @param mixed $mixed
* @param bool $no_html set to true to use ##HTMLPRE##or html escape
* @return string
*/
public static function printToString(mixed $mixed, bool $no_html = false): string
{
if (is_null($mixed)) {
return (string)'NULL';
} elseif (is_bool($mixed)) {
return self::printBool($mixed, '', 'TRUE', 'FALSE');
} elseif (is_resource($mixed)) {
return (string)$mixed;
} elseif (is_object($mixed)) {
return get_class($mixed);
} elseif (is_array($mixed)) {
// use the pre one OR debug one
return self::printAr($mixed, $no_html);
} elseif (is_string($mixed)) {
if ($no_html) {
return Html::htmlent((string)$mixed);
} else {
return (string)$mixed;
}
} else {
// should be int/float/string
return (string)$mixed;
}
}
/**
* if there is a need to find out which parent method called a child method,
* eg for debugging, this function does this
*
* call this method in the child method and you get the parent function that called
* @param int $level debug level, default 1
* @return ?string null or the function that called the function
* where this method is called
*/
public static function getCallerMethod(int $level = 1): ?string
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// print \CoreLibs\Debug\Support::printAr($traces);
// We should check from top down if unset?
// sets the start point here, and in level two (the sub call) we find this
if (isset($traces[$level])) {
return $traces[$level]['function'];
}
return null;
}
/**
* Returns array with all methods in the call stack in the order so that last
* called is last in order
* Will start with start_level to skip unwanted from stack
* Defaults to skip level 0 wich is this methid
*
* @param integer $start_level From what level on, as defaul starts with 1
* to exclude self
* @return array<mixed> All method names in list where max is last called
*/
public static function getCallerMethodList(int $start_level = 1): array
{
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$methods = [];
foreach ($traces as $level => $data) {
if ($level >= $start_level) {
if (!empty($data['function'])) {
array_unshift($methods, $data['function']);
}
}
}
return $methods;
}
/**
* Get the current class where this function is called
* Is mostly used in debug log statements to get the class where the debug
* was called
* gets top level class
* loops over the debug backtrace until if finds the first class (from the end)
*
* @return string Class name with namespace
*/
public static function getCallerClass(): string
{
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// ?? [['class' => get_called_class()]];
// TODO make sure that this doesn't loop forver
$class = null;
while ($class === null && count($backtrace) > 0) {
// if current is
// [function] => debug
// [class] => CoreLibs\Debug\Logging
// then return
// (OUTSIDE) because it was not called from a class method
// or return file name
$get_class = array_pop($backtrace);
$class = $get_class['class'] ?? null;
}
// on null or empty return empty string
return empty($class) ? '' : $class;
}
/**
* If a string is empty, sets '-' for return, or if given any other string
*
* @param string|null $string The string to check
* @param string $replace [default '-'] What to replace the empty string with
* @return string String itself or the replaced value
*/
public static function debugString(
?string $string,
string $replace = '-',
bool $no_html = false
): string {
if (empty($string)) {
$string = $replace;
}
if ($no_html) {
return Html::htmlent($string);
} else {
return $string;
}
}
}
// __END__

View File

@@ -1,110 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Get;
class DotEnv
{
/** @var string constant comment char, set to # */
private const COMMENT_CHAR = '#';
/**
* parses .env file
*
* Rules for .env file
* variable is any alphanumeric string followed by = on the same line
* content starts with the first non space part
* strings can be contained in "
* strings MUST be contained in " if they are multiline
* if string starts with " it will match until another " is found
* anything AFTER " is ignored
* if there are two variables with the same name only the first is used
* variables are case sensitive
*
* @param string $path Folder to file, default is __DIR__
* @param string $env_file What file to load, default is .env
* @return int -1 other error
* 0 for success full load
* 1 for file loadable, no data or data already loaded
* 2 for file not readable or open failed
* 3 for file not found
*/
public static function readEnvFile(
string $path = __DIR__,
string $env_file = '.env'
): int {
// default -1;
$status = -1;
$env_file_target = $path . DIRECTORY_SEPARATOR . $env_file;
// this is not a file -> abort
if (!is_file($env_file_target)) {
$status = 3;
return $status;
}
// cannot open file -> abort
if (!is_readable($env_file_target)) {
$status = 2;
return $status;
}
// open file
if (($fp = fopen($env_file_target, 'r')) === false) {
$status = 2;
return $status;
}
// set to readable but not yet any data loaded
$status = 1;
$block = false;
$var = '';
while ($line = fgets($fp)) {
// main match for variable = value part
if (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) {
$var = $matches[1];
$value = $matches[2];
$quotes = $matches[3];
// write only if env is not set yet, and write only the first time
if (empty($_ENV[$var])) {
if (!empty($quotes)) {
// match greedy for first to last so we move any " if there are
if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) {
$value = $matches[1];
} else {
// this is a multi line
$block = true;
// first " in string remove
// add removed new line back because this is a multi line
$value = ltrim($value, '"') . PHP_EOL;
}
} else {
// strip any quotes at end for unquoted single line
// an right hand spaces are removed too
$value = false !== ($pos = strpos($value, self::COMMENT_CHAR)) ?
rtrim(substr($value, 0, $pos)) : $value;
}
// if block is set, we strip line of slashes
$_ENV[$var] = $block === true ? stripslashes($value) : $value;
// set successful load
$status = 0;
}
} elseif ($block === true) {
// read line until there is a unescaped "
// this also strips everything after the last "
if (preg_match("/(.*[^\\\])\"/", $line, $matches)) {
$block = false;
// strip ending " and EVERYTHING that follows after that
$line = $matches[1];
}
// just be sure it is init before we fill
if (!isset($_ENV[$var])) {
$_ENV[$var] = '';
}
// strip line of slashes
$_ENV[$var] .= stripslashes($line);
}
}
fclose($fp);
return $status;
}
}
// __END__

View File

@@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Get;
/**
* @deprecated use \CoreLibs\Get\DotEnv instead
*/
class ReadEnvFile
{
/**
* parses .env file
*
* Rules for .env file
* variable is any alphanumeric string followed by = on the same line
* content starts with the first non space part
* strings can be contained in "
* strings MUST be contained in " if they are multiline
* if string starts with " it will match until another " is found
* anything AFTER " is ignored
* if there are two variables with the same name only the first is used
* variables are case sensitive
*
* @param string $path Folder to file, default is __DIR__
* @param string $env_file What file to load, default is .env
* @return int -1 other error
* 0 for success full load
* 1 for file loadable, but no data inside
* 2 for file not readable or open failed
* 3 for file not found
* @deprecated Use \CoreLibs\Get\DotEnv::readEnvFile() instead
*/
public static function readEnvFile(
string $path = __DIR__,
string $env_file = '.env'
): int {
return \CoreLibs\Get\DotEnv::readEnvFile($path, $env_file);
}
}
// __END__

View File

@@ -1,120 +0,0 @@
<?php
/*
* system related functions to get self name, host name, error strings
*/
declare(strict_types=1);
namespace CoreLibs\Get;
class System
{
public const WITH_EXTENSION = 0;
public const NO_EXTENSION = 1;
public const FULL_PATH = 2;
private const DEFAULT_PORT = '80';
/**
* helper function for PHP file upload error messgaes to messge string
*
* @param int $error_code integer _FILE upload error code
* @return string message string, translated
*/
public static function fileUploadErrorMessage(int $error_code): string
{
switch ($error_code) {
case UPLOAD_ERR_INI_SIZE:
$message = 'The uploaded file exceeds the upload_max_filesize directive in php.ini';
break;
case UPLOAD_ERR_FORM_SIZE:
$message = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
break;
case UPLOAD_ERR_PARTIAL:
$message = 'The uploaded file was only partially uploaded';
break;
case UPLOAD_ERR_NO_FILE:
$message = 'No file was uploaded';
break;
case UPLOAD_ERR_NO_TMP_DIR:
$message = 'Missing a temporary folder';
break;
case UPLOAD_ERR_CANT_WRITE:
$message = 'Failed to write file to disk';
break;
case UPLOAD_ERR_EXTENSION:
$message = 'File upload stopped by extension';
break;
default:
$message = 'Unknown upload error';
break;
}
return $message;
}
/**
* get the host name without the port as given by the SELF var
*
* @return array<mixed> host name/port name
*/
public static function getHostName(): array
{
$host = $_SERVER['HTTP_HOST'] ?? 'NOHOST:NOPORT';
list($host_name, $port) = array_pad(explode(':', $host), 2, self::DEFAULT_PORT);
return [$host_name, $port];
}
/**
* get the page name of the curronte page
*
* @param int $strip_ext WITH_EXTENSION: keep filename as is (default)
* NO_EXTENSION: strip page file name extension
* FULL_PATH: keep filename as is, but add dirname too
* @return string filename
*/
public static function getPageName(int $strip_ext = self::WITH_EXTENSION): string
{
// get the file info
$page_temp = pathinfo($_SERVER['PHP_SELF']);
if ($strip_ext == self::NO_EXTENSION) {
// no extension
return $page_temp['filename'];
} elseif ($strip_ext == self::FULL_PATH) {
// full path
return $_SERVER['PHP_SELF'];
} else {
// with extension
return $page_temp['basename'];
}
}
/**
* similar to getPageName, but it retuns the raw array
*
* @return array<string> pathinfo array from PHP SELF
*/
public static function getPageNameArray(): array
{
return pathinfo($_SERVER['PHP_SELF']);
}
/**
* Check if the php sapi interface has cli inside
*
* @return bool True for CLI type PHP, else false
*/
public static function checkCLI(): bool
{
return substr(
// if return is false, use empty string
(($sapi_name = php_sapi_name()) === false ?
'' :
$sapi_name
),
0,
3
) === 'cli' ? true : false;
}
}
// __END__

View File

@@ -1,58 +0,0 @@
<?php
/*
Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>.
This file is part of PHP-gettext.
PHP-gettext is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PHP-gettext is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PHP-gettext; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
declare(strict_types=1);
namespace CoreLibs\Language\Core;
// Preloads entire file in memory first, then creates a StringReader
// over it (it assumes knowledge of StringReader internals)
class CachedFileReader extends \CoreLibs\Language\Core\StringReader
{
/** @var int */
public $error = 0;
/** @var string */
public $fd_str = '';
/**
* Undocumented function
*
* @param string $filename
*/
public function __construct(string $filename)
{
parent::__construct();
if (file_exists($filename)) {
$fd = fopen($filename, 'rb');
if (!is_resource($fd)) {
$this->error = 3; // Cannot read file, probably permissions
} else {
$this->fd_str = fread($fd, filesize($filename) ?: 0) ?: '';
fclose($fd);
}
} else {
$this->error = 2; // File doesn't exist
}
}
}
// __END__

View File

@@ -1,135 +0,0 @@
<?php
/*
Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>.
This file is part of PHP-gettext.
PHP-gettext is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PHP-gettext is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PHP-gettext; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
declare(strict_types=1);
namespace CoreLibs\Language\Core;
class FileReader
{
/** @var int */
public $fr_pos;
/** @var resource|bool */
public $fr_fd;
/** @var int */
public $fr_length;
/** @var int */
public $error = 0;
/**
* file read constructor
*
* @param string $filename file name to load
*/
public function __construct(string $filename)
{
if (file_exists($filename)) {
$this->fr_length = filesize($filename) ?: 0;
$this->fr_pos = 0;
$this->fr_fd = fopen($filename, 'rb');
if (!is_resource($this->fr_fd)) {
$this->error = 3; // Cannot read file, probably permissions
}
} else {
$this->error = 2; // File doesn't exist
}
}
/**
* read byte data length
*
* @param int $bytes how many bytes to read
* @return string read data as string
*/
public function read(int $bytes): string
{
if (!$bytes || !is_resource($this->fr_fd)) {
return '';
}
fseek($this->fr_fd, $this->fr_pos);
// PHP 5.1.1 does not read more than 8192 bytes in one fread()
// the discussions at PHP Bugs suggest it's the intended behaviour
$data = '';
while ($bytes > 0) {
$chunk = fread($this->fr_fd, $bytes);
if ($chunk === false) {
break;
}
$data .= $chunk;
$bytes -= strlen($chunk);
}
$this->fr_pos = ftell($this->fr_fd) ?: 0;
return $data;
}
/**
* seek to a position in the file
*
* @param int $pos position where to go to
* @return int file position after seek done
*/
public function seekto(int $pos): int
{
if (!is_resource($this->fr_fd)) {
return 0;
}
fseek($this->fr_fd, $pos);
$this->fr_pos = ftell($this->fr_fd) ?: 0;
return $this->fr_pos;
}
/**
* get current position in file
*
* @return int current position in bytes
*/
public function currentpos(): int
{
return $this->fr_pos;
}
/**
* file length/size
*
* @return int file size in bytes
*/
public function length(): int
{
return $this->fr_length;
}
/**
* close open file handler
*
* @return void has no return
*/
public function close(): void
{
if (is_resource($this->fr_fd)) {
fclose($this->fr_fd);
}
}
}
// __END__

View File

@@ -1,546 +0,0 @@
<?php
/*
Copyright (c) 2003, 2009 Danilo Segan <danilo@kvota.net>.
Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
This file is part of PHP-gettext.
PHP-gettext is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PHP-gettext is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PHP-gettext; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
declare(strict_types=1);
namespace CoreLibs\Language\Core;
/**
* Provides a simple gettext replacement that works independently from
* the system's gettext abilities.
* It can read MO files and use them for translating strings.
* The files are passed to gettext_reader as a Stream (see streams.php)
*
* This version has the ability to cache all strings and translations to
* speed up the string lookup.
* While the cache is enabled by default, it can be switched off with the
* second parameter in the constructor (e.g. whenusing very large MO files
* that you don't want to keep in memory)
*/
class GetTextReader
{
// public:
/** @var int */
public $error = 0; // public variable that holds error code (0 if no error)
// private:
/** @var int */
private $BYTEORDER = 0; // 0: low endian, 1: big endian
/** @var FileReader */
private $STREAM;
/** @var bool */
private $short_circuit = false;
/** @var bool */
private $enable_cache = false;
/** @var int */
private $originals = 0; // offset of original table
/** @var int */
private $translations = 0; // offset of translation table
/** @var string */
private $pluralheader = ''; // cache header field for plural forms
/** @var int */
private $total = 0; // total string count
/** @var array<mixed>|null */
private $table_originals = null; // table for original strings (offsets)
/** @var array<mixed>|null */
private $table_translations = null; // table for translated strings (offsets)
/** @var array<mixed> */
private $cache_translations = []; // original -> translation mapping
/* Methods */
/**
* Reads a 32bit Integer from the Stream
*
* @access private
* @return int Integer from the Stream
*/
private function readint(): int
{
if ($this->BYTEORDER == 0) {
// low endian
$input = unpack('V', $this->STREAM->read(4)) ?: [];
} else {
// big endian
$input = unpack('N', $this->STREAM->read(4)) ?: [];
}
return array_shift($input);
}
/**
* read bytes
*
* @param int $bytes byte length to read
* @return string return data, possible string
*/
public function read(int $bytes): string
{
return $this->STREAM->read($bytes);
}
/**
* Reads an array of Integers from the Stream
*
* @param int $count How many elements should be read
* @return array<mixed> Array of Integers
*/
public function readintarray(int $count): array
{
if ($this->BYTEORDER == 0) {
// low endian
return unpack('V' . $count, $this->STREAM->read(4 * $count)) ?: [];
} else {
// big endian
return unpack('N' . $count, $this->STREAM->read(4 * $count)) ?: [];
}
}
/**
* Constructor
*
* @param FileReader|bool $Reader the StreamReader object
* @param bool $enable_cache Enable or disable caching
* of strings (default on)
*/
public function __construct($Reader, bool $enable_cache = true)
{
// If there isn't a StreamReader, turn on short circuit mode.
if ((!is_object($Reader) && !$Reader) || (is_object($Reader) && $Reader->error)) {
$this->short_circuit = true;
return;
}
// bail out for sure if this is not an objet here
if (!is_object($Reader)) {
$this->short_circuit = true;
return;
}
// Caching can be turned off
$this->enable_cache = $enable_cache;
$MAGIC1 = "\x95\x04\x12\xde";
$MAGIC2 = "\xde\x12\x04\x95";
$this->STREAM = $Reader;
$magic = $this->read(4);
if ($magic == $MAGIC1) {
$this->BYTEORDER = 1;
} elseif ($magic == $MAGIC2) {
$this->BYTEORDER = 0;
} else {
$this->error = 1; // not MO file
}
// FIXME: Do we care about revision? We should.
$revision = $this->readint();
$this->total = $this->readint();
$this->originals = $this->readint();
$this->translations = $this->readint();
}
/**
* Get current short circuit, equals to no translator running
*
* @return bool
*/
public function getShortCircuit(): bool
{
return $this->short_circuit;
}
/**
* get the current cache enabled status
*
* @return bool
*/
public function getEnableCache(): bool
{
return $this->enable_cache;
}
/**
* Loads the translation tables from the MO file into the cache
* If caching is enabled, also loads all strings into a cache
* to speed up translation lookups
*
* @access private
* @return void
*/
private function loadTables(): void
{
if (
is_array($this->cache_translations) &&
is_array($this->table_originals) &&
is_array($this->table_translations)
) {
return;
}
/* get original and translations tables */
if (!is_array($this->table_originals)) {
$this->STREAM->seekto($this->originals);
$this->table_originals = $this->readintarray($this->total * 2);
}
if (!is_array($this->table_translations)) {
$this->STREAM->seekto($this->translations);
$this->table_translations = $this->readintarray($this->total * 2);
}
if ($this->enable_cache) {
$this->cache_translations = [];
/* read all strings in the cache */
for ($i = 0; $i < $this->total; $i++) {
$this->STREAM->seekto($this->table_originals[$i * 2 + 2]);
$original = $this->STREAM->read($this->table_originals[$i * 2 + 1]);
$this->STREAM->seekto($this->table_translations[$i * 2 + 2]);
$translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]);
$this->cache_translations[$original] = $translation;
}
}
}
/**
* Returns a string from the "originals" table
*
* @access private
* @param int $num Offset number of original string
* @return string Requested string if found, otherwise ''
*/
private function getOriginalString(int $num): string
{
$length = $this->table_originals[$num * 2 + 1] ?? 0;
$offset = $this->table_originals[$num * 2 + 2] ?? 0;
if (!$length) {
return '';
}
$this->STREAM->seekto($offset);
$data = $this->STREAM->read($length);
return (string)$data;
}
/**
* Returns a string from the "translations" table
*
* @access private
* @param int $num Offset number of original string
* @return string Requested string if found, otherwise ''
*/
private function getTranslationString(int $num): string
{
$length = $this->table_translations[$num * 2 + 1] ?? 0;
$offset = $this->table_translations[$num * 2 + 2] ?? 0;
if (!$length) {
return '';
}
$this->STREAM->seekto($offset);
$data = $this->STREAM->read($length);
return (string)$data;
}
/**
* Binary search for string
*
* @access private
* @param string $string string to find
* @param int $start (internally used in recursive function)
* @param int $end (internally used in recursive function)
* @return int (offset in originals table)
*/
private function findString(string $string, int $start = -1, int $end = -1): int
{
if (($start == -1) or ($end == -1)) {
// findString is called with only one parameter, set start end end
$start = 0;
$end = $this->total;
}
if (abs($start - $end) <= 1) {
// We're done, now we either found the string, or it doesn't exist
$txt = $this->getOriginalString($start);
if ($string == $txt) {
return $start;
} else {
return -1;
}
} elseif ($start > $end) {
// start > end -> turn around and start over
return $this->findString($string, $end, $start);
} else {
// Divide table in two parts
$half = (int)(($start + $end) / 2);
$cmp = strcmp($string, $this->getOriginalString($half));
if ($cmp == 0) {
// string is exactly in the middle => return it
return $half;
} elseif ($cmp < 0) {
// The string is in the upper half
return $this->findString($string, $start, $half);
} else {
// Translateshe string is in the lower half
return $this->findString($string, $half, $end);
}
}
}
/**
* Translates a string
*
* @access public
* @param string $string to be translated
* @return string translated string (or original, if not found)
*/
public function translate(string $string): string
{
if ($this->short_circuit) {
return $string;
}
$this->loadTables();
if ($this->enable_cache) {
// Caching enabled, get translated string from cache
if (
is_array($this->cache_translations) &&
array_key_exists($string, $this->cache_translations)
) {
return $this->cache_translations[$string];
} else {
return $string;
}
} else {
// Caching not enabled, try to find string
$num = $this->findString($string);
if ($num == -1) {
return $string;
} else {
return $this->getTranslationString($num);
}
}
}
/**
* Sanitize plural form expression for use in PHP eval call.
*
* @access private
* @param string $expr an expression to match
* @return string sanitized plural form expression
*/
private function sanitizePluralExpression(string $expr): string
{
// Get rid of disallowed characters.
$expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr);
// Add parenthesis for tertiary '?' operator.
$expr .= ';';
$res = '';
$p = 0;
$expr_len = strlen($expr);
for ($i = 0; $i < $expr_len; $i++) {
$ch = $expr[$i];
switch ($ch) {
case '?':
$res .= ' ? (';
$p++;
break;
case ':':
$res .= ') : (';
break;
case ';':
$res .= str_repeat(')', $p) . ';';
$p = 0;
break;
default:
$res .= $ch;
}
}
return $res;
}
/**
* Parse full PO header and extract only plural forms line.
*
* @access private
* @param string $header header search in plurals
* @return string verbatim plural form header field
*/
private function extractPluralFormsHeaderFromPoHeader(string $header): string
{
if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs)) {
$expr = $regs[2];
} else {
$expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
}
return $expr;
}
/**
* Get possible plural forms from MO header
*
* @access private
* @return string plural form header
*/
private function getPluralForms(): string
{
// lets assume message number 0 is header
// this is true, right?
$this->loadTables();
// cache header field for plural forms
if (empty($this->pluralheader) || !is_string($this->pluralheader)) {
if ($this->enable_cache) {
$header = $this->cache_translations[''];
} else {
$header = $this->getTranslationString(0);
}
$expr = $this->extractPluralFormsHeaderFromPoHeader($header);
$this->pluralheader = $this->sanitizePluralExpression($expr);
}
return $this->pluralheader;
}
/**
* Detects which plural form to take
*
* @access private
* @param int $n count
* @return int array index of the right plural form
*/
private function selectString(int $n): int
{
$string = $this->getPluralForms();
$string = str_replace('nplurals', "\$total", $string);
$string = str_replace("n", (string)$n, $string);
$string = str_replace('plural', "\$plural", $string);
$total = 0;
$plural = 0;
// FIXME use Symfony\Component\ExpressionLanguage\ExpressionLanguage or similar
eval("$string");
/** @phpstan-ignore-next-line 0 >= 0 is always true*/
if ($plural >= $total) {
$plural = $total - 1;
}
return (int)$plural;
}
/**
* wrapper for translate() method
*
* @access public
* @param string $string
* @return string
*/
public function gettext(string $string): string
{
return $this->translate($string);
}
/**
* Plural version of gettext
*
* @access public
* @param string $single
* @param string $plural
* @param int $number
* @return string plural form
*/
public function ngettext(string $single, string $plural, int $number): string
{
if ($this->short_circuit) {
if ($number != 1) {
return $plural;
} else {
return $single;
}
}
// find out the appropriate form
$select = $this->selectString($number);
// this should contains all strings separated by NULLs
$key = $single . chr(0) . $plural;
if ($this->enable_cache) {
if (is_array($this->cache_translations) && !array_key_exists($key, $this->cache_translations)) {
return ($number != 1) ? $plural : $single;
} else {
$result = $this->cache_translations[$key];
$list = explode(chr(0), $result);
return $list[$select];
}
} else {
$num = $this->findString($key);
if ($num == -1) {
return ($number != 1) ? $plural : $single;
} else {
$result = $this->getTranslationString($num);
$list = explode(chr(0), $result);
return $list[$select];
}
}
}
/**
* p get text
*
* @param string $context [description]
* @param string $msgid [description]
* @return string [description]
*/
public function pgettext(string $context, string $msgid): string
{
$key = $context . chr(4) . $msgid;
$ret = $this->translate($key);
if (strpos($ret, "\004") !== false) {
return $msgid;
} else {
return $ret;
}
}
/**
* np get text
*
* @param string $context [description]
* @param string $singular [description]
* @param string $plural [description]
* @param int $number [description]
* @return string [description]
*/
public function npgettext(
string $context,
string $singular,
string $plural,
int $number
): string {
$key = $context . chr(4) . $singular;
$ret = $this->ngettext($key, $plural, $number);
if (strpos($ret, "\004") !== false) {
return $singular;
} else {
return $ret;
}
}
}
// __END__

View File

@@ -1,98 +0,0 @@
<?php
/*
Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>.
This file is part of PHP-gettext.
PHP-gettext is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PHP-gettext is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PHP-gettext; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
declare(strict_types=1);
namespace CoreLibs\Language\Core;
class StringReader
{
/** @var int */
public $sr_pos;
/** @var string */
public $sr_str;
/**
* constructor for string reader
*
* @param string $str basic string
*/
public function __construct(string $str = '')
{
$this->sr_str = $str;
$this->sr_pos = 0;
}
/**
* read bytes in string
*
* @param int $bytes bytes to read in string
* @return string data read in length of bytes as string
*/
public function read(int $bytes): string
{
$data = substr($this->sr_str, $this->sr_pos, $bytes);
$this->sr_pos += $bytes;
if (strlen($this->sr_str) < $this->sr_pos) {
$this->sr_pos = strlen($this->sr_str);
}
return $data;
}
/**
* go to position in string
*
* @param int $pos position in string
* @return int new position in string after seek
*/
public function seekto(int $pos): int
{
$this->sr_pos = $pos;
if (strlen($this->sr_str) < $this->sr_pos) {
$this->sr_pos = strlen($this->sr_str);
}
return $this->sr_pos;
}
/**
* get current position in string
*
* @return int position in string
*/
public function currentpos(): int
{
return $this->sr_pos;
}
/**
* get length of string
*
* @return int return length of assigned string
*/
public function length(): int
{
return strlen($this->sr_str);
}
}
// __END__

View File

@@ -1,127 +0,0 @@
<?php
/*
* deprecated function calls
* Language\Encoding::__mbMimeEncode -> Convert\MimeEncode::__mbMimeEncode
* Langauge\Encoding::checkConvertEncoding -> Check\Encoding::checkConvertEncoding
* Langauge\Encoding::setErrorChar -> Check\Encoding::setErrorChar
* Langauge\Encoding::getErrorChar -> Check\Encoding::getErrorChar
* Langauge\Encoding::convertEncoding -> Convert\Encoding::convertEncoding
*/
declare(strict_types=1);
namespace CoreLibs\Language;
class Encoding
{
/**
* wrapper function for mb mime convert
* for correct conversion with long strings
*
* @param string $string string to encode
* @param string $encoding target encoding
* @param string $line_break default line break is \r\n
* @return string encoded string
* @deprecated Use \CoreLibs\Convert\MimeEncode::__mbMimeEncode();
*/
public static function __mbMimeEncode(
string $string,
string $encoding,
string $line_break = "\r\n"
): string {
return \CoreLibs\Convert\MimeEncode::__mbMimeEncode($string, $encoding, $line_break);
}
/**
* set error char
*
* @param string|int|null $string The character to use to represent
* error chars
* "long" for long, "none" for none
* or a valid code point in int
* like 0x2234 (8756, ∴)
* default character is ? (63)
* if null is set then "none"
* @return void
* @deprecated Use \CoreLibs\Check\Encoding::setErrorChar();
*/
public static function setErrorChar($string): void
{
\CoreLibs\Check\Encoding::setErrorChar($string);
}
/**
* get the current set error character
*
* @param bool $return_substitute_func if set to true return the set
* character from the php function
* directly
* @return string|int Set error character
* @deprecated Use \CoreLibs\Check\Encoding::getErrorChar();
*/
public static function getErrorChar(bool $return_substitute_func = false)
{
return \CoreLibs\Check\Encoding::getErrorChar($return_substitute_func);
}
/**
* test if a string can be safely convert between encodings.
* mostly utf8 to shift jis
* the default compare has a possibility of failure, especially with windows
* it is recommended to the following in the script which uses this method:
* mb_substitute_character(0x2234);
* $class->mb_error_char = '∴';
* if check to Shift JIS
* if check to ISO-2022-JP
* if check to ISO-2022-JP-MS
* set three dots (∴) as wrong character for correct convert error detect
* (this char is used, because it is one of the least used ones)
*
* @param string $string string to test
* @param string $from_encoding encoding of string to test
* @param string $to_encoding target encoding
* @return bool|array<string> false if no error or
* array with failed characters
* @deprecated Use \CoreLibs\Check\Encoding::checkConvertEncoding();
*/
public static function checkConvertEncoding(
string $string,
string $from_encoding,
string $to_encoding
) {
return \CoreLibs\Check\Encoding::checkConvertEncoding($string, $from_encoding, $to_encoding);
}
/**
* detects the source encoding of the string and if doesn't match
* to the given target encoding it convert is
* if source encoding is set and auto check is true (default) a second
* check is done so that the source string encoding actually matches
* will be skipped if source encoding detection is ascii
*
* @param string $string string to convert
* @param string $to_encoding target encoding
* @param string $source_encoding optional source encoding, will try to auto detect
* @param bool $auto_check default true, if source encoding is set
* check that the source is actually matching
* to what we sav the source is
* @return string encoding converted string
* @deprecated Use \CoreLibs\Convert\Encoding::convertEncoding();
*/
public static function convertEncoding(
string $string,
string $to_encoding,
string $source_encoding = '',
bool $auto_check = true
): string {
return \CoreLibs\Convert\Encoding::convertEncoding(
$string,
$to_encoding,
$source_encoding,
$auto_check
);
}
}
// __END__

View File

@@ -1,117 +0,0 @@
<?php
/*
* Internal function for getting locale and encodig settings
* used for new locale layout
*/
declare(strict_types=1);
namespace CoreLibs\Language;
class GetLocale
{
/**
* returns locale, lang, domain, encoding, path
* from either parameter set or from sessions/config variables
*
* @param string|null $locale override auto detect
* @param string|null $domain override domain
* @param string|null $encoding override encoding
* @param string|null $path override path
* @return array<string,string> locale, domain, encoding, path
*/
public static function setLocale(
?string $locale = null,
?string $domain = null,
?string $encoding = null,
?string $path = null
): array {
// locale must match at least basic rules
if (
empty($locale) ||
!preg_match("/^[-A-Za-z0-9_.@]+$/", $locale)
) {
if (!empty($_SESSION['DEFAULT_LOCALE'])) {
// parse from session (logged in)
$locale = $_SESSION['DEFAULT_LOCALE'];
} else {
// else parse from site locale
$locale = defined('SITE_LOCALE') && !empty(SITE_LOCALE) ?
SITE_LOCALE :
// else parse from default, if not 'en'
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
(defined('DEFAULT_LOCALE') && !empty(DEFAULT_LOCALE) ?
DEFAULT_LOCALE : 'en');
}
}
// if domain is set, must be alphanumeric, if not unset
if (
empty($domain) ||
!preg_match("/^\w+$/", $domain)
) {
// if no domain is set, fall back to content path
$domain = str_replace('/', '', CONTENT_PATH);
}
// check that override encoding matches locale encoding
// if locale encoding is set
preg_match('/(?:\\.(?P<charset>[-A-Za-z0-9_]+))/', $locale, $matches);
$locale_encoding = $matches['charset'] ?? null;
if (
// empty encoding
empty($encoding) ||
// not valid encoding
!preg_match("/^[-A-Za-z0-9_]+$/", $encoding) ||
// locale encoding set and not matching to encoding
(!empty($locale_encoding) && $encoding != $locale_encoding)
) {
if (!empty($locale_encoding)) {
$encoding = strtoupper($locale_encoding);
} elseif (!empty($_SESSION['DEFAULT_CHARSET'])) {
// else set from session
$encoding = $_SESSION['DEFAULT_CHARSET'];
} else {
// else set from site encoding
$encoding = defined('SITE_ENCODING') && !empty(SITE_ENCODING) ?
SITE_ENCODING :
// or default encoding, if not 'UTF-8'
/** @phpstan-ignore-next-line DEFAULT_LOCALE could be empty */
(defined('DEFAULT_ENCODING') && !empty(DEFAULT_ENCODING) ?
DEFAULT_ENCODING : 'UTF-8');
}
}
// path checks if set, if not valid path unset to default BASE path
if (
empty($path) ||
!is_dir($path)
) {
$path = BASE . INCLUDES . LOCALE;
}
// extract lang & country from locale string, else set to en
if (
preg_match(
// lang
'/^(?P<lang>[a-z]{2,3})'
// country code
. '(?:_(?P<country>[A-Z]{2}))?/',
$locale,
$matches
)
) {
$lang = ($matches['lang'] ?? 'en')
// add country only if set
. (!empty($matches['country']) ? '_' . $matches['country'] : '');
} else {
$lang = 'en';
}
return [
'locale' => $locale,
'lang' => $lang,
'domain' => $domain,
'encoding' => $encoding,
'path' => $path,
];
}
}
// __END__

View File

@@ -1,663 +0,0 @@
<?php
/*********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2004/11/18
* VERSION: 3.0.0
* RELEASED LICENSE: GNU GPL 3
* SHORT DESCRIPTION:
* init class for gettext. Original was just a function &
* var setting include for wordpress.
* I changed that to a class to be more portable with my style of coding
* VERSION 3.0 (2022/4) removes all old folder layout and uses standard gettext
* PUBLIC METHODS
* __ : returns string (translated or original if not found)
* __n : plural string
* __p : string with context
* __np: string with context and plural
*
* HISTORY:
* 2022/4/15 (cs) drop all old folder layout support, new folder base
* in locale with standard gettext layout of
* locale/LC_MESSAGES/domain.mo
* 2005/10/17 (cs) made an on the fly switch method (reload of lang)
*********************************************************************/
declare(strict_types=1);
namespace CoreLibs\Language;
use CoreLibs\Language\Core\FileReader;
use CoreLibs\Language\Core\GetTextReader;
class L10n
{
/** @var string the current locale */
private $locale = '';
/** @var string the SET locale as WHERE the domain file is */
private $locale_set = '';
/** @var string the default selected/active domain */
private $domain = '';
/** @var array<string,array<string,GetTextReader>> locale > domain = translator */
private $domains = [];
/** @var array<string,string> bound paths for domains */
private $paths = ['' => './'];
// files
/** @var string the full path to the mo file to loaded */
private $mofile = '';
/** @var string base path to search level */
private $base_locale_path = '';
/** @var string dynamic set path to where the mo file is actually */
private $base_content_path = '';
// errors
/** @var bool if load of mo file was unsuccessful */
private $load_failure = false;
// object holders
/** @var FileReader|bool reader class for file reading, false for short circuit */
private $input = false;
/** @var GetTextReader reader class for MO data */
private $l10n;
/**
* @static
* @var L10n self class
*/
private static $instance;
/**
* class constructor call for language getstring
* if locale is not empty will load translation
* else getTranslator needs to be called
*
* @param string $locale language name, default empty string
* will return self instance
* @param string $domain override CONTENT_PATH . $encoding name for mo file
* @param string $path path, if empty fallback on default internal path
*/
public function __construct(
string $locale = '',
string $domain = '',
string $path = ''
) {
// auto load language only if at least locale and domain is set
if (!empty($locale) && !empty($domain)) {
// check hack if domain and path is switched
// Note this can be removed in future versions
if (strstr($domain, DIRECTORY_SEPARATOR) !== false) {
$_domain = $path;
$path = $domain;
$domain = $_domain;
}
$this->getTranslator($locale, $domain, $path);
}
}
/**
* Returns the singleton L10n object.
* For function wrapper use
*
* @return L10n object
*/
public static function getInstance(): L10n
{
/** @phpstan-ignore-next-line */
if (empty(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Loads global localization functions.
* prefixed with double underscore
* eg: gettext -> __gettext
*/
public static function loadFunctions(): void
{
require_once __DIR__ . '/l10n_functions.php';
}
/**
* loads the mo file base on path, locale and domain set
*
* @param string $locale language name (optional), fallback is en
* @param string $domain override CONTENT_PATH . $encoding name for mo file
* @param string $path path, if empty fallback on default internal path
* @return GetTextReader the main gettext reader object
*/
public function getTranslator(
string $locale = '',
string $domain = '',
string $path = ''
): GetTextReader {
// set local if not from parameter
if (empty($locale)) {
$locale = $this->locale;
}
// set domain if not given
if (empty($domain)) {
$domain = $this->domain;
}
// store old settings
$old_mofile = $this->mofile;
$old_lang = $this->locale;
$old_lang_set = $this->locale_set;
$old_domain = $this->domain;
$old_base_locale_path = $this->base_locale_path;
$old_base_content_path = $this->base_content_path;
// if path is a dir
// 1) from a previous set domain
// 2) from method option as is
// 3) fallback if BASE/INCLUDES/LOCALE set
// 4) current dir
if (!empty($this->paths[$domain]) && is_dir($this->paths[$domain])) {
$this->base_locale_path = $this->paths[$domain];
} elseif (is_dir($path)) {
$this->base_locale_path = $path;
} elseif (
defined('BASE') && defined('INCLUDES') && defined('LOCALE')
) {
// set fallback base path if constant set
$this->base_locale_path = BASE . INCLUDES . LOCALE;
} else {
$this->base_locale_path = './';
}
// now we loop over lang compositions to get the base path
// then we check
$locales = $this->listLocales($locale);
foreach ($locales as $_locale) {
$this->base_content_path = $_locale . DIRECTORY_SEPARATOR
. 'LC_MESSAGES' . DIRECTORY_SEPARATOR;
$this->mofile = $this->base_locale_path
. $this->base_content_path
. $domain . '.mo';
if (file_exists($this->mofile)) {
$this->locale_set = $_locale;
break;
}
}
// check if get a readable mofile
if (is_readable($this->mofile)) {
// locale and domain current wanted
$this->locale = $locale;
$this->domain = $domain;
// set empty domains path with current locale
if (empty($this->domains[$locale])) {
$this->domains[$locale] = [];
}
// store current base path (without locale, etc)
if (empty($this->paths[$domain])) {
$this->paths[$domain] = $this->base_locale_path;
}
// file reader and mo reader
$this->input = new FileReader($this->mofile);
$this->l10n = new GetTextReader($this->input);
// if short circuit is true, we failed to have a translator loaded
$this->load_failure = $this->l10n->getShortCircuit();
// below is not used at the moment, but can be to avoid reloading
$this->domains[$this->locale][$domain] = $this->l10n;
} elseif (!empty($old_mofile)) {
// mo file not readable
$this->load_failure = true;
// else fall back to the old ones
$this->mofile = $old_mofile;
$this->locale = $old_lang;
$this->locale_set = $old_lang_set;
$this->domain = $old_domain;
$this->base_locale_path = $old_base_locale_path;
$this->base_content_path = $old_base_content_path;
} else {
// mo file not readable, no previous mo file set, set short circuit
$this->load_failure = true;
// dummy
$this->l10n = new GetTextReader($this->input);
}
return $this->l10n;
}
/**
* return current set GetTextReader or return the one for given
* domain name if set
* This can be used to access all the public methods from the
* GetTextReader
*
* @param string $domain optional domain name
* @return GetTextReader
*/
public function getTranslatorClass(string $domain = ''): GetTextReader
{
if (!empty($domain) && !empty($this->domains[$this->locale][$domain])) {
return $this->domains[$this->locale][$domain];
}
// if null return short circuit version
if ($this->l10n === null) {
return new GetTextReader($this->input);
}
return $this->l10n;
}
/**
* parse the locale string for further processing
*
* @param string $locale Locale to parse
* @return array<string,string|null> array with lang, country, charset, modifier
*/
public static function parseLocale(string $locale = ''): array
{
preg_match(
// language code
'/^(?P<lang>[a-z]{2,3})'
// country code
. '(?:_(?P<country>[A-Z]{2}))?'
// charset
. '(?:\\.(?P<charset>[-A-Za-z0-9_]+))?'
// @ modifier
. '(?:@(?P<modifier>[-A-Za-z0-9_]+))?$/',
$locale,
$matches
);
return [
'lang' => $matches['lang'] ?? null,
'country' => $matches['country'] ?? null,
'charset' => $matches['charset'] ?? null,
'modifier' => $matches['modifier'] ?? null,
];
}
/**
* original:
* vendor/phpmyadmin/motranslator/src/Loader.php
*
* Returns array with all possible locale combinations based on the
* given locale name
*
* I.e. for sr_CS.UTF-8@latin, look through all of
* sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr.
*
* @param string $locale Locale string
* @return array<string> List of locale path parts that can be possible
*/
public static function listLocales(string $locale): array
{
$locale_list = [];
if (empty($locale)) {
return $locale_list;
}
// is matching regex
$locale_detail = L10n::parseLocale($locale);
// all null = nothing mached, return locale as is
if ($locale_detail === array_filter($locale_detail, 'is_null')) {
return [$locale];
}
// write to innteral vars
$lang = $locale_detail['lang'];
$country = $locale_detail['country'];
$charset = $locale_detail['charset'];
$modifier = $locale_detail['modifier'];
// we need to add all possible cominations from not null set
// entries to the list, from longest to shortest
// %s_%s.%s@%s (lang _ country . encoding @ suffix)
// %s_%s@%s (lang _ country @ suffix)
// %s@%s (lang @ suffix)
// %s_%s.%s (lang _ country . encoding)
// %s_%s (lang _ country)
// %s (lang)
// if lang is set
if ($lang) {
// modifier group
if ($modifier) {
if ($country) {
if ($charset) {
array_push(
$locale_list,
sprintf('%s_%s.%s@%s', $lang, $country, $charset, $modifier)
);
}
array_push(
$locale_list,
sprintf('%s_%s@%s', $lang, $country, $modifier)
);
} elseif ($charset) {
array_push(
$locale_list,
sprintf('%s.%s@%s', $lang, $charset, $modifier)
);
}
array_push(
$locale_list,
sprintf('%s@%s', $lang, $modifier)
);
}
// country group
if ($country) {
if ($charset) {
array_push(
$locale_list,
sprintf('%s_%s.%s', $lang, $country, $charset)
);
}
array_push(
$locale_list,
sprintf('%s_%s', $lang, $country)
);
} elseif ($charset) {
array_push(
$locale_list,
sprintf('%s.%s', $lang, $charset)
);
}
// lang only
array_push($locale_list, $lang);
}
// If the locale name doesn't match POSIX style, just include it as-is.
if (!in_array($locale, $locale_list)) {
array_push($locale_list, $locale);
}
return $locale_list;
}
/**
* tries to detect the locale set in the following order:
* - globals: LOCALE
* - globals: LANG
* - env: LC_ALL
* - env: LC_MESSAGES
* - env: LANG
* if nothing set, returns 'en' as default
*
* @return string
*/
public static function detectLocale(): string
{
// globals
foreach (['LOCALE', 'LANG'] as $global) {
if (!empty($GLOBALS[$global])) {
return $GLOBALS[$global];
}
}
// enviroment
foreach (['LC_ALL', 'LC_MESSAGES', 'LANG'] as $env) {
$locale = getenv($env);
if ($locale !== false && !empty($locale)) {
return $locale;
}
}
return 'en';
}
/************
* INTERNAL VAR SET/GET
*/
/**
* Sets the path for a domain.
* must be set before running getTranslator (former l10nReloadMOfile)
*
* @param string $domain Domain name
* @param string $path Path where to find locales
*/
public function setTextDomain(string $domain, string $path): void
{
$this->paths[$domain] = $path;
}
/**
* return set path for given domain
* if not found return false
*
* @param string $domain
* @return string|bool
*/
public function getTextDomain(string $domain)
{
return $this->paths[$domain] ?? false;
}
/**
* sets the default domain.
*
* @param string $domain Domain name
*/
public function setDomain(string $domain): void
{
$this->domain = $domain;
}
/**
* return current set domain name
*
* @return string
*/
public function getDomain(): string
{
return $this->domain;
}
/**
* sets a requested locale.
*
* @param string $locale Locale name
* @return string Set or current locale
*/
public function setLocale(string $locale): string
{
if (!empty($locale)) {
$this->locale = $locale;
}
return $this->locale;
}
/**
* get current set locale (want locale)
*
* @return string
*/
public function getLocale(): string
{
return $this->locale;
}
/**
* current set locale where mo file is located
*
* @return string
*/
public function getLocaleSet(): string
{
return $this->locale_set;
}
/**
* get current set language
*
* @return string current set language string
* @deprecated Use getLocale()
*/
public function __getLang(): string
{
return $this->getLocale();
}
/**
* get current set mo file
*
* @return string current set mo language file
*/
public function getMoFile(): string
{
return $this->mofile;
}
/**
* get current set mo file
*
* @return string current set mo language file
* @deprecated Use getMoFile()
*/
public function __getMoFile(): string
{
return $this->getMoFile();
}
/**
* get the current base path in which we search
*
* @return string
*/
public function getBaseLocalePath(): string
{
return $this->base_locale_path;
}
/**
* the path below the base path to where the mo file is located
*
* @return string
*/
public function getBaseContentPath(): string
{
return $this->base_content_path;
}
/**
* get the current load error status
* if true then the mo file failed to load
*
* @return bool
*/
public function getLoadError(): bool
{
return $this->load_failure;
}
/************
* TRANSLATION METHODS
*/
/**
* translates a string and returns translated text
*
* @param string $text text to translate
* @return string translated text
*/
public function __(string $text): string
{
// fallback passthrough
if ($this->l10n === null) {
return $text;
}
return $this->l10n->translate($text);
}
/**
* prints translated string out to the screen
* @param string $text text to translate
* @return void has no return
* @deprecated use echo __() instead
*/
public function __e(string $text): void
{
// fallback passthrough
if ($this->l10n === null) {
echo $text;
}
echo $this->l10n->translate($text);
}
/**
* Return the plural form.
*
* @param string $single string for single word
* @param string $plural string for plural word
* @param int $number number value
* @return string translated plural string
*/
public function __n(string $single, string $plural, int $number): string
{
// in case nothing got set yet, this is fallback
if ($this->l10n === null) {
return $number > 1 ? $plural : $single;
}
return $this->l10n->ngettext($single, $plural, $number);
}
/**
* context translation via msgctxt
*
* @param string $context context string
* @param string $text text to translate
* @return string
*/
public function __p(string $context, string $text): string
{
if ($this->l10n === null) {
return $text;
}
return $this->l10n->pgettext($context, $text);
}
/**
* context translation via msgctxt
*
* @param string $context context string
* @param string $single string for single word
* @param string $plural string for plural word
* @param int $number number value
* @return string
*/
public function __np(string $context, string $single, string $plural, int $number): string
{
if ($this->l10n === null) {
return $number > 1 ? $plural : $single;
}
return $this->l10n->npgettext($context, $single, $plural, $number);
}
// alias functions to mimic gettext calls
/**
* alias for gettext,
* calls __
*
* @param string $text
* @return string
* @deprecated Use __()
*/
public function gettext(string $text): string
{
return $this->__($text);
}
/**
* alias for ngettext
* calls __n
*
* @param string $single
* @param string $plural
* @param int $number
* @return string
* @deprecated Use __n()
*/
public function ngettext(string $single, string $plural, int $number): string
{
return $this->__n($single, $plural, $number);
}
// TODO: dgettext(string $domain, string $message): string
// TODO: dngettext(string $domain, string $singular, string $plural, int $count): string
// TODO: dpgettext(string $domain, string $message, int $category): string
// TODO: dpngettext(string $domain, string $singular, string $plural, int $count, int $category): string
}
// __END__

View File

@@ -1,290 +0,0 @@
<?php
/*
* elements for html output direct
*/
declare(strict_types=1);
namespace CoreLibs\Output\Form;
class Elements
{
/**
* print the date/time drop downs, used in any queue/send/insert at date/time place
*
* @param int $year year YYYY
* @param int $month month m
* @param int $day day d
* @param int $hour hour H
* @param int $min min i
* @param string $suffix additional info printed after the date time
* variable in the drop down
* also used for ID in the on change JS call
* @param int $min_steps default is 1 (minute), can set to anything,
* is used as sum up from 0
* @param bool $name_pos_back default false, if set to true,
* the name will be printend after the drop down
* and not before the drop down
* @return string HTML formated strings for drop down lists of date and time
*/
public static function printDateTime(
$year,
$month,
$day,
$hour,
$min,
string $suffix = '',
int $min_steps = 1,
bool $name_pos_back = false
) {
// if suffix given, add _ before
if ($suffix) {
$suffix = '_' . $suffix;
}
if ($min_steps < 1 || $min_steps > 59) {
$min_steps = 1;
}
$on_change_call = 'dt_list(\'' . $suffix . '\');';
// always be 1h ahead (for safety)
$timestamp = time() + 3600; // in seconds
// the max year is this year + 1;
$max_year = (int)date("Y", $timestamp) + 1;
// preset year, month, ...
$year = !$year ? date('Y', $timestamp) : $year;
$month = !$month ? date('m', $timestamp) : $month;
$day = !$day ? date('d', $timestamp) : $day;
$hour = !$hour ? date('H', $timestamp) : $hour;
$min = !$min ? date('i', $timestamp) : $min; // add to five min?
// max days in selected month
$days_in_month = date(
't',
strtotime($year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $min . ':0') ?: null
);
$string = '';
// from now to ?
if ($name_pos_back === false) {
$string = 'Year ';
}
$string .= '<select id="year' . $suffix . '" name="year' . $suffix . '" onChange="' . $on_change_call . '">';
for ($i = date("Y"); $i <= $max_year; $i++) {
$string .= '<option value="' . $i . '" ' . ($year == $i ? 'selected' : '') . '>' . $i . '</option>';
}
$string .= '</select> ';
if ($name_pos_back === true) {
$string .= 'Year ';
}
if ($name_pos_back === false) {
$string .= 'Month ';
}
$string .= '<select id="month' . $suffix . '" name="month' . $suffix . '" onChange="' . $on_change_call . '">';
for ($i = 1; $i <= 12; $i++) {
$string .= '<option value="' . ($i < 10 ? '0' . $i : $i) . '" '
. ($month == $i ? 'selected' : '') . '>' . $i . '</option>';
}
$string .= '</select> ';
if ($name_pos_back === true) {
$string .= 'Month ';
}
if ($name_pos_back === false) {
$string .= 'Day ';
}
$string .= '<select id="day' . $suffix . '" name="day' . $suffix . '" onChange="' . $on_change_call . '">';
for ($i = 1; $i <= $days_in_month; $i++) {
// set weekday text based on current month ($month) and year ($year)
$string .= '<option value="' . ($i < 10 ? '0' . $i : $i) . '" '
. ($day == $i ? 'selected' : '') . '>' . $i
. ' (' . date('D', mktime(0, 0, 0, (int)$month, $i, (int)$year) ?: null) . ')</option>';
}
$string .= '</select> ';
if ($name_pos_back === true) {
$string .= 'Day ';
}
if ($name_pos_back === false) {
$string .= 'Hour ';
}
$string .= '<select id="hour' . $suffix . '" name="hour' . $suffix . '" onChange="' . $on_change_call . '">';
for ($i = 0; $i <= 23; $i += $min_steps) {
$string .= '<option value="' . ($i < 10 ? '0' . $i : $i)
. '" ' . ($hour == $i ? 'selected' : '') . '>' . $i . '</option>';
}
$string .= '</select> ';
if ($name_pos_back === true) {
$string .= 'Hour ';
}
if ($name_pos_back === false) {
$string .= 'Minute ';
}
$string .= '<select id="min' . $suffix . '" name="min'
. $suffix . '" onChange="' . $on_change_call . '">';
for ($i = 0; $i <= 59; $i++) {
$string .= '<option value="' . ($i < 10 ? '0' . $i : $i)
. '" ' . ($min == $i ? 'selected' : '') . '>' . $i . '</option>';
}
$string .= '</select>';
if ($name_pos_back === true) {
$string .= ' Minute ';
}
// return the datetime select string
return $string;
}
/**
* tries to find mailto:user@bubu.at and changes it into ->
* <a href="mailto:user@bubu.at">E-Mail senden</a>
* or tries to take any url (http, ftp, etc) and transform it into a valid URL
* the string is in the format: some url|name#css|, same for email
*
* @param string $string data to transform to a valid HTML url
* @param string $target target string, default _blank
* @return string correctly formed html url link
*/
public static function magicLinks(string $string, string $target = "_blank"): string
{
$output = $string;
$protList = ["http", "https", "ftp", "news", "nntp"];
// find urls w/o protocol
$output = preg_replace("/([^\/])www\.([\w\.-]+)\.([a-zA-Z]{2,4})/", "\\1http://www.\\2.\\3", $output) ?: '';
$output = preg_replace("/([^\/])ftp\.([\w\.-]+)\.([a-zA-Z]{2,4})/", "\\1ftp://ftp.\\2.\\3", $output) ?: '';
// remove doubles, generate protocol-regex
// DIRTY HACK
$protRegex = "";
foreach ($protList as $protocol) {
if ($protRegex) {
$protRegex .= "|";
}
$protRegex .= "$protocol:\/\/";
}
// find urls w/ protocol
// cs: escaped -, added / for http urls
// added | |, this time mandatory, todo: if no | |use \\1\\2
// backslash at the end of a url also allowed now
// do not touch <.*=".."> things!
// _1: URL or email
// _2: atag (>)
// _3: (_1) part of url or email [main url or email pre @ part]
// _4: (_2) parameters of url or email post @ part
// _5: (_3) parameters of url or tld part of email
// _7: link name/email link name
// _9: style sheet class
$output = preg_replace_callback(
"/(href=\")?(\>)?\b($protRegex)([\w\.\-?&=+%#~,;\/]+)\b([\.\-?&=+%#~,;\/]*)(\|([^\||^#]+)(#([^\|]+))?\|)?/",
function ($matches) {
return self::createUrl(
$matches[1] ?? '',
$matches[2] ?? '',
$matches[3] ?? '',
$matches[4] ?? '',
$matches[5] ?? '',
$matches[7] ?? '',
$matches[9] ?? ''
);
},
$output
) ?: '';
// find email-addresses, but not mailto prefix ones
$output = preg_replace_callback(
"/(mailto:)?(\>)?\b([\w\.-]+)@([\w\.\-]+)\.([a-zA-Z]{2,4})\b(\|([^\||^#]+)(#([^\|]+))?\|)?/",
function ($matches) {
return self::createEmail(
$matches[1] ?? '',
$matches[2] ?? '',
$matches[3] ?? '',
$matches[4] ?? '',
$matches[5] ?? '',
$matches[7] ?? '',
$matches[9] ?? ''
);
},
$output
) ?: '';
// we have one slashes after the Protocol ->
// internal link no domain, strip out the proto
// $output = preg_replace("/($protRegex)\/(.*)/e", "\\2", $ouput);
// post processing
$output = str_replace("{TARGET}", $target, $output);
$output = str_replace("##LT##", "<", $output);
$output = str_replace("##GT##", ">", $output);
$output = str_replace("##QUOT##", "\"", $output);
return $output;
}
/**
* internal function, called by the magic url create functions.
* checks if title $_4 exists, if not, set url as title
*
* @param string $href url link
* @param string $atag anchor tag (define both type or url)
* @param string $_1 part of the URL, if atag is set, _1 is not used
* @param string $_2 part of the URL
* @param string $_3 part of the URL
* @param string $name name for the url, if not given _2 + _3 is used
* @param string $class style sheet
* @return string correct string for url href process
*/
private static function createUrl($href, $atag, $_1, $_2, $_3, $name, $class): string
{
// $this->debug('URL', "1: $_1 - 2: $_2 - $_3 - atag: $atag - name: $name - class: $class");
// if $_1 ends with //, then we strip $_1 complete & target is also blanked (its an internal link)
if (preg_match("/\/\/$/", $_1) && preg_match("/^\//", $_2)) {
$_1 = '';
$target = '';
} else {
$target = '{TARGET}';
}
// if it is a link already just return the original link do not touch anything
if (!$href && !$atag) {
return "##LT##a href=##QUOT##" . $_1 . $_2 . $_3 . "##QUOT##"
. ($class ? ' class=##QUOT##' . $class . '##QUOT##' : '')
. ($target ? " target=##QUOT##" . $target . "##QUOT##" : '')
. "##GT##" . ($name ? $name : $_2 . $_3) . "##LT##/a##GT##";
} elseif ($href && !$atag) {
return "href=##QUOT##$_1$_2$_3##QUOT##";
} elseif ($atag) {
return $atag . $_2 . $_3;
} else {
return $href;
}
}
/**
* internal function for createing email, returns data to magic_url method
*
* @param string $mailto email address
* @param string $atag atag (define type of url)
* @param string $_1 parts of the email _1 before @, 3_ tld
* @param string $_2 _2 domain part after @
* @param string $_3 _3 tld
* @param string $title name for the link, if not given use email
* @param string $class style sheet
* @return string created html email a href string
*/
private static function createEmail($mailto, $atag, $_1, $_2, $_3, $title, $class)
{
$email = $_1 . "@" . $_2 . "." . $_3;
if (!$mailto && !$atag) {
return "##LT##a href=##QUOT##mailto:" . $email . "##QUOT##"
. ($class ? ' class=##QUOT##' . $class . '##QUOT##' : '')
. "##GT##" . ($title ? $title : $email) . "##LT##/a##GT##";
} elseif ($mailto && !$atag) {
return "mailto:" . $email;
} elseif ($atag) {
return $atag . $email;
} else {
// else just return email as is
return $email;
}
}
}
// __END__

File diff suppressed because it is too large Load Diff

View File

@@ -1,140 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditAccess implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_access_id' => [
'value' => $_POST['edit_access_id'] ?? '',
'type' => 'hidden',
'pk' => 1
],
'name' => [
'value' => $_POST['name'] ?? '',
'output_name' => 'Access Group Name',
'mandatory' => 1,
'type' => 'text',
'error_check' => 'alphanumericspace|unique'
],
'description' => [
'value' => $_POST['description'] ?? '',
'output_name' => 'Description',
'type' => 'textarea'
],
'color' => [
'value' => $_POST['color'] ?? '',
'output_name' => 'Color',
'mandatory' => 0,
'type' => 'text',
'size' => 10,
'length' => 9,
'error_check' => 'custom',
// FIXME: update regex check for hex/rgb/hsl with color check class
'error_regex' => '/^#([\dA-Fa-f]{6}|[\dA-Fa-f]{8})$/',
'error_example' => '#F6A544'
],
'enabled' => [
'value' => $_POST['enabled'] ?? 0,
'output_name' => 'Enabled',
'type' => 'binary',
'int' => 1, // OR 'bool' => 1
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'protected' => [
'value' => $_POST['protected'] ?? 0,
'output_name' => 'Protected',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'additional_acl' => [
'value' => $_POST['additional_acl'] ?? '',
'output_name' => 'Additional ACL (as JSON)',
'type' => 'textarea',
'error_check' => 'json',
'rows' => 10,
'cols' => 60
],
],
'table_name' => 'edit_access',
"load_query" => "SELECT edit_access_id, name FROM edit_access ORDER BY name",
'show_fields' => [
[
'name' => 'name'
],
],
'element_list' => [
'edit_access_data' => [
'output_name' => 'Edit Access Data',
'delete_name' => 'remove_edit_access_data',
// is not a sub table read and connect, but only a sub table with data
// 'type' => 'reference_data',
// maxium visible if no data is set, if filled add this number to visible
'max_empty' => 5,
'prefix' => 'ead',
'elements' => [
'name' => [
'type' => 'text',
'error_check' => 'alphanumeric|unique',
'output_name' => 'Name',
'mandatory' => 1
],
'value' => [
'type' => 'text',
'output_name' => 'Value'
],
'enabled' => [
'type' => 'checkbox',
'output_name' => 'Activate',
'int' => 1,
'element_list' => [1]
],
/*'edit_access_id' => [
'int' => 1,
'type' => 'hidden',
// reference main key from master table above
'fk_id' => 1
],*/
'edit_access_data_id' => [
'type' => 'hidden',
'int' => 1,
'pk_id' => 1
],
],
],
],
];
}
}
// __END__

View File

@@ -1,137 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditGroups implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_group_id' => [
'value' => $_POST['edit_group_id'] ?? '',
'pk' => 1,
'type' => 'hidden'
],
'enabled' => [
'value' => $_POST['enabled'] ?? '',
'output_name' => 'Enabled',
'int' => 1,
'type' => 'binary',
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'name' => [
'value' => $_POST['name'] ?? '',
'output_name' => 'Group Name',
'type' => 'text',
'mandatory' => 1
],
'edit_access_right_id' => [
'value' => $_POST['edit_access_right_id'] ?? '',
'output_name' => 'Group Level',
'mandatory' => 1,
'int' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
],
'edit_scheme_id' => [
'value' => $_POST['edit_scheme_id'] ?? '',
'output_name' => 'Group Scheme',
'int_null' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_scheme_id, name FROM edit_scheme WHERE enabled = 1 ORDER BY name"
],
'additional_acl' => [
'value' => $_POST['additional_acl'] ?? '',
'output_name' => 'Additional ACL (as JSON)',
'type' => 'textarea',
'error_check' => 'json',
'rows' => 10,
'cols' => 60
],
],
'load_query' => "SELECT edit_group_id, name, enabled FROM edit_group ORDER BY name",
'table_name' => 'edit_group',
'show_fields' => [
[
'name' => 'name'
],
[
'name' => 'enabled',
'binary' => ['Yes', 'No'],
'before_value' => 'Enabled: '
],
],
'element_list' => [
'edit_page_access' => [
'output_name' => 'Pages',
'mandatory' => 1,
'delete' => 0, // set then reference entries are deleted, else the 'enable' flag is only set
'enable_name' => 'enable_page_access',
'prefix' => 'epa',
'read_data' => [
'table_name' => 'edit_page',
'pk_id' => 'edit_page_id',
'name' => 'name',
'order' => 'order_number'
],
'elements' => [
'edit_page_access_id' => [
'type' => 'hidden',
'int' => 1,
'pk_id' => 1
],
'enabled' => [
'type' => 'checkbox',
'output_name' => 'Activate',
'int' => 1,
'element_list' => [1],
],
'edit_access_right_id' => [
'type' => 'drop_down_db',
'output_name' => 'Access Level',
'int' => 1,
'preset' => 1, // first of the select
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
],
'edit_page_id' => [
'int' => 1,
'type' => 'hidden'
],
/*,
'edit_default' => [
'output_name' => 'Default',
'type' => 'radio',
'mandatory' => 1
],*/
],
], // edit pages ggroup
],
];
}
}
// __END__

View File

@@ -1,105 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditLanguages implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_language_id' => [
'value' => $_POST['edit_language_id'] ?? '',
'type' => 'hidden',
'pk' => 1
],
'short_name' => [
'value' => $_POST['short_name'] ?? '',
'output_name' => 'Language (short)',
'mandatory' => 1,
'type' => 'text',
'size' => 2,
'length' => 2
],
'long_name' => [
'value' => $_POST['long_name'] ?? '',
'output_name' => 'Language (long)',
'mandatory' => 1,
'type' => 'text',
'size' => 40
],
'iso_name' => [
'value' => $_POST['iso_name'] ?? '',
'output_name' => 'ISO Code',
'mandatory' => 1,
'type' => 'text'
],
'order_number' => [
'value' => $_POST['order_number'] ?? '',
'int' => 1,
'order' => 1
],
'enabled' => [
'value' => $_POST['enabled'] ?? '',
'output_name' => 'Enabled',
'int' => 1,
'type' => 'binary',
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'lang_default' => [
'value' => $_POST['lang_default'] ?? '',
'output_name' => 'Default Language',
'int' => 1,
'type' => 'binary',
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
],
'load_query' => "SELECT edit_language_id, long_name, iso_name, enabled "
. "FROM edit_language "
. "ORDER BY long_name",
'show_fields' => [
[
'name' => 'long_name'
],
[
'name' => 'iso_name',
'before_value' => 'ISO: '
],
[
'name' => 'enabled',
'before_value' => 'Enabled: ',
'binary' => ['Yes','No'],
],
],
'table_name' => 'edit_language'
];
}
}
// __END__

View File

@@ -1,68 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditMenuGroup implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_menu_group_id' => [
'value' => $_POST['edit_menu_group_id'] ?? '',
'type' => 'hidden',
'pk' => 1
],
'name' => [
'value' => $_POST['name'] ?? '',
'output_name' => 'Group name',
'mandatory' => 1,
'type' => 'text'
],
'flag' => [
'value' => $_POST['flag'] ?? '',
'output_name' => 'Flag',
'mandatory' => 1,
'type' => 'text',
'error_check' => 'alphanumeric|unique'
],
'order_number' => [
'value' => $_POST['order_number'] ?? '',
'output_name' => 'Group order',
'type' => 'order',
'int' => 1,
'order' => 1
],
],
'table_name' => 'edit_menu_group',
'load_query' => "SELECT edit_menu_group_id, name FROM edit_menu_group ORDER BY name",
'show_fields' => [
[
'name' => 'name'
],
],
];
}
}
// __END__

View File

@@ -1,275 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditPages implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_page_id' => [
'value' => $_POST['edit_page_id'] ?? '',
'type' => 'hidden',
'pk' => 1
],
'filename' => [
'value' => $_POST['filename'] ?? '',
'output_name' => 'Add File ...',
'mandatory' => 1,
'type' => 'drop_down_db',
'query' => "SELECT DISTINCT temp_files.filename AS id, "
. "temp_files.folder || temp_files.filename AS name "
. "FROM temp_files "
. "LEFT JOIN edit_page ep ON temp_files.filename = ep.filename "
. "WHERE ep.filename IS NULL"
],
'hostname' => [
'value' => $_POST['hostname'] ?? '',
'output_name' => 'Hostname or folder',
'type' => 'text'
],
'name' => [
'value' => $_POST['name'] ?? '',
'output_name' => 'Page name',
'mandatory' => 1,
'type' => 'text'
],
'order_number' => [
'value' => $_POST['order_number'] ?? '',
'output_name' => 'Page order',
'type' => 'order',
'int' => 1,
'order' => 1
],
/* 'flag' => [
'value' => $_POST['flag']) ?? '',
'output_name' => 'Page Flag',
'type' => 'drop_down_array',
'query' => [
'0' => '0',
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5'
],
],*/
'online' => [
'value' => $_POST['online'] ?? '',
'output_name' => 'Online',
'int' => 1,
'type' => 'binary',
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'menu' => [
'value' => $_POST['menu'] ?? '',
'output_name' => 'Menu',
'int' => 1,
'type' => 'binary',
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'popup' => [
'value' => $_POST['popup'] ?? '',
'output_name' => 'Popup',
'int' => 1,
'type' => 'binary',
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'popup_x' => [
'value' => $_POST['popup_x'] ?? '',
'output_name' => 'Popup Width',
'int_null' => 1,
'type' => 'text',
'size' => 4,
'length' => 4
],
'popup_y' => [
'value' => $_POST['popup_y'] ?? '',
'output_name' => 'Popup Height',
'int_null' => 1,
'type' => 'text',
'size' => 4,
'length' => 4
],
'content_alias_edit_page_id' => [
'value' => $_POST['content_alias_edit_page_id'] ?? '',
'output_name' => 'Content Alias Source',
'int_null' => 1,
'type' => 'drop_down_db',
// query creation
'select_distinct' => 0,
'pk_name' => 'edit_page_id AS content_alias_edit_page_id',
'input_name' => 'name',
'table_name' => 'edit_page',
'where_not_self' => 1,
'order_by' => 'order_number'
// 'query' => "SELECT edit_page_id AS content_alias_edit_page_id, name ".
// "FROM edit_page ".
// (!empty($_POST['edit_page_id']) ? " WHERE edit_page_id <> ".$_POST['edit_page_id'] : "")." ".
// "ORDER BY order_number"
],
],
'load_query' => "SELECT edit_page_id, "
. "CASE WHEN hostname IS NOT NULL THEN hostname ELSE ''::VARCHAR END || filename AS filename, "
. "name, online, menu, popup "
. "FROM edit_page "
. "ORDER BY order_number",
'table_name' => 'edit_page',
'show_fields' => [
[
'name' => 'name'
],
[
'name' => 'filename',
'before_value' => 'Filename: '
],
[
'name' => 'online',
'binary' => ['Yes', 'No'],
'before_value' => 'Online: '
],
[
'name' => 'menu',
'binary' => ['Yes', 'No'],
'before_value' => 'Menu: '
],
[
'name' => 'popup',
'binary' => ['Yes', 'No'],
'before_value' => 'Popup: '
],
],
'reference_arrays' => [
'edit_visible_group' => [
'table_name' => 'edit_page_visible_group',
'other_table_pk' => 'edit_visible_group_id',
'output_name' => 'Visible Groups (access)',
'mandatory' => 1,
'select_size' => 10,
'selected' => $_POST['edit_visible_group_id'] ?? '',
'query' => "SELECT edit_visible_group_id, 'Name: ' || name || ', ' || 'Flag: ' || flag "
. "FROM edit_visible_group ORDER BY name"
],
'edit_menu_group' => [
'table_name' => 'edit_page_menu_group',
'other_table_pk' => 'edit_menu_group_id',
'output_name' => 'Menu Groups (grouping)',
'mandatory' => 1,
'select_size' => 10,
'selected' => $_POST['edit_menu_group_id'] ?? '',
'query' => "SELECT edit_menu_group_id, 'Name: ' || name || ', ' || 'Flag: ' || flag "
. "FROM edit_menu_group ORDER BY order_number"
],
],
'element_list' => [
'edit_query_string' => [
'output_name' => 'Query Strings',
'delete_name' => 'remove_query_string',
'prefix' => 'eqs',
'elements' => [
'name' => [
'output_name' => 'Name',
'type' => 'text',
'error_check' => 'unique|alphanumeric',
'mandatory' => 1
],
'value' => [
'output_name' => 'Value',
'type' => 'text'
],
'enabled' => [
'output_name' => 'Enabled',
'int' => 1,
'type' => 'checkbox',
'element_list' => [1],
],
'dynamic' => [
'output_name' => 'Dynamic',
'int' => 1,
'type' => 'checkbox',
'element_list' => [1],
],
'edit_query_string_id' => [
'type' => 'hidden',
'pk_id' => 1
],
], // elements
], // query_string element list
'edit_page_content' => [
'output_name' => 'Page Content',
'delete_name' => 'remove_page_content',
'prefix' => 'epc',
'elements' => [
'name' => [
'output_name' => 'Content',
'type' => 'text',
'error_check' => 'alphanumeric',
'mandatory' => 1
],
'uid' => [
'output_name' => 'UID',
'type' => 'text',
'error_check' => 'unique|alphanumeric',
'mandatory' => 1
],
'order_number' => [
'output_name' => 'Order',
'type' => 'text',
'error_check' => 'int',
'mandatory' => 1
],
'online' => [
'output_name' => 'Online',
'int' => 1,
'type' => 'checkbox',
'element_list' => [1],
],
'edit_access_right_id' => [
'type' => 'drop_down_db',
'output_name' => 'Access Level',
'int' => 1,
'preset' => 1, // first of the select
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
],
'edit_page_content_id' => [
'type' => 'hidden',
'pk_id' => 1
],
],
],
], // element list
];
}
}
// __END__

View File

@@ -1,86 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditSchemas implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_scheme_id' => [
'value' => $_POST['edit_scheme_id'] ?? '',
'type' => 'hidden',
'pk' => 1
],
'name' => [
'value' => $_POST['name'] ?? '',
'output_name' => 'Scheme Name',
'mandatory' => 1,
'type' => 'text'
],
'header_color' => [
'value' => $_POST['header_color'] ?? '',
'output_name' => 'Header Color',
'mandatory' => 1,
'type' => 'text',
'size' => 10,
'length' => 9,
'error_check' => 'custom',
// FIXME: update regex check for hex/rgb/hsl with color check class
'error_regex' => '/^#([\dA-Fa-f]{6}|[\dA-Fa-f]{8})$/',
'error_example' => '#F6A544'
],
'enabled' => [
'value' => $_POST['enabled'] ?? '',
'output_name' => 'Enabled',
'int' => 1,
'type' => 'binary',
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
],
'template' => [
'value' => $_POST['template'] ?? '',
'output_name' => 'Template',
'type' => 'text'
],
],
'table_name' => 'edit_scheme',
'load_query' => "SELECT edit_scheme_id, name, enabled FROM edit_scheme ORDER BY name",
'show_fields' => [
[
'name' => 'name'
],
[
'name' => 'enabled',
'binary' => ['Yes', 'No'],
'before_value' => 'Enabled: '
],
],
];
}
}
// __END__

View File

@@ -1,456 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditUsers implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_user_id' => [
'value' => $_POST['edit_user_id'] ?? '',
'type' => 'hidden',
'pk' => 1,
'int' => 1
],
'username' => [
'value' => $_POST['username'] ?? '',
'output_name' => 'Username',
'mandatory' => 1,
'error_check' => 'unique|alphanumericextended',
'type' => 'text',
// if not min_edit_acl only read
// if not min_show_acl not visible
'min_edit_acl' => '100',
'min_show_acl' => '-1',
],
'password' => [
'value' => $_POST['password'] ?? '',
'HIDDEN_value' => $_POST['HIDDEN_password'] ?? '',
'CONFIRM_value' => $_POST['CONFIRM_password'] ?? '',
'output_name' => 'Password',
'mandatory' => 1,
'type' => 'password', // later has to be password for encryption in database
'update' => [ // connected field updates, and update data
'password_change_date' => [ // db row to update
'type' => 'date', // type of field (int/text/date/etc)
'value' => 'NOW()' // value [todo: complex reference
],
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
// password date when first insert and password is set, needs special field with connection to password
// password reset force interval, if set, user needs to reset password after X time period
'password_change_interval' => [
'value' => $_POST['password_change_interval'] ?? '',
'output_name' => 'Password change interval',
// can be any date length format. n Y/M/D [not H/M/S], only one set, no combination
'error_check' => 'intervalshort',
'type' => 'text',
'interval' => 1, // interval needs NULL write for empty
'size' => 5, // make it 5 chars long
'length' => 5,
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'enabled' => [
'value' => $_POST['enabled'] ?? '',
'output_name' => 'Enabled',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '-1',
],
'deleted' => [
'value' => $_POST['deleted'] ?? '',
'output_name' => 'Deleted',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'strict' => [
'value' => $_POST['strict'] ?? '',
'output_name' => 'Strict (Lock after errors)',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'locked' => [
'value' => $_POST['locked'] ?? '',
'output_name' => 'Locked (auto set if strict with errors)',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'admin' => [
'value' => $_POST['admin'] ?? '',
'output_name' => 'Admin',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'debug' => [
'value' => $_POST['debug'] ?? '',
'output_name' => 'Debug',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'db_debug' => [
'value' => $_POST['db_debug'] ?? '',
'output_name' => 'DB Debug',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'email' => [
'value' => $_POST['email'] ?? '',
'output_name' => 'E-Mail',
'type' => 'text',
'error_check' => 'email',
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'last_name' => [
'value' => $_POST['last_name'] ?? '',
'output_name' => 'Last Name',
'type' => 'text',
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'first_name' => [
'value' => $_POST['first_name'] ?? '',
'output_name' => 'First Name',
'type' => 'text',
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'lock_until' => [
'value' => $_POST['lock_until'] ?? '',
'output_name' => 'Lock account until',
'type' => 'datetime',
'error_check' => 'datetime',
'sql_read' => 'YYYY-MM-DD HH24:MI',
'datetime' => 1,
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'lock_after' => [
'value' => $_POST['lock_after'] ?? '',
'output_name' => 'Lock account after',
'type' => 'datetime',
'error_check' => 'datetime',
'sql_read' => 'YYYY-MM-DD HH24:MI',
'datetime' => 1,'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'login_user_id' => [
'value' => $_POST['login_user_id'] ?? '',
'output_name' => '_GET/_POST loginUserId direct login ID',
'type' => 'text',
'error_check' => 'unique|custom',
'error_regex' => "/^[A-Za-z0-9]+$/",
'emptynull' => 1,'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'login_user_id_set_date' => [
'output_name' => 'loginUserId set date',
'value' => $_POST['login_user_id_set_date'] ?? '',
'type' => 'view',
'empty' => '-',
'min_show_acl' => '100',
],
'login_user_id_last_revalidate' => [
'output_name' => 'loginUserId last revalidate date',
'value' => $_POST['login_user_id_last_revalidate'] ?? '',
'type' => 'view',
'empty' => '-',
'min_show_acl' => '100',
],
'login_user_id_locked' => [
'value' => $_POST['login_user_id_locked'] ?? '',
'output_name' => 'loginUserId usage locked',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'login_user_id_revalidate_after' => [
'value' => $_POST['login_user_id_revalidate_after'] ?? '',
'output_name' => 'loginUserId, User must login after n days',
'type' => 'text',
'error_check' => 'intervalshort',
'interval' => 1, // interval needs NULL write for empty
'size' => 5, // make it 5 chars long
'length' => 5,
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'login_user_id_valid_from' => [
'value' => $_POST['login_user_id_valid_from'] ?? '',
'output_name' => 'loginUserId valid from',
'type' => 'datetime',
'error_check' => 'datetime',
'sql_read' => 'YYYY-MM-DD HH24:MI',
'datetime' => 1,
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'login_user_id_valid_until' => [
'value' => $_POST['login_user_id_valid_until'] ?? '',
'output_name' => 'loginUserId valid until',
'type' => 'datetime',
'error_check' => 'datetime',
'sql_read' => 'YYYY-MM-DD HH24:MI',
'datetime' => 1,
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'edit_language_id' => [
'value' => $_POST['edit_language_id'] ?? '',
'output_name' => 'Language',
'mandatory' => 1,
'int' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_language_id, long_name "
. "FROM edit_language "
. "WHERE enabled = 1"
. "ORDER BY order_number",
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'edit_scheme_id' => [
'value' => $_POST['edit_scheme_id'] ?? '',
'output_name' => 'Scheme',
'int_null' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_scheme_id, name FROM edit_scheme WHERE enabled = 1 ORDER BY name",
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'edit_group_id' => [
'value' => $_POST['edit_group_id'] ?? '',
'output_name' => 'Group',
'int' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_group_id, name FROM edit_group WHERE enabled = 1 ORDER BY name",
'mandatory' => 1,
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'edit_access_right_id' => [
'value' => $_POST['edit_access_right_id'] ?? '',
'output_name' => 'User Level',
'mandatory' => 1,
'int' => 1,
'type' => 'drop_down_db',
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level",
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'login_error_count' => [
'output_name' => 'Login error count',
'value' => $_POST['login_error_count'] ?? '',
'type' => 'view',
'empty' => '0',
'min_show_acl' => '100',
],
'login_error_date_last' => [
'output_name' => 'Last login error',
'value' => $_POST['login_error_date_liast'] ?? '',
'type' => 'view',
'empty' => '-',
'min_show_acl' => '100',
],
'login_error_date_first' => [
'output_name' => 'First login error',
'value' => $_POST['login_error_date_first'] ?? '',
'type' => 'view',
'empty' => '-',
'min_show_acl' => '100',
],
'protected' => [
'value' => $_POST['protected'] ?? '',
'output_name' => 'Protected',
'type' => 'binary',
'int' => 1,
'element_list' => [
'1' => 'Yes',
'0' => 'No'
],
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
'additional_acl' => [
'value' => $_POST['additional_acl'] ?? '',
'output_name' => 'Additional ACL (as JSON)',
'type' => 'textarea',
'error_check' => 'json',
'rows' => 10,
'cols' => 60,
'min_edit_acl' => '100',
'min_show_acl' => '100',
],
],
'load_query' => "SELECT edit_user_id, username, enabled, deleted, "
. "strict, locked, login_error_count "
. "FROM edit_user "
// if base acl is not 90 only list enabled
// if not admin flag, do not list admin flagged
. (
!$this->form->getAclAdmin() ?
"WHERE admin = 0 "
. (
!$this->form->checkBaseACL(90) ?
// $_POST['base_acl_level'] < 90 ?
"AND enabled = 1 " :
""
)
: ''
)
. "ORDER BY username",
'table_name' => 'edit_user',
'show_fields' => [
[
'name' => 'username'
],
[
'name' => 'enabled',
'binary' => ['Yes', 'No'],
'before_value' => 'ENBL: '
],
[
'name' => 'deleted',
'binary' => ['Yes', 'No'],
'before_value' => 'DEL: '
],
[
'name' => 'strict',
'binary' => ['Yes', 'No'],
'before_value' => 'STRC: '
],
[
'name' => 'locked',
'binary' => ['Yes', 'No'],
'before_value' => 'LCK: '
],
[
'name' => 'login_error_count',
'before_value' => 'ERR: '
],
],
'element_list' => [
'edit_access_user' => [
'output_name' => 'Accounts',
'mandatory' => 1,
// set then reference entries are deleted, else the 'enable' flag is only set
'delete' => 0,
// acl
'min_edit_acl' => '40',
'min_show_acl' => '20',
// table read prefix
'prefix' => 'ecu',
'read_data' => [
'table_name' => 'edit_access',
'pk_id' => 'edit_access_id',
'name' => 'name',
'order' => 'name'
],
'elements' => [
'edit_access_user_id' => [
'output_name' => 'Activate',
'type' => 'hidden',
'int' => 1,
'pk_id' => 1
],
'enabled' => [
'type' => 'checkbox',
'output_name' => 'Activate',
'int' => 1,
'element_list' => [1],
],
'edit_access_right_id' => [
'type' => 'drop_down_db',
'output_name' => 'Access Level',
'preset' => 1, // first of the select
'int' => 1,
'query' => "SELECT edit_access_right_id, name FROM edit_access_right ORDER BY level"
],
'edit_default' => [
'type' => 'radio_group',
'output_name' => 'Default',
'int' => 1,
'element_list' => 'radio_group'
],
'edit_access_id' => [
'type' => 'hidden',
'int' => 1
],
],
], // edit pages ggroup
],
];
}
}
// __END__

View File

@@ -1,61 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form\TableArrays;
class EditVisibleGroup implements \CoreLibs\Output\Form\TableArraysInterface
{
/** @var \CoreLibs\Output\Form\Generate */
private $form;
/**
* constructor
* @param \CoreLibs\Output\Form\Generate $form base form class
*/
public function __construct(\CoreLibs\Output\Form\Generate $form)
{
$this->form = $form;
$this->form->log->debug('CLASS LOAD', __NAMESPACE__ . __CLASS__);
}
/**
* return the table array
*
* @return array<mixed>
*/
public function setTableArray(): array
{
return [
'table_array' => [
'edit_visible_group_id' => [
'value' => $_POST['edit_visible_group_id'] ?? '',
'type' => 'hidden',
'pk' => 1
],
'name' => [
'value' => $_POST['name'] ?? '',
'output_name' => 'Group name',
'mandatory' => 1,
'type' => 'text'
],
'flag' => [
'value' => $_POST['flag'] ?? '',
'output_name' => 'Flag',
'mandatory' => 1,
'type' => 'text',
'error_check' => 'alphanumeric|unique'
],
],
'table_name' => 'edit_visible_group',
'load_query' => "SELECT edit_visible_group_id, name FROM edit_visible_group ORDER BY name",
'show_fields' => [
[
'name' => 'name'
],
],
];
}
}
// __END__

View File

@@ -1,493 +0,0 @@
<?php
/*
* image thumbnail, rotate, etc
*/
declare(strict_types=1);
namespace CoreLibs\Output;
class Image
{
/**
* converts picture to a thumbnail with max x and max y size
*
* @param string $pic source image file with or without path
* @param int $size_x maximum size width
* @param int $size_y maximum size height
* @param string $dummy empty, or file_type to show an icon
* instead of nothing if file is not found
* @param string $path if source start is not ROOT path,
* if empty ROOT is choosen
* @param string $cache_source cache path, if not given TMP is used
* @param bool $clear_cache if set to true, will create thumb all the tame
* @return string|bool thumbnail name, or false for error
*/
public static function createThumbnail(
string $pic,
int $size_x,
int $size_y,
string $dummy = '',
string $path = '',
string $cache_source = '',
bool $clear_cache = false
) {
// get image type flags
$image_types = [
1 => 'gif',
2 => 'jpg',
3 => 'png'
];
$return_data = false;
$CONVERT = '';
// if CONVERT is not defined, abort
/** @phan-suppress-next-line PhanUndeclaredConstant */
if (defined('CONVERT') && is_executable(CONVERT)) {
/** @phan-suppress-next-line PhanUndeclaredConstant */
$CONVERT = CONVERT;
} else {
return $return_data;
}
if (!empty($cache_source)) {
$tmp_src = $cache_source;
} else {
$tmp_src = BASE . TMP;
}
// check if pic has a path, and override next sets
if (strstr($pic, '/') === false) {
if (empty($path)) {
$path = BASE;
}
$filename = $path . MEDIA . PICTURES . $pic;
} else {
$filename = $pic;
// and get the last part for pic (the filename)
$tmp = explode('/', $pic);
$pic = $tmp[(count($tmp) - 1)];
}
// does this picture exist and is it a picture
if (file_exists($filename) && is_file($filename)) {
[$width, $height, $type] = getimagesize($filename) ?: [];
$convert_prefix = '';
$create_file = false;
$delete_filename = '';
// check if we can skip the PDF creation: if we have size, if do not have type, we assume type png
if (!$type && is_numeric($size_x) && is_numeric($size_y)) {
$check_thumb = $tmp_src . 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[3];
if (!is_file($check_thumb)) {
$create_file = true;
} else {
$type = 3;
}
}
// if type is not in the list, but returns as PDF, we need to convert to JPEG before
if (!$type) {
$output = [];
$return = null;
// is this a PDF, if no, return from here with nothing
$convert_prefix = 'png:';
# TEMP convert to PNG, we then override the file name
$convert_string = $CONVERT . ' ' . $filename . ' ' . $convert_prefix . $filename . '_TEMP';
$status = exec($convert_string, $output, $return);
$filename .= '_TEMP';
// for delete, in case we need to glob
$delete_filename = $filename;
// find file, if we can't find base name, use -0 as the first one (ignore other pages in multiple ones)
if (!is_file($filename)) {
$filename .= '-0';
}
[$width, $height, $type] = getimagesize($filename) ?: [];
}
// if no size given, set size to original
if (!$size_x || $size_x < 1 || !is_numeric($size_x)) {
$size_x = $width;
}
if (!$size_y || $size_y < 1 || !is_numeric($size_y)) {
$size_y = $height;
}
$thumb = 'thumb_' . $pic . '_' . $size_x . 'x' . $size_y . '.' . $image_types[$type];
$thumbnail = $tmp_src . $thumb;
// check if we already have this picture converted
if (!is_file($thumbnail) || $clear_cache == true) {
// convert the picture
if ($width > $size_x) {
$convert_string = $CONVERT . ' -geometry ' . $size_x . 'x ' . $filename . ' ' . $thumbnail;
$status = exec($convert_string, $output, $return);
// get the size of the converted data, if converted
if (is_file($thumbnail)) {
[$width, $height, $type] = getimagesize($thumbnail) ?: [];
}
}
if ($height > $size_y) {
$convert_string = $CONVERT . ' -geometry x' . $size_y . ' ' . $filename . ' ' . $thumbnail;
$status = exec($convert_string, $output, $return);
}
}
if (!is_file($thumbnail)) {
copy($filename, $thumbnail);
}
$return_data = $thumb;
// if we have a delete filename, delete here with glob
if ($delete_filename) {
array_map('unlink', glob($delete_filename . '*') ?: []);
}
} else {
if (!empty($dummy) && strstr($dummy, '/') === false) {
// check if we have the "dummy" image flag set
$filename = PICTURES . ICONS . strtoupper($dummy) . ".png";
/** @phpstan-ignore-next-line */
if (!empty($dummy) && file_exists($filename) && is_file($filename)) {
$return_data = $filename;
} else {
$return_data = false;
}
} else {
$filename = $dummy;
}
}
return $return_data;
}
/**
* simple thumbnail creation for jpeg, png only
* TODO: add other types like gif, etc
* - bails with false on failed create
* - if either size_x or size_y are empty (0)
* the resize is to max of one size
* if both are set, those are the max sizes (aspect ration is always ekpt)
* - if path is not given will cache folder for current path set
*
* @param string $filename source file name with full path
* @param int $thumb_width thumbnail width
* @param int $thumb_height thumbnail height
* @param string|null $thumbnail_path altnerative path for thumbnails
* @param bool $create_dummy if we encounter an invalid file
* create a dummy image file and return it
* @param bool $use_cache default to true, set to false to skip
* creating new image if exists
* @param bool $high_quality default to true, uses sample version,
* set to false to not use (default true)
* to use quick but less nice version
* @param int $jpeg_quality default 80, set image quality for jpeg only
* @return string|bool thumbnail with path
*/
public static function createThumbnailSimple(
string $filename,
int $thumb_width = 0,
int $thumb_height = 0,
?string $thumbnail_path = null,
bool $create_dummy = true,
bool $use_cache = true,
bool $high_quality = true,
int $jpeg_quality = 80
) {
$thumbnail = false;
// $this->debug('IMAGE PREPARE', "FILE: $filename (exists "
// .(string)file_exists($filename)."), WIDTH: $thumb_width, HEIGHT: $thumb_height");
// check that input image exists and is either jpeg or png
// also fail if the basic CACHE folder does not exist at all
if (
file_exists($filename) &&
is_dir(BASE . LAYOUT . CONTENT_PATH . CACHE) &&
is_writable(BASE . LAYOUT . CONTENT_PATH . CACHE)
) {
// $this->debug('IMAGE PREPARE', "FILENAME OK, THUMB WIDTH/HEIGHT OK");
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [];
$thumbnail_write_path = null;
$thumbnail_web_path = null;
// path set first
if (
$img_type == IMAGETYPE_JPEG ||
$img_type == IMAGETYPE_PNG ||
$create_dummy === true
) {
// $this->debug('IMAGE PREPARE', "IMAGE TYPE OK: ".$inc_width.'x'.$inc_height);
// set thumbnail paths
$thumbnail_write_path = BASE . LAYOUT . CONTENT_PATH . CACHE . IMAGES;
$thumbnail_web_path = LAYOUT . CACHE . IMAGES;
// if images folder in cache does not exist create it, if failed, fall back to base cache folder
if (!is_dir($thumbnail_write_path)) {
if (false === mkdir($thumbnail_write_path)) {
$thumbnail_write_path = BASE . LAYOUT . CONTENT_PATH . CACHE;
$thumbnail_web_path = LAYOUT . CACHE;
}
}
}
// do resize or fall back on dummy run
if (
$img_type == IMAGETYPE_JPEG ||
$img_type == IMAGETYPE_PNG
) {
// if missing width or height in thumb, use the set one
if ($thumb_width == 0) {
$thumb_width = $inc_width;
}
if ($thumb_height == 0) {
$thumb_height = $inc_height;
}
// check resize parameters
if ($inc_width > $thumb_width || $inc_height > $thumb_height) {
$thumb_width_r = 0;
$thumb_height_r = 0;
// we need to keep the aspect ration on longest side
if (
($inc_height > $inc_width &&
// and the height is bigger than thumb set
$inc_height > $thumb_height) ||
// or the height is smaller or equal width
// but the width for the thumb is equal to the image height
($inc_height <= $inc_width &&
$inc_width == $thumb_width
)
) {
// $this->debug('IMAGE PREPARE', 'HEIGHT > WIDTH');
$ratio = $inc_height / $thumb_height;
$thumb_width_r = (int)ceil($inc_width / $ratio);
$thumb_height_r = $thumb_height;
} else {
// $this->debug('IMAGE PREPARE', 'WIDTH > HEIGHT');
$ratio = $inc_width / $thumb_width;
$thumb_width_r = $thumb_width;
$thumb_height_r = (int)ceil($inc_height / $ratio);
}
// $this->debug('IMAGE PREPARE', "Ratio: $ratio, Target size $thumb_width_r x $thumb_height_r");
// set output thumbnail name
$thumbnail = 'thumb-' . pathinfo($filename)['filename'] . '-'
. $thumb_width_r . 'x' . $thumb_height_r;
if (
$use_cache === false ||
!file_exists($thumbnail_write_path . $thumbnail)
) {
// image, copy source image, offset in image, source x/y, new size, source image size
$thumb = imagecreatetruecolor($thumb_width_r, $thumb_height_r);
if ($thumb === false) {
return false;
}
if ($img_type == IMAGETYPE_PNG) {
$imagecolorallocatealpha = imagecolorallocatealpha($thumb, 0, 0, 0, 127);
if ($imagecolorallocatealpha === false) {
return false;
}
// preservere transaprency
imagecolortransparent(
$thumb,
$imagecolorallocatealpha
);
imagealphablending($thumb, false);
imagesavealpha($thumb, true);
}
$source = null;
switch ($img_type) {
case IMAGETYPE_JPEG:
$source = imagecreatefromjpeg($filename);
break;
case IMAGETYPE_PNG:
$source = imagecreatefrompng($filename);
break;
}
// check that we have a source image resource
if ($source !== null && $source !== false) {
// resize no shift
if ($high_quality === true) {
imagecopyresized(
$thumb,
$source,
0,
0,
0,
0,
$thumb_width_r,
$thumb_height_r,
$inc_width,
$inc_height
);
} else {
imagecopyresampled(
$thumb,
$source,
0,
0,
0,
0,
$thumb_width_r,
$thumb_height_r,
$inc_width,
$inc_height
);
}
// write file
switch ($img_type) {
case IMAGETYPE_JPEG:
imagejpeg($thumb, $thumbnail_write_path . $thumbnail, $jpeg_quality);
break;
case IMAGETYPE_PNG:
imagepng($thumb, $thumbnail_write_path . $thumbnail);
break;
}
// free up resources (in case we are called in a loop)
imagedestroy($source);
imagedestroy($thumb);
} else {
$thumbnail = false;
}
}
} else {
// we just copy over the image as is, we never upscale
$thumbnail = 'thumb-' . pathinfo($filename)['filename'] . '-' . $inc_width . 'x' . $inc_height;
if (
$use_cache === false ||
!file_exists($thumbnail_write_path . $thumbnail)
) {
copy($filename, $thumbnail_write_path . $thumbnail);
}
}
// add output path
if ($thumbnail !== false) {
$thumbnail = $thumbnail_web_path . $thumbnail;
}
} elseif ($create_dummy === true) {
// create dummy image in the thumbnail size
// if one side is missing, use the other side to create a square
if (!$thumb_width) {
$thumb_width = $thumb_height;
}
if (!$thumb_height) {
$thumb_height = $thumb_width;
}
// do we have an image already?
$thumbnail = 'thumb-' . pathinfo($filename)['filename'] . '-' . $thumb_width . 'x' . $thumb_height;
if (
$use_cache === false ||
!file_exists($thumbnail_write_path . $thumbnail)
) {
// if both are unset, set to 250
if ($thumb_height == 0) {
$thumb_height = 250;
}
if ($thumb_width == 0) {
$thumb_width = 250;
}
$thumb = imagecreatetruecolor($thumb_width, $thumb_height);
if ($thumb === false) {
return false;
}
// add outside border px = 5% (rounded up)
// eg 50px -> 2.5px
$gray = imagecolorallocate($thumb, 200, 200, 200);
$white = imagecolorallocate($thumb, 255, 255, 255);
if ($gray === false || $white === false) {
return false;
}
// fill gray background
imagefill($thumb, 0, 0, $gray);
// now create rectangle
if (imagesx($thumb) < imagesy($thumb)) {
$width = (int)round(imagesx($thumb) / 100 * 5);
} else {
$width = (int)round(imagesy($thumb) / 100 * 5);
}
imagefilledrectangle(
$thumb,
0 + $width,
0 + $width,
imagesx($thumb) - $width,
imagesy($thumb) - $width,
$white
);
// add "No valid images source"
// OR add circle
// * find center
// * width/height is 75% of size - border
// smaller size is taken
$base_width = imagesx($thumb) > imagesy($thumb) ? imagesy($thumb) : imagesx($thumb);
// get 75% width
$cross_width = (int)round((($base_width - ($width * 2)) / 100 * 75) / 2);
$center_x = (int)round(imagesx($thumb) / 2);
$center_y = (int)round(imagesy($thumb) / 2);
imagefilledellipse($thumb, $center_x, $center_y, $cross_width, $cross_width, $gray);
// find top left and bottom left for first line
imagepng($thumb, $thumbnail_write_path . $thumbnail);
}
// add web path
$thumbnail = $thumbnail_web_path . $thumbnail;
}
}
// either return false or the thumbnail name + output path web
return $thumbnail;
}
/**
* reads the rotation info of an file and rotates it to be correctly upright
* this is done because not all software honers the exit Orientation flag
* only works with jpg or png
*
* @param string $filename path + filename to rotate. This file must be writeable
* @return void
*/
public static function correctImageOrientation($filename): void
{
// function exists & file is writeable, else do nothing
if (!function_exists('exif_read_data') || !is_writeable($filename)) {
return;
}
[$inc_width, $inc_height, $img_type] = getimagesize($filename) ?: [];
// add @ to avoid "file not supported error"
$exif = @exif_read_data($filename);
$orientation = null;
$img = null;
if ($exif && isset($exif['Orientation'])) {
$orientation = $exif['Orientation'];
}
// only if we need to rotate, if 1 it is already upright
if ($orientation === null || $orientation == 1) {
return;
}
switch ($img_type) {
case IMAGETYPE_JPEG:
$img = imagecreatefromjpeg($filename);
break;
case IMAGETYPE_PNG:
$img = imagecreatefrompng($filename);
break;
}
// no image loaded (wrong type)
if ($img === null || $img === false) {
return;
}
$deg = 0;
// 1 top, 6: left, 8: right, 3: bottom
switch ($orientation) {
case 3:
$deg = 180;
break;
case 6:
$deg = -90;
break;
case 8:
$deg = 90;
break;
}
// rotate if needed
if ($deg) {
$img = imagerotate($img, $deg, 0);
}
// rotate failed
if ($img === false) {
return;
}
// then rewrite the rotated image back to the disk as $filename
switch ($img_type) {
case IMAGETYPE_JPEG:
imagejpeg($img, $filename);
break;
case IMAGETYPE_PNG:
imagepng($img, $filename);
break;
}
// clean up image if we have an image
imagedestroy($img);
}
}
// __END__

View File

@@ -1,932 +0,0 @@
<?php
/*
* Class ProgressBar
*
* Author: Gerd Weitenberg (hahnebuechen@web.de)
* Date: 2005.03.09
*
* Update: Clemens Schwaighofer
* Date: 2012.9.5 [stacked output]
* Date: 2013.2.21 [proper class formatting]
* Date: 2017.4.13 [no output fix with cache overload]
* Date: 2018.3.28 [PHPCS + namespace]
*
*/
declare(strict_types=1);
namespace CoreLibs\Output;
class ProgressBar
{
// private vars
/** @var string */
public $code; // unique code
/** @var string */
public $status = 'new'; // current status (new,show,hide)
/** @var float|int */
public $step = 0; // current step
/** @var array<string,null|int|float> */
public $position = [ // current bar position
'left' => null,
'top' => null,
'width' => null,
'height' => null,
];
/** @var int */
public $clear_buffer_size = 1; // we need to send this before the lfush to get browser output
/** @var int */
public $clear_buffer_size_init = 1024 * 1024; // if I don't send that junk, it won't send anything
// public vars
/** @var int */
public $min = 0; // minimal steps
/** @var int */
public $max = 100; // maximal steps
/** @var int */
public $left = 5; // bar position from left
/** @var int */
public $top = 5; // bar position from top
/** @var int */
public $width = 300; // bar width
/** @var int */
public $height = 25; // bar height
/** @var int */
public $pedding = 0; // bar pedding
/** @var string */
public $color = '#0033ff'; // bar color
/** @var string */
public $bgr_color = '#c0c0c0'; // bar background color
/** @var string */
public $bgr_color_master = '#ffffff'; // master div background color
/** @var int */
public $border = 1; // bar border width
/** @var string */
public $brd_color = '#000000'; // bar border color
/** @var string */
public $direction = 'right'; // direction of motion (right,left,up,down)
/** @var array<string,string|bool|int> */
public $frame = ['show' => false]; // ProgressBar Frame
/* 'show' => false, # frame show (true/false)
'left' => 200, # frame position from left
'top' => 100, # frame position from top
'width' => 300, # frame width
'height' => 75, # frame height
'color' => '#c0c0c0', # frame color
'border' => 2, # frame border
'brd_color' => '#dfdfdf #404040 #404040 #dfdfdf' # frame border color
*/
/** @#var array{string}{string: string|int} */
/** @var mixed[][] */
public $label = []; // ProgressBar Labels
/* 'name' => [ # label name
'type' => 'text', # label type (text,button,step,percent,crossbar)
'value' => 'Please wait ...', # label value
'left' => 10, # label position from left
'top' => 20, # label position from top
'width' => 0, # label width
'height' => 0, # label height
'align' => 'left', # label align
'font-size' => 11, # label font size
'font-family' => 'Verdana, Tahoma, Arial', # label font family
'font-weight' => '', # label font weight
'color' => '#000000', # label font color
'bgr_color' => '' # label background color
]
*/
/** @var string */
// output strings
public $prefix_message = '';
/**
* progress bar constructor
*
* @param integer $width progress bar width, default 0
* @param integer $height progress bar height, default 0
*/
public function __construct(int $width = 0, int $height = 0)
{
$this->code = substr(md5(microtime()), 0, 6);
if ($width > 0) {
$this->width = $width;
}
if ($height > 0) {
$this->height = $height;
}
// needs to be called twice or I do not get any output
$this->__flushCache($this->clear_buffer_size_init);
$this->__flushCache($this->clear_buffer_size_init);
}
// private functions
/**
* flush cache hack for IE and others
*
* @param integer $clear_buffer_size buffer size override
* @return void has not return
*/
private function __flushCache(int $clear_buffer_size = 0): void
{
if (!$clear_buffer_size) {
$clear_buffer_size = $this->clear_buffer_size;
}
echo str_repeat(' ', $clear_buffer_size);
// a small hack to avoid warnings about no buffer to flush
@ob_flush();
flush();
}
/**
* [__calculatePercent description]
*
* @param float $step percent step to do
* @return float percent step done
*/
private function __calculatePercent(float $step): float
{
// avoid divison through 0
if ($this->max - $this->min == 0) {
$this->max ++;
}
$percent = round(($step - $this->min) / ($this->max - $this->min) * 100);
if ($percent > 100) {
$percent = 100;
}
return $percent;
}
/**
* calculate position in bar step
*
* @param float $step percent step to do
* @return array<string,int|float> bar position as array
*/
private function __calculatePosition(float $step): array
{
$bar = 0;
switch ($this->direction) {
case 'right':
case 'left':
$bar = $this->width;
break;
case 'down':
case 'up':
$bar = $this->height;
break;
}
// avoid divison through 0
if ($this->max - $this->min == 0) {
$this->max ++;
}
$pixel = round(($step - $this->min) * ($bar - ($this->pedding * 2)) / ($this->max - $this->min));
if ($step <= $this->min) {
$pixel = 0;
}
if ($step >= $this->max) {
$pixel = $bar - ($this->pedding * 2);
}
$position = [];
switch ($this->direction) {
case 'right':
$position['left'] = $this->pedding;
$position['top'] = $this->pedding;
$position['width'] = $pixel;
$position['height'] = $this->height - ($this->pedding * 2);
break;
case 'left':
$position['left'] = $this->width - $this->pedding - $pixel;
$position['top'] = $this->pedding;
$position['width'] = $pixel;
$position['height'] = $this->height - ($this->pedding * 2);
break;
case 'down':
$position['left'] = $this->pedding;
$position['top'] = $this->pedding;
$position['width'] = $this->width - ($this->pedding * 2);
$position['height'] = $pixel;
break;
case 'up':
$position['left'] = $this->pedding;
$position['top'] = $this->height - $this->pedding - $pixel;
$position['width'] = $this->width - ($this->pedding * 2);
$position['height'] = $pixel;
break;
}
return $position;
}
/**
* set the step
*
* @param float $step percent step to do
* @return void
*/
private function __setStep(float $step): void
{
if ($step > $this->max) {
$step = $this->max;
}
if ($step < $this->min) {
$step = $this->min;
}
$this->step = $step;
}
// public functions
/**
* set frame layout
*
* @param integer $width bar width
* @param integer $height bar height
* @return void
*/
public function setFrame(int $width = 0, int $height = 0): void
{
$this->frame = [
'show' => true,
'left' => 20,
'top' => 35,
'width' => $this->width + 6,
'height' => 'auto',
'color' => '#c0c0c0',
'border' => 2,
'brd_color' => '#dfdfdf #404040 #404040 #dfdfdf'
];
if ($width > 0) {
$this->frame['width'] = $width;
}
if ($height > 0) {
$this->frame['height'] = $height;
}
}
/**
* set bar label text
* allowed types are: text, button, step, percent, percentlbl, crossbar
*
* @param string $type label type
* @param string $name label name (internal)
* @param string $value label output name (optional)
* @return void
*/
public function addLabel(string $type, string $name, string $value = '&nbsp;'): void
{
switch ($type) {
case 'text':
$this->label[$name] = [
'type' => 'text',
'value' => $value,
'left' => 0, // keep all to the left in box
'top' => 2, // default top is 2px
'width' => $this->width,
'height' => 0,
'align' => 'left',
'font-size' => 11,
'font-family' => 'Verdana, Tahoma, Arial',
'font-weight' => 'normal',
'color' => '#000000',
'bgr_color' => ''
];
break;
case 'button':
$this->label[$name] = [
'type' => 'button',
'value' => $value,
'action' => '',
'target' => 'self',
'left' => 5,
'top' => 5,
'width' => 0,
'height' => 0,
'align' => 'center',
'font-size' => 11,
'font-family' => 'Verdana, Tahoma, Arial',
'font-weight' => 'normal',
'color' => '#000000',
'bgr_color' => ''
];
break;
case 'step':
$this->label[$name] = [
'type' => 'step',
'value' => $value,
'left' => $this->left + 5,
'top' => $this->top + 5,
'width' => 10,
'height' => 0,
'align' => 'right',
'font-size' => 11,
'font-family' => 'Verdana, Tahoma, Arial',
'font-weight' => 'normal',
'color' => '#000000',
'bgr_color' => ''
];
break;
case 'percentlbl':
case 'percent':
// check font size
if ($this->height <= 11) {
$font_size = $this->height - 1;
} else {
$font_size = 11;
}
$this->label[$name] = [
'type' => $type, // either percent or percentlbl
'value' => $value,
'left' => false,
'top' => round(
($this->height - $font_size) / log($this->height - $font_size, 7),
0
) - $this->pedding,
'width' => $this->width,
'height' => 0,
'align' => 'center',
'font-size' => $font_size,
'font-family' => 'sans-serif',
'font-weight' => 'normal',
'color' => '#000000',
'bgr_color' => ''
];
break;
case 'crossbar':
$this->label[$name] = [
'type' => 'crossbar',
'value' => $value,
'left' => $this->left + ($this->width / 2),
'top' => $this->top - 16,
'width' => 10,
'height' => 0,
'align' => 'center',
'font-size' => 11,
'font-family' => 'Verdana, Tahoma, Arial',
'font-weight' => 'normal',
'color' => '#000000',
'bgr_color' => ''
];
break;
}
}
/**
* add a button to the progress bar
*
* @param string $name button name (internal)
* @param string $value button text (output)
* @param string $action button action (link)
* @param string $target button action target (default self)
* @return void
*/
public function addButton(string $name, string $value, string $action, string $target = 'self'): void
{
$this->addLabel('button', $name, $value);
$this->label[$name]['action'] = $action;
$this->label[$name]['target'] = $target;
}
/**
* set the label position
*
* @param string $name label name to set
* @param int $left left px
* @param int $top top px
* @param int $width width px
* @param int $height height px
* @param string $align alignment (left/right/etc), default empty
* @return void
*/
public function setLabelPosition(
string $name,
int $left,
int $top,
int $width,
int $height,
string $align = ''
): void {
// print "SET POSITION[$name]: $left<br>";
// if this is percent, we ignore anything, it is auto positioned
if ($this->label[$name]['type'] != 'percent') {
foreach (['top', 'left', 'width', 'height'] as $pos_name) {
if ($$pos_name !== false) {
$this->label[$name][$pos_name] = intval($$pos_name);
}
}
if ($align != '') {
$this->label[$name]['align'] = $align;
}
}
// init
if ($this->status != 'new') {
$output = '<script type="text/JavaScript">';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.top="' . $this->label[$name]['top'] . 'px";';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.left="' . $this->label[$name]['left'] . 'px";';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.width="' . $this->label[$name]['width'] . 'px";';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.height="' . $this->label[$name]['height'] . 'px";';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.align="' . $this->label[$name]['align'] . '";';
$output .= '</script>' . "\n";
echo $output;
$this->__flushCache();
}
}
/**
* set label color
*
* @param string $name label name to set
* @param string $color color value in rgb html hex
* @return void
*/
public function setLabelColor(string $name, string $color): void
{
$this->label[$name]['color'] = $color;
if ($this->status != 'new') {
echo '<script type="text/JavaScript">document.getElementById("plbl' . $name
. $this->code . '").style.color="' . $color . '";</script>' . "\n";
$this->__flushCache();
}
}
/**
* set the label background color
*
* @param string $name label name to set
* @param string $color background color to set in rgb html hex
* @return void
*/
public function setLabelBackground(string $name, string $color): void
{
$this->label[$name]['bgr_color'] = $color;
if ($this->status != 'new') {
echo '<script type="text/JavaScript">document.getElementById("plbl' . $name
. $this->code . '").style.background="' . $color . '";</script>' . "\n";
$this->__flushCache();
}
}
/**
* [setLabelFont description]
*
* @param string $name label name to set
* @param int $size font size in px
* @param string $family font family (default empty)
* @param string $weight font weight (default empty)
* @return void
*/
public function setLabelFont(string $name, int $size, string $family = '', string $weight = ''): void
{
// just in case if it is too small
if (intval($size) < 0) {
$size = 11;
}
// if this is percent, the size is not allowed to be bigger than the bar size - 5px
if ($this->label[$name]['type'] == 'percent' && intval($size) >= $this->height) {
$size = $this->height - 1;
}
// position the label new if this is percent
if ($this->label[$name]['type'] == 'percent') {
$this->label[$name]['top'] = round(
($this->height - intval($size)) / log($this->height - intval($size), 7),
0
) - $this->pedding;
}
// print "HEIGHT: ".$this->height.", Size: ".intval($size)
// .", Pedding: ".$this->pedding.", Calc: ".round($this->height - intval($size))
// .", Log: ".log($this->height - intval($size), 7)."<br>";
// then set like usual
$this->label[$name]['font-size'] = intval($size);
if ($family != '') {
$this->label[$name]['font-family'] = $family;
}
if ($weight != '') {
$this->label[$name]['font-weight'] = $weight;
}
if ($this->status != 'new') {
$output = '<script type="text/JavaScript">';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.font-size="' . $this->label[$name]['font-size'] . 'px";';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.font-family="' . $this->label[$name]['font-family'] . '";';
$output .= 'document.getElementById("plbl' . $name
. $this->code . '").style.font-weight="' . $this->label[$name]['font-weight'] . '";';
$output .= '</script>' . "\n";
echo $output;
$this->__flushCache();
}
}
/**
* set the label value
*
* @param string $name label name to set
* @param string $value label value (output)
* @return void
*/
public function setLabelValue(string $name, string $value): void
{
$this->label[$name]['value'] = $value;
// print "NAME[$name], Status: ".$this->status.": ".$value."<Br>";
if ($this->status != 'new') {
echo '<script type="text/JavaScript">PBlabelText' . $this->code
. '("' . $name . '","' . $this->label[$name]['value'] . '");</script>' . "\n";
$this->__flushCache();
}
}
/**
* set the bar color
*
* @param string $color color for the progress bar in rgb html hex
* @return void
*/
public function setBarColor(string $color): void
{
$this->color = $color;
if ($this->status != 'new') {
echo '<script type="text/JavaScript">document.getElementById("pbar' . $this->code
. '").style.background="' . $color . '";</script>' . "\n";
$this->__flushCache();
}
}
/**
* set the progress bar background color
*
* @param string $color background color in rgb html hex
* @return void
*/
public function setBarBackground(string $color): void
{
$this->bgr_color = $color;
if ($this->status != 'new') {
echo '<script type="text/JavaScript">document.getElementById("pbrd' . $this->code
. '").style.background="' . $color . '";</script>' . "\n";
$this->__flushCache();
}
}
/**
* progress bar direct (left/right)
*
* @param string $direction set direction as left/right
* @return void
*/
public function setBarDirection(string $direction): void
{
$this->direction = $direction;
if ($this->status != 'new') {
$this->position = $this->__calculatePosition($this->step);
echo '<script type="text/JavaScript">';
echo 'PBposition' . $this->code . '("left",' . $this->position['left'] . ');';
echo 'PBposition' . $this->code . '("top",' . $this->position['top'] . ');';
echo 'PBposition' . $this->code . '("width",' . $this->position['width'] . ');';
echo 'PBposition' . $this->code . '("height",' . $this->position['height'] . ');';
echo '</script>' . "\n";
$this->__flushCache();
}
}
/**
* get the progress bar base HTML
*
* @return string progress bar HTML code
*/
public function getHtml(): string
{
$html = '';
$js = '';
$html_button = '';
$html_percent = '';
$this->__setStep($this->step);
$this->position = $this->__calculatePosition($this->step);
$style_master = '';
if ($this->top || $this->left) {
$style_master = 'position:relative;top:' . $this->top
. 'px;left:' . $this->left . 'px;width:' . ($this->width + 10) . 'px;';
}
$html = '<div id="pbm' . $this->code . '" style="' . $style_master
. 'background:' . $this->bgr_color_master . ';">';
$style_brd = 'width:' . $this->width . 'px;height:' . $this->height
. 'px;background:' . $this->bgr_color . ';';
if ($this->border > 0) {
$style_brd .= 'border:'
. $this->border . 'px solid; border-color:'
. $this->brd_color . '; -webkit-border-radius: 5px 5px 5px 5px; '
. 'border-radius: 5px 5px 5px 5px; -webkit-shadow: 2px 2px 10px rgba(0, 0, 0, 0.25) inset; '
. 'box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.25) inset;';
}
$style_bar = 'position:relative;width:' . $this->position['width']
. 'px;height:' . $this->position['height'] . 'px;background:' . $this->color . ';';
if ($this->position['top'] !== false) {
$style_bar .= 'top:' . $this->position['top'] . 'px;';
}
if ($this->position['left'] !== false) {
$style_bar .= 'left:' . $this->position['left'] . 'px;';
}
if ($this->border > 0) {
$style_bar .= '-webkit-border-radius: 5px 5px 5px 5px; '
. 'border-radius: 5px 5px 5px 5px; -webkit-shadow: 2px 2px 10px rgba(0, 0, 0, 0.25) inset; '
. 'box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.25) inset;';
}
if ($this->frame['show'] == true) {
$border = '';
if ($this->frame['border'] > 0) {
$border = 'border:' . $this->frame['border']
. 'px solid;border-color:' . $this->frame['brd_color'] . ';margin-top:2px; '
. '-webkit-border-radius: 5px 5px 5px 5px; border-radius: 5px 5px 5px 5px;';
}
$html .= '<div id="pfrm' . $this->code . '" style="width:'
. $this->frame['width'] . 'px;height:' . $this->frame['height'] . 'px;'
. $border . 'background:' . $this->frame['color'] . ';">' . "\n";
}
// temp write the bar here, we add that later, below all the html + progress %
$html_bar_top = '<div id="pbrd' . $this->code . '" style="' . $style_brd
. ($this->frame['show'] == true ? 'margin-left: 2px;margin-bottom:2px;' : '') . '">' . "\n";
$html_bar_top .= '<div id="pbar' . $this->code . '" style="' . $style_bar . '">';
// insert single percent there
$html_bar_bottom = '</div></div>' . "\n";
$js .= 'function PBposition' . $this->code . '(item,pixel) {' . "\n";
$js .= ' pixel = parseInt(pixel);' . "\n";
$js .= ' switch(item) {' . "\n";
$js .= ' case "left": document.getElementById("pbar' . $this->code
. '").style.left=(pixel) + \'px\'; break;' . "\n";
$js .= ' case "top": document.getElementById("pbar' . $this->code
. '").style.top=(pixel) + \'px\'; break;' . "\n";
$js .= ' case "width": document.getElementById("pbar' . $this->code
. '").style.width=(pixel) + \'px\'; break;' . "\n";
$js .= ' case "height": document.getElementById("pbar' . $this->code
. '").style.height=(pixel) + \'px\'; break;' . "\n";
$js .= ' }' . "\n";
$js .= '}' . "\n";
// print "DUMP LABEL: <br><pre>".print_r($this->label, true)."</pre><br>";
foreach ($this->label as $name => $data) {
// set what type of move we do
$move_prefix = $data['type'] == 'button' ? 'margin' : 'padding';
$style_lbl = 'position:relative;';
if ($data['top'] !== false) {
$style_lbl .= $move_prefix . '-top:' . $data['top'] . 'px;';
}
if ($data['left'] !== false) {
$style_lbl .= $move_prefix . '-left:' . $data['left'] . 'px;';
}
$style_lbl .= 'text-align:' . $data['align'] . ';';
if ($data['width'] > 0) {
$style_lbl .= 'width:' . $data['width'] . 'px;';
}
if ($data['height'] > 0) {
$style_lbl .= 'height:' . $data['height'] . 'px;';
}
if (array_key_exists('font-size', $data)) {
$style_lbl .= 'font-size:' . $data['font-size'] . 'px;';
}
if (array_key_exists('font-family', $data)) {
$style_lbl .= 'font-family:' . $data['font-family'] . ';';
}
if (array_key_exists('font-weight', $data)) {
$style_lbl .= 'font-weight:' . $data['font-weight'] . ';';
}
if (array_key_exists('bgr_color', $data) && ($data['bgr_color'] != '')) {
$style_lbl .= 'background:' . $data['bgr_color'] . ';';
}
if (array_key_exists('type', $data)) {
switch ($data['type']) {
case 'text':
$html .= '<div id="plbl' . $name . $this->code . '" style="'
. $style_lbl . 'margin-bottom:2px;">' . $data['value'] . '</div>' . "\n";
break;
case 'button':
$html_button .= '<div><input id="plbl' . $name
. $this->code . '" type="button" value="' . $data['value'] . '" style="'
. $style_lbl . 'margin-bottom:5px;" onclick="' . $data['target'] . '.location.href=\''
. $data['action'] . '\'" /></div>' . "\n";
break;
case 'step':
$html .= '<div id="plbl' . $name . $this->code . '" style="'
. $style_lbl . '">' . $this->step . '</div>' . "\n";
break;
case 'percent':
// only one inner percent
// print "STYLE[$name]: ".$style_lbl."<br>";
if (empty($html_percent)) {
$html_percent = '<div id="plbl' . $name . $this->code
. '" style="' . $style_lbl . 'width:' . $data['width']
. 'px;line-height:1;text-shadow: 0 0 .2em white, 0 0 .5em white;">'
. $this->__calculatePercent($this->step) . '%</div>' . "\n";
}
break;
case 'percentlbl':
$html .= '<div id="plbl' . $name . $this->code . '" style="'
. $style_lbl . 'width:' . $data['width'] . 'px;">'
. $this->__calculatePercent($this->step) . '%</div>' . "\n";
break;
case 'crossbar':
$html .= '<div id="plbl' . $name . $this->code . '" style="'
. $style_lbl . '">' . $data['value'] . '</div>' . "\n";
$js .= 'function PBrotaryCross' . $name . $this->code . '() {'
. "\n"
. ' cross = document.getElementById("plbl' . $name
. $this->code . '").firstChild.nodeValue;' . "\n"
. ' switch(cross) {' . "\n"
. ' case "--": cross = "\\\\"; break;' . "\n"
. ' case "\\\\": cross = "|"; break;' . "\n"
. ' case "|": cross = "/"; break;' . "\n"
. ' default: cross = "--"; break;' . "\n"
. ' }' . "\n"
. ' document.getElementById("plbl' . $name
. $this->code . '").firstChild.nodeValue = cross;' . "\n"
. '}' . "\n";
break;
}
}
}
// write the progress bar + inner percent inside
$html .= $html_bar_top;
$html .= $html_percent;
$html .= $html_bar_bottom;
$html .= $html_button; // any buttons on bottom
if (count($this->label) > 0) {
$js .= 'function PBlabelText' . $this->code . '(name,text) {' . "\n";
$js .= ' name = "plbl" + name + "' . $this->code . '";' . "\n";
$js .= ' document.getElementById(name).innerHTML=text;' . "\n";
$js .= '}' . "\n";
}
if ($this->frame['show'] == true) {
$html .= '</div>' . "\n";
}
$html .= '<script type="text/JavaScript">' . "\n";
$html .= $js;
$html .= '</script>' . "\n";
$html .= '</div>';
return $html;
}
/**
* show the progress bar after initialize
*
* @return void has no return
*/
public function show(): void
{
$this->status = 'show';
echo $this->getHtml();
$this->__flushCache();
}
/**
* move the progress bar by one step
* prints out javascript to move progress bar
*
* @param float $step percent step
* @return void has no return
*/
public function moveStep(float $step): void
{
$last_step = $this->step;
$this->__setStep($step);
$js = '';
$new_position = $this->__calculatePosition($this->step);
if (
$new_position['width'] != $this->position['width'] &&
($this->direction == 'right' || $this->direction == 'left')
) {
if ($this->direction == 'left') {
$js .= 'PBposition' . $this->code . '("left",' . $new_position['left'] . ');';
}
$js .= 'PBposition' . $this->code . '("width",' . $new_position['width'] . ');';
}
if (
$new_position['height'] != $this->position['height'] &&
($this->direction == 'up' || $this->direction == 'down')
) {
if ($this->direction == 'up') {
$js .= 'PBposition' . $this->code . '("top",' . $new_position['top'] . ');';
}
$js .= 'PBposition' . $this->code . '("height",' . $new_position['height'] . ');';
}
$this->position = $new_position;
foreach ($this->label as $name => $data) {
if (array_key_exists('type', $data)) {
switch ($data['type']) {
case 'step':
if ($this->step != $last_step) {
$js .= 'PBlabelText' . $this->code . '("'
. $name . '","' . $this->step . '/' . $this->max . '");';
}
break;
case 'percentlbl':
case 'percent':
$percent = $this->__calculatePercent($this->step);
if ($percent != $this->__calculatePercent($last_step)) {
$js .= 'PBlabelText' . $this->code . '("' . $name . '","' . $percent . '%");';
}
break;
case 'crossbar':
$js .= 'PBrotaryCross' . $name . $this->code . '();';
break;
}
}
}
if ($js != '') {
echo '<script type="text/JavaScript">' . $js . '</script>' . "\n";
$this->__flushCache();
}
}
/**
* moves progress bar by one step (1)
*
* @return void has no return
*/
public function moveNext(): void
{
$this->moveStep($this->step + 1);
}
/**
* moves the progress bar back to the beginning
*
* @return void has no return
*/
public function moveMin(): void
{
$this->moveStep($this->min);
}
/**
* hide the progress bar if it is visible
*
* @return void has no return
*/
public function hide(): void
{
if ($this->status == 'show') {
$this->status = 'hide';
$output = '<script type="text/JavaScript">'
. 'document.getElementById("pbm' . $this->code
. '").style.visibility="hidden";document.getElementById("pbm'
. $this->code . '").style.display="none";'
. '</script>' . "\n";
echo $output;
$this->__flushCache();
}
}
/**
* show progress bar again after it was hidden with hide()
*
* @return void has no return
*/
public function unhide(): void
{
if ($this->status == 'hide') {
$this->status = 'show';
$output = '<script type="text/JavaScript">'
. 'document.getElementById("pbm' . $this->code
. '").style.visibility="visible";document.getElementById("pbm'
. $this->code . '").style.visibility="block";'
. '</script>' . "\n";
echo $output;
$this->__flushCache();
}
}
}
// __END__

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace CoreLibs\Output\Form;
interface TableArraysInterface
{
/**
* setTableArray interface, set the table array
* @return array<mixed>
*/
public function setTableArray(): array;
}
// __END__

View File

@@ -1,551 +0,0 @@
<?php // phpcs:ignore PSR1.Files.SideEffects
/**
* @phan-file-suppress PhanRedefinedExtendedClass
*/
// because smarty is symlinked folder
declare(strict_types=1);
/********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2004/12/21
* SHORT DESCRIPTION:
* extends smarty with the l10n class so I can use __(, etc calls
* HISTORY:
* 2005/06/22 (cs) include smarty class here, so there is no need to include it in the main file
*********************************************************************/
namespace CoreLibs\Template;
// leading slash if this is in lib\Smarty
class SmartyExtend extends \Smarty
{
// internal translation engine
/** @var \CoreLibs\Language\L10n */
public $l10n;
// lang & encoding
/** @var string */
public $lang_dir = '';
/** @var string */
public $lang;
/** @var string */
public $locale_set;
/** @var string */
public $lang_short;
/** @var string */
public $domain;
/** @var string */
public $encoding;
// page name
/** @var string */
public $page_name;
// array for data parsing
/** @var array<mixed> */
public $HEADER = [];
/** @var array<mixed> */
public $DATA = [];
/** @var array<mixed> */
public $DEBUG_DATA = [];
/** @var array<mixed> */
private $CONTENT_DATA = [];
// control vars
/** @var bool */
public $USE_PROTOTYPE = USE_PROTOTYPE;
/** @var bool */
public $USE_JQUERY = USE_JQUERY;
/** @var bool */
public $USE_SCRIPTACULOUS = USE_SCRIPTACULOUS;
// sub content input vars
/** @var bool */
public $USE_TINY_MCE = false;
/** @var bool */
public $JS_DATEPICKR = false;
/** @var bool */
public $JS_FLATPICKR = false;
/** @var bool */
public $JS_FILE_UPLOADER = false;
/** @var bool */
public $DEBUG_TMPL = false;
/** @var bool */
public $USE_INCLUDE_TEMPLATE = false;
// cache & compile
/** @var string */
public $CACHE_ID = '';
/** @var string */
public $COMPILE_ID = '';
// template vars
/** @var string */
public $MASTER_TEMPLATE_NAME;
/** @var string */
public $PAGE_FILE_NAME;
/** @var string */
public $CONTENT_INCLUDE;
/** @var string */
public $FORM_NAME;
/** @var string */
public $FORM_ACTION;
/** @var string */
public $L_TITLE;
/** @var string|int */
public $PAGE_WIDTH;
// smarty include/set var
/** @var string */
public $TEMPLATE_PATH;
/** @var string */
public $TEMPLATE_NAME;
/** @var string */
public $INC_TEMPLATE_NAME;
/** @var string */
public $JS_TEMPLATE_NAME;
/** @var string */
public $CSS_TEMPLATE_NAME;
/** @var string|null */
public $TEMPLATE_TRANSLATE;
/** @var string|null */
public $JS_TRANSLATE;
// core group
/** @var string */
public $JS_CORE_TEMPLATE_NAME;
/** @var string */
public $CSS_CORE_TEMPLATE_NAME;
/** @var string */
public $JS_CORE_INCLUDE;
/** @var string */
public $CSS_CORE_INCLUDE;
// local names
/** @var string */
public $JS_SPECIAL_TEMPLATE_NAME = '';
/** @var string */
public $CSS_SPECIAL_TEMPLATE_NAME = '';
/** @var string */
public $JS_INCLUDE;
/** @var string */
public $CSS_INCLUDE;
/** @var string */
public $JS_SPECIAL_INCLUDE;
/** @var string */
public $CSS_SPECIAL_INCLUDE;
/** @var string */
public $ADMIN_JAVASCRIPT;
/** @var string */
public $ADMIN_STYLESHEET;
/** @var string */
public $FRONTEND_JAVASCRIPT;
/** @var string */
public $FRONTEND_STYLESHEET;
// other smarty folder vars
/** @var string */
public $INCLUDES;
/** @var string */
public $JAVASCRIPT;
/** @var string */
public $CSS;
/** @var string */
public $FONT;
/** @var string */
public $PICTURES;
/** @var string */
public $CACHE_PICTURES;
/** @var string */
public $CACHE_PICTURES_ROOT;
// constructor class, just sets the language stuff
/**
* constructor class, just sets the language stuff
* calls L10 for pass on internaly in smarty
* also registers the getvar caller plugin
*
* @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,string> $locale locale data read from setLocale
*/
public function __construct(\CoreLibs\Language\L10n $l10n, array $locale)
{
// call basic smarty
// or Smarty::__construct();
parent::__construct();
// iinit lang
$this->l10n = $l10n;
// parse and read, legacy stuff
$this->encoding = $locale['encoding'];
$this->lang = $locale['lang'];
// get first part from lang
$this->lang_short = explode('_', $locale['lang'])[0];
$this->domain = $this->l10n->getDomain();
$this->locale_set = $this->l10n->getLocaleSet();
$this->lang_dir = $this->l10n->getBaseLocalePath();
// opt load functions so we can use legacy init for smarty run perhaps
\CoreLibs\Language\L10n::loadFunctions();
_setlocale(LC_MESSAGES, $locale['locale']);
_textdomain($this->domain);
_bindtextdomain($this->domain, $this->lang_dir);
_bind_textdomain_codeset($this->domain, $this->encoding);
// register smarty variable
$this->registerPlugin('modifier', 'getvar', [&$this, 'getTemplateVars']);
$this->page_name = \CoreLibs\Get\System::getPageName();
// set internal settings
$this->CACHE_ID = defined('CACHE_ID') ? CACHE_ID : '';
$this->COMPILE_ID = defined('COMPILE_ID') ? COMPILE_ID : '';
}
/**
* @return void
*/
private function setSmartCoreIncludeCssJs(): void
{
// core CS
$this->CSS_CORE_INCLUDE = '';
if (
file_exists($this->CSS . $this->CSS_CORE_TEMPLATE_NAME) &&
is_file($this->CSS . $this->CSS_CORE_TEMPLATE_NAME)
) {
$this->CSS_CORE_INCLUDE = $this->CSS . $this->CSS_CORE_TEMPLATE_NAME;
}
// core JS
$this->JS_CORE_INCLUDE = '';
if (
file_exists($this->JAVASCRIPT . $this->JS_CORE_TEMPLATE_NAME) &&
is_file($this->JAVASCRIPT . $this->JS_CORE_TEMPLATE_NAME)
) {
$this->JS_CORE_INCLUDE = $this->JAVASCRIPT . $this->JS_CORE_TEMPLATE_NAME;
}
// additional per page Javascript include
$this->JS_INCLUDE = '';
if (
file_exists($this->JAVASCRIPT . $this->JS_TEMPLATE_NAME) &&
is_file($this->JAVASCRIPT . $this->JS_TEMPLATE_NAME)
) {
$this->JS_INCLUDE = $this->JAVASCRIPT . $this->JS_TEMPLATE_NAME;
}
// per page css file
$this->CSS_INCLUDE = '';
if (
file_exists($this->CSS . $this->CSS_TEMPLATE_NAME) &&
is_file($this->CSS . $this->CSS_TEMPLATE_NAME)
) {
$this->CSS_INCLUDE = $this->CSS . $this->CSS_TEMPLATE_NAME;
}
// optional CSS file
$this->CSS_SPECIAL_INCLUDE = '';
if (
file_exists($this->CSS . $this->CSS_SPECIAL_TEMPLATE_NAME) &&
is_file($this->CSS . $this->CSS_SPECIAL_TEMPLATE_NAME)
) {
$this->CSS_SPECIAL_INCLUDE = $this->CSS . $this->CSS_SPECIAL_TEMPLATE_NAME;
}
// optional JS file
$this->JS_SPECIAL_INCLUDE = '';
if (
file_exists($this->JAVASCRIPT . $this->JS_SPECIAL_TEMPLATE_NAME) &&
is_file($this->JAVASCRIPT . $this->JS_SPECIAL_TEMPLATE_NAME)
) {
$this->JS_SPECIAL_INCLUDE = $this->JAVASCRIPT . $this->JS_SPECIAL_TEMPLATE_NAME;
}
}
/**
* sets all internal paths and names that need to be passed on to the smarty template
*
* @return void
*/
public function setSmartyPaths(): void
{
// master template
if (empty($this->MASTER_TEMPLATE_NAME)) {
$this->MASTER_TEMPLATE_NAME = MASTER_TEMPLATE_NAME;
}
// set include & template names
if (empty($this->CONTENT_INCLUDE)) {
$this->CONTENT_INCLUDE = str_replace('.php', '', $this->page_name) . '.tpl';
}
// strip tpl and replace it with php
// php include file per page
$this->INC_TEMPLATE_NAME = str_replace('.tpl', '.php', $this->CONTENT_INCLUDE);
// javascript include per page
$this->JS_TEMPLATE_NAME = str_replace('.tpl', '.js', $this->CONTENT_INCLUDE);
// css per page
$this->CSS_TEMPLATE_NAME = str_replace('.tpl', '.css', $this->CONTENT_INCLUDE);
// set basic template path (tmp)
$this->INCLUDES = BASE . INCLUDES; // no longer in templates, only global
$this->TEMPLATE_PATH = BASE . INCLUDES . TEMPLATES . CONTENT_PATH;
$this->setTemplateDir($this->TEMPLATE_PATH);
$this->JAVASCRIPT = LAYOUT . JS;
$this->CSS = LAYOUT . CSS;
$this->FONT = LAYOUT . FONT;
$this->PICTURES = LAYOUT . IMAGES;
$this->CACHE_PICTURES = LAYOUT . CACHE;
$this->CACHE_PICTURES_ROOT = ROOT . $this->CACHE_PICTURES;
// check if we have an external file with the template name
if (
file_exists($this->INCLUDES . $this->INC_TEMPLATE_NAME) &&
is_file($this->INCLUDES . $this->INC_TEMPLATE_NAME)
) {
include($this->INCLUDES . $this->INC_TEMPLATE_NAME);
}
// check for template include
if (
$this->USE_INCLUDE_TEMPLATE === true &&
!$this->TEMPLATE_NAME
) {
$this->TEMPLATE_NAME = $this->CONTENT_INCLUDE;
// add to cache & compile id
$this->COMPILE_ID .= '_' . $this->TEMPLATE_NAME;
$this->CACHE_ID .= '_' . $this->TEMPLATE_NAME;
}
// set all the additional CSS/JS parths
$this->setSmartCoreIncludeCssJs();
// check if template names exist
if (!$this->MASTER_TEMPLATE_NAME) {
exit('MASTER TEMPLATE is not set');
} elseif (!file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->MASTER_TEMPLATE_NAME)) {
// abort if master template could not be found
exit('MASTER TEMPLATE: ' . $this->MASTER_TEMPLATE_NAME . ' could not be found');
}
if (
$this->TEMPLATE_NAME &&
!file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->TEMPLATE_NAME)
) {
exit('INCLUDE TEMPLATE: ' . $this->TEMPLATE_NAME . ' could not be found');
}
// javascript translate data as template for auto translate
if (empty($this->TEMPLATE_TRANSLATE)) {
$this->TEMPLATE_TRANSLATE = 'jsTranslate-'
. $this->locale_set . '.' . $this->encoding
. '.tpl';
} else {
// we assume we have some fixed set
// we must add _<locale>.<encoding>
// if .tpl, put before .tpl
// if not .tpl, add _<locale>.<encoding>.tpl
if (strpos($this->TEMPLATE_TRANSLATE, '.tpl')) {
$this->TEMPLATE_TRANSLATE = str_replace(
'.tpl',
'-' . $this->locale_set . '.' . $this->encoding . '.tpl',
$this->TEMPLATE_TRANSLATE
);
} else {
$this->TEMPLATE_TRANSLATE .= '-'
. $this->locale_set . '.' . $this->encoding
. '.tpl';
}
}
// if we can't find it, dump it
if (!file_exists($this->getTemplateDir()[0] . DIRECTORY_SEPARATOR . $this->TEMPLATE_TRANSLATE)) {
$this->TEMPLATE_TRANSLATE = null;
}
if (empty($this->JS_TRANSLATE)) {
$this->JS_TRANSLATE = 'translate-'
. $this->locale_set . '.' . $this->encoding . '.js';
} else {
// we assume we have some fixed set
// we must add _<locale>.<encoding>
// if .js, put before .js
// if not .js, add _<locale>.<encoding>.js
if (strpos($this->JS_TRANSLATE, '.js')) {
$this->JS_TRANSLATE = str_replace(
'.js',
'-' . $this->locale_set . '.' . $this->encoding . '.js',
$this->JS_TRANSLATE
);
} else {
$this->JS_TRANSLATE .= '-'
. $this->locale_set . '.' . $this->encoding
. '.js';
}
}
if (!file_exists($this->JAVASCRIPT . $this->JS_TRANSLATE)) {
$this->JS_TRANSLATE = null;
} else {
$this->JS_TRANSLATE = $this->JAVASCRIPT . $this->JS_TRANSLATE;
}
}
/**
* wrapper call for setSmartyVars
* this is for frontend type and will not set any only admin needed variables
*
* @return void
*/
public function setSmartyVarsFrontend(): void
{
$this->setSmartyVars();
}
/**
* wrapper call for setSmartyVars
* this is only for admin interface and will set additional variables
*/
public function setSmartyVarsAdmin(): void
{
$this->setSmartyVars(true);
}
/**
* set smarty pass on variables, sub template names and finally calls the smarty parser
*
* @param boolean $admin_call default false, will set admin only variables
* @return void
*/
private function setSmartyVars($admin_call = false): void
{
/** @var \CoreLibs\Admin\Backend This is an assumption */
global $cms;
if (is_object($cms)) {
$this->mergeCmsSmartyVars($cms);
}
// trigger flags
$this->HEADER['USE_PROTOTYPE'] = $this->USE_PROTOTYPE;
// scriptacolous, can only be used with prototype
if ($this->HEADER['USE_PROTOTYPE']) {
$this->HEADER['USE_SCRIPTACULOUS'] = $this->USE_SCRIPTACULOUS;
}
// jquery and prototype should not be used together
$this->HEADER['USE_JQUERY'] = $this->USE_JQUERY;
// set all the additional CSS/JS parths
$this->setSmartCoreIncludeCssJs();
// the actual include files for javascript (per page)
$this->HEADER['JS_CORE_INCLUDE'] = $this->JS_CORE_INCLUDE;
$this->HEADER['CSS_CORE_INCLUDE'] = $this->CSS_CORE_INCLUDE;
$this->HEADER['JS_INCLUDE'] = $this->JS_INCLUDE;
$this->HEADER['CSS_INCLUDE'] = $this->CSS_INCLUDE;
$this->HEADER['CSS_SPECIAL_INCLUDE'] = $this->CSS_SPECIAL_INCLUDE;
$this->HEADER['JS_SPECIAL_INCLUDE'] = $this->JS_SPECIAL_INCLUDE;
// paths to the files
$this->DATA['includes'] = $this->INCLUDES;
$this->DATA['js'] = $this->JAVASCRIPT;
$this->DATA['css'] = $this->CSS;
$this->DATA['pictures'] = $this->PICTURES;
// default CMS settings
// define all needed smarty stuff for the general HTML/page building
$this->HEADER['CSS'] = CSS;
$this->HEADER['FONT'] = FONT;
$this->HEADER['JS'] = JS;
$this->HEADER['ENCODING'] = $this->encoding;
$this->HEADER['DEFAULT_ENCODING'] = DEFAULT_ENCODING;
// form name
$this->DATA['FORM_NAME'] = !$this->FORM_NAME ?
str_replace('.php', '', $this->page_name) :
$this->FORM_NAME;
$this->DATA['FORM_ACTION'] = $this->FORM_ACTION;
// special for admin
if ($admin_call === true) {
// set ACL extra show
if (is_object($cms)) {
$this->DATA['show_ea_extra'] = $cms->acl['show_ea_extra'] ?? false;
$this->DATA['ADMIN'] = $cms->acl['admin'] ?? 0;
// top menu
$this->DATA['nav_menu'] = $cms->adbTopMenu();
$this->DATA['nav_menu_count'] = count($this->DATA['nav_menu']);
// messages = ['msg' =>, 'class' => 'error/warning/...']
$this->DATA['messages'] = $cms->messages;
} else { /** @phpstan-ignore-line Because I assume object for phpstan */
$this->DATA['show_ea_extra'] = false;
$this->DATA['ADMIN'] = 0;
$this->DATA['nav_menu'] = [];
$this->DATA['nav_menu_count'] = 0;
$this->DATA['messages'] = [];
}
// set style sheets
$this->HEADER['STYLESHEET'] = $this->ADMIN_STYLESHEET ? $this->ADMIN_STYLESHEET : ADMIN_STYLESHEET;
$this->HEADER['JAVASCRIPT'] = $this->ADMIN_JAVASCRIPT ? $this->ADMIN_JAVASCRIPT : ADMIN_JAVASCRIPT;
// the page name
$this->DATA['page_name'] = $this->page_name;
$this->DATA['table_width'] = !empty($this->PAGE_WIDTH) ?: PAGE_WIDTH;
$this->DATA['form_name'] = $this->DATA['FORM_NAME'];
// for tinymce special
$this->DATA['TINYMCE_LANG'] = $this->lang_short;
// include flags
$this->DATA['USE_TINY_MCE'] = $this->USE_TINY_MCE;
// debug data, if DEBUG flag is on, this data is print out
$this->DEBUG_DATA['DEBUG'] = $this->DEBUG_TMPL;
} else {
$this->HEADER['STYLESHEET'] = $this->FRONTEND_STYLESHEET ? $this->FRONTEND_STYLESHEET : STYLESHEET;
$this->HEADER['JAVASCRIPT'] = $this->FRONTEND_JAVASCRIPT ? $this->FRONTEND_JAVASCRIPT : JAVASCRIPT;
}
// html title
// set local page title
$this->HEADER['HTML_TITLE'] = !$this->L_TITLE ?
ucfirst(str_replace('_', ' ', \CoreLibs\Get\System::getPageName(1)))
. (defined('G_TITLE') ? ' - ' . $this->l10n->__(G_TITLE) : '') :
$this->l10n->__($this->L_TITLE);
// LANG
$this->DATA['LANG'] = $this->lang;
// include flags
$this->DATA['JS_DATEPICKR'] = $this->JS_DATEPICKR;
$this->DATA['JS_FLATPICKR'] = $this->JS_FLATPICKR;
$this->DATA['JS_FILE_UPLOADER'] = $this->JS_FILE_UPLOADER;
// user name
$this->DATA['USER_NAME'] = !empty($_SESSION['USER_NAME']) ? $_SESSION['USER_NAME'] : '';
// the template part to include into the body
$this->DATA['TEMPLATE_NAME'] = $this->TEMPLATE_NAME;
$this->DATA['CONTENT_INCLUDE'] = $this->CONTENT_INCLUDE;
$this->DATA['TEMPLATE_TRANSLATE'] = $this->TEMPLATE_TRANSLATE ?? null;
$this->DATA['JS_TRANSLATE'] = $this->JS_TRANSLATE ?? null;
$this->DATA['PAGE_FILE_NAME'] = str_replace('.php', '', $this->page_name) . '.tpl';
// render page
$this->renderSmarty();
}
/**
* merge outside object HEADER/DATA/DEBUG_DATA vars into the smarty class
*
* @param object $cms object that has header/data/debug_data
* @return void
*/
public function mergeCmsSmartyVars(object $cms): void
{
// array merge HEADER, DATA, DEBUG DATA
foreach (['HEADER', 'DATA', 'DEBUG_DATA'] as $ext_smarty) {
if (
isset($cms->{$ext_smarty}) &&
is_array($cms->{$ext_smarty})
) {
$this->{$ext_smarty} = array_merge($this->{$ext_smarty}, $cms->{$ext_smarty});
}
}
}
/**
* render smarty data (can be called sepparate)
*
* @return void
*/
public function renderSmarty(): void
{
// create main data array
$this->CONTENT_DATA = array_merge($this->HEADER, $this->DATA, $this->DEBUG_DATA);
// data is 1:1 mapping (all vars, values, etc)
foreach ($this->CONTENT_DATA as $key => $value) {
$this->assign($key, $value);
}
if (is_dir(BASE . TEMPLATES_C)) {
$this->setCompileDir(BASE . TEMPLATES_C);
}
if (is_dir(BASE . CACHE)) {
$this->setCacheDir(BASE . CACHE);
}
$this->display(
$this->MASTER_TEMPLATE_NAME,
$this->CACHE_ID . ($this->CACHE_ID ? '_' : '') . $this->lang,
$this->COMPILE_ID . ($this->COMPILE_ID ? '_' : '') . $this->lang
);
}
}
// __END__

View File

@@ -1 +0,0 @@
smarty-4.3.0/libs/

View File

@@ -1,111 +0,0 @@
<?php
/**
* Smarty Autoloader
*
* @package Smarty
*/
if (!defined('SMARTY_HELPER_FUNCTIONS_LOADED')) {
include __DIR__ . '/functions.php';
}
/**
* Smarty Autoloader
*
* @package Smarty
* @author Uwe Tews
* Usage:
* require_once '...path/Autoloader.php';
* Smarty_Autoloader::register();
* or
* include '...path/bootstrap.php';
*
* $smarty = new Smarty();
*/
class Smarty_Autoloader
{
/**
* Filepath to Smarty root
*
* @var string
*/
public static $SMARTY_DIR = null;
/**
* Filepath to Smarty internal plugins
*
* @var string
*/
public static $SMARTY_SYSPLUGINS_DIR = null;
/**
* Array with Smarty core classes and their filename
*
* @var array
*/
public static $rootClasses = array('smarty' => 'Smarty.class.php');
/**
* Registers Smarty_Autoloader backward compatible to older installations.
*
* @param bool $prepend Whether to prepend the autoloader or not.
*/
public static function registerBC($prepend = false)
{
/**
* register the class autoloader
*/
if (!defined('SMARTY_SPL_AUTOLOAD')) {
define('SMARTY_SPL_AUTOLOAD', 0);
}
if (SMARTY_SPL_AUTOLOAD
&& set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false
) {
$registeredAutoLoadFunctions = spl_autoload_functions();
if (!isset($registeredAutoLoadFunctions[ 'spl_autoload' ])) {
spl_autoload_register();
}
} else {
self::register($prepend);
}
}
/**
* Registers Smarty_Autoloader as an SPL autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not.
*/
public static function register($prepend = false)
{
self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : __DIR__ . DIRECTORY_SEPARATOR;
self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR :
self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR;
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
}
/**
* Handles auto loading of classes.
*
* @param string $class A class name.
*/
public static function autoload($class)
{
if ($class[ 0 ] !== 'S' || strpos($class, 'Smarty') !== 0) {
return;
}
$_class = smarty_strtolower_ascii($class);
if (isset(self::$rootClasses[ $_class ])) {
$file = self::$SMARTY_DIR . self::$rootClasses[ $_class ];
if (is_file($file)) {
include $file;
}
} else {
$file = self::$SMARTY_SYSPLUGINS_DIR . $_class . '.php';
if (is_file($file)) {
include $file;
}
}
return;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
<?php
/**
* This file is part of the Smarty package.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Load and register Smarty Autoloader
*/
if (!class_exists('Smarty_Autoloader')) {
include __DIR__ . '/Autoloader.php';
}
Smarty_Autoloader::register(true);

View File

@@ -1,175 +0,0 @@
{capture name='_smarty_debug' assign=debug_output}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Smarty Debug Console</title>
<style>
{literal}
body, h1, h2, h3, td, th, p {
font-family: sans-serif;
font-weight: normal;
font-size: 0.9em;
margin: 1px;
padding: 0;
}
h1 {
margin: 0;
text-align: left;
padding: 2px;
background-color: #f0c040;
color: black;
font-weight: bold;
font-size: 1.2em;
}
h2 {
background-color: #9B410E;
color: white;
text-align: left;
font-weight: bold;
padding: 2px;
border-top: 1px solid black;
}
h3 {
text-align: left;
font-weight: bold;
color: black;
font-size: 0.7em;
padding: 2px;
}
body {
background: black;
}
p, table, div {
background: #f0ead8;
}
p {
margin: 0;
font-style: italic;
text-align: center;
}
table {
width: 100%;
}
th, td {
font-family: monospace;
vertical-align: top;
text-align: left;
}
td {
color: green;
}
tr:nth-child(odd) {
background-color: #eeeeee;
}
tr:nth-child(even) {
background-color: #fafafa;
}
.exectime {
font-size: 0.8em;
font-style: italic;
}
#bold div {
color: black;
font-weight: bold;
}
#blue h3 {
color: blue;
}
#normal div {
color: black;
font-weight: normal;
}
#table_assigned_vars th {
color: blue;
font-weight: bold;
}
#table_config_vars th {
color: maroon;
}
{/literal}
</style>
</head>
<body>
<h1>Smarty {Smarty::SMARTY_VERSION} Debug Console
- {if isset($template_name)}{$template_name|debug_print_var nofilter} {/if}{if !empty($template_data)}Total Time {$execution_time|string_format:"%.5f"}{/if}</h1>
{if !empty($template_data)}
<h2>included templates &amp; config files (load time in seconds)</h2>
<div>
{foreach $template_data as $template}
<span style="color: brown;">{$template.name}</span>
<br>&nbsp;&nbsp;<span class="exectime">
(compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"})
</span>
<br>
{/foreach}
</div>
{/if}
<h2>assigned template variables</h2>
<table id="table_assigned_vars">
{foreach $assigned_vars as $vars}
<tr>
<td>
<h3 style="color: blue;">${$vars@key}</h3>
{if isset($vars['nocache'])}<strong>Nocache</strong><br>{/if}
{if isset($vars['scope'])}<strong>Origin:</strong> {$vars['scope']|debug_print_var nofilter}{/if}
</td>
<td>
<h3>Value</h3>
{$vars['value']|debug_print_var:10:80 nofilter}
</td>
<td>
{if isset($vars['attributes'])}
<h3>Attributes</h3>
{$vars['attributes']|debug_print_var nofilter}
{/if}
</td>
{/foreach}
</table>
<h2>assigned config file variables</h2>
<table id="table_config_vars">
{foreach $config_vars as $vars}
<tr>
<td>
<h3 style="color: blue;">#{$vars@key}#</h3>
{if isset($vars['scope'])}<strong>Origin:</strong> {$vars['scope']|debug_print_var nofilter}{/if}
</td>
<td>
{$vars['value']|debug_print_var:10:80 nofilter}
</td>
</tr>
{/foreach}
</table>
</body>
</html>
{/capture}
<script type="text/javascript">
{$id = '__Smarty__'}
{if $display_mode}{$id = "$offset$template_name"|md5}{/if}
_smarty_console = window.open("", "console{$id}", "width=1024,height=600,left={$offset},top={$offset},resizable,scrollbars=yes");
_smarty_console.document.write("{$debug_output|escape:'javascript' nofilter}");
_smarty_console.document.close();
</script>

View File

@@ -1,51 +0,0 @@
<?php
/**
* This file is part of the Smarty package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Registers some helper/polyfill functions.
*/
const SMARTY_HELPER_FUNCTIONS_LOADED = true;
/**
* Converts the first characters in $string to uppercase (A-Z) if it is an ASCII lowercase character (a-z).
*
* May not be required when running PHP8.2+: https://wiki.php.net/rfc/strtolower-ascii
*
* @param $string
*
* @return string
*/
function smarty_ucfirst_ascii($string): string {
return smarty_strtoupper_ascii(substr($string, 0, 1)) . substr($string, 1);
}
/**
* Converts all uppercase ASCII characters (A-Z) in $string to lowercase (a-z).
*
* May not be required when running PHP8.2+: https://wiki.php.net/rfc/strtolower-ascii
*
* @param $string
*
* @return string
*/
function smarty_strtolower_ascii($string): string {
return strtr($string, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
}
/**
* Converts all lowercase ASCII characters (a-z) in $string to uppercase (A-Z).
*
* May not be required when running PHP8.2+: https://wiki.php.net/rfc/strtolower-ascii
*
* @param $string
*
* @return string
*/
function smarty_strtoupper_ascii($string): string {
return strtr($string, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
}

View File

@@ -1,211 +0,0 @@
<?php
/**
* smarty-gettext.php - Gettext support for smarty
*
* To register as a smarty block function named 't', use:
* $smarty->register_block('t', 'smarty_translate');
*
* NOTE: native php support for conext sensitive does not exist
* Those jumps are disabled
*
* @package smarty-gettext
* @version $Id: block.t.php 4738 2022-05-06 01:28:48Z clemens $
* @link http://smarty-gettext.sf.net/
* @author Sagi Bashari <sagi@boom.org.il>
* @copyright 2004 Sagi Bashari
* @copyright Elan Ruusamäe
* @copyright Clemens Schwaighofer
*/
/**
* Replaces arguments in a string with their values.
* Arguments are represented by % followed by their number.
*
* @param string $str Source string
* @param mixed mixed Arguments, can be passed in an array or through single variables.
* @return string Modified string
*/
function smarty_gettext_strarg($str/*, $varargs... */)
{
$tr = [];
$p = 0;
$nargs = func_num_args();
for ($i = 1; $i < $nargs; $i++) {
$arg = func_get_arg($i);
if (is_array($arg)) {
foreach ($arg as $aarg) {
$tr['%' . ++$p] = $aarg;
}
} else {
$tr['%' . ++$p] = $arg;
}
}
return strtr($str, $tr);
}
/**
* Smarty block function, provides gettext support for smarty.
*
* The block content is the text that should be translated.
*
* Any parameter that is sent to the function will be represented as %n in the translation text,
* where n is 1 for the first parameter. The following parameters are reserved:
* - escape - sets escape mode:
* - 'html' for HTML escaping, this is the default.
* - 'js' for javascript escaping.
* - 'url' for url escaping.
* - 'no'/'off'/0 - turns off escaping
* - plural - The plural version of the text (2nd parameter of ngettext())
* - count - The item count for plural mode (3rd parameter of ngettext())
* - domain - Textdomain to be used, default if skipped (dgettext() instead of gettext())
* - context - gettext context. reserved for future use.
*
*/
// cs modified: __ calls instead of direct gettext calls
function smarty_block_t($params, $text)
{
if (!isset($text)) {
return $text;
}
// set escape mode, default html escape
if (isset($params['escape'])) {
$escape = $params['escape'];
unset($params['escape']);
} else {
$escape = 'html';
}
// set plural parameters 'plural' and 'count'.
if (isset($params['plural'])) {
$plural = $params['plural'];
unset($params['plural']);
// set count
if (isset($params['count'])) {
$count = $params['count'];
unset($params['count']);
}
}
// get domain param
if (isset($params['domain'])) {
$domain = $params['domain'];
unset($params['domain']);
} else {
$domain = null;
}
// get context param
if (isset($params['context'])) {
$context = $params['context'];
unset($params['context']);
} else {
$context = null;
}
// use plural if required parameters are set
if (isset($count) && isset($plural)) {
if (isset($domain) && isset($context)) {
if (is_callable('_dnpgettext')) {
$text = _dnpgettext($domain, $context, $text, $plural, $count);
}/* elseif (is_callable('dnpgettext')) {
$text = dnpgettext($domain, $context, $text, $plural, $count);
} */
} elseif (isset($domain)) {
if (is_callable('_dngettext')) {
$text = _dngettext($domain, $text, $plural, $count);
} elseif (is_callable('dngettext')) {
$text = dngettext($domain, $text, $plural, $count);
}
} elseif (isset($context)) {
if (is_callable('_npgettext')) {
$text = _npgettext($context, $text, $plural, $count);
}/* elseif (is_callable('npgettext')) {
$text = npgettext($context, $text, $plural, $count);
} */
} else {
if (is_callable('_ngettext')) {
$text = _ngettext($text, $plural, $count);
} elseif (is_callable('ngettext')) {
$text = ngettext($text, $plural, $count);
}
}
} else { // use normal
if (isset($domain) && isset($context)) {
if (is_callable('_dpgettext')) {
$text = _dpgettext($domain, $context, $text);
}/* elseif (is_callable('dpgettext')) {
$text = dpgettext($domain, $context, $text);
} */
} elseif (isset($domain)) {
if (is_callable('_dgettext')) {
$text = _dgettext($domain, $text);
} elseif (is_callable('dpgettext')) {
$text = dgettext($domain, $text);
}
} elseif (isset($context)) {
if (is_callable('_pgettext')) {
$text = _pgettext($context, $text);
}/* elseif (is_callable('pgettext')) {
$text = pgettext($context, $text);
} */
} else {
if (is_callable('_gettext')) {
$text = _gettext($text);
} elseif (is_callable('gettext')) {
$text = gettext($text);
}
}
}
// run strarg if there are parameters
if (count($params)) {
$text = smarty_gettext_strarg($text, $params);
}
switch ($escape) {
case 'html':
// default
$text = nl2br(htmlspecialchars($text));
break;
case 'javascript':
case 'js':
// javascript escape
$text = strtr(
$text,
[
'\\' => '\\\\',
"'" => "\\'",
'"' => '\\"',
"\r" => '\\r',
"\n" => '\\n',
'</' => '<\/'
]
);
break;
case 'url':
// url escape
$text = urlencode($text);
break;
// below is a list for explicit OFF
case 'no':
case 'off':
case 'false':
case '0':
case 0:
// explicit OFF
default:
break;
}
return $text;
}
// __END__

View File

@@ -1,121 +0,0 @@
<?php
/**
* Smarty plugin to format text blocks
*
* @package Smarty
* @subpackage PluginsBlock
*/
/**
* Smarty {textformat}{/textformat} block plugin
* Type: block function
* Name: textformat
* Purpose: format text a certain way with preset styles
* or custom wrap/indent settings
* Params:
*
* - style - string (email)
* - indent - integer (0)
* - wrap - integer (80)
* - wrap_char - string ("\n")
* - indent_char - string (" ")
* - wrap_boundary - boolean (true)
*
* @link https://www.smarty.net/manual/en/language.function.textformat.php {textformat}
* (Smarty online manual)
*
* @param array $params parameters
* @param string $content contents of the block
* @param Smarty_Internal_Template $template template object
* @param boolean &$repeat repeat flag
*
* @return string content re-formatted
* @author Monte Ohrt <monte at ohrt dot com>
* @throws \SmartyException
*/
function smarty_block_textformat($params, $content, Smarty_Internal_Template $template, &$repeat)
{
if (is_null($content)) {
return;
}
if (Smarty::$_MBSTRING) {
$template->_checkPlugins(
array(
array(
'function' => 'smarty_modifier_mb_wordwrap',
'file' => SMARTY_PLUGINS_DIR . 'modifier.mb_wordwrap.php'
)
)
);
}
$style = null;
$indent = 0;
$indent_first = 0;
$indent_char = ' ';
$wrap = 80;
$wrap_char = "\n";
$wrap_cut = false;
$assign = null;
foreach ($params as $_key => $_val) {
switch ($_key) {
case 'style':
case 'indent_char':
case 'wrap_char':
case 'assign':
$$_key = (string)$_val;
break;
case 'indent':
case 'indent_first':
case 'wrap':
$$_key = (int)$_val;
break;
case 'wrap_cut':
$$_key = (bool)$_val;
break;
default:
trigger_error("textformat: unknown attribute '{$_key}'");
}
}
if ($style === 'email') {
$wrap = 72;
}
// split into paragraphs
$_paragraphs = preg_split('![\r\n]{2}!', $content);
foreach ($_paragraphs as &$_paragraph) {
if (!$_paragraph) {
continue;
}
// convert mult. spaces & special chars to single space
$_paragraph =
preg_replace(
array(
'!\s+!' . Smarty::$_UTF8_MODIFIER,
'!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER
),
array(
' ',
''
),
$_paragraph
);
// indent first line
if ($indent_first > 0) {
$_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph;
}
// wordwrap sentences
if (Smarty::$_MBSTRING) {
$_paragraph = smarty_modifier_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut);
} else {
$_paragraph = wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut);
}
// indent lines
if ($indent > 0) {
$_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph);
}
}
$_output = implode($wrap_char . $wrap_char, $_paragraphs);
if ($assign) {
$template->assign($assign, $_output);
} else {
return $_output;
}
}

View File

@@ -1,62 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {counter} function plugin
* Type: function
* Name: counter
* Purpose: print out a counter value
*
* @author Monte Ohrt <monte at ohrt dot com>
* @link https://www.smarty.net/manual/en/language.function.counter.php {counter}
* (Smarty online manual)
*
* @param array $params parameters
* @param Smarty_Internal_Template $template template object
*
* @return string|null
*/
function smarty_function_counter($params, $template)
{
static $counters = array();
$name = (isset($params[ 'name' ])) ? $params[ 'name' ] : 'default';
if (!isset($counters[ $name ])) {
$counters[ $name ] = array('start' => 1, 'skip' => 1, 'direction' => 'up', 'count' => 1);
}
$counter =& $counters[ $name ];
if (isset($params[ 'start' ])) {
$counter[ 'start' ] = $counter[ 'count' ] = (int)$params[ 'start' ];
}
if (!empty($params[ 'assign' ])) {
$counter[ 'assign' ] = $params[ 'assign' ];
}
if (isset($counter[ 'assign' ])) {
$template->assign($counter[ 'assign' ], $counter[ 'count' ]);
}
if (isset($params[ 'print' ])) {
$print = (bool)$params[ 'print' ];
} else {
$print = empty($counter[ 'assign' ]);
}
if ($print) {
$retval = $counter[ 'count' ];
} else {
$retval = null;
}
if (isset($params[ 'skip' ])) {
$counter[ 'skip' ] = $params[ 'skip' ];
}
if (isset($params[ 'direction' ])) {
$counter[ 'direction' ] = $params[ 'direction' ];
}
if ($counter[ 'direction' ] === 'down') {
$counter[ 'count' ] -= $counter[ 'skip' ];
} else {
$counter[ 'count' ] += $counter[ 'skip' ];
}
return $retval;
}

View File

@@ -1,92 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {cycle} function plugin
* Type: function
* Name: cycle
* Date: May 3, 2002
* Purpose: cycle through given values
* Params:
*
* - name - name of cycle (optional)
* - values - comma separated list of values to cycle, or an array of values to cycle
* (this can be left out for subsequent calls)
* - reset - boolean - resets given var to true
* - print - boolean - print var or not. default is true
* - advance - boolean - whether or not to advance the cycle
* - delimiter - the value delimiter, default is ","
* - assign - boolean, assigns to template var instead of printed.
*
* Examples:
*
* {cycle values="#eeeeee,#d0d0d0d"}
* {cycle name=row values="one,two,three" reset=true}
* {cycle name=row}
*
* @link https://www.smarty.net/manual/en/language.function.cycle.php {cycle}
* (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
* @author credit to Mark Priatel <mpriatel@rogers.com>
* @author credit to Gerard <gerard@interfold.com>
* @author credit to Jason Sweat <jsweat_php@yahoo.com>
* @version 1.3
*
* @param array $params parameters
* @param Smarty_Internal_Template $template template object
*
* @return string|null
*/
function smarty_function_cycle($params, $template)
{
static $cycle_vars;
$name = (empty($params[ 'name' ])) ? 'default' : $params[ 'name' ];
$print = (isset($params[ 'print' ])) ? (bool)$params[ 'print' ] : true;
$advance = (isset($params[ 'advance' ])) ? (bool)$params[ 'advance' ] : true;
$reset = (isset($params[ 'reset' ])) ? (bool)$params[ 'reset' ] : false;
if (!isset($params[ 'values' ])) {
if (!isset($cycle_vars[ $name ][ 'values' ])) {
trigger_error('cycle: missing \'values\' parameter');
return;
}
} else {
if (isset($cycle_vars[ $name ][ 'values' ]) && $cycle_vars[ $name ][ 'values' ] !== $params[ 'values' ]) {
$cycle_vars[ $name ][ 'index' ] = 0;
}
$cycle_vars[ $name ][ 'values' ] = $params[ 'values' ];
}
if (isset($params[ 'delimiter' ])) {
$cycle_vars[ $name ][ 'delimiter' ] = $params[ 'delimiter' ];
} elseif (!isset($cycle_vars[ $name ][ 'delimiter' ])) {
$cycle_vars[ $name ][ 'delimiter' ] = ',';
}
if (is_array($cycle_vars[ $name ][ 'values' ])) {
$cycle_array = $cycle_vars[ $name ][ 'values' ];
} else {
$cycle_array = explode($cycle_vars[ $name ][ 'delimiter' ], $cycle_vars[ $name ][ 'values' ]);
}
if (!isset($cycle_vars[ $name ][ 'index' ]) || $reset) {
$cycle_vars[ $name ][ 'index' ] = 0;
}
if (isset($params[ 'assign' ])) {
$print = false;
$template->assign($params[ 'assign' ], $cycle_array[ $cycle_vars[ $name ][ 'index' ] ]);
}
if ($print) {
$retval = $cycle_array[ $cycle_vars[ $name ][ 'index' ] ];
} else {
$retval = null;
}
if ($advance) {
if ($cycle_vars[ $name ][ 'index' ] >= count($cycle_array) - 1) {
$cycle_vars[ $name ][ 'index' ] = 0;
} else {
$cycle_vars[ $name ][ 'index' ]++;
}
}
return $retval;
}

View File

@@ -1,204 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {fetch} plugin
* Type: function
* Name: fetch
* Purpose: fetch file, web or ftp data and display results
*
* @link https://www.smarty.net/manual/en/language.function.fetch.php {fetch}
* (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
*
* @param array $params parameters
* @param Smarty_Internal_Template $template template object
*
* @throws SmartyException
* @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable
*/
function smarty_function_fetch($params, $template)
{
if (empty($params[ 'file' ])) {
trigger_error('[plugin] fetch parameter \'file\' cannot be empty', E_USER_NOTICE);
return;
}
// strip file protocol
if (stripos($params[ 'file' ], 'file://') === 0) {
$params[ 'file' ] = substr($params[ 'file' ], 7);
}
$protocol = strpos($params[ 'file' ], '://');
if ($protocol !== false) {
$protocol = strtolower(substr($params[ 'file' ], 0, $protocol));
}
if (isset($template->smarty->security_policy)) {
if ($protocol) {
// remote resource (or php stream, …)
if (!$template->smarty->security_policy->isTrustedUri($params[ 'file' ])) {
return;
}
} else {
// local file
if (!$template->smarty->security_policy->isTrustedResourceDir($params[ 'file' ])) {
return;
}
}
}
$content = '';
if ($protocol === 'http') {
// http fetch
if ($uri_parts = parse_url($params[ 'file' ])) {
// set defaults
$host = $server_name = $uri_parts[ 'host' ];
$timeout = 30;
$accept = 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*';
$agent = 'Smarty Template Engine ' . Smarty::SMARTY_VERSION;
$referer = '';
$uri = !empty($uri_parts[ 'path' ]) ? $uri_parts[ 'path' ] : '/';
$uri .= !empty($uri_parts[ 'query' ]) ? '?' . $uri_parts[ 'query' ] : '';
$_is_proxy = false;
if (empty($uri_parts[ 'port' ])) {
$port = 80;
} else {
$port = $uri_parts[ 'port' ];
}
if (!empty($uri_parts[ 'user' ])) {
$user = $uri_parts[ 'user' ];
}
if (!empty($uri_parts[ 'pass' ])) {
$pass = $uri_parts[ 'pass' ];
}
// loop through parameters, setup headers
foreach ($params as $param_key => $param_value) {
switch ($param_key) {
case 'file':
case 'assign':
case 'assign_headers':
break;
case 'user':
if (!empty($param_value)) {
$user = $param_value;
}
break;
case 'pass':
if (!empty($param_value)) {
$pass = $param_value;
}
break;
case 'accept':
if (!empty($param_value)) {
$accept = $param_value;
}
break;
case 'header':
if (!empty($param_value)) {
if (!preg_match('![\w\d-]+: .+!', $param_value)) {
trigger_error("[plugin] invalid header format '{$param_value}'", E_USER_NOTICE);
return;
} else {
$extra_headers[] = $param_value;
}
}
break;
case 'proxy_host':
if (!empty($param_value)) {
$proxy_host = $param_value;
}
break;
case 'proxy_port':
if (!preg_match('!\D!', $param_value)) {
$proxy_port = (int)$param_value;
} else {
trigger_error("[plugin] invalid value for attribute '{$param_key }'", E_USER_NOTICE);
return;
}
break;
case 'agent':
if (!empty($param_value)) {
$agent = $param_value;
}
break;
case 'referer':
if (!empty($param_value)) {
$referer = $param_value;
}
break;
case 'timeout':
if (!preg_match('!\D!', $param_value)) {
$timeout = (int)$param_value;
} else {
trigger_error("[plugin] invalid value for attribute '{$param_key}'", E_USER_NOTICE);
return;
}
break;
default:
trigger_error("[plugin] unrecognized attribute '{$param_key}'", E_USER_NOTICE);
return;
}
}
if (!empty($proxy_host) && !empty($proxy_port)) {
$_is_proxy = true;
$fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout);
} else {
$fp = fsockopen($server_name, $port, $errno, $errstr, $timeout);
}
if (!$fp) {
trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE);
return;
} else {
if ($_is_proxy) {
fputs($fp, 'GET ' . $params[ 'file' ] . " HTTP/1.0\r\n");
} else {
fputs($fp, "GET $uri HTTP/1.0\r\n");
}
if (!empty($host)) {
fputs($fp, "Host: $host\r\n");
}
if (!empty($accept)) {
fputs($fp, "Accept: $accept\r\n");
}
if (!empty($agent)) {
fputs($fp, "User-Agent: $agent\r\n");
}
if (!empty($referer)) {
fputs($fp, "Referer: $referer\r\n");
}
if (isset($extra_headers) && is_array($extra_headers)) {
foreach ($extra_headers as $curr_header) {
fputs($fp, $curr_header . "\r\n");
}
}
if (!empty($user) && !empty($pass)) {
fputs($fp, 'Authorization: BASIC ' . base64_encode("$user:$pass") . "\r\n");
}
fputs($fp, "\r\n");
while (!feof($fp)) {
$content .= fgets($fp, 4096);
}
fclose($fp);
$csplit = preg_split("!\r\n\r\n!", $content, 2);
$content = $csplit[ 1 ];
if (!empty($params[ 'assign_headers' ])) {
$template->assign($params[ 'assign_headers' ], preg_split("!\r\n!", $csplit[ 0 ]));
}
}
} else {
trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE);
return;
}
} else {
$content = @file_get_contents($params[ 'file' ]);
if ($content === false) {
throw new SmartyException("{fetch} cannot read resource '" . $params[ 'file' ] . "'");
}
}
if (!empty($params[ 'assign' ])) {
$template->assign($params[ 'assign' ], $content);
} else {
return $content;
}
}

View File

@@ -1,296 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {html_checkboxes} function plugin
* File: function.html_checkboxes.php
* Type: function
* Name: html_checkboxes
* Date: 24.Feb.2003
* Purpose: Prints out a list of checkbox input types
* Examples:
*
* {html_checkboxes values=$ids output=$names}
* {html_checkboxes values=$ids name='box' separator='<br>' output=$names}
* {html_checkboxes values=$ids checked=$checked separator='<br>' output=$names}
*
* Params:
*
* - name (optional) - string default "checkbox"
* - values (required) - array
* - options (optional) - associative array
* - checked (optional) - array default not set
* - separator (optional) - ie <br> or &nbsp;
* - output (optional) - the output next to each checkbox
* - assign (optional) - assign the output as an array to this variable
* - escape (optional) - escape the content (not value), defaults to true
*
* @link https://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes}
* (Smarty online manual)
* @author Christopher Kvarme <christopher.kvarme@flashjab.com>
* @author credits to Monte Ohrt <monte at ohrt dot com>
* @version 1.0
*
* @param array $params parameters
* @param Smarty_Internal_Template $template template object
*
* @return string
* @uses smarty_function_escape_special_chars()
* @throws \SmartyException
*/
function smarty_function_html_checkboxes($params, Smarty_Internal_Template $template)
{
$template->_checkPlugins(
array(
array(
'function' => 'smarty_function_escape_special_chars',
'file' => SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'
)
)
);
$name = 'checkbox';
$values = null;
$options = null;
$selected = array();
$separator = '';
$escape = true;
$labels = true;
$label_ids = false;
$output = null;
$pos = null;
$extra = '';
foreach ($params as $_key => $_val) {
switch ($_key) {
case 'name':
case 'separator':
$$_key = (string)$_val;
break;
case 'escape':
case 'labels':
case 'label_ids':
$$_key = (bool)$_val;
break;
case 'options':
$$_key = (array)$_val;
break;
case 'values':
case 'output':
$$_key = array_values((array)$_val);
break;
case 'checked':
case 'selected':
if (is_array($_val)) {
$selected = array();
foreach ($_val as $_sel) {
if (is_object($_sel)) {
if (method_exists($_sel, '__toString')) {
$_sel = smarty_function_escape_special_chars((string)$_sel->__toString());
} else {
trigger_error(
'html_checkboxes: selected attribute contains an object of class \'' .
get_class($_sel) . '\' without __toString() method',
E_USER_NOTICE
);
continue;
}
} else {
$_sel = smarty_function_escape_special_chars((string)$_sel);
}
$selected[ $_sel ] = true;
}
} elseif (is_object($_val)) {
if (method_exists($_val, '__toString')) {
$selected = smarty_function_escape_special_chars((string)$_val->__toString());
} else {
trigger_error(
'html_checkboxes: selected attribute is an object of class \'' . get_class($_val) .
'\' without __toString() method',
E_USER_NOTICE
);
}
} else {
$selected = smarty_function_escape_special_chars((string)$_val);
}
break;
case 'checkboxes':
trigger_error(
'html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead',
E_USER_WARNING
);
$options = (array)$_val;
break;
case 'assign':
break;
case 'pos':
$$_key = array_values((array)$_val);
break;
case 'strict':
break;
case 'disabled':
case 'readonly':
if (!empty($params[ 'strict' ])) {
if (!is_scalar($_val)) {
trigger_error(
"html_options: {$_key} attribute must be a scalar, only boolean true or string '{$_key}' will actually add the attribute",
E_USER_NOTICE
);
}
if ($_val === true || $_val === $_key) {
$extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"';
}
break;
}
// omit break; to fall through!
// no break
default:
if (!is_array($_val)) {
$extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
} else {
trigger_error("html_checkboxes: extra attribute '{$_key}' cannot be an array", E_USER_NOTICE);
}
break;
}
}
if (!isset($options) && !isset($values)) {
return '';
} /* raise error here? */
$_html_result = array();
if (isset($options)) {
foreach ($options as $_key => $_val) {
$_pos = isset($pos[ $_key ]) ? $pos[ $_key ] : '';
$_html_result[] =
smarty_function_html_checkboxes_output(
$name,
$_key,
$_val,
$selected,
$extra,
$separator,
$labels,
$label_ids,
$_pos,
$escape
);
}
} else {
foreach ($values as $_i => $_key) {
$_val = isset($output[ $_i ]) ? $output[ $_i ] : '';
$_pos = isset($pos[ $_i ]) ? $pos[ $_i ] : '';
$_html_result[] =
smarty_function_html_checkboxes_output(
$name,
$_key,
$_val,
$selected,
$extra,
$separator,
$labels,
$label_ids,
$_pos,
$escape
);
}
}
if (!empty($params[ 'assign' ])) {
$template->assign($params[ 'assign' ], $_html_result);
} else {
return implode("\n", $_html_result);
}
}
/**
* @param $name
* @param $value
* @param $output
* @param $selected
* @param $extra
* @param $separator
* @param $labels
* @param $label_ids
* @param $pos
* @param bool $escape
*
* @return string
*/
function smarty_function_html_checkboxes_output(
$name,
$value,
$output,
$selected,
$extra,
$separator,
$labels,
$label_ids,
$pos,
$escape = true
) {
$_output = '';
if (is_object($value)) {
if (method_exists($value, '__toString')) {
$value = (string)$value->__toString();
} else {
trigger_error(
'html_options: value is an object of class \'' . get_class($value) .
'\' without __toString() method',
E_USER_NOTICE
);
return '';
}
} else {
$value = (string)$value;
}
if (is_object($output)) {
if (method_exists($output, '__toString')) {
$output = (string)$output->__toString();
} else {
trigger_error(
'html_options: output is an object of class \'' . get_class($output) .
'\' without __toString() method',
E_USER_NOTICE
);
return '';
}
} else {
$output = (string)$output;
}
if ($labels) {
if ($label_ids) {
$_id = smarty_function_escape_special_chars(
preg_replace(
'![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER,
'_',
$name . '_' . $value
)
);
$_output .= '<label for="' . $_id . '">';
} else {
$_output .= '<label>';
}
}
$name = smarty_function_escape_special_chars($name);
$value = smarty_function_escape_special_chars($value);
if ($escape) {
$output = smarty_function_escape_special_chars($output);
}
$_output .= '<input type="checkbox" name="' . $name . '[' . $pos . ']" value="' . $value . '"';
if ($labels && $label_ids) {
$_output .= ' id="' . $_id . '"';
}
if (is_array($selected)) {
if (isset($selected[ $value ])) {
$_output .= ' checked="checked"';
}
} elseif ($value === $selected) {
$_output .= ' checked="checked"';
}
$_output .= $extra . ' />' . $output;
if ($labels) {
$_output .= '</label>';
}
$_output .= $separator;
return $_output;
}

View File

@@ -1,158 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {html_image} function plugin
* Type: function
* Name: html_image
* Date: Feb 24, 2003
* Purpose: format HTML tags for the image
* Examples: {html_image file="/images/masthead.gif"}
* Output: <img src="/images/masthead.gif" width=400 height=23>
* Params:
*
* - file - (required) - file (and path) of image
* - height - (optional) - image height (default actual height)
* - width - (optional) - image width (default actual width)
* - basedir - (optional) - base directory for absolute paths, default is environment variable DOCUMENT_ROOT
* - path_prefix - prefix for path output (optional, default empty)
*
* @link https://www.smarty.net/manual/en/language.function.html.image.php {html_image}
* (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
* @author credits to Duda <duda@big.hu>
* @version 1.0
*
* @param array $params parameters
* @param Smarty_Internal_Template $template template object
*
* @throws SmartyException
* @return string
* @uses smarty_function_escape_special_chars()
*/
function smarty_function_html_image($params, Smarty_Internal_Template $template)
{
$template->_checkPlugins(
array(
array(
'function' => 'smarty_function_escape_special_chars',
'file' => SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'
)
)
);
$alt = '';
$file = '';
$height = '';
$width = '';
$extra = '';
$prefix = '';
$suffix = '';
$path_prefix = '';
$basedir = isset($_SERVER[ 'DOCUMENT_ROOT' ]) ? $_SERVER[ 'DOCUMENT_ROOT' ] : '';
foreach ($params as $_key => $_val) {
switch ($_key) {
case 'file':
case 'height':
case 'width':
case 'dpi':
case 'path_prefix':
case 'basedir':
$$_key = $_val;
break;
case 'alt':
if (!is_array($_val)) {
$$_key = smarty_function_escape_special_chars($_val);
} else {
throw new SmartyException(
"html_image: extra attribute '{$_key}' cannot be an array",
E_USER_NOTICE
);
}
break;
case 'link':
case 'href':
$prefix = '<a href="' . $_val . '">';
$suffix = '</a>';
break;
default:
if (!is_array($_val)) {
$extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
} else {
throw new SmartyException(
"html_image: extra attribute '{$_key}' cannot be an array",
E_USER_NOTICE
);
}
break;
}
}
if (empty($file)) {
trigger_error('html_image: missing \'file\' parameter', E_USER_NOTICE);
return;
}
if ($file[ 0 ] === '/') {
$_image_path = $basedir . $file;
} else {
$_image_path = $file;
}
// strip file protocol
if (stripos($params[ 'file' ], 'file://') === 0) {
$params[ 'file' ] = substr($params[ 'file' ], 7);
}
$protocol = strpos($params[ 'file' ], '://');
if ($protocol !== false) {
$protocol = strtolower(substr($params[ 'file' ], 0, $protocol));
}
if (isset($template->smarty->security_policy)) {
if ($protocol) {
// remote resource (or php stream, …)
if (!$template->smarty->security_policy->isTrustedUri($params[ 'file' ])) {
return;
}
} else {
// local file
if (!$template->smarty->security_policy->isTrustedResourceDir($_image_path)) {
return;
}
}
}
if (!isset($params[ 'width' ]) || !isset($params[ 'height' ])) {
// FIXME: (rodneyrehm) getimagesize() loads the complete file off a remote resource, use custom [jpg,png,gif]header reader!
if (!$_image_data = @getimagesize($_image_path)) {
if (!file_exists($_image_path)) {
trigger_error("html_image: unable to find '{$_image_path}'", E_USER_NOTICE);
return;
} elseif (!is_readable($_image_path)) {
trigger_error("html_image: unable to read '{$_image_path}'", E_USER_NOTICE);
return;
} else {
trigger_error("html_image: '{$_image_path}' is not a valid image file", E_USER_NOTICE);
return;
}
}
if (!isset($params[ 'width' ])) {
$width = $_image_data[ 0 ];
}
if (!isset($params[ 'height' ])) {
$height = $_image_data[ 1 ];
}
}
if (isset($params[ 'dpi' ])) {
if (strstr($_SERVER[ 'HTTP_USER_AGENT' ], 'Mac')) {
// FIXME: (rodneyrehm) wrong dpi assumption
// don't know who thought this up… even if it was true in 1998, it's definitely wrong in 2011.
$dpi_default = 72;
} else {
$dpi_default = 96;
}
$_resize = $dpi_default / $params[ 'dpi' ];
$width = round($width * $_resize);
$height = round($height * $_resize);
}
return $prefix . '<img src="' . $path_prefix . $file . '" alt="' . $alt . '" width="' . $width . '" height="' .
$height . '"' . $extra . ' />' . $suffix;
}

View File

@@ -1,260 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {html_options} function plugin
* Type: function
* Name: html_options
* Purpose: Prints the list of <option> tags generated from
* the passed parameters
* Params:
*
* - name (optional) - string default "select"
* - values (required) - if no options supplied) - array
* - options (required) - if no values supplied) - associative array
* - selected (optional) - string default not set
* - output (required) - if not options supplied) - array
* - id (optional) - string default not set
* - class (optional) - string default not set
*
* @link https://www.smarty.net/manual/en/language.function.html.options.php {html_image}
* (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
* @author Ralf Strehle (minor optimization) <ralf dot strehle at yahoo dot de>
*
* @param array $params parameters
*
* @param \Smarty_Internal_Template $template
*
* @return string
* @uses smarty_function_escape_special_chars()
* @throws \SmartyException
*/
function smarty_function_html_options($params, Smarty_Internal_Template $template)
{
$template->_checkPlugins(
array(
array(
'function' => 'smarty_function_escape_special_chars',
'file' => SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'
)
)
);
$name = null;
$values = null;
$options = null;
$selected = null;
$output = null;
$id = null;
$class = null;
$label = true;
$extra = '';
foreach ($params as $_key => $_val) {
switch ($_key) {
case 'name':
case 'class':
case 'id':
$$_key = (string)$_val;
break;
case 'options':
$options = (array)$_val;
break;
case 'values':
case 'output':
$$_key = array_values((array)$_val);
break;
case 'selected':
if (is_array($_val)) {
$selected = array();
foreach ($_val as $_sel) {
if (is_object($_sel)) {
if (method_exists($_sel, '__toString')) {
$_sel = smarty_function_escape_special_chars((string)$_sel->__toString());
} else {
trigger_error(
'html_options: selected attribute contains an object of class \'' .
get_class($_sel) . '\' without __toString() method',
E_USER_NOTICE
);
continue;
}
} else {
$_sel = smarty_function_escape_special_chars((string)$_sel);
}
$selected[ $_sel ] = true;
}
} elseif (is_object($_val)) {
if (method_exists($_val, '__toString')) {
$selected = smarty_function_escape_special_chars((string)$_val->__toString());
} else {
trigger_error(
'html_options: selected attribute is an object of class \'' . get_class($_val) .
'\' without __toString() method',
E_USER_NOTICE
);
}
} else {
$selected = smarty_function_escape_special_chars((string)$_val);
}
break;
case 'label':
if ($_val == 'true' || $_val == 'false') {
$$_key = (string)$_val;
}
break;
case 'strict':
break;
case 'disabled':
case 'readonly':
if (!empty($params[ 'strict' ])) {
if (!is_scalar($_val)) {
trigger_error(
"html_options: {$_key} attribute must be a scalar, only boolean true or string '{$_key}' will actually add the attribute",
E_USER_NOTICE
);
}
if ($_val === true || $_val === $_key) {
$extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"';
}
break;
}
// omit break; to fall through!
// no break
default:
if (!is_array($_val)) {
$extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
} else {
trigger_error("html_options: extra attribute '{$_key}' cannot be an array", E_USER_NOTICE);
}
break;
}
}
if (!isset($options) && !isset($values)) {
/* raise error here? */
return '';
}
$_html_result = '';
$_idx = 0;
if (isset($options)) {
foreach ($options as $_key => $_val) {
$_html_result .= smarty_function_html_options_optoutput(
$_key,
$_val,
$selected,
$id,
$class,
$label,
$_idx
);
}
} else {
foreach ($values as $_i => $_key) {
$_val = isset($output[ $_i ]) ? $output[ $_i ] : '';
$_html_result .= smarty_function_html_options_optoutput(
$_key,
$_val,
$selected,
$id,
$class,
$label,
$_idx
);
}
}
if (!empty($name)) {
$_html_class = !empty($class) ? ' class="' . $class . '"' : '';
$_html_id = !empty($id) ? ' id="' . $id . '"' : '';
$_html_result =
'<select name="' . $name . '"' . $_html_class . $_html_id . $extra . '>' . "\n" . $_html_result .
'</select>' . "\n";
}
return $_html_result;
}
/**
* @param $key
* @param $value
* @param $selected
* @param $id
* @param $class
* @param $label
* @param $idx
*
* @return string
*/
function smarty_function_html_options_optoutput($key, $value, $selected, $id, $class, $label, &$idx)
{
if (!is_array($value)) {
$_key = smarty_function_escape_special_chars($key);
$_html_result = '<option'
. (($label == 'true') ?
' label="' . smarty_function_escape_special_chars($value) . '"' : ''
)
. ' value="' . $_key . '"';
if (is_array($selected)) {
if (isset($selected[ $_key ])) {
$_html_result .= ' selected="selected"';
}
} elseif ($_key === $selected) {
$_html_result .= ' selected="selected"';
}
$_html_class = !empty($class) ? ' class="' . $class . ' option"' : '';
$_html_id = !empty($id) ? ' id="' . $id . '-' . $idx . '"' : '';
if (is_object($value)) {
if (method_exists($value, '__toString')) {
$value = smarty_function_escape_special_chars((string)$value->__toString());
} else {
trigger_error(
'html_options: value is an object of class \'' . get_class($value) .
'\' without __toString() method',
E_USER_NOTICE
);
return '';
}
} else {
$value = smarty_function_escape_special_chars((string)$value);
}
$_html_result .= $_html_class . $_html_id . '>' . $value . '</option>' . "\n";
$idx++;
} else {
$_idx = 0;
$_html_result =
smarty_function_html_options_optgroup(
$key,
$value,
$selected,
!empty($id) ? ($id . '-' . $idx) : null,
$class,
$label,
$_idx
);
$idx++;
}
return $_html_result;
}
/**
* @param $key
* @param $values
* @param $selected
* @param $id
* @param $class
* @param $label
* @param $idx
*
* @return string
*/
function smarty_function_html_options_optgroup($key, $values, $selected, $id, $class, $label, &$idx)
{
$optgroup_html = '<optgroup label="' . smarty_function_escape_special_chars($key) . '">' . "\n";
foreach ($values as $key => $value) {
$optgroup_html .= smarty_function_html_options_optoutput($key, $value, $selected, $id, $class, $label, $idx);
}
$optgroup_html .= "</optgroup>\n";
return $optgroup_html;
}

View File

@@ -1,266 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {html_radios} function plugin
* File: function.html_radios.php
* Type: function
* Name: html_radios
* Date: 24.Feb.2003
* Purpose: Prints out a list of radio input types
* Params:
*
* - name (optional) - string default "radio"
* - values (required) - array
* - options (required) - associative array
* - checked (optional) - array default not set
* - separator (optional) - ie <br> or &nbsp;
* - output (optional) - the output next to each radio button
* - assign (optional) - assign the output as an array to this variable
* - escape (optional) - escape the content (not value), defaults to true
*
* Examples:
*
* {html_radios values=$ids output=$names}
* {html_radios values=$ids name='box' separator='<br>' output=$names}
* {html_radios values=$ids checked=$checked separator='<br>' output=$names}
*
* @link https://www.smarty.net/manual/en/language.function.html.radios.php {html_radios}
* (Smarty online manual)
* @author Christopher Kvarme <christopher.kvarme@flashjab.com>
* @author credits to Monte Ohrt <monte at ohrt dot com>
* @version 1.0
*
* @param array $params parameters
* @param Smarty_Internal_Template $template template object
*
* @return string
* @uses smarty_function_escape_special_chars()
* @throws \SmartyException
*/
function smarty_function_html_radios($params, Smarty_Internal_Template $template)
{
$template->_checkPlugins(
array(
array(
'function' => 'smarty_function_escape_special_chars',
'file' => SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'
)
)
);
$name = 'radio';
$values = null;
$options = null;
$selected = null;
$separator = '';
$escape = true;
$labels = true;
$label_ids = false;
$output = null;
$extra = '';
foreach ($params as $_key => $_val) {
switch ($_key) {
case 'name':
case 'separator':
$$_key = (string)$_val;
break;
case 'checked':
case 'selected':
if (is_array($_val)) {
trigger_error('html_radios: the "' . $_key . '" attribute cannot be an array', E_USER_WARNING);
} elseif (is_object($_val)) {
if (method_exists($_val, '__toString')) {
$selected = smarty_function_escape_special_chars((string)$_val->__toString());
} else {
trigger_error(
'html_radios: selected attribute is an object of class \'' . get_class($_val) .
'\' without __toString() method',
E_USER_NOTICE
);
}
} else {
$selected = (string)$_val;
}
break;
case 'escape':
case 'labels':
case 'label_ids':
$$_key = (bool)$_val;
break;
case 'options':
$$_key = (array)$_val;
break;
case 'values':
case 'output':
$$_key = array_values((array)$_val);
break;
case 'radios':
trigger_error(
'html_radios: the use of the "radios" attribute is deprecated, use "options" instead',
E_USER_WARNING
);
$options = (array)$_val;
break;
case 'assign':
break;
case 'strict':
break;
case 'disabled':
case 'readonly':
if (!empty($params[ 'strict' ])) {
if (!is_scalar($_val)) {
trigger_error(
"html_options: {$_key} attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute",
E_USER_NOTICE
);
}
if ($_val === true || $_val === $_key) {
$extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"';
}
break;
}
// omit break; to fall through!
// no break
default:
if (!is_array($_val)) {
$extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
} else {
trigger_error("html_radios: extra attribute '{$_key}' cannot be an array", E_USER_NOTICE);
}
break;
}
}
if (!isset($options) && !isset($values)) {
/* raise error here? */
return '';
}
$_html_result = array();
if (isset($options)) {
foreach ($options as $_key => $_val) {
$_html_result[] =
smarty_function_html_radios_output(
$name,
$_key,
$_val,
$selected,
$extra,
$separator,
$labels,
$label_ids,
$escape
);
}
} else {
foreach ($values as $_i => $_key) {
$_val = isset($output[ $_i ]) ? $output[ $_i ] : '';
$_html_result[] =
smarty_function_html_radios_output(
$name,
$_key,
$_val,
$selected,
$extra,
$separator,
$labels,
$label_ids,
$escape
);
}
}
if (!empty($params[ 'assign' ])) {
$template->assign($params[ 'assign' ], $_html_result);
} else {
return implode("\n", $_html_result);
}
}
/**
* @param $name
* @param $value
* @param $output
* @param $selected
* @param $extra
* @param $separator
* @param $labels
* @param $label_ids
* @param $escape
*
* @return string
*/
function smarty_function_html_radios_output(
$name,
$value,
$output,
$selected,
$extra,
$separator,
$labels,
$label_ids,
$escape
) {
$_output = '';
if (is_object($value)) {
if (method_exists($value, '__toString')) {
$value = (string)$value->__toString();
} else {
trigger_error(
'html_options: value is an object of class \'' . get_class($value) .
'\' without __toString() method',
E_USER_NOTICE
);
return '';
}
} else {
$value = (string)$value;
}
if (is_object($output)) {
if (method_exists($output, '__toString')) {
$output = (string)$output->__toString();
} else {
trigger_error(
'html_options: output is an object of class \'' . get_class($output) .
'\' without __toString() method',
E_USER_NOTICE
);
return '';
}
} else {
$output = (string)$output;
}
if ($labels) {
if ($label_ids) {
$_id = smarty_function_escape_special_chars(
preg_replace(
'![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER,
'_',
$name . '_' . $value
)
);
$_output .= '<label for="' . $_id . '">';
} else {
$_output .= '<label>';
}
}
$name = smarty_function_escape_special_chars($name);
$value = smarty_function_escape_special_chars($value);
if ($escape) {
$output = smarty_function_escape_special_chars($output);
}
$_output .= '<input type="radio" name="' . $name . '" value="' . $value . '"';
if ($labels && $label_ids) {
$_output .= ' id="' . $_id . '"';
}
if ($value === $selected) {
$_output .= ' checked="checked"';
}
$_output .= $extra . ' />' . $output;
if ($labels) {
$_output .= '</label>';
}
$_output .= $separator;
return $_output;
}

View File

@@ -1,395 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {html_select_date} plugin
* Type: function
* Name: html_select_date
* Purpose: Prints the dropdowns for date selection.
* ChangeLog:
*
* - 1.0 initial release
* - 1.1 added support for +/- N syntax for begin
* and end year values. (Monte)
* - 1.2 added support for yyyy-mm-dd syntax for
* time value. (Jan Rosier)
* - 1.3 added support for choosing format for
* month values (Gary Loescher)
* - 1.3.1 added support for choosing format for
* day values (Marcus Bointon)
* - 1.3.2 support negative timestamps, force year
* dropdown to include given date unless explicitly set (Monte)
* - 1.3.4 fix behaviour of 0000-00-00 00:00:00 dates to match that
* of 0000-00-00 dates (cybot, boots)
* - 2.0 complete rewrite for performance,
* added attributes month_names, *_id
*
* @link https://www.smarty.net/manual/en/language.function.html.select.date.php {html_select_date}
* (Smarty online manual)
* @version 2.0
* @author Andrei Zmievski
* @author Monte Ohrt <monte at ohrt dot com>
* @author Rodney Rehm
*
* @param array $params parameters
*
* @param \Smarty_Internal_Template $template
*
* @return string
* @throws \SmartyException
*/
function smarty_function_html_select_date($params, Smarty_Internal_Template $template)
{
$template->_checkPlugins(
array(
array(
'function' => 'smarty_function_escape_special_chars',
'file' => SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'
)
)
);
// generate timestamps used for month names only
static $_month_timestamps = null;
static $_current_year = null;
if ($_month_timestamps === null) {
$_current_year = date('Y');
$_month_timestamps = array();
for ($i = 1; $i <= 12; $i++) {
$_month_timestamps[ $i ] = mktime(0, 0, 0, $i, 1, 2000);
}
}
/* Default values. */
$prefix = 'Date_';
$start_year = null;
$end_year = null;
$display_days = true;
$display_months = true;
$display_years = true;
$month_format = '%B';
/* Write months as numbers by default GL */
$month_value_format = '%m';
$day_format = '%02d';
/* Write day values using this format MB */
$day_value_format = '%d';
$year_as_text = false;
/* Display years in reverse order? Ie. 2000,1999,.... */
$reverse_years = false;
/* Should the select boxes be part of an array when returned from PHP?
e.g. setting it to "birthday", would create "birthday[Day]",
"birthday[Month]" & "birthday[Year]". Can be combined with prefix */
$field_array = null;
/* <select size>'s of the different <select> tags.
If not set, uses default dropdown. */
$day_size = null;
$month_size = null;
$year_size = null;
/* Unparsed attributes common to *ALL* the <select>/<input> tags.
An example might be in the template: all_extra ='class ="foo"'. */
$all_extra = null;
/* Separate attributes for the tags. */
$day_extra = null;
$month_extra = null;
$year_extra = null;
/* Order in which to display the fields.
"D" -> day, "M" -> month, "Y" -> year. */
$field_order = 'MDY';
/* String printed between the different fields. */
$field_separator = "\n";
$option_separator = "\n";
$time = null;
// $all_empty = null;
// $day_empty = null;
// $month_empty = null;
// $year_empty = null;
$extra_attrs = '';
$all_id = null;
$day_id = null;
$month_id = null;
$year_id = null;
foreach ($params as $_key => $_value) {
switch ($_key) {
case 'time':
$$_key = $_value; // we'll handle conversion below
break;
case 'month_names':
if (is_array($_value) && count($_value) === 12) {
$$_key = $_value;
} else {
trigger_error('html_select_date: month_names must be an array of 12 strings', E_USER_NOTICE);
}
break;
case 'prefix':
case 'field_array':
case 'start_year':
case 'end_year':
case 'day_format':
case 'day_value_format':
case 'month_format':
case 'month_value_format':
case 'day_size':
case 'month_size':
case 'year_size':
case 'all_extra':
case 'day_extra':
case 'month_extra':
case 'year_extra':
case 'field_order':
case 'field_separator':
case 'option_separator':
case 'all_empty':
case 'month_empty':
case 'day_empty':
case 'year_empty':
case 'all_id':
case 'month_id':
case 'day_id':
case 'year_id':
$$_key = (string)$_value;
break;
case 'display_days':
case 'display_months':
case 'display_years':
case 'year_as_text':
case 'reverse_years':
$$_key = (bool)$_value;
break;
default:
if (!is_array($_value)) {
$extra_attrs .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_value) . '"';
} else {
trigger_error("html_select_date: extra attribute '{$_key}' cannot be an array", E_USER_NOTICE);
}
break;
}
}
// Note: date() is faster than strftime()
// Note: explode(date()) is faster than date() date() date()
if (isset($time) && is_array($time)) {
if (isset($time[$prefix . 'Year'])) {
// $_REQUEST[$field_array] given
foreach ([
'Y' => 'Year',
'm' => 'Month',
'd' => 'Day'
] as $_elementKey => $_elementName) {
$_variableName = '_' . strtolower($_elementName);
$$_variableName =
isset($time[$prefix . $_elementName]) ? $time[$prefix . $_elementName] :
date($_elementKey);
}
} elseif (isset($time[$field_array][$prefix . 'Year'])) {
// $_REQUEST given
foreach ([
'Y' => 'Year',
'm' => 'Month',
'd' => 'Day'
] as $_elementKey => $_elementName) {
$_variableName = '_' . strtolower($_elementName);
$$_variableName = isset($time[$field_array][$prefix . $_elementName]) ?
$time[$field_array][$prefix . $_elementName] : date($_elementKey);
}
} else {
// no date found, use NOW
[$_year, $_month, $_day] = explode('-', date('Y-m-d'));
}
} elseif (isset($time) && preg_match("/(\d*)-(\d*)-(\d*)/", $time, $matches)) {
$_year = $_month = $_day = null;
if ($matches[1] > '') $_year = (int) $matches[1];
if ($matches[2] > '') $_month = (int) $matches[2];
if ($matches[3] > '') $_day = (int) $matches[3];
} elseif ($time === null) {
if (array_key_exists('time', $params)) {
$_year = $_month = $_day = null;
} else {
[$_year, $_month, $_day] = explode('-', date('Y-m-d'));
}
} else {
$template->_checkPlugins(
array(
array(
'function' => 'smarty_make_timestamp',
'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'
)
)
);
$time = smarty_make_timestamp($time);
[$_year, $_month, $_day] = explode('-', date('Y-m-d', $time));
}
// make syntax "+N" or "-N" work with $start_year and $end_year
// Note preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match) is slower than trim+substr
foreach (array(
'start',
'end'
) as $key) {
$key .= '_year';
$t = $$key;
if ($t === null) {
$$key = (int)$_current_year;
} elseif ($t[ 0 ] === '+') {
$$key = (int)($_current_year + (int)trim(substr($t, 1)));
} elseif ($t[ 0 ] === '-') {
$$key = (int)($_current_year - (int)trim(substr($t, 1)));
} else {
$$key = (int)$$key;
}
}
// flip for ascending or descending
if (($start_year > $end_year && !$reverse_years) || ($start_year < $end_year && $reverse_years)) {
$t = $end_year;
$end_year = $start_year;
$start_year = $t;
}
// generate year <select> or <input>
if ($display_years) {
$_extra = '';
$_name = $field_array ? ($field_array . '[' . $prefix . 'Year]') : ($prefix . 'Year');
if ($all_extra) {
$_extra .= ' ' . $all_extra;
}
if ($year_extra) {
$_extra .= ' ' . $year_extra;
}
if ($year_as_text) {
$_html_years =
'<input type="text" name="' . $_name . '" value="' . $_year . '" size="4" maxlength="4"' . $_extra .
$extra_attrs . ' />';
} else {
$_html_years = '<select name="' . $_name . '"';
if ($year_id !== null || $all_id !== null) {
$_html_years .= ' id="' . smarty_function_escape_special_chars(
$year_id !== null ?
($year_id ? $year_id : $_name) :
($all_id ? ($all_id . $_name) :
$_name)
) . '"';
}
if ($year_size) {
$_html_years .= ' size="' . $year_size . '"';
}
$_html_years .= $_extra . $extra_attrs . '>' . $option_separator;
if (isset($year_empty) || isset($all_empty)) {
$_html_years .= '<option value="">' . (isset($year_empty) ? $year_empty : $all_empty) . '</option>' .
$option_separator;
}
$op = $start_year > $end_year ? -1 : 1;
for ($i = $start_year; $op > 0 ? $i <= $end_year : $i >= $end_year; $i += $op) {
$_html_years .= '<option value="' . $i . '"' . ($_year == $i ? ' selected="selected"' : '') . '>' . $i .
'</option>' . $option_separator;
}
$_html_years .= '</select>';
}
}
// generate month <select> or <input>
if ($display_months) {
$_extra = '';
$_name = $field_array ? ($field_array . '[' . $prefix . 'Month]') : ($prefix . 'Month');
if ($all_extra) {
$_extra .= ' ' . $all_extra;
}
if ($month_extra) {
$_extra .= ' ' . $month_extra;
}
$_html_months = '<select name="' . $_name . '"';
if ($month_id !== null || $all_id !== null) {
$_html_months .= ' id="' . smarty_function_escape_special_chars(
$month_id !== null ?
($month_id ? $month_id : $_name) :
($all_id ? ($all_id . $_name) :
$_name)
) . '"';
}
if ($month_size) {
$_html_months .= ' size="' . $month_size . '"';
}
$_html_months .= $_extra . $extra_attrs . '>' . $option_separator;
if (isset($month_empty) || isset($all_empty)) {
$_html_months .= '<option value="">' . (isset($month_empty) ? $month_empty : $all_empty) . '</option>' .
$option_separator;
}
for ($i = 1; $i <= 12; $i++) {
$_val = sprintf('%02d', $i);
$_text = isset($month_names) ? smarty_function_escape_special_chars($month_names[ $i ]) :
($month_format === '%m' ? $_val : @strftime($month_format, $_month_timestamps[ $i ]));
$_value = $month_value_format === '%m' ? $_val : @strftime($month_value_format, $_month_timestamps[ $i ]);
$_html_months .= '<option value="' . $_value . '"' . ($_val == $_month ? ' selected="selected"' : '') .
'>' . $_text . '</option>' . $option_separator;
}
$_html_months .= '</select>';
}
// generate day <select> or <input>
if ($display_days) {
$_extra = '';
$_name = $field_array ? ($field_array . '[' . $prefix . 'Day]') : ($prefix . 'Day');
if ($all_extra) {
$_extra .= ' ' . $all_extra;
}
if ($day_extra) {
$_extra .= ' ' . $day_extra;
}
$_html_days = '<select name="' . $_name . '"';
if ($day_id !== null || $all_id !== null) {
$_html_days .= ' id="' .
smarty_function_escape_special_chars(
$day_id !== null ? ($day_id ? $day_id : $_name) :
($all_id ? ($all_id . $_name) : $_name)
) . '"';
}
if ($day_size) {
$_html_days .= ' size="' . $day_size . '"';
}
$_html_days .= $_extra . $extra_attrs . '>' . $option_separator;
if (isset($day_empty) || isset($all_empty)) {
$_html_days .= '<option value="">' . (isset($day_empty) ? $day_empty : $all_empty) . '</option>' .
$option_separator;
}
for ($i = 1; $i <= 31; $i++) {
$_val = sprintf('%02d', $i);
$_text = $day_format === '%02d' ? $_val : sprintf($day_format, $i);
$_value = $day_value_format === '%02d' ? $_val : sprintf($day_value_format, $i);
$_html_days .= '<option value="' . $_value . '"' . ($_val == $_day ? ' selected="selected"' : '') . '>' .
$_text . '</option>' . $option_separator;
}
$_html_days .= '</select>';
}
// order the fields for output
$_html = '';
for ($i = 0; $i <= 2; $i++) {
switch ($field_order[ $i ]) {
case 'Y':
case 'y':
if (isset($_html_years)) {
if ($_html) {
$_html .= $field_separator;
}
$_html .= $_html_years;
}
break;
case 'm':
case 'M':
if (isset($_html_months)) {
if ($_html) {
$_html .= $field_separator;
}
$_html .= $_html_months;
}
break;
case 'd':
case 'D':
if (isset($_html_days)) {
if ($_html) {
$_html .= $field_separator;
}
$_html .= $_html_days;
}
break;
}
}
return $_html;
}

View File

@@ -1,354 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {html_select_time} function plugin
* Type: function
* Name: html_select_time
* Purpose: Prints the dropdowns for time selection
*
* @link https://www.smarty.net/manual/en/language.function.html.select.time.php {html_select_time}
* (Smarty online manual)
* @author Roberto Berto <roberto@berto.net>
* @author Monte Ohrt <monte AT ohrt DOT com>
*
* @param array $params parameters
*
* @param \Smarty_Internal_Template $template
*
* @return string
* @uses smarty_make_timestamp()
* @throws \SmartyException
*/
function smarty_function_html_select_time($params, Smarty_Internal_Template $template)
{
$template->_checkPlugins(
array(
array(
'function' => 'smarty_function_escape_special_chars',
'file' => SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'
)
)
);
$prefix = 'Time_';
$field_array = null;
$field_separator = "\n";
$option_separator = "\n";
$time = null;
$display_hours = true;
$display_minutes = true;
$display_seconds = true;
$display_meridian = true;
$hour_format = '%02d';
$hour_value_format = '%02d';
$minute_format = '%02d';
$minute_value_format = '%02d';
$second_format = '%02d';
$second_value_format = '%02d';
$hour_size = null;
$minute_size = null;
$second_size = null;
$meridian_size = null;
$all_empty = null;
$hour_empty = null;
$minute_empty = null;
$second_empty = null;
$meridian_empty = null;
$all_id = null;
$hour_id = null;
$minute_id = null;
$second_id = null;
$meridian_id = null;
$use_24_hours = true;
$minute_interval = 1;
$second_interval = 1;
$extra_attrs = '';
$all_extra = null;
$hour_extra = null;
$minute_extra = null;
$second_extra = null;
$meridian_extra = null;
foreach ($params as $_key => $_value) {
switch ($_key) {
case 'time':
if (!is_array($_value) && $_value !== null) {
$template->_checkPlugins(
array(
array(
'function' => 'smarty_make_timestamp',
'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'
)
)
);
$time = smarty_make_timestamp($_value);
}
break;
case 'prefix':
case 'field_array':
case 'field_separator':
case 'option_separator':
case 'all_extra':
case 'hour_extra':
case 'minute_extra':
case 'second_extra':
case 'meridian_extra':
case 'all_empty':
case 'hour_empty':
case 'minute_empty':
case 'second_empty':
case 'meridian_empty':
case 'all_id':
case 'hour_id':
case 'minute_id':
case 'second_id':
case 'meridian_id':
case 'hour_format':
case 'hour_value_format':
case 'minute_format':
case 'minute_value_format':
case 'second_format':
case 'second_value_format':
$$_key = (string)$_value;
break;
case 'display_hours':
case 'display_minutes':
case 'display_seconds':
case 'display_meridian':
case 'use_24_hours':
$$_key = (bool)$_value;
break;
case 'minute_interval':
case 'second_interval':
case 'hour_size':
case 'minute_size':
case 'second_size':
case 'meridian_size':
$$_key = (int)$_value;
break;
default:
if (!is_array($_value)) {
$extra_attrs .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_value) . '"';
} else {
trigger_error("html_select_date: extra attribute '{$_key}' cannot be an array", E_USER_NOTICE);
}
break;
}
}
if (isset($params[ 'time' ]) && is_array($params[ 'time' ])) {
if (isset($params[ 'time' ][ $prefix . 'Hour' ])) {
// $_REQUEST[$field_array] given
foreach (array(
'H' => 'Hour',
'i' => 'Minute',
's' => 'Second'
) as $_elementKey => $_elementName) {
$_variableName = '_' . strtolower($_elementName);
$$_variableName =
isset($params[ 'time' ][ $prefix . $_elementName ]) ? $params[ 'time' ][ $prefix . $_elementName ] :
date($_elementKey);
}
$_meridian =
isset($params[ 'time' ][ $prefix . 'Meridian' ]) ? (' ' . $params[ 'time' ][ $prefix . 'Meridian' ]) :
'';
$time = strtotime($_hour . ':' . $_minute . ':' . $_second . $_meridian);
list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s', $time));
} elseif (isset($params[ 'time' ][ $field_array ][ $prefix . 'Hour' ])) {
// $_REQUEST given
foreach (array(
'H' => 'Hour',
'i' => 'Minute',
's' => 'Second'
) as $_elementKey => $_elementName) {
$_variableName = '_' . strtolower($_elementName);
$$_variableName = isset($params[ 'time' ][ $field_array ][ $prefix . $_elementName ]) ?
$params[ 'time' ][ $field_array ][ $prefix . $_elementName ] : date($_elementKey);
}
$_meridian = isset($params[ 'time' ][ $field_array ][ $prefix . 'Meridian' ]) ?
(' ' . $params[ 'time' ][ $field_array ][ $prefix . 'Meridian' ]) : '';
$time = strtotime($_hour . ':' . $_minute . ':' . $_second . $_meridian);
list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s', $time));
} else {
// no date found, use NOW
list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d'));
}
} elseif ($time === null) {
if (array_key_exists('time', $params)) {
$_hour = $_minute = $_second = $time = null;
} else {
list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s'));
}
} else {
list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s', $time));
}
// generate hour <select>
if ($display_hours) {
$_html_hours = '';
$_extra = '';
$_name = $field_array ? ($field_array . '[' . $prefix . 'Hour]') : ($prefix . 'Hour');
if ($all_extra) {
$_extra .= ' ' . $all_extra;
}
if ($hour_extra) {
$_extra .= ' ' . $hour_extra;
}
$_html_hours = '<select name="' . $_name . '"';
if ($hour_id !== null || $all_id !== null) {
$_html_hours .= ' id="' .
smarty_function_escape_special_chars(
$hour_id !== null ? ($hour_id ? $hour_id : $_name) :
($all_id ? ($all_id . $_name) : $_name)
) . '"';
}
if ($hour_size) {
$_html_hours .= ' size="' . $hour_size . '"';
}
$_html_hours .= $_extra . $extra_attrs . '>' . $option_separator;
if (isset($hour_empty) || isset($all_empty)) {
$_html_hours .= '<option value="">' . (isset($hour_empty) ? $hour_empty : $all_empty) . '</option>' .
$option_separator;
}
$start = $use_24_hours ? 0 : 1;
$end = $use_24_hours ? 23 : 12;
for ($i = $start; $i <= $end; $i++) {
$_val = sprintf('%02d', $i);
$_text = $hour_format === '%02d' ? $_val : sprintf($hour_format, $i);
$_value = $hour_value_format === '%02d' ? $_val : sprintf($hour_value_format, $i);
if (!$use_24_hours) {
$_hour12 = $_hour == 0 ? 12 : ($_hour <= 12 ? $_hour : $_hour - 12);
}
$selected = $_hour !== null ? ($use_24_hours ? $_hour == $_val : $_hour12 == $_val) : null;
$_html_hours .= '<option value="' . $_value . '"' . ($selected ? ' selected="selected"' : '') . '>' .
$_text . '</option>' . $option_separator;
}
$_html_hours .= '</select>';
}
// generate minute <select>
if ($display_minutes) {
$_html_minutes = '';
$_extra = '';
$_name = $field_array ? ($field_array . '[' . $prefix . 'Minute]') : ($prefix . 'Minute');
if ($all_extra) {
$_extra .= ' ' . $all_extra;
}
if ($minute_extra) {
$_extra .= ' ' . $minute_extra;
}
$_html_minutes = '<select name="' . $_name . '"';
if ($minute_id !== null || $all_id !== null) {
$_html_minutes .= ' id="' . smarty_function_escape_special_chars(
$minute_id !== null ?
($minute_id ? $minute_id : $_name) :
($all_id ? ($all_id . $_name) :
$_name)
) . '"';
}
if ($minute_size) {
$_html_minutes .= ' size="' . $minute_size . '"';
}
$_html_minutes .= $_extra . $extra_attrs . '>' . $option_separator;
if (isset($minute_empty) || isset($all_empty)) {
$_html_minutes .= '<option value="">' . (isset($minute_empty) ? $minute_empty : $all_empty) . '</option>' .
$option_separator;
}
$selected = $_minute !== null ? ($_minute - $_minute % $minute_interval) : null;
for ($i = 0; $i <= 59; $i += $minute_interval) {
$_val = sprintf('%02d', $i);
$_text = $minute_format === '%02d' ? $_val : sprintf($minute_format, $i);
$_value = $minute_value_format === '%02d' ? $_val : sprintf($minute_value_format, $i);
$_html_minutes .= '<option value="' . $_value . '"' . ($selected === $i ? ' selected="selected"' : '') .
'>' . $_text . '</option>' . $option_separator;
}
$_html_minutes .= '</select>';
}
// generate second <select>
if ($display_seconds) {
$_html_seconds = '';
$_extra = '';
$_name = $field_array ? ($field_array . '[' . $prefix . 'Second]') : ($prefix . 'Second');
if ($all_extra) {
$_extra .= ' ' . $all_extra;
}
if ($second_extra) {
$_extra .= ' ' . $second_extra;
}
$_html_seconds = '<select name="' . $_name . '"';
if ($second_id !== null || $all_id !== null) {
$_html_seconds .= ' id="' . smarty_function_escape_special_chars(
$second_id !== null ?
($second_id ? $second_id : $_name) :
($all_id ? ($all_id . $_name) :
$_name)
) . '"';
}
if ($second_size) {
$_html_seconds .= ' size="' . $second_size . '"';
}
$_html_seconds .= $_extra . $extra_attrs . '>' . $option_separator;
if (isset($second_empty) || isset($all_empty)) {
$_html_seconds .= '<option value="">' . (isset($second_empty) ? $second_empty : $all_empty) . '</option>' .
$option_separator;
}
$selected = $_second !== null ? ($_second - $_second % $second_interval) : null;
for ($i = 0; $i <= 59; $i += $second_interval) {
$_val = sprintf('%02d', $i);
$_text = $second_format === '%02d' ? $_val : sprintf($second_format, $i);
$_value = $second_value_format === '%02d' ? $_val : sprintf($second_value_format, $i);
$_html_seconds .= '<option value="' . $_value . '"' . ($selected === $i ? ' selected="selected"' : '') .
'>' . $_text . '</option>' . $option_separator;
}
$_html_seconds .= '</select>';
}
// generate meridian <select>
if ($display_meridian && !$use_24_hours) {
$_html_meridian = '';
$_extra = '';
$_name = $field_array ? ($field_array . '[' . $prefix . 'Meridian]') : ($prefix . 'Meridian');
if ($all_extra) {
$_extra .= ' ' . $all_extra;
}
if ($meridian_extra) {
$_extra .= ' ' . $meridian_extra;
}
$_html_meridian = '<select name="' . $_name . '"';
if ($meridian_id !== null || $all_id !== null) {
$_html_meridian .= ' id="' . smarty_function_escape_special_chars(
$meridian_id !== null ?
($meridian_id ? $meridian_id :
$_name) :
($all_id ? ($all_id . $_name) :
$_name)
) . '"';
}
if ($meridian_size) {
$_html_meridian .= ' size="' . $meridian_size . '"';
}
$_html_meridian .= $_extra . $extra_attrs . '>' . $option_separator;
if (isset($meridian_empty) || isset($all_empty)) {
$_html_meridian .= '<option value="">' . (isset($meridian_empty) ? $meridian_empty : $all_empty) .
'</option>' . $option_separator;
}
$_html_meridian .= '<option value="am"' . ($_hour > 0 && $_hour < 12 ? ' selected="selected"' : '') .
'>AM</option>' . $option_separator . '<option value="pm"' .
($_hour < 12 ? '' : ' selected="selected"') . '>PM</option>' . $option_separator .
'</select>';
}
$_html = '';
foreach (array(
'_html_hours',
'_html_minutes',
'_html_seconds',
'_html_meridian'
) as $k) {
if (isset($$k)) {
if ($_html) {
$_html .= $field_separator;
}
$_html .= $$k;
}
}
return $_html;
}

View File

@@ -1,164 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {html_table} function plugin
* Type: function
* Name: html_table
* Date: Feb 17, 2003
* Purpose: make an html table from an array of data
* Params:
*
* - loop - array to loop through
* - cols - number of columns, comma separated list of column names
* or array of column names
* - rows - number of rows
* - table_attr - table attributes
* - th_attr - table heading attributes (arrays are cycled)
* - tr_attr - table row attributes (arrays are cycled)
* - td_attr - table cell attributes (arrays are cycled)
* - trailpad - value to pad trailing cells with
* - caption - text for caption element
* - vdir - vertical direction (default: "down", means top-to-bottom)
* - hdir - horizontal direction (default: "right", means left-to-right)
* - inner - inner loop (default "cols": print $loop line by line,
* $loop will be printed column by column otherwise)
*
* Examples:
*
* {table loop=$data}
* {table loop=$data cols=4 tr_attr='"bgcolor=red"'}
* {table loop=$data cols="first,second,third" tr_attr=$colors}
*
* @author Monte Ohrt <monte at ohrt dot com>
* @author credit to Messju Mohr <messju at lammfellpuschen dot de>
* @author credit to boots <boots dot smarty at yahoo dot com>
* @version 1.1
* @link https://www.smarty.net/manual/en/language.function.html.table.php {html_table}
* (Smarty online manual)
*
* @param array $params parameters
*
* @return string
*/
function smarty_function_html_table($params)
{
$table_attr = 'border="1"';
$tr_attr = '';
$th_attr = '';
$td_attr = '';
$cols = $cols_count = 3;
$rows = 3;
$trailpad = '&nbsp;';
$vdir = 'down';
$hdir = 'right';
$inner = 'cols';
$caption = '';
$loop = null;
if (!isset($params[ 'loop' ])) {
trigger_error("html_table: missing 'loop' parameter", E_USER_WARNING);
return;
}
foreach ($params as $_key => $_value) {
switch ($_key) {
case 'loop':
$$_key = (array)$_value;
break;
case 'cols':
if (is_array($_value) && !empty($_value)) {
$cols = $_value;
$cols_count = count($_value);
} elseif (!is_numeric($_value) && is_string($_value) && !empty($_value)) {
$cols = explode(',', $_value);
$cols_count = count($cols);
} elseif (!empty($_value)) {
$cols_count = (int)$_value;
} else {
$cols_count = $cols;
}
break;
case 'rows':
$$_key = (int)$_value;
break;
case 'table_attr':
case 'trailpad':
case 'hdir':
case 'vdir':
case 'inner':
case 'caption':
$$_key = (string)$_value;
break;
case 'tr_attr':
case 'td_attr':
case 'th_attr':
$$_key = $_value;
break;
}
}
$loop_count = count($loop);
if (empty($params[ 'rows' ])) {
/* no rows specified */
$rows = ceil($loop_count / $cols_count);
} elseif (empty($params[ 'cols' ])) {
if (!empty($params[ 'rows' ])) {
/* no cols specified, but rows */
$cols_count = ceil($loop_count / $rows);
}
}
$output = "<table $table_attr>\n";
if (!empty($caption)) {
$output .= '<caption>' . $caption . "</caption>\n";
}
if (is_array($cols)) {
$cols = ($hdir === 'right') ? $cols : array_reverse($cols);
$output .= "<thead><tr>\n";
for ($r = 0; $r < $cols_count; $r++) {
$output .= '<th' . smarty_function_html_table_cycle('th', $th_attr, $r) . '>';
$output .= $cols[ $r ];
$output .= "</th>\n";
}
$output .= "</tr></thead>\n";
}
$output .= "<tbody>\n";
for ($r = 0; $r < $rows; $r++) {
$output .= "<tr" . smarty_function_html_table_cycle('tr', $tr_attr, $r) . ">\n";
$rx = ($vdir === 'down') ? $r * $cols_count : ($rows - 1 - $r) * $cols_count;
for ($c = 0; $c < $cols_count; $c++) {
$x = ($hdir === 'right') ? $rx + $c : $rx + $cols_count - 1 - $c;
if ($inner !== 'cols') {
/* shuffle x to loop over rows*/
$x = floor($x / $cols_count) + ($x % $cols_count) * $rows;
}
if ($x < $loop_count) {
$output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">" . $loop[ $x ] . "</td>\n";
} else {
$output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">$trailpad</td>\n";
}
}
$output .= "</tr>\n";
}
$output .= "</tbody>\n";
$output .= "</table>\n";
return $output;
}
/**
* @param $name
* @param $var
* @param $no
*
* @return string
*/
function smarty_function_html_table_cycle($name, $var, $no)
{
if (!is_array($var)) {
$ret = $var;
} else {
$ret = $var[ $no % count($var) ];
}
return ($ret) ? ' ' . $ret : '';
}

View File

@@ -1,142 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {mailto} function plugin
* Type: function
* Name: mailto
* Date: May 21, 2002
* Purpose: automate mailto address link creation, and optionally encode them.
* Params:
*
* - address - (required) - e-mail address
* - text - (optional) - text to display, default is address
* - encode - (optional) - can be one of:
* * none : no encoding (default)
* * javascript : encode with javascript
* * javascript_charcode : encode with javascript charcode
* * hex : encode with hexadecimal (no javascript)
* - cc - (optional) - address(es) to carbon copy
* - bcc - (optional) - address(es) to blind carbon copy
* - subject - (optional) - e-mail subject
* - newsgroups - (optional) - newsgroup(s) to post to
* - followupto - (optional) - address(es) to follow up to
* - extra - (optional) - extra tags for the href link
*
* Examples:
*
* {mailto address="me@domain.com"}
* {mailto address="me@domain.com" encode="javascript"}
* {mailto address="me@domain.com" encode="hex"}
* {mailto address="me@domain.com" subject="Hello to you!"}
* {mailto address="me@domain.com" cc="you@domain.com,they@domain.com"}
* {mailto address="me@domain.com" extra='class="mailto"'}
*
* @link https://www.smarty.net/manual/en/language.function.mailto.php {mailto}
* (Smarty online manual)
* @version 1.2
* @author Monte Ohrt <monte at ohrt dot com>
* @author credits to Jason Sweat (added cc, bcc and subject functionality)
*
* @param array $params parameters
*
* @return string
*/
function smarty_function_mailto($params)
{
static $_allowed_encoding = [
'javascript' => true,
'javascript_charcode' => true,
'hex' => true,
'none' => true
];
$extra = '';
if (empty($params[ 'address' ])) {
trigger_error("mailto: missing 'address' parameter", E_USER_WARNING);
return;
} else {
$address = $params[ 'address' ];
}
$text = $address;
// netscape and mozilla do not decode %40 (@) in BCC field (bug?)
// so, don't encode it.
$mail_parms = [];
foreach ($params as $var => $value) {
switch ($var) {
case 'cc':
case 'bcc':
case 'followupto':
if (!empty($value)) {
$mail_parms[] = $var . '=' . str_replace(['%40', '%2C'], ['@', ','], rawurlencode($value));
}
break;
case 'subject':
case 'newsgroups':
$mail_parms[] = $var . '=' . rawurlencode($value);
break;
case 'extra':
case 'text':
$$var = $value;
// no break
default:
}
}
if ($mail_parms) {
$address .= '?' . join('&', $mail_parms);
}
$encode = (empty($params[ 'encode' ])) ? 'none' : $params[ 'encode' ];
if (!isset($_allowed_encoding[ $encode ])) {
trigger_error(
"mailto: 'encode' parameter must be none, javascript, javascript_charcode or hex",
E_USER_WARNING
);
return;
}
$string = '<a href="mailto:' . htmlspecialchars($address, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, Smarty::$_CHARSET) .
'" ' . $extra . '>' . htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, Smarty::$_CHARSET) . '</a>';
if ($encode === 'javascript') {
$js_encode = '';
for ($x = 0, $_length = strlen($string); $x < $_length; $x++) {
$js_encode .= '%' . bin2hex($string[ $x ]);
}
return '<script type="text/javascript">document.write(unescape(\'' . $js_encode . '\'))</script>';
} elseif ($encode === 'javascript_charcode') {
for ($x = 0, $_length = strlen($string); $x < $_length; $x++) {
$ord[] = ord($string[ $x ]);
}
return '<script type="text/javascript">document.write(String.fromCharCode(' . implode(',', $ord) . '))</script>';
} elseif ($encode === 'hex') {
preg_match('!^(.*)(\?.*)$!', $address, $match);
if (!empty($match[ 2 ])) {
trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.", E_USER_WARNING);
return;
}
$address_encode = '';
for ($x = 0, $_length = strlen($address); $x < $_length; $x++) {
if (preg_match('!\w!' . Smarty::$_UTF8_MODIFIER, $address[ $x ])) {
$address_encode .= '%' . bin2hex($address[ $x ]);
} else {
$address_encode .= $address[ $x ];
}
}
$text_encode = '';
for ($x = 0, $_length = strlen($text); $x < $_length; $x++) {
$text_encode .= '&#x' . bin2hex($text[ $x ]) . ';';
}
$mailto = "&#109;&#97;&#105;&#108;&#116;&#111;&#58;";
return '<a href="' . $mailto . $address_encode . '" ' . $extra . '>' . $text_encode . '</a>';
} else {
// no encoding
return $string;
}
}

View File

@@ -1,142 +0,0 @@
<?php
/**
* Smarty plugin
* This plugin is only for Smarty2 BC
*
* @package Smarty
* @subpackage PluginsFunction
*/
/**
* Smarty {math} function plugin
* Type: function
* Name: math
* Purpose: handle math computations in template
*
* @link https://www.smarty.net/manual/en/language.function.math.php {math}
* (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
*
* @param array $params parameters
* @param Smarty_Internal_Template $template template object
*
* @return string|null
*/
function smarty_function_math($params, $template)
{
static $_allowed_funcs =
array(
'int' => true,
'abs' => true,
'ceil' => true,
'acos' => true,
'acosh' => true,
'cos' => true,
'cosh' => true,
'deg2rad' => true,
'rad2deg' => true,
'exp' => true,
'floor' => true,
'log' => true,
'log10' => true,
'max' => true,
'min' => true,
'pi' => true,
'pow' => true,
'rand' => true,
'round' => true,
'asin' => true,
'asinh' => true,
'sin' => true,
'sinh' => true,
'sqrt' => true,
'srand' => true,
'atan' => true,
'atanh' => true,
'tan' => true,
'tanh' => true
);
// be sure equation parameter is present
if (empty($params[ 'equation' ])) {
trigger_error("math: missing equation parameter", E_USER_WARNING);
return;
}
$equation = $params[ 'equation' ];
// Remove whitespaces
$equation = preg_replace('/\s+/', '', $equation);
// Adapted from https://www.php.net/manual/en/function.eval.php#107377
$number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number
$functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))';
$operators = '[,+\/*\^%-]'; // Allowed math operators
$regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)*\)|\((?1)*\)))(?:'.$operators.'(?1))?)+$/';
if (!preg_match($regexp, $equation)) {
trigger_error("math: illegal characters", E_USER_WARNING);
return;
}
// make sure parenthesis are balanced
if (substr_count($equation, '(') !== substr_count($equation, ')')) {
trigger_error("math: unbalanced parenthesis", E_USER_WARNING);
return;
}
// disallow backticks
if (strpos($equation, '`') !== false) {
trigger_error("math: backtick character not allowed in equation", E_USER_WARNING);
return;
}
// also disallow dollar signs
if (strpos($equation, '$') !== false) {
trigger_error("math: dollar signs not allowed in equation", E_USER_WARNING);
return;
}
foreach ($params as $key => $val) {
if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') {
// make sure value is not empty
if (strlen($val) === 0) {
trigger_error("math: parameter '{$key}' is empty", E_USER_WARNING);
return;
}
if (!is_numeric($val)) {
trigger_error("math: parameter '{$key}' is not numeric", E_USER_WARNING);
return;
}
}
}
// match all vars in equation, make sure all are passed
preg_match_all('!(?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)!', $equation, $match);
foreach ($match[ 1 ] as $curr_var) {
if ($curr_var && !isset($params[ $curr_var ]) && !isset($_allowed_funcs[ $curr_var ])) {
trigger_error(
"math: function call '{$curr_var}' not allowed, or missing parameter '{$curr_var}'",
E_USER_WARNING
);
return;
}
}
foreach ($params as $key => $val) {
if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') {
$equation = preg_replace("/\b$key\b/", " \$params['$key'] ", $equation);
}
}
$smarty_math_result = null;
eval("\$smarty_math_result = " . $equation . ";");
if (empty($params[ 'format' ])) {
if (empty($params[ 'assign' ])) {
return $smarty_math_result;
} else {
$template->assign($params[ 'assign' ], $smarty_math_result);
}
} else {
if (empty($params[ 'assign' ])) {
printf($params[ 'format' ], $smarty_math_result);
} else {
$template->assign($params[ 'assign' ], sprintf($params[ 'format' ], $smarty_math_result));
}
}
}

View File

@@ -1,119 +0,0 @@
<?php
/**
* Smarty plugin
* @package Smarty
* @subpackage plugins
*/
/**
* Smarty {popup} function plugin
*
* Type: function<br>
* Name: popup<br>
* Purpose: make text pop up in windows via overlib
* @link http://smarty.php.net/manual/en/language.function.popup.php {popup}
* (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
* @param array
* @param Smarty
* @return string
*/
function smarty_function_popup($params, &$smarty)
{
$append = '';
foreach ($params as $_key=>$_value) {
switch ($_key) {
case 'text':
case 'trigger':
case 'function':
case 'inarray':
$$_key = (string)$_value;
if ($_key == 'function' || $_key == 'inarray')
$append .= ',' . strtoupper($_key) . ",'$_value'";
break;
case 'caption':
case 'closetext':
case 'status':
$append .= ',' . strtoupper($_key) . ",'" . str_replace("'","\'",$_value) . "'";
break;
case 'fgcolor':
case 'bgcolor':
case 'textcolor':
case 'capcolor':
case 'closecolor':
case 'textfont':
case 'captionfont':
case 'closefont':
case 'fgbackground':
case 'bgbackground':
case 'caparray':
case 'capicon':
case 'background':
case 'frame':
$append .= ',' . strtoupper($_key) . ",'$_value'";
break;
case 'textsize':
case 'captionsize':
case 'closesize':
case 'width':
case 'height':
case 'border':
case 'offsetx':
case 'offsety':
case 'snapx':
case 'snapy':
case 'fixx':
case 'fixy':
case 'padx':
case 'pady':
case 'timeout':
case 'delay':
$append .= ',' . strtoupper($_key) . ",$_value";
break;
case 'sticky':
case 'left':
case 'right':
case 'center':
case 'above':
case 'below':
case 'noclose':
case 'autostatus':
case 'autostatuscap':
case 'fullhtml':
case 'hauto':
case 'vauto':
case 'mouseoff':
case 'followmouse':
case 'closeclick':
if ($_value) $append .= ',' . strtoupper($_key);
break;
default:
$smarty->trigger_error("[popup] unknown parameter $_key", E_USER_WARNING);
}
}
if (empty($text) && !isset($inarray) && empty($function)) {
$smarty->trigger_error("overlib: attribute 'text' or 'inarray' or 'function' required");
return false;
}
if (empty($trigger)) { $trigger = "onmouseover"; }
$retval = $trigger . '="return overlib(\''.preg_replace(array("!'!","![\r\n]!"),array("\'",'\r'),$text).'\'';
$retval .= $append . ');"';
if ($trigger == 'onmouseover')
$retval .= ' onmouseout="nd();"';
return $retval;
}
/* vim: set expandtab: */
?>

View File

@@ -1,40 +0,0 @@
<?php
/**
* Smarty plugin
* @package Smarty
* @subpackage plugins
*/
/**
* Smarty {popup_init} function plugin
*
* Type: function<br>
* Name: popup_init<br>
* Purpose: initialize overlib
* @link http://smarty.php.net/manual/en/language.function.popup.init.php {popup_init}
* (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
* @param array
* @param Smarty
* @return string
*/
function smarty_function_popup_init($params, &$smarty)
{
$zindex = 1000;
if (!empty($params['zindex'])) {
$zindex = $params['zindex'];
}
if (!empty($params['src'])) {
return '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:'.$zindex.';"></div>' . "\n"
. '<script type="text/javascript" language="JavaScript" src="'.$params['src'].'"></script>' . "\n";
} else {
$smarty->trigger_error("popup_init: missing src parameter");
}
}
/* vim: set expandtab: */
?>

View File

@@ -1,147 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty capitalize modifier plugin
* Type: modifier
* Name: capitalize
* Purpose: capitalize words in the string
* {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }}
*
* @param string $string string to capitalize
* @param boolean $uc_digits also capitalize "x123" to "X123"
* @param boolean $lc_rest capitalize first letters, lowercase all following letters "aAa" to "Aaa"
*
* @return string capitalized string
* @author Monte Ohrt <monte at ohrt dot com>
* @author Rodney Rehm
*/
function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false)
{
$string = (string) $string;
if (Smarty::$_MBSTRING) {
if ($lc_rest) {
// uppercase (including hyphenated words)
$upper_string = mb_convert_case($string, MB_CASE_TITLE, Smarty::$_CHARSET);
} else {
// uppercase word breaks
$upper_string = preg_replace_callback(
"!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER,
'smarty_mod_cap_mbconvert_cb',
$string
);
}
// check uc_digits case
if (!$uc_digits) {
if (preg_match_all(
"!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER,
$string,
$matches,
PREG_OFFSET_CAPTURE
)
) {
foreach ($matches[ 1 ] as $match) {
$upper_string =
substr_replace(
$upper_string,
mb_strtolower($match[ 0 ], Smarty::$_CHARSET),
$match[ 1 ],
strlen($match[ 0 ])
);
}
}
}
$upper_string =
preg_replace_callback(
"!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER,
'smarty_mod_cap_mbconvert2_cb',
$upper_string
);
return $upper_string;
}
// lowercase first
if ($lc_rest) {
$string = strtolower($string);
}
// uppercase (including hyphenated words)
$upper_string =
preg_replace_callback(
"!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER,
'smarty_mod_cap_ucfirst_cb',
$string
);
// check uc_digits case
if (!$uc_digits) {
if (preg_match_all(
"!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER,
$string,
$matches,
PREG_OFFSET_CAPTURE
)
) {
foreach ($matches[ 1 ] as $match) {
$upper_string =
substr_replace($upper_string, strtolower($match[ 0 ]), $match[ 1 ], strlen($match[ 0 ]));
}
}
}
$upper_string = preg_replace_callback(
"!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER,
'smarty_mod_cap_ucfirst2_cb',
$upper_string
);
return $upper_string;
}
/**
*
* Bug: create_function() use exhausts memory when used in long loops
* Fix: use declared functions for callbacks instead of using create_function()
* Note: This can be fixed using anonymous functions instead, but that requires PHP >= 5.3
*
* @author Kyle Renfrow
*/
/**
* @param $matches
*
* @return string
*/
function smarty_mod_cap_mbconvert_cb($matches)
{
return stripslashes($matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 2 ]), MB_CASE_UPPER, Smarty::$_CHARSET);
}
/**
* @param $matches
*
* @return string
*/
function smarty_mod_cap_mbconvert2_cb($matches)
{
return stripslashes($matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 3 ]), MB_CASE_UPPER, Smarty::$_CHARSET);
}
/**
* @param $matches
*
* @return string
*/
function smarty_mod_cap_ucfirst_cb($matches)
{
return stripslashes($matches[ 1 ]) . ucfirst(stripslashes($matches[ 2 ]));
}
/**
* @param $matches
*
* @return string
*/
function smarty_mod_cap_ucfirst2_cb($matches)
{
return stripslashes($matches[ 1 ]) . ucfirst(stripslashes($matches[ 3 ]));
}

View File

@@ -1,36 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty count modifier plugin
* Type: modifier
* Name: count
* Purpose: counts all elements in an array or in a Countable object
* Input:
* - Countable|array: array or object to count
* - mode: int defaults to 0 for normal count mode, if set to 1 counts recursive
*
* @param mixed $arrayOrObject input array/object
* @param int $mode count mode
*
* @return int
*/
function smarty_modifier_count($arrayOrObject, $mode = 0)
{
/*
* @see https://www.php.net/count
* > Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface,
* > 1 would be returned, unless value was null, in which case 0 would be returned.
*/
if ($arrayOrObject instanceof Countable || is_array($arrayOrObject)) {
return count($arrayOrObject, (int) $mode);
} elseif ($arrayOrObject === null) {
return 0;
}
return 1;
}

View File

@@ -1,86 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty date_format modifier plugin
* Type: modifier
* Name: date_format
* Purpose: format datestamps via strftime
* Input:
* - string: input date string
* - format: strftime format for output
* - default_date: default date if $string is empty
*
* @link https://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual)
* @author Monte Ohrt <monte at ohrt dot com>
*
* @param string $string input date string
* @param string $format strftime format for output
* @param string $default_date default date if $string is empty
* @param string $formatter either 'strftime' or 'auto'
*
* @return string |void
* @uses smarty_make_timestamp()
*/
function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto')
{
if ($format === null) {
$format = Smarty::$_DATE_FORMAT;
}
/**
* require_once the {@link shared.make_timestamp.php} plugin
*/
static $is_loaded = false;
if (!$is_loaded) {
if (!is_callable('smarty_make_timestamp')) {
include_once SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php';
}
$is_loaded = true;
}
if (!empty($string) && $string !== '0000-00-00' && $string !== '0000-00-00 00:00:00') {
$timestamp = smarty_make_timestamp($string);
} elseif (!empty($default_date)) {
$timestamp = smarty_make_timestamp($default_date);
} else {
return;
}
if ($formatter === 'strftime' || ($formatter === 'auto' && strpos($format, '%') !== false)) {
if (Smarty::$_IS_WINDOWS) {
$_win_from = array(
'%D',
'%h',
'%n',
'%r',
'%R',
'%t',
'%T'
);
$_win_to = array(
'%m/%d/%y',
'%b',
"\n",
'%I:%M:%S %p',
'%H:%M',
"\t",
'%H:%M:%S'
);
if (strpos($format, '%e') !== false) {
$_win_from[] = '%e';
$_win_to[] = sprintf('%\' 2d', date('j', $timestamp));
}
if (strpos($format, '%l') !== false) {
$_win_from[] = '%l';
$_win_to[] = sprintf('%\' 2d', date('h', $timestamp));
}
$format = str_replace($_win_from, $_win_to, $format);
}
// @ to suppress deprecation errors when running in PHP8.1 or higher.
return @strftime($format, $timestamp);
} else {
return date($format, $timestamp);
}
}

View File

@@ -1,103 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage Debug
*/
/**
* Smarty debug_print_var modifier plugin
* Type: modifier
* Name: debug_print_var
* Purpose: formats variable contents for display in the console
*
* @author Monte Ohrt <monte at ohrt dot com>
*
* @param array|object $var variable to be formatted
* @param int $max maximum recursion depth if $var is an array or object
* @param int $length maximum string length if $var is a string
* @param int $depth actual recursion depth
* @param array $objects processed objects in actual depth to prevent recursive object processing
*
* @return string
*/
function smarty_modifier_debug_print_var($var, $max = 10, $length = 40, $depth = 0, $objects = array())
{
$_replace = array("\n" => '\n', "\r" => '\r', "\t" => '\t');
switch (gettype($var)) {
case 'array':
$results = '<b>Array (' . count($var) . ')</b>';
if ($depth === $max) {
break;
}
foreach ($var as $curr_key => $curr_val) {
$results .= '<br>' . str_repeat('&nbsp;', $depth * 2) . '<b>' . strtr($curr_key, $_replace) .
'</b> =&gt; ' .
smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects);
$depth--;
}
break;
case 'object':
$object_vars = get_object_vars($var);
$results = '<b>' . get_class($var) . ' Object (' . count($object_vars) . ')</b>';
if (in_array($var, $objects)) {
$results .= ' called recursive';
break;
}
if ($depth === $max) {
break;
}
$objects[] = $var;
foreach ($object_vars as $curr_key => $curr_val) {
$results .= '<br>' . str_repeat('&nbsp;', $depth * 2) . '<b> -&gt;' . strtr($curr_key, $_replace) .
'</b> = ' . smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects);
$depth--;
}
break;
case 'boolean':
case 'NULL':
case 'resource':
if (true === $var) {
$results = 'true';
} elseif (false === $var) {
$results = 'false';
} elseif (null === $var) {
$results = 'null';
} else {
$results = htmlspecialchars((string)$var);
}
$results = '<i>' . $results . '</i>';
break;
case 'integer':
case 'float':
$results = htmlspecialchars((string)$var);
break;
case 'string':
$results = strtr($var, $_replace);
if (Smarty::$_MBSTRING) {
if (mb_strlen($var, Smarty::$_CHARSET) > $length) {
$results = mb_substr($var, 0, $length - 3, Smarty::$_CHARSET) . '...';
}
} else {
if (isset($var[ $length ])) {
$results = substr($var, 0, $length - 3) . '...';
}
}
$results = htmlspecialchars('"' . $results . '"', ENT_QUOTES, Smarty::$_CHARSET);
break;
case 'unknown type':
default:
$results = strtr((string)$var, $_replace);
if (Smarty::$_MBSTRING) {
if (mb_strlen($results, Smarty::$_CHARSET) > $length) {
$results = mb_substr($results, 0, $length - 3, Smarty::$_CHARSET) . '...';
}
} else {
if (strlen($results) > $length) {
$results = substr($results, 0, $length - 3) . '...';
}
}
$results = htmlspecialchars($results, ENT_QUOTES, Smarty::$_CHARSET);
}
return $results;
}

View File

@@ -1,187 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty escape modifier plugin
* Type: modifier
* Name: escape
* Purpose: escape string for output
*
* @link https://www.smarty.net/docs/en/language.modifier.escape
* @author Monte Ohrt <monte at ohrt dot com>
*
* @param string $string input string
* @param string $esc_type escape type
* @param string $char_set character set, used for htmlspecialchars() or htmlentities()
* @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities()
*
* @return string escaped input string
*/
function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true)
{
static $is_loaded_1 = false;
static $is_loaded_2 = false;
if (!$char_set) {
$char_set = Smarty::$_CHARSET;
}
$string = (string)$string;
switch ($esc_type) {
case 'html':
return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
// no break
case 'htmlall':
if (Smarty::$_MBSTRING) {
$string = mb_convert_encoding($string, 'UTF-8', $char_set);
return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode);
}
// no MBString fallback
return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
// no break
case 'url':
return rawurlencode($string);
case 'urlpathinfo':
return str_replace('%2F', '/', rawurlencode($string));
case 'quotes':
// escape unescaped single quotes
return preg_replace("%(?<!\\\\)'%", "\\'", $string);
case 'hex':
// escape every byte into hex
// Note that the UTF-8 encoded character ä will be represented as %c3%a4
$return = '';
$_length = strlen($string);
for ($x = 0; $x < $_length; $x++) {
$return .= '%' . bin2hex($string[ $x ]);
}
return $return;
case 'hexentity':
$return = '';
if (Smarty::$_MBSTRING) {
if (!$is_loaded_1) {
if (!is_callable('smarty_mb_to_unicode')) {
include_once SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php';
}
$is_loaded_1 = true;
}
$return = '';
foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
$return .= '&#x' . strtoupper(dechex($unicode)) . ';';
}
return $return;
}
// no MBString fallback
$_length = strlen($string);
for ($x = 0; $x < $_length; $x++) {
$return .= '&#x' . bin2hex($string[ $x ]) . ';';
}
return $return;
case 'decentity':
$return = '';
if (Smarty::$_MBSTRING) {
if (!$is_loaded_1) {
if (!is_callable('smarty_mb_to_unicode')) {
include_once SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php';
}
$is_loaded_1 = true;
}
$return = '';
foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
$return .= '&#' . $unicode . ';';
}
return $return;
}
// no MBString fallback
$_length = strlen($string);
for ($x = 0; $x < $_length; $x++) {
$return .= '&#' . ord($string[ $x ]) . ';';
}
return $return;
case 'javascript':
// escape quotes and backslashes, newlines, etc.
return strtr(
$string,
array(
'\\' => '\\\\',
"'" => "\\'",
'"' => '\\"',
"\r" => '\\r',
"\n" => '\\n',
'</' => '<\/',
// see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
'<!--' => '<\!--',
'<s' => '<\s',
'<S' => '<\S'
)
);
case 'mail':
if (Smarty::$_MBSTRING) {
if (!$is_loaded_2) {
if (!is_callable('smarty_mb_str_replace')) {
include_once SMARTY_PLUGINS_DIR . 'shared.mb_str_replace.php';
}
$is_loaded_2 = true;
}
return smarty_mb_str_replace(
array(
'@',
'.'
),
array(
' [AT] ',
' [DOT] '
),
$string
);
}
// no MBString fallback
return str_replace(
array(
'@',
'.'
),
array(
' [AT] ',
' [DOT] '
),
$string
);
case 'nonstd':
// escape non-standard chars, such as ms document quotes
$return = '';
if (Smarty::$_MBSTRING) {
if (!$is_loaded_1) {
if (!is_callable('smarty_mb_to_unicode')) {
include_once SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php';
}
$is_loaded_1 = true;
}
foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
if ($unicode >= 126) {
$return .= '&#' . $unicode . ';';
} else {
$return .= chr($unicode);
}
}
return $return;
}
$_length = strlen($string);
for ($_i = 0; $_i < $_length; $_i++) {
$_ord = ord(substr($string, $_i, 1));
// non-standard char, escape it
if ($_ord >= 126) {
$return .= '&#' . $_ord . ';';
} else {
$return .= substr($string, $_i, 1);
}
}
return $return;
default:
trigger_error("escape: unsupported type: $esc_type - returning unmodified string", E_USER_NOTICE);
return $string;
}
}

View File

@@ -1,25 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty explode modifier plugin
* Type: modifier
* Name: explode
* Purpose: split a string by a string
*
* @param string $separator
* @param string $string
* @param int|null $limit
*
* @return array
*/
function smarty_modifier_explode($separator, $string, ?int $limit = null)
{
// provide $string default to prevent deprecation errors in PHP >=8.1
return explode($separator, $string ?? '', $limit ?? PHP_INT_MAX);
}

View File

@@ -1,71 +0,0 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty wordwrap modifier plugin
* Type: modifier
* Name: mb_wordwrap
* Purpose: Wrap a string to a given number of characters
*
* @link https://php.net/manual/en/function.wordwrap.php for similarity
*
* @param string $str the string to wrap
* @param int $width the width of the output
* @param string $break the character used to break the line
* @param boolean $cut ignored parameter, just for the sake of
*
* @return string wrapped string
* @author Rodney Rehm
*/
function smarty_modifier_mb_wordwrap($str, $width = 75, $break = "\n", $cut = false)
{
// break words into tokens using white space as a delimiter
$tokens = preg_split('!(\s)!S' . Smarty::$_UTF8_MODIFIER, $str, -1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE);
$length = 0;
$t = '';
$_previous = false;
$_space = false;
foreach ($tokens as $_token) {
$token_length = mb_strlen($_token, Smarty::$_CHARSET);
$_tokens = array($_token);
if ($token_length > $width) {
if ($cut) {
$_tokens = preg_split(
'!(.{' . $width . '})!S' . Smarty::$_UTF8_MODIFIER,
$_token,
-1,
PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE
);
}
}
foreach ($_tokens as $token) {
$_space = !!preg_match('!^\s$!S' . Smarty::$_UTF8_MODIFIER, $token);
$token_length = mb_strlen($token, Smarty::$_CHARSET);
$length += $token_length;
if ($length > $width) {
// remove space before inserted break
if ($_previous) {
$t = mb_substr($t, 0, -1, Smarty::$_CHARSET);
}
if (!$_space) {
// add the break before the token
if (!empty($t)) {
$t .= $break;
}
$length = $token_length;
}
} elseif ($token === "\n") {
// hard break must reset counters
$length = 0;
}
$_previous = $_space;
// add the token
$t .= $token;
}
}
return $t;
}

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