From 3b8d89df37c014d7afcbf07d1504ee2628df52ff Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Tue, 1 Apr 2025 22:09:56 +0200 Subject: [PATCH 01/14] docs: add @method annotations for IDE support --- src/Traverse.php | 144 ++++++++++++++++++++++++++++++++- tests/unitary-dto-traverse.php | 10 ++- 2 files changed, 150 insertions(+), 4 deletions(-) diff --git a/src/Traverse.php b/src/Traverse.php index 3be6031..e2ad523 100755 --- a/src/Traverse.php +++ b/src/Traverse.php @@ -18,6 +18,148 @@ use stdClass; /** + * @method self strStrVal() + * @method self strPosition(string $needle, int $offset, ?string $encoding) + * @method self strPositionLast(string $needle, ?string $encoding) + * @method self strStrlen(?string $encoding) + * @method self strContains(string $needle) + * @method self strStartsWith(string $needle) + * @method self strEndsWith(string $needle) + * @method self strGetContains(string $needle) + * @method self strGetStartsWith(string $needle) + * @method self strGetEndsWith(string $needle) + * @method self strExcerpt(int $length, string $ending, ?string $encoding) + * @method self strNl2br() + * @method self strAddTrailingSlash() + * @method self strTrailingSlash() + * @method self strTrimTrailingSlash() + * @method self strStripTags(string $whitelist) + * @method self strEncode(int $flag) + * @method self strDecode(?int $flag) + * @method self strSpecialChars(int $flag, string $encoding) + * @method self strSanitizeIdentifiers() + * @method self strClearBreaks() + * @method self strNormalizeSpaces() + * @method self strNormalizeSeparators() + * @method self strEntityEncode(int $flags, ?string $encoding, bool $doubleEncode) + * @method self strEntityDecode(int $flags, ?string $encoding) + * @method self strTrim(string $characters) + * @method self strLtrim(string $characters) + * @method self strRtrim(string $characters) + * @method self strToLower() + * @method self strToUpper() + * @method self strUcFirst() + * @method self strUcWords() + * @method self strPad(int $length, string $padString, int $padType) + * @method self strLeadingZero(int $length) + * @method self strReplaceSpaces(string $replaceWith) + * @method self strFormatEmail() + * @method self strSlug() + * @method self strFormatSlug() + * @method self strNormalizeAccents() + * @method self strReplaceSpecialChar() + * @method self strUrlDecode() + * @method self strUrlEncode() + * @method self strRawUrlEncode() + * @method self strRawUrlDecode() + * @method self strReplace(array|string $find, array|string $replace) + * @method self strNormalizeUrlEncoding() + * @method self strToggleUrlEncode() + * @method self strExplodeCamelCase() + * @method self strCamelCaseToArr() + * @method self strGetUrlPath() + * @method self strGetUrlScheme() + * @method self strGetUrlHost() + * @method self strGetUrlPort() + * @method self strGetUrlUser() + * @method self strGetUrlPassword() + * @method self strGetUrlQuery() + * @method self strGetUrlFragment() + * @method self strGetUrlParts(array $parts) + * @method self strGetDirname() + * @method self strEscape() + * @method self strXss() + * @method self strJsonDecode(?bool $associative, int $depth, int $flags) + * @method self strCompare(string|int|float|bool|null $compare) + * @method self strGet() + * @method self strFallback(string $fallback) + * @method self strClone() + * @method self strDto(string $dtoClassName) + * @method self numSetLocale(string $locale, int $type) + * @method self numGetNumFormatter() + * @method self numFloat() + * @method self numInt() + * @method self numRound(int $precision, int $mode) + * @method self numFloor() + * @method self numCeil() + * @method self numAbs() + * @method self numNumberFormat(int $decimals, string $decimalSeparator, string $thousandsSeparator) + * @method self numLeadingZero(int $length) + * @method self numClamp(float $min, float $max) + * @method self numIsEven() + * @method self numIsOdd() + * @method self numPercentToDecimal() + * @method self numToPercent(int $precision) + * @method self numToKb() + * @method self numToFilesize() + * @method self numToBytes() + * @method self numToCurrency(string $currency, int $decimals, int $roundingMode) + * @method self numGetCurrencySymbol(string $currency) + * @method self numToCurrencyIso(string $currency, int $decimals) + * @method self numGet() + * @method self numFallback(string $fallback) + * @method self numClone() + * @method self numDto(string $dtoClassName) + * @method self clockSetLocale(string $locale, int $type) + * @method self clockGetNumFormatter() + * @method self clockFloat() + * @method self clockInt() + * @method self clockRound(int $precision, int $mode) + * @method self clockFloor() + * @method self clockCeil() + * @method self clockAbs() + * @method self clockNumberFormat(int $decimals, string $decimalSeparator, string $thousandsSeparator) + * @method self clockLeadingZero(int $length) + * @method self clockClamp(float $min, float $max) + * @method self clockIsEven() + * @method self clockIsOdd() + * @method self clockPercentToDecimal() + * @method self clockToPercent(int $precision) + * @method self clockToKb() + * @method self clockToFilesize() + * @method self clockToBytes() + * @method self clockToCurrency(string $currency, int $decimals, int $roundingMode) + * @method self clockGetCurrencySymbol(string $currency) + * @method self clockToCurrencyIso(string $currency, int $decimals) + * @method self clockGet() + * @method self clockFallback(string $fallback) + * @method self clockClone() + * @method self clockDto(string $dtoClassName) + * @method self domSetLocale(string $locale, int $type) + * @method self domGetNumFormatter() + * @method self domFloat() + * @method self domInt() + * @method self domRound(int $precision, int $mode) + * @method self domFloor() + * @method self domCeil() + * @method self domAbs() + * @method self domNumberFormat(int $decimals, string $decimalSeparator, string $thousandsSeparator) + * @method self domLeadingZero(int $length) + * @method self domClamp(float $min, float $max) + * @method self domIsEven() + * @method self domIsOdd() + * @method self domPercentToDecimal() + * @method self domToPercent(int $precision) + * @method self domToKb() + * @method self domToFilesize() + * @method self domToBytes() + * @method self domToCurrency(string $currency, int $decimals, int $roundingMode) + * @method self domGetCurrencySymbol(string $currency) + * @method self domToCurrencyIso(string $currency, int $decimals) + * @method self domGet() + * @method self domFallback(string $fallback) + * @method self domClone() + * @method self domDto(string $dtoClassName) * @method \MaplePHP\DTO\Format\Str str() * @method \MaplePHP\DTO\Format\Arr arr() * @method \MaplePHP\DTO\Format\Num num() @@ -25,7 +167,6 @@ * @method \MaplePHP\DTO\Format\Dom dom() * @method \MaplePHP\DTO\Format\Encode encode() * @method \MaplePHP\DTO\Format\Local local() - * @mixin \MaplePHP\DTO\Format\Clock */ class Traverse extends DynamicDataAbstract implements TraverseInterface { @@ -192,7 +333,6 @@ public function valid(string $method, array $args = []): bool * * https://www.php.net/manual/en/function.json-encode.php * - * @param mixed $value * @param int $flags * @param int $depth * @return string|false diff --git a/tests/unitary-dto-traverse.php b/tests/unitary-dto-traverse.php index c2de6fd..5b80ddf 100644 --- a/tests/unitary-dto-traverse.php +++ b/tests/unitary-dto-traverse.php @@ -275,7 +275,7 @@ return $row->strToUpper(); })->toArray(); - $this->add($flatten, function ($arr) { + $this->add($flatten, function ($inst, $arr) { $isStr = true; foreach ($arr as $row) { if (!is_string($row)) { @@ -328,7 +328,7 @@ }, "Value should equal to 'john 1'"); - $this->add($obj->feed->fetch(), function ($value) { + $this->add($obj->feed->fetch(), function ($inst, $value) { return ($this->isArray() && count($value) === 2); }, "Expect fetch to return an array"); @@ -425,4 +425,10 @@ "equal" => "fish" ], "last did return wrong value"); + + echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Str::class, "str"); + echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Num::class, "num"); + echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Num::class, "clock"); + echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Num::class, "dom"); + }); \ No newline at end of file From 8c6f9cb5c8b4101aed46d845c37471c747cc094e Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Tue, 1 Apr 2025 22:11:06 +0200 Subject: [PATCH 02/14] Comment out helper in test --- tests/unitary-dto-traverse.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unitary-dto-traverse.php b/tests/unitary-dto-traverse.php index 5b80ddf..0d3dbce 100644 --- a/tests/unitary-dto-traverse.php +++ b/tests/unitary-dto-traverse.php @@ -426,9 +426,11 @@ ], "last did return wrong value"); + /* echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Str::class, "str"); echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Num::class, "num"); echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Num::class, "clock"); echo $this->listAllProxyMethods(\MaplePHP\DTO\Format\Num::class, "dom"); + */ }); \ No newline at end of file From 7cd34dab03509e03d994297d74b1b3bd8b88f6d1 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Wed, 2 Apr 2025 23:03:52 +0200 Subject: [PATCH 03/14] Test args --- tests/unitary-dto-traverse.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unitary-dto-traverse.php b/tests/unitary-dto-traverse.php index 0d3dbce..7abd1b9 100644 --- a/tests/unitary-dto-traverse.php +++ b/tests/unitary-dto-traverse.php @@ -275,7 +275,7 @@ return $row->strToUpper(); })->toArray(); - $this->add($flatten, function ($inst, $arr) { + $this->add($flatten, function ($arr) { $isStr = true; foreach ($arr as $row) { if (!is_string($row)) { @@ -328,7 +328,7 @@ }, "Value should equal to 'john 1'"); - $this->add($obj->feed->fetch(), function ($inst, $value) { + $this->add($obj->feed->fetch(), function ($value, $inst) { return ($this->isArray() && count($value) === 2); }, "Expect fetch to return an array"); From 61d2ed12f1df72d8306ab5b6ccab451879c54114 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Tue, 15 Apr 2025 22:56:05 +0200 Subject: [PATCH 04/14] Better data type check on passed data --- src/Traverse.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Traverse.php b/src/Traverse.php index e2ad523..68b33a3 100755 --- a/src/Traverse.php +++ b/src/Traverse.php @@ -246,8 +246,13 @@ public function __get($key) return $this::value($data); } - if(isset($this->raw[$key]) || isset($this->raw->{$key})) { - return $this::value($this->raw[$key] ?? $this->raw->{$key}); + if ( + (is_array($this->raw) && isset($this->raw[$key])) || + (is_object($this->raw) && isset($this->raw->{$key})) + ) { + return $this::value( + is_array($this->raw) ? $this->raw[$key] : $this->raw->{$key} + ); } $this->raw = null; From f607aa4c728272edb98ecba13b368e2bc11b4806 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Sun, 27 Apr 2025 20:42:06 +0200 Subject: [PATCH 05/14] Add more string transformation methods Improved semantics --- src/Format/Str.php | 69 ++++++++++++++++++++++++++++++++++-- src/Helpers.php | 5 +-- src/Iconv.php | 9 +++-- src/MB.php | 9 +++-- src/Traverse.php | 48 ++++++++++++++++--------- tests/unitary-dto-string.php | 14 ++++++++ 6 files changed, 129 insertions(+), 25 deletions(-) diff --git a/src/Format/Str.php b/src/Format/Str.php index b945319..c3341d5 100755 --- a/src/Format/Str.php +++ b/src/Format/Str.php @@ -83,6 +83,25 @@ public function positionLast(string $needle, ?string $encoding = null): self return $inst; } + + /** + * Get part of string + * This method uses multibyte functionality and provides a polyfill if your environment lacks support. + * + * @param int $start The start position of the substring + * @param int|null $length The length of the substring. If null, extract all characters to the end + * @param string|null $encoding The character encoding (e.g., 'UTF-8'). Default is null + * @return self + * @throws ErrorException + */ + public function substr(int $start, ?int $length = null, ?string $encoding = null): self + { + $inst = clone $this; + $mb = new MB($inst->raw); + $inst->raw = (string)$mb->substr($start, $length, $encoding); + return $inst; + } + /** * Get string length * This method uses multibyte functionality and provides a polyfill if your environment lacks support. @@ -151,6 +170,24 @@ public function getContains(string $needle): self return $inst; } + + /** + * Get a substring that appears after the first occurrence of needle + * Returns null if the needle is not found in the string + * + * @param string $needle The substring to search for + * @param int $offset Additional offset to add after needle position (default: 0) + * @return self + * @throws ErrorException + */ + public function getContainAfter(string $needle, int $offset = 0): self + { + $inst = clone $this; + $position = $this->position($needle)->get(); + $inst->raw = ($position !== false) ? $inst->substr($position + 1 + $offset)->get() : null; + return $inst; + } + /** * Checks if a string starts with a given substring and return needle if true else false * @@ -439,7 +476,7 @@ public function toUpper(): self } /** - * Uppercase first letter in text + * Uppercase the first letter in text * * @return self */ @@ -451,7 +488,7 @@ public function ucFirst(): self } /** - * Uppercase first letter in every word + * Uppercase the first letter in every word * * @return self */ @@ -810,6 +847,34 @@ public function xss(): self return $this->escape(); } + /** + * Export a variable as a valid PHP string representation + * This method uses PHP's var_export() function to get a parseable string representation + * of the raw value + * + * @return self + */ + public function varExport(): self + { + $inst = clone $this; + $inst->raw = var_export($inst->raw, true); + return $inst; + } + + /** + * Export raw value to string and escape special characters like newlines, tabs etc. + * This method is used internally to get a readable string representation of the value. + * + * @return self + */ + public function exportReadableValue(): self + { + return $this->replace( + ["\n", "\r", "\t", "\v", "\0"], + ['\\n', '\\r', '\\t', '\\v', '\\0'] + )->varExport()->replace('\\\\', '\\'); + } + /** * Return a string to bool value * diff --git a/src/Helpers.php b/src/Helpers.php index 30d98a4..0464851 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -66,11 +66,12 @@ static function traversArrFromStr(array $array, string $key): mixed $new = $array; $exp = explode(".", $key); foreach ($exp as $index) { - if(!isset($new[$index])) { + $data = is_object($new) ? ($new->{$index} ?? null) : ($new[$index] ?? null); + if(is_null($data)) { $new = false; break; } - $new = $new[$index]; + $new = $data; } return $new; } diff --git a/src/Iconv.php b/src/Iconv.php index 519e2fe..153d7c4 100755 --- a/src/Iconv.php +++ b/src/Iconv.php @@ -40,18 +40,23 @@ public function __construct(string $value) */ public function __toString(): string { - return (string)$this->getValue(); + return (string)$this->get(); } /** * Get value * @return string|false */ - public function getValue(): string|false + public function get(): string|false { return $this->value; } + public function getValue(): string|false + { + return $this->get(); + } + /** * Will disable vanilla iconv function, used mostly for testing. * diff --git a/src/MB.php b/src/MB.php index 45548f2..5387d7c 100755 --- a/src/MB.php +++ b/src/MB.php @@ -32,18 +32,23 @@ public function __construct(string $value) */ public function __toString(): string { - return (string)$this->getValue(); + return (string)$this->get(); } /** * Get value * @return string|false */ - public function getValue(): string|false + public function get(): string|false { return $this->value; } + public function getValue(): string|false + { + return $this->get(); + } + /** * Will disable vanilla iconv function, used mostly for testing. * diff --git a/src/Traverse.php b/src/Traverse.php index 68b33a3..1c20253 100755 --- a/src/Traverse.php +++ b/src/Traverse.php @@ -19,40 +19,42 @@ /** * @method self strStrVal() - * @method self strPosition(string $needle, int $offset, ?string $encoding) - * @method self strPositionLast(string $needle, ?string $encoding) - * @method self strStrlen(?string $encoding) + * @method self strPosition(string $needle, int $offset = '0', ?string $encoding = '') + * @method self strPositionLast(string $needle, ?string $encoding = '') + * @method self strSubstr(int $start, ?int $length = '', ?string $encoding = '') + * @method self strStrlen(?string $encoding = '') * @method self strContains(string $needle) * @method self strStartsWith(string $needle) * @method self strEndsWith(string $needle) * @method self strGetContains(string $needle) + * @method self strGetContainAfter(string $needle, int $offset = '0') * @method self strGetStartsWith(string $needle) * @method self strGetEndsWith(string $needle) - * @method self strExcerpt(int $length, string $ending, ?string $encoding) + * @method self strExcerpt(int $length = '40', string $ending = '...', ?string $encoding = '') * @method self strNl2br() * @method self strAddTrailingSlash() * @method self strTrailingSlash() * @method self strTrimTrailingSlash() - * @method self strStripTags(string $whitelist) - * @method self strEncode(int $flag) - * @method self strDecode(?int $flag) - * @method self strSpecialChars(int $flag, string $encoding) + * @method self strStripTags(string $whitelist = '') + * @method self strEncode(int $flag = '3') + * @method self strDecode(?int $flag = '3') + * @method self strSpecialChars(int $flag = '3', string $encoding = 'UTF-8') * @method self strSanitizeIdentifiers() * @method self strClearBreaks() * @method self strNormalizeSpaces() * @method self strNormalizeSeparators() - * @method self strEntityEncode(int $flags, ?string $encoding, bool $doubleEncode) - * @method self strEntityDecode(int $flags, ?string $encoding) - * @method self strTrim(string $characters) - * @method self strLtrim(string $characters) - * @method self strRtrim(string $characters) + * @method self strEntityEncode(int $flags = '11', ?string $encoding = '', bool $doubleEncode = '1') + * @method self strEntityDecode(int $flags = '11', ?string $encoding = '') + * @method self strTrim(string $characters = ' \n\r\t\v\0') + * @method self strLtrim(string $characters = ' \n\r\t\v\0') + * @method self strRtrim(string $characters = ' \n\r\t\v\0') * @method self strToLower() * @method self strToUpper() * @method self strUcFirst() * @method self strUcWords() - * @method self strPad(int $length, string $padString, int $padType) - * @method self strLeadingZero(int $length) - * @method self strReplaceSpaces(string $replaceWith) + * @method self strPad(int $length, string $padString = ' ', int $padType = '1') + * @method self strLeadingZero(int $length = '2') + * @method self strReplaceSpaces(string $replaceWith = '-') * @method self strFormatEmail() * @method self strSlug() * @method self strFormatSlug() @@ -79,7 +81,9 @@ * @method self strGetDirname() * @method self strEscape() * @method self strXss() - * @method self strJsonDecode(?bool $associative, int $depth, int $flags) + * @method self strVarExport() + * @method self strExportReadableValue() + * @method self strJsonDecode(?bool $associative = '', int $depth = '512', int $flags = '0') * @method self strCompare(string|int|float|bool|null $compare) * @method self strGet() * @method self strFallback(string $fallback) @@ -347,6 +351,16 @@ public function toJson(int $flags = 0, int $depth = 512): string|false return json_encode($this->get(), $flags, $depth); } + /** + * Returns the string representation of the value + * + * @return string + */ + public function toString(): string + { + return (string)$this->get(); + } + /** * Returns the int representation of the value * diff --git a/tests/unitary-dto-string.php b/tests/unitary-dto-string.php index 636503b..9bb0195 100644 --- a/tests/unitary-dto-string.php +++ b/tests/unitary-dto-string.php @@ -1,6 +1,7 @@ '{"name":"Alice","email":"alice@example.com","roles":["admin","editor"]}', "date" => "2023-08-21 14:35:12", "content" => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse euismod turpis eget elit eleifend, non pulvinar enim dapibus.\n Nullam blandit vitae justo vitae viverra. Aliquam varius eu leo a euismod.", + "exportReadable" => "hello \n\t", ]); $this->add($obj->firstname->str()->stripTags()->ucFirst()->get(), [ @@ -53,6 +55,18 @@ "equal" => 18, ], "strlen: Failed"); + $this->add($obj->content->str()->substr(0, 5)->get(), [ + "equal" => "Lorem", + ], "substr: Failed"); + + $this->add($obj->email->str()->getContainAfter("@")->get(), [ + "equal" => "gmail.com", + ], "getContainAfter required to be true"); + + $this->add($obj->exportReadable->str()->exportReadableValue()->get(), [ + "equal" => Str::value("hello \n\t")->exportReadableValue()->get(), + ], "exportReadableValue required to be true"); + $data = $obj->json->str()->jsonDecode()->get(); $this->add($data->name ?? "", [ "equal" => 'Alice', From 6f474b94fdeedbe57c0ed75d91c1f76bcabb980d Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Mon, 28 Apr 2025 22:38:42 +0200 Subject: [PATCH 06/14] Change validation name --- src/Format/Str.php | 3 +++ src/Traverse.php | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Format/Str.php b/src/Format/Str.php index c3341d5..e2a8f76 100755 --- a/src/Format/Str.php +++ b/src/Format/Str.php @@ -35,6 +35,9 @@ public function __construct(mixed $value) */ public static function value(mixed $value): FormatInterface { + if(is_array($value) || is_object($value)) { + $value = ""; + } return new Str((string)$value); } diff --git a/src/Traverse.php b/src/Traverse.php index 1c20253..2f66633 100755 --- a/src/Traverse.php +++ b/src/Traverse.php @@ -12,7 +12,7 @@ use ErrorException; use MaplePHP\DTO\Format\FormatInterface; use MaplePHP\DTO\Format\Str; -use MaplePHP\Validate\Inp; +use MaplePHP\Validate\Validator; use ReflectionClass; use ReflectionException; use stdClass; @@ -330,7 +330,7 @@ public function add(string $key, mixed $value): self */ public function valid(string $method, array $args = []): bool { - $inp = Inp::value($this->raw); + $inp = Validator::value($this->raw); if(!method_exists($inp, $method)) { throw new BadMethodCallException("The MaplePHP validation method \"$method\" does not exist!", 1); } From 8f6b7417f9c41dfd27ee25c7d233887ef7bbd41e Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Fri, 2 May 2025 16:17:14 +0200 Subject: [PATCH 07/14] Improve data type detection Enhance code quality and readability --- src/DynamicDataAbstract.php | 11 +- src/Format/Arr.php | 4 +- src/Format/Clock.php | 22 ++-- src/Format/Dom.php | 2 +- src/Format/Num.php | 5 +- src/Format/Str.php | 12 +- src/Helpers.php | 17 ++- src/Iconv.php | 53 ++++----- src/MB.php | 11 +- src/Traits/CollectionUtilities.php | 182 +++++++++++++++-------------- src/Traverse.php | 106 +++++++++++++---- src/TraverseInterface.php | 7 +- 12 files changed, 249 insertions(+), 183 deletions(-) diff --git a/src/DynamicDataAbstract.php b/src/DynamicDataAbstract.php index 74c923e..2d96dee 100755 --- a/src/DynamicDataAbstract.php +++ b/src/DynamicDataAbstract.php @@ -9,12 +9,11 @@ namespace MaplePHP\DTO; -abstract class DynamicDataAbstract +/** @psalm-no-seal-properties */ +abstract class DynamicDataAbstract extends \stdClass { private object $data; - abstract public function get(); - /** * Create a dynamic object that holds a dynamic set of items */ @@ -59,11 +58,11 @@ public function __set(string $key, mixed $value): void * Try to get data object item * * @param $key - * @return null + * @return self */ - public function __get($key) + public function __get($key): self { - return ($this->data->{$key} ?? null); + return ($this->data->{$key} ?? $this); } /** diff --git a/src/Format/Arr.php b/src/Format/Arr.php index 6da579d..3e829db 100644 --- a/src/Format/Arr.php +++ b/src/Format/Arr.php @@ -39,10 +39,10 @@ public function __construct(mixed $value) * @param array $arguments * @return mixed */ - function __call(string $name, array $arguments) + public function __call(string $name, array $arguments) { $inst = new Traverse($this->raw); - if(!method_exists($inst, $name)) { + if (!method_exists($inst, $name)) { throw new \BadMethodCallException("Method '$name' does not exist."); } return $inst->$name(...$arguments); diff --git a/src/Format/Clock.php b/src/Format/Clock.php index 37f38eb..619bc0a 100755 --- a/src/Format/Clock.php +++ b/src/Format/Clock.php @@ -19,9 +19,9 @@ final class Clock extends FormatAbstract implements FormatInterface { - static protected ?string $defaultLocale = 'en'; + protected static ?string $defaultLocale = 'en'; - static protected string|DateTimeZone|null $defaultTimezone = null; + protected static string|DateTimeZone|null $defaultTimezone = null; protected ?string $locale = null; protected array $parts = []; @@ -38,7 +38,7 @@ public function __construct(mixed $value) throw new InvalidArgumentException("Is expecting a string or a convertable string value.", 1); } $date = new DateTime($value); - if(!is_null(self::$defaultTimezone)) { + if (!is_null(self::$defaultTimezone)) { $date->setTimezone(self::$defaultTimezone); } parent::__construct($date); @@ -100,7 +100,7 @@ public function setLocale(string $localeCode): self * @param string $localeCode * @return void */ - static public function setDefaultLocale(string $localeCode): void + public static function setDefaultLocale(string $localeCode): void { self::$defaultLocale = $localeCode; } @@ -130,7 +130,7 @@ public function setTimezone(DateTimeZone|string $timezone): self * @return void * @throws \DateInvalidTimeZoneException */ - static public function setDefaultTimezone(string|DateTimeZone $timezone): void + public static function setDefaultTimezone(string|DateTimeZone $timezone): void { self::$defaultTimezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); } @@ -156,7 +156,7 @@ public function format(string $format = 'Y-m-d H:i:s', ?string $locale = null): { $locale = !is_null($locale) ? $locale : $this->getLocale(); $translations = $this->getTranslationData($this->raw, $locale); - if($translations) { + if ($translations) { return str_replace($translations['find'], $translations['replace'], $this->raw->format($format)); } return $this->raw->format($format); @@ -386,14 +386,14 @@ public function shortWeekday(): string protected function getTranslationData(DateTime $date, string $locale): array { - if($locale !== "en") { + if ($locale !== "en") { $translations = $this->getTranslation($locale); - if($translations === false) { + if ($translations === false) { $formatters = $this->getLocaleTranslation($locale); } } - if(!isset($translations) && !isset($formatters)) { + if (!isset($translations) && !isset($formatters)) { return []; } @@ -417,7 +417,7 @@ protected function getTranslationData(DateTime $date, string $locale): array protected function getTranslation(string $locale): array|false { $translationFile = realpath(__DIR__ . "/../lang/$locale.php"); - if($translationFile !== false) { + if ($translationFile !== false) { return require $translationFile; } return false; @@ -441,7 +441,7 @@ protected function getLocaleTranslation(string $locale): array 'monthFull' => new IntlDateFormatter($locale, IntlDateFormatter::LONG, IntlDateFormatter::NONE, null, null, 'MMMM'), 'monthShort' => new IntlDateFormatter($locale, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE, null, null, 'MMM'), 'weekdayFull' => new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::NONE, null, null, 'EEEE'), - 'weekdayShort'=> new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::NONE, null, null, 'E') + 'weekdayShort' => new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::NONE, null, null, 'E') ]; } return $this->parts[$locale]; diff --git a/src/Format/Dom.php b/src/Format/Dom.php index 2649b97..9dd546b 100755 --- a/src/Format/Dom.php +++ b/src/Format/Dom.php @@ -152,7 +152,7 @@ public function element(): ElementInterface { $this->str = Str::value($this->raw); $elem = $this->dom->create($this->tag, $this->str)->hideEmptyTag(true); - if($this->attr) { + if ($this->attr) { $elem->attrArr($this->attr); } return $elem; diff --git a/src/Format/Num.php b/src/Format/Num.php index e8b0948..3a61885 100755 --- a/src/Format/Num.php +++ b/src/Format/Num.php @@ -1,4 +1,5 @@ numInst)) { + if (!is_null($this->numInst)) { return $this->numInst; } - if(is_null(self::$defNumInst)) { + if (is_null(self::$defNumInst)) { throw new \InvalidArgumentException("NumberFormatter instance not set."); } return self::$defNumInst; diff --git a/src/Format/Str.php b/src/Format/Str.php index e2a8f76..af114d4 100755 --- a/src/Format/Str.php +++ b/src/Format/Str.php @@ -35,7 +35,7 @@ public function __construct(mixed $value) */ public static function value(mixed $value): FormatInterface { - if(is_array($value) || is_object($value)) { + if (is_array($value) || is_object($value)) { $value = ""; } return new Str((string)$value); @@ -394,7 +394,7 @@ public function normalizeSeparators(): self * @param bool $doubleEncode * @return self */ - public function entityEncode(int $flags = ENT_QUOTES|ENT_SUBSTITUTE, ?string $encoding = null, bool $doubleEncode = true): self + public function entityEncode(int $flags = ENT_QUOTES | ENT_SUBSTITUTE, ?string $encoding = null, bool $doubleEncode = true): self { $inst = clone $this; $inst->raw = htmlentities($inst->strVal(), $flags, $encoding, $doubleEncode); @@ -408,7 +408,7 @@ public function entityEncode(int $flags = ENT_QUOTES|ENT_SUBSTITUTE, ?string $en * @param string|null $encoding * @return self */ - public function entityDecode(int $flags = ENT_QUOTES|ENT_SUBSTITUTE, ?string $encoding = null): self + public function entityDecode(int $flags = ENT_QUOTES | ENT_SUBSTITUTE, ?string $encoding = null): self { $inst = clone $this; $inst->raw = html_entity_decode($inst->strVal(), $flags, $encoding); @@ -655,7 +655,7 @@ public function replace(array|string $find, array|string $replace): self { $inst = clone $this; $inst->raw = str_replace($find, $replace, $inst->strVal()); - if(!is_string($inst->raw)) { + if (!is_string($inst->raw)) { throw new InvalidArgumentException("The value has to be an string value!", 1); } return $inst; @@ -808,7 +808,7 @@ public function getUrlParts(array $parts): self foreach ($parts as $part) { $method = 'getUrl' . ucfirst($part); $subInst = new self($inst->raw); - if(!method_exists($subInst, $method)) { + if (!method_exists($subInst, $method)) { throw new InvalidArgumentException("The part '$part' does not exist as a part in getUrlParts.", 1); } $subInst = call_user_func([$subInst, $method]); @@ -902,7 +902,7 @@ public function jsonDecode(?bool $associative = null, int $depth = 512, int $fla public function compare(string|int|float|bool|null $compare): self { $inst = clone $this; - if(is_numeric($inst->raw)) { + if (is_numeric($inst->raw)) { $inst->raw = ((float)$inst->raw > 0); return $inst; } diff --git a/src/Helpers.php b/src/Helpers.php index 0464851..651c951 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -4,12 +4,12 @@ use ReflectionClass; -class Helpers { - +class Helpers +{ /** * @throws \ReflectionException */ - static function debugDump($var, $label = null): void + public static function debugDump($var, $label = null): void { if (is_object($var)) { $reflection = new ReflectionClass($var); @@ -32,11 +32,11 @@ static function debugDump($var, $label = null): void } } - static function printFormattedValue($value, $indent = 0): void + public static function printFormattedValue($value, $indent = 0): void { $spacingS = $spacingA = str_repeat(" ", $indent); - $spacingB = str_repeat(" ", $indent+1); - if($indent > 1) { + $spacingB = str_repeat(" ", $indent + 1); + if ($indent > 1) { $spacingS = ""; } if (is_array($value)) { @@ -61,13 +61,13 @@ static function printFormattedValue($value, $indent = 0): void * @param string $key * @return array|bool */ - static function traversArrFromStr(array $array, string $key): mixed + public static function traversArrFromStr(array $array, string $key): mixed { $new = $array; $exp = explode(".", $key); foreach ($exp as $index) { $data = is_object($new) ? ($new->{$index} ?? null) : ($new[$index] ?? null); - if(is_null($data)) { + if (is_null($data)) { $new = false; break; } @@ -77,4 +77,3 @@ static function traversArrFromStr(array $array, string $key): mixed } } - diff --git a/src/Iconv.php b/src/Iconv.php index 153d7c4..f77bbf6 100755 --- a/src/Iconv.php +++ b/src/Iconv.php @@ -1,4 +1,5 @@ setEncoding($toEncoding); - if(function_exists('iconv') && !$inst->disableVanilla) { + if (function_exists('iconv') && !$inst->disableVanilla) { $inst->value = iconv($fromEncoding, $toEncoding, $inst->value); } else { @@ -91,11 +92,11 @@ public function encode(string $fromEncoding, string $toEncoding): self throw new ErrorException("iconv(): Detected an illegal character in input string"); } */ - if($fromEncoding !== "utf-8") { + if ($fromEncoding !== "utf-8") { // Convert input to UTF-8 $inMap = $inst->getMap("from", $fromEncoding); $inst->value = $inst->encodeUtf8($inMap, $inst->value); - if($inMap === false) { + if ($inMap === false) { throw new ErrorException('iconv_strlen(): Wrong encoding, conversion from "' . $fromEncoding . '"'); } @@ -107,7 +108,7 @@ public function encode(string $fromEncoding, string $toEncoding): self if ($toEncoding !== 'utf-8') { $outMap = $inst->getMap("to", $toEncoding); $inst->value = $inst->decodeUtf8($inst->value, $outMap, ($translit ? $inst->getMapFile("translit") : [])); - if($outMap === false) { + if ($outMap === false) { throw new ErrorException('iconv_strlen(): Wrong encoding, conversion to "' . $toEncoding . '"'); } } @@ -118,7 +119,7 @@ public function encode(string $fromEncoding, string $toEncoding): self protected function setEncoding(?string $encoding = null): self { $inst = clone $this; - if(is_string($encoding)) { + if (is_string($encoding)) { $inst->encoding = $encoding; } return $inst; @@ -136,12 +137,12 @@ protected function setEncoding(?string $encoding = null): self public function strlen(?string $encoding = null): int|false { $inst = $this->setEncoding($encoding); - if(function_exists("iconv_strlen") && !$inst->disableVanilla) { + if (function_exists("iconv_strlen") && !$inst->disableVanilla) { return iconv_strlen($inst->value, $inst->encoding); } - if(is_string($encoding)) { + if (is_string($encoding)) { $inst = $this->encode("utf-8", $inst->encoding); - if($inst->getValue() === false) { + if ($inst->getValue() === false) { return false; } } @@ -164,19 +165,19 @@ public function substr(int $start, ?int $length = null, ?string $encoding = null $value = ""; $inst = $this->setEncoding($encoding); $length = $inst->getLength($length); - if(function_exists("iconv_substr") && !$inst->disableVanilla) { + if (function_exists("iconv_substr") && !$inst->disableVanilla) { $value = (string)iconv_substr($inst->value, $start, $length, $inst->encoding); } else { - if(is_string($encoding)) { + if (is_string($encoding)) { $inst = $inst->encode("utf-8", $inst->encoding); } $inc = 0; $inst->loop($inst->value, function ($character, $charCount) use (&$value, &$inc, $start, $length) { - if(($charCount + 1) > $start) { + if (($charCount + 1) > $start) { $value .= $character; $inc++; - if($inc >= $length) { + if ($inc >= $length) { return $inc; } } @@ -206,19 +207,19 @@ public function strpos(string $needle, int $offset = 0, ?string $encoding = null $inc = 0; $total = 0; $completed = false; - if(function_exists("iconv_strpos") && !$inst->disableVanilla) { + if (function_exists("iconv_strpos") && !$inst->disableVanilla) { return iconv_strpos($inst->value, $needle, $offset, $inst->encoding); } - if(is_string($encoding)) { + if (is_string($encoding)) { $inst = $inst->encode("utf-8", $inst->encoding); } $needleInst = new self($needle); - if(is_string($encoding)) { + if (is_string($encoding)) { $needleInst->encode("utf-8", $inst->encoding); } $needleLength = $needleInst->strlen(); - if($offset < 0) { + if ($offset < 0) { $offset = ($inst->strlen() + $offset); } @@ -232,13 +233,13 @@ public function strpos(string $needle, int $offset = 0, ?string $encoding = null $encoding ) { - if(($charCount + 1) > $offset) { + if (($charCount + 1) > $offset) { $char = (string)$needleInst->substr($inc, 1); - if($character === $char) { + if ($character === $char) { $inc++; - if($inc === $needleLength) { + if ($inc === $needleLength) { $completed = ($charCount + 1) - $inc; - if(!$this->strposFollowThrough) { + if (!$this->strposFollowThrough) { return $completed; } } @@ -249,7 +250,7 @@ public function strpos(string $needle, int $offset = 0, ?string $encoding = null $total++; return false; }); - if($offset > $total) { + if ($offset > $total) { throw new ValueError('iconv_strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack)'); } return ($completed && $inc > 0) ? $completed : false; @@ -269,7 +270,7 @@ public function strrpos(string $needle, ?string $encoding = null): false|int { $inst = $this->setEncoding($encoding); $inst = $inst->strposFollowThrough(true); - if(function_exists("iconv_strrpos") && !$inst->disableVanilla) { + if (function_exists("iconv_strrpos") && !$inst->disableVanilla) { return iconv_strrpos($inst->value, $needle, $inst->encoding); } return $inst->strpos($needle, 0, $encoding); @@ -285,7 +286,7 @@ public function strrpos(string $needle, ?string $encoding = null): false|int public function clearSuffix(string &$string, string $suffix): bool { $length = strlen($suffix); - if(substr($string, -$length) === $suffix) { + if (substr($string, -$length) === $suffix) { $string = substr($string, 0, -$length); return true; } @@ -315,8 +316,8 @@ final protected function strposFollowThrough(bool $followThrough): self */ private function getMap(string $type, string $charset): array|false { - if($map = $this->getMapFile("from.$charset")) { - if($type === "to") { + if ($map = $this->getMapFile("from.$charset")) { + if ($type === "to") { return array_flip($map); } return $map; @@ -479,7 +480,7 @@ protected function loop(string $value, ?callable $call = null): int if (is_callable($call)) { $returnValue = $call($character, $charCount, $int); - if($returnValue !== false) { + if ($returnValue !== false) { $charCount = $returnValue; break; } diff --git a/src/MB.php b/src/MB.php index 5387d7c..14af275 100755 --- a/src/MB.php +++ b/src/MB.php @@ -1,4 +1,5 @@ disableVanilla) { + if (function_exists('mb_convert_encoding') && !$inst->disableVanilla) { $inst->value = mb_convert_encoding($inst->value, $toEncoding, $fromEncoding); } else { $inst->iconv = $inst->iconv->encode($fromEncoding, $toEncoding); @@ -90,7 +91,7 @@ public function encode(string $fromEncoding, string $toEncoding): self */ public function strlen(?string $encoding = null): int|false { - if(function_exists('mb_strlen') && !$this->disableVanilla) { + if (function_exists('mb_strlen') && !$this->disableVanilla) { return mb_strlen($this->value, $encoding); } return $this->iconv->strlen($encoding); @@ -108,7 +109,7 @@ public function strlen(?string $encoding = null): int|false public function substr(int $start, ?int $length = null, ?string $encoding = null): self { $inst = clone $this; - if(function_exists('mb_substr') && !$inst->disableVanilla) { + if (function_exists('mb_substr') && !$inst->disableVanilla) { $inst->value = mb_substr($inst->value, $start, $length, $encoding); } else { $this->iconv = $this->iconv->substr($start, $length, $encoding); @@ -128,7 +129,7 @@ public function substr(int $start, ?int $length = null, ?string $encoding = null */ public function strpos(string $needle, int $offset = 0, ?string $encoding = null): false|int { - if(function_exists('mb_strpos') && !$this->disableVanilla) { + if (function_exists('mb_strpos') && !$this->disableVanilla) { return mb_strpos($this->value, $needle, $offset, $encoding); } return $this->iconv->strpos($needle, $offset, $encoding); @@ -144,7 +145,7 @@ public function strpos(string $needle, int $offset = 0, ?string $encoding = null */ public function strrpos(string $needle, ?string $encoding = null): false|int { - if(function_exists('mb_strrpos') && !$this->disableVanilla) { + if (function_exists('mb_strrpos') && !$this->disableVanilla) { return mb_strrpos($this->value, $needle, 0, $encoding); } return $this->iconv->strrpos($needle, $encoding); diff --git a/src/Traits/CollectionUtilities.php b/src/Traits/CollectionUtilities.php index 8d498aa..fa88ccf 100644 --- a/src/Traits/CollectionUtilities.php +++ b/src/Traits/CollectionUtilities.php @@ -3,6 +3,7 @@ namespace MaplePHP\DTO\Traits; use Closure; +use ErrorException; use MaplePHP\DTO\Format\Arr; use MaplePHP\DTO\Helpers; use MaplePHP\DTO\Traverse; @@ -16,7 +17,7 @@ trait CollectionUtilities * * @param callable $callback A callable to run for each element in each array. * @param array $array Supplementary variable list of array arguments - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function map(callable $callback, array ...$array): self { @@ -30,10 +31,10 @@ public function map(callable $callback, array ...$array): self * https://www.php.net/manual/en/function.array-filter.php * * @param callable|null $callback The callback function to use - * If no callback is supplied, all empty entries of array will be + * If no callback is supplied, all empty entries of an array will be * removed. See empty() for how PHP defines empty in this case. * @param int $mode Flag determining what arguments are sent to callback: - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function filter(?callable $callback = null, int $mode = 0): self { @@ -49,7 +50,7 @@ public function filter(?callable $callback = null, int $mode = 0): self * * @param callable $callback * @param mixed|null $initial - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function reduce(callable $callback, mixed $initial = null): self { @@ -64,7 +65,7 @@ public function reduce(callable $callback, mixed $initial = null): self * * @param int $length * @param bool $preserveKeys - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function chunk(int $length, bool $preserveKeys = false): self { @@ -77,7 +78,7 @@ public function chunk(int $length, bool $preserveKeys = false): self * Flatten a array * * @param bool $preserveKeys - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function flatten(bool $preserveKeys = false): self { @@ -99,7 +100,7 @@ public function flatten(bool $preserveKeys = false): self /** * Flatten array and preserve keys * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function flattenWithKeys(): self { @@ -110,8 +111,8 @@ public function flattenWithKeys(): self * Merge two arrays * * @param array|TraverseInterface $combine - * @param bool $before Merge before main collection - * @return Arr|CollectionUtilities|Traverse + * @param bool $before Merge before the main collection + * @return Arr|self|Traverse */ public function merge(array|TraverseInterface $combine, bool $before = false): self { @@ -126,10 +127,10 @@ public function merge(array|TraverseInterface $combine, bool $before = false): s } /** - * Append array after main collection + * Append array after the main collection * * @param array|TraverseInterface $combine - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function append(array|TraverseInterface $combine): self { @@ -137,10 +138,10 @@ public function append(array|TraverseInterface $combine): self } /** - * Perpend array after main collection + * Perpend array after the main collection * * @param array|TraverseInterface $combine - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function prepend(array|TraverseInterface $combine): self { @@ -155,10 +156,13 @@ public function prepend(array|TraverseInterface $combine): self * @param int|null $length * @param mixed $replacement * @param mixed|null $splicedResults - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function splice( - int $offset, ?int $length, mixed $replacement = [], mixed &$splicedResults = null + int $offset, + ?int $length, + mixed $replacement = [], + mixed &$splicedResults = null ): self { $inst = clone $this; $splicedResults = array_splice($inst->raw, $offset, $length, $replacement); @@ -173,7 +177,7 @@ public function splice( * @param int $offset * @param int|null $length * @param bool $preserveKeys - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function slice(int $offset, ?int $length, bool $preserveKeys = false): self { @@ -187,7 +191,7 @@ public function slice(int $offset, ?int $length, bool $preserveKeys = false): se * https://www.php.net/manual/en/function.array-diff.php * * @param array|TraverseInterface $array - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function diff(array|TraverseInterface $array): self { @@ -202,7 +206,7 @@ public function diff(array|TraverseInterface $array): self * https://www.php.net/manual/en/function.array-diff-assoc.php * * @param array|TraverseInterface $array - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function diffAssoc(array|TraverseInterface $array): self { @@ -217,7 +221,7 @@ public function diffAssoc(array|TraverseInterface $array): self * https://www.php.net/manual/en/function.array-diff-key.php * * @param array|TraverseInterface $array - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function diffKey(array|TraverseInterface $array): self { @@ -232,7 +236,7 @@ public function diffKey(array|TraverseInterface $array): self * https://www.php.net/manual/en/function.array-unique.php * * @param int $flags - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function unique(int $flags = SORT_STRING): self { @@ -245,7 +249,7 @@ public function unique(int $flags = SORT_STRING): self /** * Will only return duplicate items * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function duplicates(): self { @@ -258,7 +262,7 @@ public function duplicates(): self * Exchanges all keys with their associated values in an array * https://www.php.net/manual/en/function.array-flip.php * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function flip(): self { @@ -272,7 +276,7 @@ public function flip(): self * https://www.php.net/manual/en/function.unset.php * * @param string ...$keySpread - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function unset(string|int|float|array ...$keySpread): self { @@ -286,10 +290,10 @@ public function unset(string|int|float|array ...$keySpread): self } /** - * Will explode an array item value and then merge it into array in same hierarchy + * Will explode an array item value and then merge it into an array in the same hierarchy * * @param string $separator - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function expMerge(string $separator): self { @@ -314,7 +318,7 @@ public function arrayItemExpMerge(string $separator): self * * @param int|string|null $columnKey * @param int|string|null $indexKey - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function column(int|string|null $columnKey, int|string|null $indexKey = null): self { @@ -332,11 +336,11 @@ public function pluck(int|string|null $columnKey, int|string|null $indexKey = nu } /** - * Shift an element off the beginning of array + * Shift an element off the beginning of an array * https://www.php.net/manual/en/function.array-shift.php * * @param mixed $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function shift(mixed &$value = null): self { @@ -346,11 +350,11 @@ public function shift(mixed &$value = null): self } /** - * Pop the element off the end of array + * Pop the element off the end of an array * https://www.php.net/manual/en/function.array-pop.php * * @param mixed $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function pop(mixed &$value = null): self { @@ -364,7 +368,7 @@ public function pop(mixed &$value = null): self * https://www.php.net/manual/en/function.array-unshift.php * * @param mixed $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function unshift(mixed ...$value): self { @@ -374,11 +378,11 @@ public function unshift(mixed ...$value): self } /** - * Push one or more elements onto the end of array + * Push one or more elements onto the end of an array * https://www.php.net/manual/en/function.array-push.php * * @param mixed $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function push(mixed ...$value): self { @@ -388,12 +392,12 @@ public function push(mixed ...$value): self } /** - * Pad array to the specified length with a value + * Pad an array to the specified length with a value * https://www.php.net/manual/en/function.array-pad.php * * @param int $length * @param mixed $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function pad(int $length, mixed $value): self { @@ -409,7 +413,7 @@ public function pad(int $length, mixed $value): self * @param int $startIndex * @param int $count * @param mixed $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function fill(int $startIndex, int $count, mixed $value): self { @@ -425,7 +429,7 @@ public function fill(int $startIndex, int $count, mixed $value): self * @param string|int|float $start * @param string|int|float $end * @param int|float $step - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function range(string|int|float $start, string|int|float $end, int|float $step = 1): self { @@ -438,7 +442,7 @@ public function range(string|int|float $start, string|int|float $end, int|float * Shuffle an array * https://www.php.net/manual/en/function.shuffle.php * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function shuffle(): self { @@ -452,7 +456,7 @@ public function shuffle(): self * https://www.php.net/manual/en/function.array-rand.php * * @param int $num - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function rand(int $num = 1): self { @@ -466,7 +470,7 @@ public function rand(int $num = 1): self * https://www.php.net/manual/en/function.array-replace.php * * @param array ...$replacements - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function replace(array ...$replacements): self { @@ -480,7 +484,7 @@ public function replace(array ...$replacements): self * https://www.php.net/manual/en/function.array-replace.php * * @param array ...$replacements - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function replaceRecursive(array ...$replacements): self { @@ -494,7 +498,7 @@ public function replaceRecursive(array ...$replacements): self * https://www.php.net/manual/en/function.array-reverse.php * * @param bool $preserveKeys - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function reverse(bool $preserveKeys = false): self { @@ -505,12 +509,12 @@ public function reverse(bool $preserveKeys = false): self /** * Searches the array for a given value and returns the first corresponding 'key' if - * successful to collection + * successful to a collection * https://www.php.net/manual/en/function.array-search.php * * @param mixed $needle * @param bool $strict - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function search(mixed $needle, bool $strict = false): self { @@ -521,17 +525,17 @@ public function search(mixed $needle, bool $strict = false): self /** * Searches the array for a given value and returns the first corresponding 'value' if - * successful to collection + * successful to a collection * * @param mixed $needle * @param bool $strict - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function find(mixed $needle, bool $strict = false): self { $inst = clone $this; $key = $this->search($needle, $strict)->get(); - if($key === false) { + if ($key === false) { $inst->raw = null; return $inst; } @@ -539,57 +543,57 @@ public function find(mixed $needle, bool $strict = false): self } /** - * Searches and filter out the array items that is found + * Searches and filter out the array items that are found * https://www.php.net/manual/en/function.array-search.php * * @param mixed $needle * @param bool $strict - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function searchFilter(array $needle, bool $strict = false): self { - return $this->filter(function ($item) use($needle, $strict) { + return $this->filter(function ($item) use ($needle, $strict) { return !in_array($item, $needle, $strict); }); } /** - * Searches and filter out the array items that is not found + * Searches and filter out the array items that are not found * https://www.php.net/manual/en/function.array-search.php * * @param mixed $needle * @param bool $strict - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function searchMatch(array $needle, bool $strict = false): self { - return $this->filter(function ($item) use($needle, $strict) { + return $this->filter(function ($item) use ($needle, $strict) { return in_array($item, $needle, $strict); }); } /** - * Apply a user supplied function to every member of an array + * Apply a user-supplied function to every member of an array * https://www.php.net/manual/en/function.array-walk.php * * @param array $needle * @param bool $strict - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function select(array $needle, bool $strict = false): self { - return $this->filter(function ($keyItem) use($needle, $strict) { + return $this->filter(function ($keyItem) use ($needle, $strict) { return in_array($keyItem, $needle, $strict); }, ARRAY_FILTER_USE_KEY); } /** - * Apply a user supplied function to every member of an array + * Apply a user-supplied function to every member of an array * https://www.php.net/manual/en/function.array-walk.php * * @param Closure $call * @param mixed|null $arg - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function walk(Closure $call, mixed $arg = null): self { @@ -606,7 +610,7 @@ public function walk(Closure $call, mixed $arg = null): self * * @param Closure $call * @param mixed|null $arg - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function walkRecursive(Closure $call, mixed $arg = null): self { @@ -618,8 +622,8 @@ public function walkRecursive(Closure $call, mixed $arg = null): self } /** - * Get first item in collection - * @return Arr|CollectionUtilities|Traverse + * Get the first item in a collection + * @return Arr|self|Traverse */ public function next(): self { @@ -629,8 +633,8 @@ public function next(): self } /** - * Get first item in collection - * @return Arr|CollectionUtilities|Traverse + * Get the first item in a collection + * @return Arr|self|Traverse */ public function prev(): self { @@ -644,7 +648,7 @@ public function prev(): self * https://www.php.net/manual/en/function.reset.php * * @param mixed|null $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function reset(mixed &$value = null): self { @@ -658,7 +662,7 @@ public function reset(mixed &$value = null): self * https://www.php.net/manual/en/function.end.php * * @param mixed|null $value - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function end(mixed &$value = null): self { @@ -668,10 +672,10 @@ public function end(mixed &$value = null): self } /** - * Get first item in collection + * Get the first item in a collection * https://www.php.net/manual/en/function.end.php * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function first(): self { @@ -681,10 +685,10 @@ public function first(): self } /** - * Get last item in collection + * Get the last item in a collection * https://www.php.net/manual/en/function.end.php * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function last(): self { @@ -697,7 +701,7 @@ public function last(): self * Fetch a key from an array * https://www.php.net/manual/en/function.key.php * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function key(): self { @@ -714,7 +718,7 @@ public function key(): self * Return all the keys or a subset of the keys of an array * https://www.php.net/manual/en/function.array-keys.php * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function keys(): self { @@ -732,7 +736,7 @@ public function keys(): self * https://www.php.net/implode * * @param array|string $separator - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function implode(array|string $separator = ""): self { @@ -742,11 +746,11 @@ public function implode(array|string $separator = ""): self } /** - * Will return array item at index/key + * Will return the array item at index/key * https://www.php.net/manual/en/function.shuffle.php * * @param int|float|string $key - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function eq(int|float|string $key): self { @@ -757,11 +761,11 @@ public function eq(int|float|string $key): self } /** - * Extract all array items values in array with a wildcard search ("2025-*") + * Extract all array items values in an array with a wildcard search ("2025-*") * * @param string $search wildcard prefix * @param bool $searchByKey - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function wildcardMatch(string $search, bool $searchByKey = false): self { @@ -779,16 +783,16 @@ public function wildcardMatch(string $search, bool $searchByKey = false): self } // Alias it wildcardMatch - function wildcardSearch(string $search, bool $searchByKey = false): self + public function wildcardSearch(string $search, bool $searchByKey = false): self { return $this->wildcardMatch($search, $searchByKey); } /** - * Find all array keys array with a wildcard search ("prefix_*") + * Find all array keys with a wildcard search ("prefix_*") * * @param string $search wildcard prefix - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function wildcardMatchKeys(string $search): self { @@ -796,10 +800,10 @@ public function wildcardMatchKeys(string $search): self } /** - * Create a fallback value if value is Empty/Null/0/false + * Create a fallback value if the value is Empty/Null/0/false * * @param string $fallback - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function fallback(mixed $fallback): self { @@ -814,7 +818,7 @@ public function fallback(mixed $fallback): self * Sprint over values * * @param string $add - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function sprint(string $add): self { @@ -827,18 +831,18 @@ public function sprint(string $add): self /** * Same as value validate but will method chain. - * If invalid then the value will be set to "null" OR whatever you set the fallback + * If invalid, then the value will be set to "null" OR whatever you set the fallback * * @param string $method * @param array $args * @param mixed|null $fallback - * @return Arr|CollectionUtilities|Traverse - * @throws \ErrorException + * @return Arr|self|Traverse + * @throws ErrorException */ public function validOrFallback(string $method, array $args = [], mixed $fallback = null): self { $inst = clone $this; - if(!$this->valid($method, $args)) { + if (!$this->valid($method, $args)) { $inst->raw = $fallback; } return $inst; @@ -848,13 +852,13 @@ public function validOrFallback(string $method, array $args = [], mixed $fallbac * Calculate the sum of values in an array * https://www.php.net/manual/en/function.array-sum.php * - * @return Arr|CollectionUtilities|Traverse + * @return Arr|self|Traverse */ public function sum(): self { $inst = clone $this; $arr = $this->raw; - if(!is_array($arr)) { + if (!is_array($arr)) { $arr = $this->toArray(); } $inst->raw = array_sum($arr); @@ -875,4 +879,4 @@ protected function handleCollectArg(array|TraverseInterface $collect): array return $collect; } -} \ No newline at end of file +} diff --git a/src/Traverse.php b/src/Traverse.php index 2f66633..ebc54c7 100755 --- a/src/Traverse.php +++ b/src/Traverse.php @@ -1,14 +1,16 @@ getData()->{$key})) { + if (isset($this->getData()->{$key})) { $data = $this->getData()->{$key}; - if(is_object($data) && !($data instanceof DynamicDataAbstract)) { + if (is_object($data) && !($data instanceof DynamicDataAbstract)) { return $data; } return $this::value($data); @@ -279,12 +281,12 @@ public function __call($method, $args) $expectedClass = array_shift($data); $formatClassInst = $this->format($expectedClass, $this->raw); $expectedMethod = implode('', $data); - if(!$expectedMethod) { + if (!$expectedMethod) { return $formatClassInst; } $expectedMethod = lcfirst($expectedMethod); - if(!method_exists($formatClassInst, $expectedMethod) && + if (!method_exists($formatClassInst, $expectedMethod) && ($formatClassInst === "Collection" && !function_exists($expectedMethod))) { throw new BadMethodCallException("The DTO method \"$expectedMethod\" does not exist!", 1); } @@ -321,7 +323,7 @@ public function add(string $key, mixed $value): self } /** - * Validate current item and set to fallback (default: null) if not valid + * Validate the current item and set to fallback (default: null) if not valid * * @param string $method * @param array $args @@ -331,7 +333,7 @@ public function add(string $key, mixed $value): self public function valid(string $method, array $args = []): bool { $inp = Validator::value($this->raw); - if(!method_exists($inp, $method)) { + if (!method_exists($inp, $method)) { throw new BadMethodCallException("The MaplePHP validation method \"$method\" does not exist!", 1); } return $inp->{$method}(...$args); @@ -389,17 +391,17 @@ public function toFloat(): float public function toBool(): bool { $value = $this->get(); - if(is_bool($value)) { + if (is_bool($value)) { return $value; } - if(is_numeric($value)) { + if (is_numeric($value)) { return ((float)$value > 0); } return ($value !== "false" && strlen($value)); } /** - * Convert collection into an array + * Convert a collection into an array * * @param callable|null $callback * @return array @@ -414,7 +416,7 @@ public function toArray(?callable $callback = null): array $inst->raw = $inst->getData(); } - if(!is_object($inst->raw) && !is_array($inst->raw)) { + if (!is_object($inst->raw) && !is_array($inst->raw)) { $inst->raw = [$inst->raw]; } @@ -423,7 +425,7 @@ public function toArray(?callable $callback = null): array (($get = $callback($row, $key, $index)) !== false)) { $row = $get; } - if($row instanceof self) { + if ($row instanceof self) { $row = $row->get(); } $new[$key] = $row; @@ -432,10 +434,70 @@ public function toArray(?callable $callback = null): array return $new; } + /** + * Accepts and validates data types + * + * This method checks if the raw data type matches any of the valid data types provided. + * If matched, returns the raw value, otherwise throws an exception. + * + * @template T of 'array'|'object'|'bool'|'int'|'float'|'string'|'resource'|'null'|'callable'|'closure' + * @param array $validDataType List of valid data types to check against + * @psalm-param list $validDataType + * @return mixed The raw value if type matches, otherwise throws exception + * @throws BadMethodCallException If data type is not supported + * @psalm-return ( + * T is 'array'? array: + * T is 'object'? object: + * T is 'bool'? bool: + * T is 'int'? int: + * T is 'float'? float: + * T is 'string'? string: + * T is 'resource'? mixed: + * T is 'null'? null: + * T is 'callable'? callable: + * T is 'closure'? \Closure: + * mixed + * ) + */ + public function acceptType(array $validDataType = []): mixed + { + if (is_array($this->raw) && in_array("array", $validDataType)) { + return $this->raw; + } + if (is_object($this->raw) && in_array("object", $validDataType)) { + return $this->raw; + } + if (is_bool($this->raw) && in_array("bool", $validDataType)) { + return $this->raw; + } + if (is_int($this->raw) && in_array("int", $validDataType)) { + return $this->raw; + } + if (is_float($this->raw) && in_array("float", $validDataType)) { + return $this->raw; + } + if (is_string($this->raw) && in_array("string", $validDataType)) { + return $this->raw; + } + if (is_null($this->raw) && in_array("null", $validDataType)) { + return $this->raw; + } + if (is_callable($this->raw) && in_array("callable", $validDataType)) { + return $this->raw; + } + if (($this->raw instanceof Closure) && in_array("closure", $validDataType)) { + return $this->raw; + } + if (is_resource($this->raw) && in_array("resource", $validDataType)) { + return $this->raw; + } + throw new BadMethodCallException("The DTO data type is not supported!", 1); + } + /** * Immutable: Access incremental array * - * @param callable|null $callback Access array row in the callbacks argument + * @param callable|null $callback Access array row in the callback argument * @return array|object|null */ public function fetch(?callable $callback = null): array|object|null @@ -487,7 +549,7 @@ public function each(callable $callback): array|object|null } /** - * Dump collection into a human-readable array dump + * Dump a collection into a human-readable array dump * * @return void * @throws ReflectionException @@ -498,7 +560,7 @@ public function dump(): void } /** - * Count if row is array. Can be used to validate before @fetch method + * Count if the row is an array. Can be used to validate before @fetch method * * @return int */ @@ -530,7 +592,7 @@ protected function format(string $dtoClassName, mixed $value): object $name = ucfirst($dtoClassName); $className = "MaplePHP\\DTO\\Format\\$name"; - if(!in_array($name, self::$helpers)) { + if (!in_array($name, self::$helpers)) { throw new BadMethodCallException("The DTO class \"$dtoClassName\" is not a Helper class! " . "You can add helper class with 'addHelper' if you wish.", 1); } diff --git a/src/TraverseInterface.php b/src/TraverseInterface.php index fe1474f..bd352d4 100644 --- a/src/TraverseInterface.php +++ b/src/TraverseInterface.php @@ -8,13 +8,12 @@ interface TraverseInterface { - /** * Object traverser * @param $key - * @return Traverse|null + * @return Traverse */ - public function __get($key); + public function __get($key): self; /** * Immutable formating class @@ -93,4 +92,4 @@ public function isset(): mixed; * @return self */ public function fallback(mixed $fallback): self; -} \ No newline at end of file +} From 1f531bb426cf62f561bfb280eb8e699f47834102 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Fri, 2 May 2025 16:28:53 +0200 Subject: [PATCH 08/14] Update README.md guide with data type handling --- README.md | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index be32af3..4792617 100755 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ DTO stands for **Darn Tidy Object**, a playful twist on the traditional Data Transfer Object. But this isn’t your average DTO. It’s a fully-loaded toolkit for **traversing, transforming, and tidying up structured data** in PHP with style, power, and simplicity. +_It also ensures your data returns in the **correct data type** with ease._ + +--- ## 📦 Installation @@ -10,13 +13,15 @@ composer require maplephp/dto ``` ## 📘 Documentation -- [Why DTO?](https://maplephp.github.io/DTO/docs/intro#why-dto) -- [Traverse Collection](https://maplephp.github.io/DTO/docs/traverse) -- [Format string](https://maplephp.github.io/DTO/docs/format-string) -- [Format Number](https://maplephp.github.io/DTO/docs/format-number) -- [Format Clock](https://maplephp.github.io/DTO/docs/format-clock) -- [Format Dom](https://maplephp.github.io/DTO/docs/format-dom) +* [Why DTO?](https://maplephp.github.io/DTO/docs/intro#why-dto) +* [Traverse Collection](https://maplephp.github.io/DTO/docs/traverse) +* [Format string](https://maplephp.github.io/DTO/docs/format-string) +* [Format Number](https://maplephp.github.io/DTO/docs/format-number) +* [Format Clock](https://maplephp.github.io/DTO/docs/format-clock) +* [Format Dom](https://maplephp.github.io/DTO/docs/format-dom) + +--- ## How It Works @@ -60,6 +65,26 @@ echo $obj->article->content->strExcerpt()->strUcFirst(); --- +### Correct Type Handling (with ease) + +No more clunky `is_numeric` checks or `intval` casts. DTO makes it simple to extract values in the exact type you expect: + +```php +$orderId = $dto->order->id->toInt(); +// Result: 1234 (int) +``` + +Handle flexible types cleanly with fallbacks: + +```php +$callback = $dto->settings->onReady->acceptType(['callable', 'null']); +if (is_callable($callback)) { + $callback(); // Result: Runs a startup hook or closure +} +``` + +--- + ### Built-In Data Transformation Transform values directly using built-in helpers like: @@ -130,4 +155,6 @@ print_r($updated->toArray()); --- -Now go forth, write cleaner code, and let DTO handle the messy parts. \ No newline at end of file +Now go forth, write cleaner code, and let DTO handle the messy parts. + +--- From dcfabd76e17cbdb3ad1304958463854ef1cbe7f2 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Fri, 2 May 2025 16:44:39 +0200 Subject: [PATCH 09/14] Update README.md guide with data type handling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4792617..9315691 100755 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ DTO stands for **Darn Tidy Object**, a playful twist on the traditional Data Transfer Object. But this isn’t your average DTO. It’s a fully-loaded toolkit for **traversing, transforming, and tidying up structured data** in PHP with style, power, and simplicity. -_It also ensures your data returns in the **correct data type** with ease._ +_It also makes your life easier by ensuring every piece of data is returned in the correct type-helping. Whether you expect an int, string, bool, or even a callable, DTO gives you strict, reliable access to your data with minimal effort._ --- From f407d75a3c1a4dd9867efd32f0b0260f42c05bfb Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Wed, 14 May 2025 21:09:18 +0200 Subject: [PATCH 10/14] Change to strict null comparison --- src/Format/Clock.php | 4 ++-- src/Format/Encode.php | 4 ++-- src/Format/Local.php | 10 +++++----- src/Format/Num.php | 4 ++-- src/Format/Str.php | 2 +- src/Helpers.php | 2 +- src/Iconv.php | 2 +- src/Traits/CollectionUtilities.php | 11 ++++++++++- src/Traverse.php | 22 +++++++++++++++++----- tests/unitary-dto-traverse.php | 3 +++ 10 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/Format/Clock.php b/src/Format/Clock.php index 619bc0a..b8fa1c0 100755 --- a/src/Format/Clock.php +++ b/src/Format/Clock.php @@ -38,7 +38,7 @@ public function __construct(mixed $value) throw new InvalidArgumentException("Is expecting a string or a convertable string value.", 1); } $date = new DateTime($value); - if (!is_null(self::$defaultTimezone)) { + if (self::$defaultTimezone !== null) { $date->setTimezone(self::$defaultTimezone); } parent::__construct($date); @@ -154,7 +154,7 @@ public function timezone(): string */ public function format(string $format = 'Y-m-d H:i:s', ?string $locale = null): string { - $locale = !is_null($locale) ? $locale : $this->getLocale(); + $locale = $locale !== null ? $locale : $this->getLocale(); $translations = $this->getTranslationData($this->raw, $locale); if ($translations) { return str_replace($translations['find'], $translations['replace'], $this->raw->format($format)); diff --git a/src/Format/Encode.php b/src/Format/Encode.php index 9670ed2..61434a8 100755 --- a/src/Format/Encode.php +++ b/src/Format/Encode.php @@ -101,7 +101,7 @@ public function urldecode(?callable $callback = null): string|array if (is_array($this->raw)) { $this->raw = Arr::value($this->raw)->walk(function ($value) use ($callback) { $value = Str::value((string)$value)->rawurldecode()->get(); - if (!is_null($callback)) { + if ($callback !== null) { $value = $callback($value); } return $value; @@ -110,7 +110,7 @@ public function urldecode(?callable $callback = null): string|array } else { $this->raw = Str::value($this->raw)->rawurldecode()->get(); - if (!is_null($callback)) { + if ($callback !== null) { $this->raw = $callback($this->raw); } } diff --git a/src/Format/Local.php b/src/Format/Local.php index 10e9c23..7610df9 100755 --- a/src/Format/Local.php +++ b/src/Format/Local.php @@ -30,7 +30,7 @@ public static function value(mixed $data) { if (is_string($data)) { - if (is_null(self::$dir)) { + if (self::$dir === null) { throw new Exception("You need to set default lang directory.", 1); } if (!is_file(self::$dir . "{$data}.php")) { @@ -44,7 +44,7 @@ public static function value(mixed $data) } } - if (is_null(self::$data[$data])) { + if (self::$data[$data] === null) { throw new Exception("Could not propagate the language data object with any information.", 1); } @@ -89,10 +89,10 @@ public function getValue(string $key): ?string public function get(string|array $key, string $fallback = "", ?array $sprint = null): ?string { - if (is_null($this::$prefix)) { + if ($this::$prefix === null) { throw new Exception("Lang prefix is null.", 1); } - if (!is_null($sprint)) { + if ($sprint !== null) { $this->sprint($sprint); } @@ -105,7 +105,7 @@ public function get(string|array $key, string $fallback = "", ?array $sprint = n } $value = ($this->value[$key][$this::$prefix] ?? $fallback); - if (is_null($sprint)) { + if ($sprint === null) { return $value; } diff --git a/src/Format/Num.php b/src/Format/Num.php index 3a61885..a34542b 100755 --- a/src/Format/Num.php +++ b/src/Format/Num.php @@ -63,10 +63,10 @@ public function setLocale(string $locale, int $type = NumberFormatter::CURRENCY) */ public function getNumFormatter(): NumberFormatter { - if (!is_null($this->numInst)) { + if ($this->numInst !== null) { return $this->numInst; } - if (is_null(self::$defNumInst)) { + if (self::$defNumInst === null) { throw new \InvalidArgumentException("NumberFormatter instance not set."); } return self::$defNumInst; diff --git a/src/Format/Str.php b/src/Format/Str.php index af114d4..8768dc1 100755 --- a/src/Format/Str.php +++ b/src/Format/Str.php @@ -838,7 +838,7 @@ public function getDirname(): self public function escape(): self { $inst = clone $this; - if (is_null($inst->raw)) { + if ($inst->raw === null) { $inst->raw = null; } return $inst->specialchars(); diff --git a/src/Helpers.php b/src/Helpers.php index 651c951..eab7952 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -67,7 +67,7 @@ public static function traversArrFromStr(array $array, string $key): mixed $exp = explode(".", $key); foreach ($exp as $index) { $data = is_object($new) ? ($new->{$index} ?? null) : ($new[$index] ?? null); - if (is_null($data)) { + if ($data === null) { $new = false; break; } diff --git a/src/Iconv.php b/src/Iconv.php index f77bbf6..b1348fa 100755 --- a/src/Iconv.php +++ b/src/Iconv.php @@ -509,6 +509,6 @@ final public function hasIllegalChar(string $value): bool */ final public function getLength(?int $length = null): int { - return (is_null($length) || $length > self::STRING_MAX_LENGTH) ? self::STRING_MAX_LENGTH : $length; + return ($length === null || $length > self::STRING_MAX_LENGTH) ? self::STRING_MAX_LENGTH : $length; } } diff --git a/src/Traits/CollectionUtilities.php b/src/Traits/CollectionUtilities.php index fa88ccf..7d141c3 100644 --- a/src/Traits/CollectionUtilities.php +++ b/src/Traits/CollectionUtilities.php @@ -39,7 +39,7 @@ public function map(callable $callback, array ...$array): self public function filter(?callable $callback = null, int $mode = 0): self { $inst = clone $this; - $data = is_null($callback) ? $inst->raw : $inst->fetch(); + $data = $callback === null ? $inst->raw : $inst->fetch(); $inst->raw = array_filter($data, $callback, $mode); return $inst; } @@ -829,6 +829,15 @@ public function sprint(string $add): self return $inst; } + public function parseStr(): self + { + $inst = clone $this; + if (is_string($inst->raw)) { + parse_str($this->toString(), $inst->raw); + } + return $inst; + } + /** * Same as value validate but will method chain. * If invalid, then the value will be set to "null" OR whatever you set the fallback diff --git a/src/Traverse.php b/src/Traverse.php index ebc54c7..6911663 100755 --- a/src/Traverse.php +++ b/src/Traverse.php @@ -322,6 +322,18 @@ public function add(string $key, mixed $value): self return $inst; } + /** + * Validate the current item + * + * @example $this->email->validator()->isEmail() // returns bool + * @return Validator + * @throws ErrorException + */ + public function validator(): Validator + { + return Validator::value($this->raw); + } + /** * Validate the current item and set to fallback (default: null) if not valid * @@ -412,7 +424,7 @@ public function toArray(?callable $callback = null): array $new = []; $inst = clone $this; - if (is_null($inst->raw)) { + if ($inst->raw === null) { $inst->raw = $inst->getData(); } @@ -479,7 +491,7 @@ public function acceptType(array $validDataType = []): mixed if (is_string($this->raw) && in_array("string", $validDataType)) { return $this->raw; } - if (is_null($this->raw) && in_array("null", $validDataType)) { + if ($this->raw === null && in_array("null", $validDataType)) { return $this->raw; } if (is_callable($this->raw) && in_array("callable", $validDataType)) { @@ -506,12 +518,12 @@ public function fetch(?callable $callback = null): array|object|null $new = []; $inst = clone $this; - if (is_null($inst->raw)) { + if ($inst->raw === null) { $inst->raw = $inst->getData(); } foreach ($inst->raw as $key => $row) { - if (!is_null($callback)) { + if ($callback !== null) { if (($get = $callback($inst::value($row), $key, $row, $index)) !== false) { $new[$key] = $get; } else { @@ -526,7 +538,7 @@ public function fetch(?callable $callback = null): array|object|null $value = $row; } else { // Incremental -> value - $value = !is_null($row) ? Format\Str::value($row) : null; + $value = $row !== null ? Format\Str::value($row) : null; } $new[$key] = $value; } diff --git a/tests/unitary-dto-traverse.php b/tests/unitary-dto-traverse.php index 7abd1b9..2791cd6 100644 --- a/tests/unitary-dto-traverse.php +++ b/tests/unitary-dto-traverse.php @@ -30,6 +30,9 @@ 'randSumList' => [12, 77, 62, 626], ]); + $this->validate($obj->email->validator()->isEmail(), function($inst) { + return $inst->isTrue(); + }); $this->add($obj->meta->wildcardSearch("2023-*")->count(), [ 'equal' => 2, From c05807c70261e0ecf736b63b3552b2cbab6e9616 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Sat, 17 May 2025 13:26:16 +0200 Subject: [PATCH 11/14] Add expect as a alias to validator in traverse class --- src/Traverse.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Traverse.php b/src/Traverse.php index 6911663..f80b9a0 100755 --- a/src/Traverse.php +++ b/src/Traverse.php @@ -334,6 +334,12 @@ public function validator(): Validator return Validator::value($this->raw); } + // Alias to validator + public function expect(): Validator + { + return Validator::value($this->raw); + } + /** * Validate the current item and set to fallback (default: null) if not valid * From 3ed74a18063e91f70ba59118e7d52d1e82fa62c1 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Sat, 21 Jun 2025 18:44:02 +0200 Subject: [PATCH 12/14] Add object support to eq traverse helper method --- src/Traits/CollectionUtilities.php | 5 +++++ tests/unitary-dto-traverse.php | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/src/Traits/CollectionUtilities.php b/src/Traits/CollectionUtilities.php index 7d141c3..61466cb 100644 --- a/src/Traits/CollectionUtilities.php +++ b/src/Traits/CollectionUtilities.php @@ -757,6 +757,11 @@ public function eq(int|float|string $key): self if (is_string($key) && str_contains($key, ".")) { return new self(Helpers::traversArrFromStr($this->toArray(), $key)); } + + if(is_object($this->raw)) { + return new self($this->raw->{$key} ?? false); + } + return new self($this->raw[$key] ?? false); } diff --git a/tests/unitary-dto-traverse.php b/tests/unitary-dto-traverse.php index 2791cd6..476137e 100644 --- a/tests/unitary-dto-traverse.php +++ b/tests/unitary-dto-traverse.php @@ -2,6 +2,7 @@ use MaplePHP\DTO\Format\Arr; use MaplePHP\DTO\Traverse; +use MaplePHP\Unitary\Expect; $unit = new MaplePHP\Unitary\Unit(); @@ -11,6 +12,7 @@ "firstname" => "daniel", "lastname" => "doe", "email" => "john.doe@gmail.com", + "contact_email" => "john.doe@gmail.com", "slug" => "Lorem ipsum åäö", "price" => "1999.99", "publish_date" => "2023-08-01 14:35:12", @@ -30,6 +32,11 @@ 'randSumList' => [12, 77, 62, 626], ]); + $this->validate($obj->contact_email->toString(), function(Expect $inst) { + $inst->isEmail(); + }); + + $this->validate($obj->email->validator()->isEmail(), function($inst) { return $inst->isTrue(); }); From 390bb105ac4f0cada43c9f5afba16ebc2293cf23 Mon Sep 17 00:00:00 2001 From: danielRConsid Date: Thu, 17 Jul 2025 10:44:51 +0200 Subject: [PATCH 13/14] Add a lightweight DOM library --- composer.json | 5 +- src/Dom/Document.php | 237 +++++++++++++++++++++++++++ src/Dom/Element.php | 162 ++++++++++++++++++ src/Format/Dom.php | 4 +- src/Interfaces/DocumentInterface.php | 76 +++++++++ src/Interfaces/ElementInterface.php | 69 ++++++++ 6 files changed, 548 insertions(+), 5 deletions(-) create mode 100755 src/Dom/Document.php create mode 100755 src/Dom/Element.php create mode 100755 src/Interfaces/DocumentInterface.php create mode 100755 src/Interfaces/ElementInterface.php diff --git a/composer.json b/composer.json index 167cbaa..276d0eb 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "maplephp/dto", "type": "library", - "version": "v3.0.0", + "version": "v3.0.1", "description": "DTO library in PHP provides benefits such as encapsulating data, enforcing immutability and facilitating data transformation.", "keywords": [ "dto", @@ -32,8 +32,7 @@ } ], "require": { - "php": ">=8.0", - "maplephp/swiftrender": "^2.0" + "php": ">=8.0" }, "autoload": { "psr-4": { diff --git a/src/Dom/Document.php b/src/Dom/Document.php new file mode 100755 index 0000000..6a838b2 --- /dev/null +++ b/src/Dom/Document.php @@ -0,0 +1,237 @@ +get(); + } + + /** + * Get get Dom/document (Will only trigger execute once per instance) + * @return string + */ + public function get(): string + { + if ($this->html === null) { + $this->execute(); + } + return $this->html; + } + + /** + * Init DOM instance + * @param string $key DOM access key + * @return self + */ + public static function dom(string $key): self + { + if (empty(self::$inst[$key])) { + self::$inst[$key] = self::withDom($key); + } + return self::$inst[$key]; + } + + /** + * Init DOM instance + * @param string $key DOM access key + * @return self + */ + public static function withDom(string $key): self + { + self::$inst[$key] = new self(); + return self::$inst[$key]; + } + + /** + * Create and bind tag to a key so it can be overwritten + * @param string $tag HTML tag (without brackets) + * @param string $key Bind tag to key + * @param bool|boolean $prepend Prepend instead of append + * @return ElementInterface + */ + public function bindTag(string $tag, string $key, bool $prepend = false): ElementInterface + { + if ($prepend) { + $this->elem = $this->createPrepend($tag, null, $key); + } else { + $this->elem = $this->create($tag, null, $key); + } + return $this->elem; + } + + /** + * Create (append) element + * + * @param string $element HTML tag (without brackets) + * @param null $value add value to tag + * @param string|null $bind + * @return ElementInterface + */ + public function create($element, $value = null, ?string $bind = null): ElementInterface + { + $inst = new Element($element, $value); + + if ($bind !== null) { + $this->elements[$bind] = $inst; + } else { + $this->elements[] = $inst; + } + + return $inst; + } + + /** + * Prepend element first + * @param string $element HTML tag (without brackets) + * @param string $value add value to tag + * @return ElementInterface + */ + public function createPrepend(string $element, ?string $value = null, ?string $bind = null): ElementInterface + { + $inst = new Element($element, $value); + if ($this->elements === null) { + $this->elements = []; + } + if ($bind !== null) { + //$new[$bind] = $inst; + $this->elements = array_merge([$bind => $inst], $this->elements); + } else { + $this->elements = array_merge([$inst], $this->elements); + } + + return $inst; + } + + /** + * Get one element from key + * @return ElementInterface|null + */ + public function getElement(string $key): ?ElementInterface + { + return ($this->elements[$key] ?? null); + } + + /** + * Get all elements + * @return array + */ + public function getElements(): array + { + return $this->elements; + } + + /** + * Get html tag + * @param string $key + * @return string|null + */ + public function getTag(string $key): ?string + { + return ($this->el[$key] ?? null); + } + + /** + * Execute and get Dom/document + * @param callable|null $call Can be used to manipulate element within feed + * @return string + */ + public function execute(?callable $call = null): string + { + $this->html = ""; + + if ($this->elements === null) { + if (method_exists($this, "withElement")) { + $inst = $this->withElement(); + $this->elements[] = $inst; + } + } + if (is_array($this->elements)) { + $this->build($this->elements, $call); + } + + return $this->html; + } + + /** + * Build document + * @param array $arr elements + * @param callable|null $call Can be used to manipulate element within feed + * @return void + */ + private function build(array $arr, ?callable $call = null): void + { + foreach ($arr as $key => $elemObj) { + $hasNoEnding = $this->elemHasEnding($elemObj->getEl()); + $this->buildCallable($elemObj, $key, $hasNoEnding, $call); + + if (!$elemObj->hideTagValid()) { + $this->html .= "\t<" . $elemObj->getEl() . $elemObj->buildAttr() . ">"; + } + if (!$hasNoEnding) { + $this->html .= $elemObj->getValue(); + } + if (isset($elemObj->elements)) { + $this->build($elemObj->elements, $call); + } + if (!$hasNoEnding && !$elemObj->hideTagValid()) { + $this->html .= "getEl() . ">\n"; + } + if ($hasNoEnding && !$elemObj->hideTagValid()) { + $this->html .= "\n"; + } + } + } + + private function buildCallable($elemObj, $key, $hasNoEnding, ?callable $call): void + { + if ($call !== null) { + $call($elemObj, $key, $hasNoEnding); + } + } + + /** + * Validate if element has ending + * @param string $elem + * @return bool + */ + final protected function elemHasEnding(string $elem): bool + { + return (in_array($elem, $this::TAG_NO_ENDING)); + } +} diff --git a/src/Dom/Element.php b/src/Dom/Element.php new file mode 100755 index 0000000..272628a --- /dev/null +++ b/src/Dom/Element.php @@ -0,0 +1,162 @@ +elem = $elem; + $this->value = $value; + $this->snippet = $snippet; + } + + /** + * Overwrite the current element + * @param string $elem HTML Tag name + */ + public function setElement(string $elem): self + { + $this->elem = $elem; + return $this; + } + + /** + * Set html attribute + * @param string $key attr key + * @param string|null $val attr value + * @return self + */ + public function attr(string $key, ?string $val = null): self + { + $this->attr[$key] = $val; + return $this; + } + + /** + * Set multiple html attributes + * @param array [key => value] + * @return self + */ + public function attrArr(?array $arr): self + { + if (is_array($arr)) { + $this->attr = array_merge($this->attr, $arr); + } + return $this; + } + + /** + * Hide html tag if its value is empty + * @param bool $bool + * @return self + */ + public function hideEmptyTag(bool $bool): self + { + $this->hideEmptyTag = $bool; + return $this; + } + + /** + * Validate hide tag value + * @return bool + */ + protected function hideTagValid(): bool + { + return (($this->hideEmptyTag && !$this->value)); + } + + /** + * Add value to attr + * @param string $key + * @param string $value + * @param string $sep + * @return self + */ + public function addAttr(string $key, string $value, string $sep = " "): self + { + if (isset($this->attr[$key])) { + $this->attr[$key] .= "{$sep}{$value}"; + } else { + $this->attr[$key] = $value; + } + return $this; + } + + /** + * Set elem value [VALUE] + * @param string|null null value can be used to auto skip HTML tag + * @return self + */ + public function setValue(?string $value): self + { + $this->value = $value; + return $this; + } + + /** + * Set elem value + * @return string + */ + public function getValue(): string + { + return (string)$this->value; + } + + /** + * Get elem/HTML tag + * @return string + */ + public function getEl(): string + { + return $this->elem; + } + + /** + * With cloned element or new element if is specifed + * @param string|null $elem + * @return self + */ + public function withElement(?string $elem = null): self + { + $inst = clone $this; + if ($elem !== null) { + $inst->elem = $elem; + } + return $inst; + } + + /** + * Array attr to string + * @return string + */ + protected function buildAttr(): string + { + $attr = ""; + if (count($this->attr) > 0) { + foreach ($this->attr as $k => $v) { + $attr .= " {$k}"; + if ($v !== null) { + $attr .= "=\"{$v}\""; + } + } + } + return $attr; + } +} diff --git a/src/Format/Dom.php b/src/Format/Dom.php index 9dd546b..b6ec825 100755 --- a/src/Format/Dom.php +++ b/src/Format/Dom.php @@ -9,9 +9,9 @@ namespace MaplePHP\DTO\Format; -use MaplePHP\Output\Dom\Document; use InvalidArgumentException; -use MaplePHP\Output\Interfaces\ElementInterface; +use MaplePHP\DTO\Dom\Document; +use MaplePHP\DTO\Interfaces\ElementInterface; final class Dom extends FormatAbstract { diff --git a/src/Interfaces/DocumentInterface.php b/src/Interfaces/DocumentInterface.php new file mode 100755 index 0000000..4ccc1ca --- /dev/null +++ b/src/Interfaces/DocumentInterface.php @@ -0,0 +1,76 @@ + value] + * @return self + */ + public function attrArr(?array $arr): self; + + /** + * Hide html tag if its value is empty + * @param bool $bool + * @return self + */ + public function hideEmptyTag(bool $bool): self; + + /** + * Add value to attr + * @param string $key + * @param string $value + * @param string $sep + * @return self + */ + public function addAttr(string $key, string $value, string $sep = " "): self; + + /** + * Set elem value [VALUE] + * @param string|null null value can be used to auto skip HTML tag + * @return self + */ + public function setValue(?string $value): self; + + /** + * Set elem value + * @return string + */ + public function getValue(): string; + + /** + * Get elem/HTML tag + * @return string + */ + public function getEl(): string; + + /** + * With cloned element or new element if is specifed + * @param string|null $elem + * @return self + */ + public function withElement(?string $elem = null): self; +} From bed3faa5bd49ddc6fe4cbe8e656dfe47033ca4b2 Mon Sep 17 00:00:00 2001 From: danielRConsid Date: Thu, 17 Jul 2025 10:48:59 +0200 Subject: [PATCH 14/14] Bump version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 276d0eb..de46b4c 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "maplephp/dto", "type": "library", - "version": "v3.0.1", + "version": "v3.1.0", "description": "DTO library in PHP provides benefits such as encapsulating data, enforcing immutability and facilitating data transformation.", "keywords": [ "dto",