From 05fd7dca56ee2730b9076ab95777b36ffee6482a Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 09:43:09 +0500 Subject: [PATCH 01/95] change minimal PHP version to 8.0 --- CHANGELOG.md | 6 ++++++ composer.json | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 501cf82..7c14d05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.0.0 + +### Changed + +- The package has PHP's minimal version is 8.0 now. + ## v3.1.1 ### Changed diff --git a/composer.json b/composer.json index 77033a6..b02b1dd 100755 --- a/composer.json +++ b/composer.json @@ -15,8 +15,7 @@ } ], "require": { - "php": "^7.4|^8.0", - "ext-json": "*", + "php": "^8.0", "ext-mbstring": "*" }, "require-dev": { From cb24e6280b0decda107cd5b28acdbe996d6e58f2 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 10:00:39 +0500 Subject: [PATCH 02/95] add Number::safeInt --- CHANGELOG.md | 7 ++++++ src/Helpers/Number.php | 28 ++++++++++++++++++++++ tests/helpers/NumberTest.php | 45 ++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 src/Helpers/Number.php create mode 100644 tests/helpers/NumberTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c14d05..ca2e636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.1.0 + +### Added + +- Add new Helper Class: `Nubmer`. +- Add method, working with integers: `Number::safeInt`. + ## v4.0.0 ### Changed diff --git a/src/Helpers/Number.php b/src/Helpers/Number.php new file mode 100644 index 0000000..1018681 --- /dev/null +++ b/src/Helpers/Number.php @@ -0,0 +1,28 @@ += 9007199254740991 || $value <= -9007199254740991)) { + return (string) $value; + } + + return (int) $value; + } +} diff --git a/tests/helpers/NumberTest.php b/tests/helpers/NumberTest.php new file mode 100644 index 0000000..15c509e --- /dev/null +++ b/tests/helpers/NumberTest.php @@ -0,0 +1,45 @@ + Date: Wed, 28 Apr 2021 10:14:52 +0500 Subject: [PATCH 03/95] fix --- .github/workflows/php.yml | 44 ++++++++++++++++---------------- .gitlab-ci.yml | 53 --------------------------------------- composer.json | 6 ++--- 3 files changed, 25 insertions(+), 78 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 4cb7f52..83b4c71 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -1,30 +1,30 @@ name: PHP Package -on: [push] +on: [ push ] jobs: - phpcs: - name: PHPCS - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: PHPCS check - uses: chekalsky/phpcs-action@v1 - with: - enable_warnings: true - -# lint-changelog: -# name: Lint changelog file +# phpcs: +# name: PHPCS # runs-on: ubuntu-latest # steps: -# - name: Check out code -# uses: actions/checkout@v2 -# - name: Lint changelog file -# uses: avto-dev/markdown-lint@v1 +# - uses: actions/checkout@v2 +# - name: PHPCS check +# uses: chekalsky/phpcs-action@v1 # with: -# rules: './.github/workflows/lint/rules/changelog.js' -# config: '/lint/config/changelog.yml' -# args: './CHANGELOG.md' +# enable_warnings: true + + # lint-changelog: + # name: Lint changelog file + # runs-on: ubuntu-latest + # steps: + # - name: Check out code + # uses: actions/checkout@v2 + # - name: Lint changelog file + # uses: avto-dev/markdown-lint@v1 + # with: + # rules: './.github/workflows/lint/rules/changelog.js' + # config: '/lint/config/changelog.yml' + # args: './CHANGELOG.md' testing: name: Test on PHP ${{ matrix.php }} with ${{ matrix.setup }} dependencies @@ -35,8 +35,8 @@ jobs: strategy: fail-fast: false matrix: - setup: ['basic', 'lowest', 'stable'] - php: ['7.4','8.0'] + setup: [ 'basic', 'lowest', 'stable' ] + php: [ '8.0' ] steps: - uses: actions/checkout@v2 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 16fe7f7..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,53 +0,0 @@ -cache: - key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" - paths: - - vendor/ - -.job-template: &job-template - stage: test - before_script: - - pecl install xdebug - - docker-php-ext-enable xdebug - # Install composer dependencies - - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - - php composer-setup.php - - php -r "unlink('composer-setup.php');" - - php composer.phar install - # Install Xdebug - coverage: '/^\s*Lines:\s*\d+.\d+\%/' - only: - - master - script: - - ./vendor/bin/phpunit --coverage-text --colors=never - - ./vendor/bin/phpstan analyze --ansi --level=max ./src - artifacts: - paths: - - tests/_output/coverage - expire_in: 1 month - -# We test PHP7.1 -test:PHP-7.1: - <<: *job-template - image: php:7.1 - only: - - php7 - except: - - master - -# We test PHP7.2 -test:PHP-7.2: - <<: *job-template - image: php:7.2 - only: - - php7 - except: - - master - -# We test PHP7.3 -test:PHP-7.3: - <<: *job-template - image: php:7.3 - only: - - php7 - except: - - master diff --git a/composer.json b/composer.json index b02b1dd..9507d27 100755 --- a/composer.json +++ b/composer.json @@ -19,9 +19,9 @@ "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^9.2", - "phpstan/phpstan": "~0.12.69", - "squizlabs/php_codesniffer": "^3.5" + "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "~0.12.85", + "squizlabs/php_codesniffer": "^3.6" }, "autoload": { "files": [ From 7675142f00710d53699cf108e18477a116d40601 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 10:16:45 +0500 Subject: [PATCH 04/95] add GH CI --- .github/workflows/php.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 83b4c71..04cbf48 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -3,15 +3,15 @@ name: PHP Package on: [ push ] jobs: -# phpcs: -# name: PHPCS -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v2 -# - name: PHPCS check -# uses: chekalsky/phpcs-action@v1 -# with: -# enable_warnings: true + phpcs: + name: PHPCS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: PHPCS check + uses: chekalsky/phpcs-action@v1 + with: + enable_warnings: true # lint-changelog: # name: Lint changelog file From 90f3c1803e2f708a5996afe8df2ef422d797cf34 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 10:18:30 +0500 Subject: [PATCH 05/95] add GH CI --- .github/workflows/php.yml | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 04cbf48..b93c2a2 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -3,34 +3,35 @@ name: PHP Package on: [ push ] jobs: - phpcs: - name: PHPCS +# phpcs: +# name: PHPCS +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v2 +# - name: PHPCS check +# uses: chekalsky/phpcs-action@v1 +# with: +# enable_warnings: true + + lint-changelog: + name: Lint changelog file runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: PHPCS check - uses: chekalsky/phpcs-action@v1 + - name: Check out code + uses: actions/checkout@v2 + - name: Lint changelog file + uses: avto-dev/markdown-lint@v1 with: - enable_warnings: true - - # lint-changelog: - # name: Lint changelog file - # runs-on: ubuntu-latest - # steps: - # - name: Check out code - # uses: actions/checkout@v2 - # - name: Lint changelog file - # uses: avto-dev/markdown-lint@v1 - # with: - # rules: './.github/workflows/lint/rules/changelog.js' - # config: '/lint/config/changelog.yml' - # args: './CHANGELOG.md' + rules: './.github/workflows/lint/rules/changelog.js' + config: '/lint/config/changelog.yml' + args: './CHANGELOG.md' testing: name: Test on PHP ${{ matrix.php }} with ${{ matrix.setup }} dependencies runs-on: ubuntu-latest timeout-minutes: 10 + needs: [lint-changelog] strategy: fail-fast: false From c3e3db3b5aa980883bbf3e4ad14e2cddad792b45 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 10:20:47 +0500 Subject: [PATCH 06/95] add GH CI --- CHANGELOG.md | 217 +-------------------------------------------------- 1 file changed, 3 insertions(+), 214 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca2e636..05428ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,225 +8,14 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added -- Add new Helper Class: `Nubmer`. -- Add method, working with integers: `Number::safeInt`. +- Add new Helper Class: `Nubmer` +- Add method, working with integers: `Number::safeInt` ## v4.0.0 ### Changed -- The package has PHP's minimal version is 8.0 now. - -## v3.1.1 - -### Changed - -- Remove default option `JSON_THROW_ON_ERROR` from `JSON::encode` & `JSON::decode` - -## v3.1.0 - -### Changed - -- Changed behavior on `JSON::encode` & `JSON::decode`: add default option `JSON_THROW_ON_ERROR`. Remove - custom `JsonException` - -## v3.0.0 - -### Added - -- Add support PHP: minimal >= 7.4 and maximum <=8.0 - -## v2.14.0 - -### Added - -- Add new global function `class_basename`. Get the class "basename" of the given object / class. -- Add new global function `trait_uses_recursive`. Returns all traits used by a trait and its traits. -- Add new global function `class_uses_recursive`. Returns all traits used by a class, its parent classes and trait of - their traits. -- Add trait `TraitBooter`. Helps to boot trait's static `boot-function`. -- Add trait `TraitInitializer`. Helps to init trait's `initialize-function`. - -## v2.13.0 - -### Added - -- Add new global function `instance` - -## v2.12.0 - -### Added - -- Add new trait `ReadOnlyProperties` - -## v2.11.6 - -### Added - -- Add into trait `Thrower` new function `throwIf` - -## v2.11.3 - -### Added - -- Add into `Str` helper function for define Regular Expression `isRegExp` - -## v2.11.2 - -### Added - -- Add into `Arr` helper function for fill a keyed array by values from another array: `fillKeysByValues` - -## v2.11.1 - -### Added - -- Add into `Arr` helper function for finding duplicates: `duplicates` - -## v2.11.0 - -### Added - -- Add trait `Whener` -- Add trait `Thrower` - -## v2.10.0 - -### Added - -- Add global function: `when` - -## v2.9.0 - -### Added - -- Add global function: `isTrue` - -## v2.8.0 - -### Added - -- Add Helper for base64: `B64` - -## v2.7.0 - -### Added - -- Add types `Point` and `GeoPoint` - -## v2.6.3 - -### Added - -- Helper `Bit`: function `decBinPad`. Convert decimal to binary string with left pad zero-filling - -## v2.6.0 - -### Added - -- Helper `Bit`: contains operations with bits and bit-masks - -## v2.5.0 - -### Changed - -- Logic has been changed for the trait `ConfigurableTrait::configurable`: - at first, on applying props, checking a magic method and then a property - -## v2.4.2 - -### Changed - -- Fix the trait `ConfigurableTrait::configurable` - -## v2.4.0 - -### Added - -- Add global function `classNamespace` - -## v2.3.0 - -### Added - -- Move Helper::Arr::ToPostgresArray from candidate to basic functionality - -## v2.2.5 - -### Added - -- Add functionality to trait `ArrayStorage`: now it implements `Arrayable` - -## v2.2.4 - -### Added - -- Add functionality to trait `ArrayStorage`: now it implements `ArrayAccess` - -## v2.2.3 - -### Added - -- Add trait `ArrayStorageConfigurableTrait` - -## v2.2.2 - -### Added - -- Fix CI - -## v2.2.0 - -### Added - -- Add function `has` to `Array` Helper -- Add function `set` to `Array` Helper -- Add function `remove` to `Array` Helper - -## v2.1.1 - -### Added - -- Add trait Maker - -## v2.1.0 - -### Added - -- Add trait Metable - -## v2.0.5 - -### Added - -- Add new String helpers: - + `replaceByTemplate` Replace templates into string -- Add new Array helpers: - + `replaceByTemplate` Replace templates into array - -## v2.0.2 - -### Added - -- Add new String helpers: - + `replaceStrTo` Replace substr by start and finish indents - -## v2.0.1 - -### Added - -- Add `CHANGELOG.md` -- Add new Array helpers: - + `get` Get an item from an array using "dot" notation -- Add new String helpers: - + `toSnake` Converts a string to `snake_case` - + `toScreamingSnake` Converts a string to `SCREAMING_SNAKE_CASE` - + `toKebab` Converts a string to `kebab-case` - + `toCamel` Converts a string to `CamelCase` - + `toLowerCamel` Converts a string to `lowerCamelCase` - + `removeMultiSpace` Converts all multi-spaced characters to once - -## v2.0.0 +- The package has PHP's minimal version is 8.0 now ### Reformat From 10d6d24fed77bbcc1e3ca75b4a801cc6fae2d1a1 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 10:21:55 +0500 Subject: [PATCH 07/95] add GH CI --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05428ad..6ece6bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,8 +17,6 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher - The package has PHP's minimal version is 8.0 now -### Reformat - [keepachangelog]:https://keepachangelog.com/en/1.0.0/ [semver]:https://semver.org/spec/v2.0.0.html From bd8f9b745520ce0dd2e8a772b2072a94dc54cd48 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 10:24:53 +0500 Subject: [PATCH 08/95] add GH CI --- .github/workflows/php.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index b93c2a2..13e877f 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -37,13 +37,13 @@ jobs: fail-fast: false matrix: setup: [ 'basic', 'lowest', 'stable' ] - php: [ '8.0' ] + php: [ '8.0', '8.1' ] steps: - uses: actions/checkout@v2 - name: Use PHP ${{ matrix.php }} - uses: shivammathur/setup-php@v1 # Action page: + uses: shivammathur/setup-php@v2 # Action page: with: php-version: ${{ matrix.php }} extensions: mbstring From 56179f5d4b3ae7348fb63c0010aa3b6dcaea93b9 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 28 Apr 2021 10:27:12 +0500 Subject: [PATCH 09/95] add GH CI --- .github/workflows/php.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 13e877f..2b78e91 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -37,7 +37,7 @@ jobs: fail-fast: false matrix: setup: [ 'basic', 'lowest', 'stable' ] - php: [ '8.0', '8.1' ] + php: [ '8.0' ] steps: - uses: actions/checkout@v2 From 21a5bf613e09a104fa21f9b1e17b60306b9d8311 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 29 Apr 2021 16:05:10 +0500 Subject: [PATCH 10/95] fix safeInt --- src/Helpers/Number.php | 2 +- tests/helpers/NumberTest.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Helpers/Number.php b/src/Helpers/Number.php index 1018681..dcaebb9 100644 --- a/src/Helpers/Number.php +++ b/src/Helpers/Number.php @@ -23,6 +23,6 @@ public static function safeInt(int|string $value): int|string return (string) $value; } - return (int) $value; + return is_numeric($value) ? (int) $value : (string) $value; } } diff --git a/tests/helpers/NumberTest.php b/tests/helpers/NumberTest.php index 15c509e..4686108 100644 --- a/tests/helpers/NumberTest.php +++ b/tests/helpers/NumberTest.php @@ -28,6 +28,8 @@ public function providerSafeInt(): array [-9007199254740992, '-9007199254740992'], [-9007199254740990, -9007199254740990], [-9007199254740992, '-9007199254740992'], + ['66ede6f7-1b11-4d01-8bbb-c6412b12eac3', '66ede6f7-1b11-4d01-8bbb-c6412b12eac3'], + ['test', 'test'], ]; } @@ -41,5 +43,4 @@ public function testSafeInt(int|string $value, int|string $exp): void { self::assertTrue($exp === Number::safeInt($value)); } - } From 165e23ff8adad0b488bb8add9bd95582b65d16e9 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 29 Apr 2021 16:12:20 +0500 Subject: [PATCH 11/95] change readme --- readme.md | 179 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 85 deletions(-) diff --git a/readme.md b/readme.md index ca04ec8..25817bf 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,6 @@ # PHP Support -![](https://img.shields.io/badge/php->=7.2-blue.svg) + +![](https://img.shields.io/badge/php-^8.0-blue.svg) ![PHP Package](https://github.com/efureev/php-support/workflows/PHP%20Package/badge.svg?branch=master) [![Build Status](https://travis-ci.org/efureev/php-support.svg?branch=master)](https://travis-ci.org/efureev/php-support) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a53fb85fd1ab46169758e10dd2d818cb)](https://app.codacy.com/app/efureev/php-support?utm_source=github.com&utm_medium=referral&utm_content=efureev/php-support&utm_campaign=Badge_Grade_Settings) @@ -11,12 +12,20 @@ ## Install +For php >= 8.0 + +```bash +composer require efureev/support "^4.0" +``` + For php >= 7.4 and <=8.0 + ```bash composer require efureev/support "^3.0" ``` -For php >= 7.2 && <=7.4 +For php >= 8.0 + ```bash composer require efureev/support "^2.0" ``` @@ -24,94 +33,94 @@ composer require efureev/support "^2.0" ## Content - Helpers - + Array - - accessible - - dataToArray - - exists - - fromPostgresArray - - get - - has - - merge - - remove - - removeByValue - - set - - toArray - - toIndexedArray - - toPostgresArray - - replaceByTemplate - + String - - removeMultiSpace - - replaceByTemplate - - replaceStrTo - - toCamel - - toDelimited - - toKebab - - toLowerCamel - - toScreamingDelimited - - toScreamingSnake - - toSnake - + Json - - decode - - encode - - htmlEncode - + Bit - - addFlag - - checkFlag - - decBinPad - - exist - - grant - - removeFlag - + B64 - - decode - - decodeSafe - - encode - - encodeSafe + + Array + - accessible + - dataToArray + - exists + - fromPostgresArray + - get + - has + - merge + - remove + - removeByValue + - set + - toArray + - toIndexedArray + - toPostgresArray + - replaceByTemplate + + String + - removeMultiSpace + - replaceByTemplate + - replaceStrTo + - toCamel + - toDelimited + - toKebab + - toLowerCamel + - toScreamingDelimited + - toScreamingSnake + - toSnake + + Json + - decode + - encode + - htmlEncode + + Bit + - addFlag + - checkFlag + - decBinPad + - exist + - grant + - removeFlag + + B64 + - decode + - decodeSafe + - encode + - encodeSafe - Global functions - + classNamespace - + class_basename - + class_uses_recursive - + instance - + isTrue - + trait_uses_recursive - + value - + when + + classNamespace + + class_basename + + class_uses_recursive + + instance + + isTrue + + trait_uses_recursive + + value + + when - Exceptions - + ConfigException - + Exception - + InvalidArgumentException - + InvalidCallException - + InvalidConfigException - + InvalidParamException - + InvalidValueException - + JsonException - + MethodNotAllowedException - + MissingClassException - + MissingConfigException - + MissingPropertyException - + NotSupportedException - + UnknownMethodException - + UnknownPropertyException + + ConfigException + + Exception + + InvalidArgumentException + + InvalidCallException + + InvalidConfigException + + InvalidParamException + + InvalidValueException + + JsonException + + MethodNotAllowedException + + MissingClassException + + MissingConfigException + + MissingPropertyException + + NotSupportedException + + UnknownMethodException + + UnknownPropertyException - Interfaces - + Arrayable - + Command - + Jsonable - + Prototype + + Arrayable + + Command + + Jsonable + + Prototype - Traits - + ArrayStorage - + ArrayStorageConfigurableTrait - + ConfigurableTrait - + ConsolePrint - + Maker - + Metable - + ReadOnlyProperties - + Singleton - + Thrower - + TraitBooter - + TraitInitializer - + Whener + + ArrayStorage + + ArrayStorageConfigurableTrait + + ConfigurableTrait + + ConsolePrint + + Maker + + Metable + + ReadOnlyProperties + + Singleton + + Thrower + + TraitBooter + + TraitInitializer + + Whener - Types - + GeoPoint - + Point + + GeoPoint + + Point ## Test From 31099eb3eb4c3bae8c945436ea48c45a0a7ffa42 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 29 Apr 2021 16:12:54 +0500 Subject: [PATCH 12/95] change readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 25817bf..3a5f4fc 100644 --- a/readme.md +++ b/readme.md @@ -24,7 +24,7 @@ For php >= 7.4 and <=8.0 composer require efureev/support "^3.0" ``` -For php >= 8.0 +For php >= 7.2 && <=7.4 ```bash composer require efureev/support "^2.0" From 31733811db4184835e8bd7bb587c3f0ff892e004 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 30 Jul 2021 14:29:15 +0500 Subject: [PATCH 13/95] fix: Point --- src/Types/Point.php | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/Types/Point.php b/src/Types/Point.php index e7e6621..6b71c0f 100644 --- a/src/Types/Point.php +++ b/src/Types/Point.php @@ -26,21 +26,12 @@ class Point implements Jsonable, Arrayable * @param float $x * @param float $y */ - public function __construct(float $x, float $y) + public function __construct(float $x = 0, float $y = 0) { $this->x = $x; $this->y = $y; } - - /** - * @return string - */ - /*public function __toString() - { - return $this->toDB(); - }*/ - /** * @return array */ @@ -71,7 +62,6 @@ public static function fromArray(array $array): ?self * @param int $options * * @return string|null - * @throws \Php\Support\Exceptions\JsonException */ public function toJson($options = 320): ?string { @@ -88,7 +78,6 @@ public function toJson($options = 320): ?string * @param string|null $string * * @return Jsonable|null - * @throws \Php\Support\Exceptions\JsonException */ public static function fromJson(?string $string): ?Jsonable { @@ -124,7 +113,12 @@ public function castFromDatabase(?string $value): ?self return null; } - return new static(...explode(',', $string)); + [ + $x, + $y, + ] = explode(',', $string); + + return new static((float)$x, (float)$y); } From 512331b0f146962886d8291813b41c656e7acde8 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 30 Jul 2021 14:30:20 +0500 Subject: [PATCH 14/95] fix --- src/Types/Point.php | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Types/Point.php b/src/Types/Point.php index 6b71c0f..50c2166 100644 --- a/src/Types/Point.php +++ b/src/Types/Point.php @@ -16,25 +16,10 @@ */ class Point implements Jsonable, Arrayable { - public float $x = 0; - - public float $y = 0; - - /** - * Point constructor. - * - * @param float $x - * @param float $y - */ - public function __construct(float $x = 0, float $y = 0) + public function __construct(public float $x = 0, public float $y = 0) { - $this->x = $x; - $this->y = $y; } - /** - * @return array - */ public function toArray(): array { return [ From a467853d952649f1549af4eb7c695c88aa026819 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 8 Aug 2021 14:41:11 +0500 Subject: [PATCH 15/95] add setMetaAttribute --- CHANGELOG.md | 6 ++++++ src/Traits/Metable.php | 17 +++++++++++++++-- tests/traits/MetableTest.php | 37 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ece6bf..0f2435b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.2.0 + +### Added + +- Add method to trait `Metable`: `setMetaAttribute` + ## v4.1.0 ### Added diff --git a/src/Traits/Metable.php b/src/Traits/Metable.php index 1f5fd06..e206507 100755 --- a/src/Traits/Metable.php +++ b/src/Traits/Metable.php @@ -35,11 +35,24 @@ public function meta(): array * * @return mixed */ - public function metaAttribute(string $key, $default = null) + public function metaAttribute(string $key, mixed $default = null) { return Arr::get($this->meta, $key, $default); } + /** + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function setMetaAttribute(string $key, mixed $value): static + { + Arr::set($this->meta, $key, $value); + + return $this; + } + /** * Set additional meta information for the element. * @@ -47,7 +60,7 @@ public function metaAttribute(string $key, $default = null) * * @return $this */ - public function withMeta(array $meta): self + public function withMeta(array $meta): static { $this->meta = Arr::merge($this->meta, $meta); diff --git a/tests/traits/MetableTest.php b/tests/traits/MetableTest.php index 9cdbffc..8a19e9b 100644 --- a/tests/traits/MetableTest.php +++ b/tests/traits/MetableTest.php @@ -80,6 +80,43 @@ public function testRecursive(): void ] ); } + + public function testSetMetaAttribute(): void + { + $instance = new MetableClassTest(); + + $instance->setMetaAttribute('test', 123); + static::assertEquals($instance->metaAttribute('test'), 123); + + $instance->setMetaAttribute('params.id', 1); + $instance->setMetaAttribute('params.isBool', true); + $instance->setMetaAttribute('params.string', 'test'); + static::assertEquals(1, $instance->metaAttribute('params.id')); + static::assertEquals(true, $instance->metaAttribute('params.isBool')); + static::assertEquals('test', $instance->metaAttribute('params.string')); + + static::assertEquals( + [ + 'id' => 1, + 'isBool' => true, + 'string' => 'test', + ], + $instance->metaAttribute('params') + ); + + static::assertEquals( + [ + 'test' => 123, + 'params' => + [ + 'id' => 1, + 'isBool' => true, + 'string' => 'test', + ], + ], + $instance->meta() + ); + } } /** From e37b94ec8685a6f8195f19d7a8039002d8243e2c Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 10 Aug 2021 14:58:14 +0500 Subject: [PATCH 16/95] feat: add throwIfReturn --- src/Traits/Thrower.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Traits/Thrower.php b/src/Traits/Thrower.php index a8c2119..fb6127d 100755 --- a/src/Traits/Thrower.php +++ b/src/Traits/Thrower.php @@ -26,10 +26,23 @@ public static function throw(...$arguments): void * @param mixed $value * @param mixed ...$arguments */ - public static function throwIf($value, ...$arguments): void + public static function throwIf(mixed $value, ...$arguments): void { if ($value) { static::throw(...$arguments); } } + + /** + * @param mixed $value + * @param mixed ...$arguments + * + * @return bool + */ + public static function throwIfReturn(mixed $value, ...$arguments): bool + { + static::throwIf($value, ...$arguments); + + return true; + } } From c4668fe194025eb178ab6391a1b7bf55a401b49f Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 8 Sep 2021 10:57:35 +0500 Subject: [PATCH 17/95] feat: add global function remoteStaticCall --- CHANGELOG.md | 6 ++++++ src/Global/base.php | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f2435b..1ceafb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.3.0 + +### Added + +- Add global function: `remoteStaticCall` + ## v4.2.0 ### Added diff --git a/src/Global/base.php b/src/Global/base.php index 4d99581..e816fd3 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -156,3 +156,28 @@ function class_uses_recursive($class): array return array_unique($results); } } + + +if (!function_exists('remoteStaticCall')) { + /** + * Returns result of an object's method if it exists in the object. + * + * @param string|object|null $class + * @param string $method + * @param mixed ...$params + * + * @return mixed + */ + function remoteStaticCall(object|string|null $class, string $method, mixed ...$params): mixed + { + if (!$class) { + return null; + } + + if (is_object($class) || (is_string($class) && class_exists($class))) { + return $class::$method(...$params); + } + + return null; + } +} From 96939954684db5d16b9fa759d40228cc5d1e5585 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 8 Sep 2021 15:00:50 +0500 Subject: [PATCH 18/95] fix --- src/Global/base.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Global/base.php b/src/Global/base.php index e816fd3..70a1546 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -174,7 +174,10 @@ function remoteStaticCall(object|string|null $class, string $method, mixed ...$p return null; } - if (is_object($class) || (is_string($class) && class_exists($class))) { + if ( + (is_object($class) || (is_string($class) && class_exists($class))) && + method_exists($class, $method) + ) { return $class::$method(...$params); } From 6a1a4959f0d58be71904cac8a8369ebc01d44beb Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 8 Sep 2021 15:04:01 +0500 Subject: [PATCH 19/95] feat: add remoteCall --- CHANGELOG.md | 3 ++- src/Global/base.php | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ceafb3..e3cdfe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. -## v4.3.0 +## v4.3.1 ### Added +- Add global function: `remoteCall` - Add global function: `remoteStaticCall` ## v4.2.0 diff --git a/src/Global/base.php b/src/Global/base.php index 70a1546..c7483f4 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -184,3 +184,27 @@ function remoteStaticCall(object|string|null $class, string $method, mixed ...$p return null; } } + +if (!function_exists('remoteCall')) { + /** + * Returns result of an object's method if it exists in the object. + * + * @param object|null $class + * @param string $method + * @param mixed ...$params + * + * @return mixed + */ + function remoteCall(?object $class, string $method, mixed ...$params): mixed + { + if (!$class) { + return null; + } + + if (method_exists($class, $method)) { + return $class->$method(...$params); + } + + return null; + } +} From 95644fbdbf24812d331539f756e432cbcb48913c Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 29 Sep 2021 12:02:42 +0500 Subject: [PATCH 20/95] feat: add global function does_trait_use --- src/Global/base.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Global/base.php b/src/Global/base.php index c7483f4..0b3df90 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -133,6 +133,19 @@ function trait_uses_recursive(string $trait): array } } +if (!function_exists('does_trait_use')) { + /** + * @param string $class + * @param string $trait + * + * @return bool + */ + function does_trait_use(string $class, string $trait): bool + { + return isset(trait_uses_recursive($class)[$trait]); + } +} + if (!function_exists('class_uses_recursive')) { /** * Returns all traits used by a class, its parent classes and trait of their traits. From ad7eb0db089354b641b5cf144d4b0c96e647ea18 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 29 Sep 2021 12:03:52 +0500 Subject: [PATCH 21/95] bump version --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3cdfe4..e88fa0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.4.0 + +### Added + +- Add global function: `does_trait_use` + ## v4.3.1 ### Added From 08fd108a697d2e849accd1f2c30912c4ff5f13bf Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 1 Oct 2021 21:48:43 +0500 Subject: [PATCH 22/95] fix --- src/Global/base.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Global/base.php b/src/Global/base.php index 0b3df90..2350754 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -79,9 +79,9 @@ function isTrue($val, bool $return_null = false): ?bool * @param string|object $instance * @param mixed ...$params * - * @return object|null + * @return mixed */ - function instance($instance, ...$params) + function instance($instance, ...$params): mixed { if (is_object($instance)) { return $instance; From e9cfbd87986360979e4e7a286d2dc9d617ec2ca6 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 20 Oct 2021 17:19:45 +0500 Subject: [PATCH 23/95] feat: Add param `removeNull` to method: `Metable::setMetaAttribute` --- CHANGELOG.md | 6 ++++++ src/Traits/Metable.php | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e88fa0e..9411e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.4.2 + +### Changed + +- Add param `removeNull` to method: `Metable::setMetaAttribute` + ## v4.4.0 ### Added diff --git a/src/Traits/Metable.php b/src/Traits/Metable.php index e206507..03a61fb 100755 --- a/src/Traits/Metable.php +++ b/src/Traits/Metable.php @@ -43,12 +43,15 @@ public function metaAttribute(string $key, mixed $default = null) /** * @param string $key * @param mixed $value + * @param bool $removeNull * * @return $this */ - public function setMetaAttribute(string $key, mixed $value): static + public function setMetaAttribute(string $key, mixed $value, bool $removeNull = false): static { - Arr::set($this->meta, $key, $value); + if ($value !== null || !$removeNull) { + Arr::set($this->meta, $key, $value); + } return $this; } From 7e440631283cea5aa9c600b4ef8af4b61ac9a4a1 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 14 Nov 2021 00:37:14 +0500 Subject: [PATCH 24/95] fix: MethodNotAllowedException --- src/Exceptions/MethodNotAllowedException.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Exceptions/MethodNotAllowedException.php b/src/Exceptions/MethodNotAllowedException.php index 73271b3..f5d0775 100644 --- a/src/Exceptions/MethodNotAllowedException.php +++ b/src/Exceptions/MethodNotAllowedException.php @@ -23,6 +23,7 @@ class MethodNotAllowedException extends Exception public function __construct(string $reason, $message = 'Method Not Allowed') { $this->reason = $reason; - parent::__construct($message); + + parent::__construct($message ? "$message: $reason" : $reason); } } From 1cb9ccfb9ba4263d9539548bf12ff396f4b562f3 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 15 Nov 2021 13:53:18 +0500 Subject: [PATCH 25/95] feat: add trait HasPrePostActions --- CHANGELOG.md | 6 ++++++ src/Traits/HasPrePostActions.php | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/Traits/HasPrePostActions.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9411e2b..8941212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.5.0 + +### Added + +- Add trait `HasPrePostActions` + ## v4.4.2 ### Changed diff --git a/src/Traits/HasPrePostActions.php b/src/Traits/HasPrePostActions.php new file mode 100644 index 0000000..0053b10 --- /dev/null +++ b/src/Traits/HasPrePostActions.php @@ -0,0 +1,35 @@ +> */ + protected array $executeCallbacks = []; + + public function addCallbackAction(string $key, callable $action): self + { + $this->executeCallbacks[$key][] = $action; + + return $this; + } + + public function getCallbackActions(string $key): array + { + return (array)($this->executeCallbacks[$key] ?? []); + } + + protected function runActions(string $actionGroup, ...$arguments): bool + { + foreach ($this->getCallbackActions($actionGroup) as $action) { + $res = $action(...$arguments); + if ($res === false) { + return false; + } + } + + return true; + } +} From 66541f929152e24eece89204d58f395758373778 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 22 Nov 2021 16:45:13 +0500 Subject: [PATCH 26/95] feat: add ConditionalHandler --- CHANGELOG.md | 6 +++ src/ConditionalHandler.php | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/ConditionalHandler.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 8941212..a227039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.6.0 + +### Added + +- Add class `ConditionalHandler` + ## v4.5.0 ### Added diff --git a/src/ConditionalHandler.php b/src/ConditionalHandler.php new file mode 100644 index 0000000..aec77a4 --- /dev/null +++ b/src/ConditionalHandler.php @@ -0,0 +1,82 @@ + MorphMany::make( + * self::translate('Notifications'), + * 'notifications', + * NotificationResource::class + * ) + * ) + * ->handleIf(static function (Request $request) { + * $request->user()->id === Auth()->id() + * }); + * + * // call + * $field($request); + * // or + * $field->resolve($request); + */ +final class ConditionalHandler +{ + private array $params = []; + + private bool|Closure $condition = true; + + public function __construct(private Closure $handler) + { + } + + public function handleIf(Closure|bool $fn): self + { + $this->condition = $fn; + + return $this; + } + + private function resolveCondition(): bool + { + if ($this->condition instanceof Closure) { + return ($this->condition)(...$this->params); + } + + return $this->condition; + } + + public function resolve(mixed ...$params): mixed + { + $this->params = $params; + + if (!$this->resolveCondition()) { + return null; + } + + return ($this->handler)(...$this->params); + } + + /** + * @param mixed ...$params + * + * @return mixed + */ + public function __invoke(mixed ...$params) + { + return $this->resolve(...$params); + } + + public static function make(Closure $fn): self + { + return new self($fn); + } +} From 14c01fe78c6c24c56eadafc86808d0b4ec5e767c Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 22 Nov 2021 17:15:16 +0500 Subject: [PATCH 27/95] fix --- src/ConditionalHandler.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ConditionalHandler.php b/src/ConditionalHandler.php index aec77a4..4c40ed2 100644 --- a/src/ConditionalHandler.php +++ b/src/ConditionalHandler.php @@ -32,9 +32,7 @@ final class ConditionalHandler { private array $params = []; - private bool|Closure $condition = true; - - public function __construct(private Closure $handler) + public function __construct(private Closure $handler, private bool|Closure $condition = true) { } @@ -75,8 +73,8 @@ public function __invoke(mixed ...$params) return $this->resolve(...$params); } - public static function make(Closure $fn): self + public static function make(Closure $fn, bool|Closure $condition = true): self { - return new self($fn); + return new self($fn, $condition); } } From ea881496cfc679cb74ba961ae67181d8299fe2f6 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 24 Nov 2021 11:39:58 +0500 Subject: [PATCH 28/95] feat: add exception MissingMethodException --- CHANGELOG.md | 7 +++++ src/Exceptions/MissingMethodException.php | 37 +++++++++++++++++++++++ src/Global/base.php | 27 +++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 src/Exceptions/MissingMethodException.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a227039..1251676 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.7.0 + +### Added + +- Add exception `MissingMethodException` +- Add global function `remoteStaticCallOrTrow` + ## v4.6.0 ### Added diff --git a/src/Exceptions/MissingMethodException.php b/src/Exceptions/MissingMethodException.php new file mode 100644 index 0000000..225244c --- /dev/null +++ b/src/Exceptions/MissingMethodException.php @@ -0,0 +1,37 @@ +getName() . ($this->method ? ': "' . $this->method . '"' : '')) + ); + } + + /** + * @return string + */ + public function getName(): string + { + return 'Missing method'; + } + + /** + * @return null|string + */ + public function getMethod(): ?string + { + return $this->method; + } +} diff --git a/src/Global/base.php b/src/Global/base.php index 2350754..acd70a6 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -198,6 +198,33 @@ function remoteStaticCall(object|string|null $class, string $method, mixed ...$p } } +if (!function_exists('remoteStaticCall')) { + /** + * Returns result of an object's method if it exists in the object or trow exception. + * + * @param string|object|null $class + * @param string $method + * @param mixed ...$params + * + * @return mixed + */ + function remoteStaticCallOrTrow(object|string|null $class, string $method, mixed ...$params): mixed + { + if (!$class) { + throw new RuntimeException('Target Class is absent'); + } + + if ( + (is_object($class) || (is_string($class) && class_exists($class))) && + method_exists($class, $method) + ) { + return $class::$method(...$params); + } + + \Php\Support\Exceptions\MissingMethodException::throw("$class::$method"); + } +} + if (!function_exists('remoteCall')) { /** * Returns result of an object's method if it exists in the object. From df1a0e104016b0536ee3b8b8e3556422bb736519 Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 8 Feb 2022 18:00:13 +0500 Subject: [PATCH 29/95] feat: add toPostgresPoint --- readme.md | 2 + src/ErrorCollection.php | 2 - src/Exceptions/InvalidArgumentException.php | 1 - src/Helpers/Arr.php | 63 +++++- src/Types/GeoPoint.php | 1 - src/Types/Point.php | 12 +- tests/helpers/ArrTest.php | 208 +++++++++++++------- 7 files changed, 197 insertions(+), 92 deletions(-) diff --git a/readme.md b/readme.md index 3a5f4fc..b0386df 100644 --- a/readme.md +++ b/readme.md @@ -38,6 +38,7 @@ composer require efureev/support "^2.0" - dataToArray - exists - fromPostgresArray + - fromPostgresPoint - get - has - merge @@ -47,6 +48,7 @@ composer require efureev/support "^2.0" - toArray - toIndexedArray - toPostgresArray + - toPostgresPoint - replaceByTemplate + String - removeMultiSpace diff --git a/src/ErrorCollection.php b/src/ErrorCollection.php index 6fee9e0..42e7f90 100644 --- a/src/ErrorCollection.php +++ b/src/ErrorCollection.php @@ -6,6 +6,4 @@ class ErrorCollection { - - } diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php index a51e1e1..d4a7ec0 100644 --- a/src/Exceptions/InvalidArgumentException.php +++ b/src/Exceptions/InvalidArgumentException.php @@ -13,7 +13,6 @@ */ class InvalidArgumentException extends \InvalidArgumentException { - /** * Exception constructor. * diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index 1d9e004..30a2943 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -162,6 +162,20 @@ public static function toPostgresArray(array $array): string return str_replace(['[', ']', '"'], ['{', '}', ''], $json); } + public static function toPostgresPoint(array $array): ?string + { + if (count($array) !== 2) { + return null; + } + + [ + $x, + $y, + ] = $array; + + return '(' . $x . ',' . $y . ')'; + } + /** * Remove named keys from arrays * @@ -190,9 +204,29 @@ public static function toIndexedArray(array $array): array * * @return array */ - public static function fromPostgresArray(?string $s, int $start = 0, &$end = null): array - { - if (empty($s) || $s[0] !== '{') { + /** + * Load from PG array to PHP array + * + * @param string|null $s + * @param int $start + * @param null $end + * + * @return array + */ + public static function fromPostgresArrayWithBraces( + ?string $s, + int $start = 0, + &$end = null, + array $braces = [ + '{', + '}', + ] + ): array { + [ + $braceOpen, + $braceClose, + ] = $braces; + if (empty($s) || $s[0] !== $braceOpen) { return []; } @@ -204,14 +238,14 @@ public static function fromPostgresArray(?string $s, int $start = 0, &$end = nul for ($i = $start + 1; $i < $len; $i++) { $ch = $s[$i]; - if (!$string && $ch === '}') { + if (!$string && $ch === $braceClose) { if ($v !== '' || !empty($return)) { $return[] = $v; } $end = $i; break; } else { - if (!$string && $ch === '{') { + if (!$string && $ch === $braceOpen) { $v = self::fromPostgresArray($s, (int)$i, $i); } else { if (!$string && $ch === ',') { @@ -250,6 +284,25 @@ public static function fromPostgresArray(?string $s, int $start = 0, &$end = nul return $return; } + public static function fromPostgresArray(?string $s, int $start = 0, &$end = null): array + { + return static::fromPostgresArrayWithBraces($s, $start, $end, ['{', '}']); + } + + public static function fromPostgresPoint(?string $value): ?array + { + if (empty($value)) { + return null; + } + + $string = mb_substr($value, 1, -1); + if (empty($string)) { + return null; + } + + return explode(',', $string); + } + /** * Get an item from an array using "dot" notation. * diff --git a/src/Types/GeoPoint.php b/src/Types/GeoPoint.php index 4cadd4e..b8b3924 100644 --- a/src/Types/GeoPoint.php +++ b/src/Types/GeoPoint.php @@ -17,7 +17,6 @@ */ class GeoPoint extends Point { - /** * @param int $options * diff --git a/src/Types/Point.php b/src/Types/Point.php index 50c2166..31b89c3 100644 --- a/src/Types/Point.php +++ b/src/Types/Point.php @@ -5,6 +5,7 @@ namespace Php\Support\Types; use Php\Support\Exceptions\InvalidParamException; +use Php\Support\Helpers\Arr; use Php\Support\Helpers\Json; use Php\Support\Interfaces\Arrayable; use Php\Support\Interfaces\Jsonable; @@ -88,21 +89,14 @@ public function toPgDB(): string */ public function castFromDatabase(?string $value): ?self { - if (empty($value)) { - return null; - } - - $string = mb_substr($value, 1, -1); - - if (empty($string)) { + if (!$result = Arr::fromPostgresPoint($value)) { return null; } [ $x, $y, - ] = explode(',', $string); - + ] = $result; return new static((float)$x, (float)$y); } diff --git a/tests/helpers/ArrTest.php b/tests/helpers/ArrTest.php index 38b31a8..616f5aa 100644 --- a/tests/helpers/ArrTest.php +++ b/tests/helpers/ArrTest.php @@ -4,11 +4,11 @@ namespace Php\Support\Tests; +use ArrayObject; use Php\Support\Helpers\Arr; use Php\Support\Helpers\Json; use Php\Support\Interfaces\Jsonable; use PHPUnit\Framework\TestCase; -use ArrayObject; /** * Class ArrTest @@ -143,7 +143,8 @@ public function toJson($options = 320): ?string 1, 2, 3, - ], [ + ], + [ 1, 2, 3, @@ -219,23 +220,25 @@ public function toJson($options = 320): ?string 'test', ], ], - [new class () implements \JsonSerializable { - private $data = [ + [ + new class () implements \JsonSerializable { + private $data = [ + + '132', + 12, + 'test', + ]; + public function jsonSerialize() + { + return $this->data; + } + }, + [ '132', 12, 'test', - ]; - - public function jsonSerialize() - { - return $this->data; - } - }, [ - '132', - 12, - 'test', - ], + ], ], [ new ArrayObject([12, 'test 1']), @@ -303,7 +306,8 @@ public function toJson($options = 320): ?string 1, 2, 3, - ], [ + ], + [ 1, 2, 3, @@ -341,23 +345,25 @@ public function toJson($options = 320): ?string 'test', ], ], - [new class () implements \JsonSerializable { - private $data = [ + [ + new class () implements \JsonSerializable { + private $data = [ + + '132', + 12, + 'test', + ]; + public function jsonSerialize() + { + return $this->data; + } + }, + [ '132', 12, 'test', - ]; - - public function jsonSerialize() - { - return $this->data; - } - }, [ - '132', - 12, - 'test', - ], + ], ], [ new ArrayObject([12, 'test 1']), @@ -495,10 +501,6 @@ public function testToIndexedArray(): void static::assertEquals($expected, $res); } - - /** - * @throws \Php\Support\Exceptions\JsonException - */ public function testToPostgresArray(): void { static::assertEquals( @@ -550,14 +552,36 @@ public function testToPostgresArray(): void static::assertEquals('{}', Arr::ToPostgresArray([])); } + public function testToPostgresPoint(): void + { + static::assertEquals('(123,0.332)', Arr::ToPostgresPoint([123.00, 0.332])); + static::assertEquals('(123.012,10.332)', Arr::ToPostgresPoint([123.012, 10.332])); + + static::assertNull(Arr::ToPostgresPoint([])); + static::assertNull(Arr::ToPostgresPoint([1])); + static::assertNull(Arr::ToPostgresPoint([1, 2, 3])); + } + public function testFromPostgresArray(): void { static::assertEquals(['val1', 'test', 'null', '', 'null'], Arr::fromPostgresArray('{val1,test,null,,null}')); - static::assertEquals(['val1', '1', '', '3', 'null', '', 'null'], Arr::fromPostgresArray('{val1,1,,3,null,,null}')); + static::assertEquals( + ['val1', '1', '', '3', 'null', '', 'null'], + Arr::fromPostgresArray('{val1,1,,3,null,,null}') + ); static::assertEquals([], Arr::fromPostgresArray('{}')); } + public function testFromPostgresPoint(): void + { + static::assertEquals(['val1', '2342342'], Arr::fromPostgresPoint('(val1,2342342)')); + static::assertEquals(['12.3223', '0.3223',], Arr::fromPostgresPoint('(12.3223,0.3223)')); + static::assertNull(Arr::fromPostgresPoint('()')); + static::assertNull(Arr::fromPostgresPoint(null)); + static::assertNull(Arr::fromPostgresPoint('')); + } + /** * @return array */ @@ -568,25 +592,29 @@ public function providerRemoveByValue(): array [ 0 => 1, 2 => 3, - ], 1, + ], + 1, [ 1, 2, 3, - ], 2, + ], + 2, ], [ [ 1 => 'val 21', 2 => 'vat', 3 => 'test', - ], 0, + ], + 0, [ 'val 2', 'val 21', 'vat', 'test', - ], 'val 2', + ], + 'val 2', ], [ [ @@ -594,27 +622,31 @@ public function providerRemoveByValue(): array 2 => 'val 21', 3 => 'vat', 4 => 'test', - ], 0, + ], + 0, [ 'val 2', 'val 2', 'val 21', 'vat', 'test', - ], 'val 2', + ], + 'val 2', ], [ [ 'val 2', 'val 21', 'vat', - ], 3, + ], + 3, [ 'val 2', 'val 21', 'vat', null, - ], null, + ], + null, ], [ [ @@ -622,24 +654,28 @@ public function providerRemoveByValue(): array 'val 21', 'vat', null, - ], null, + ], + null, [ 'val 2', 'val 21', 'vat', null, - ], 1, + ], + 1, ], [ [ 'key 1' => 'val 1', 'key 2' => 'val 2', - ], 'key 4', + ], + 'key 4', [ 'key 1' => 'val 1', 'key 2' => 'val 2', 'key 4' => 'val 4', - ], 'val 4', + ], + 'val 4', ], [ ['a'], @@ -717,25 +753,29 @@ public function providerRemoveByValueAndReindex(): array [ 1, 3, - ], 1, + ], + 1, [ 1, 2, 3, - ], 2, + ], + 2, ], [ [ 'val 21', 'vat', 'test', - ], 0, + ], + 0, [ 'val 2', 'val 21', 'vat', 'test', - ], 'val 2', + ], + 'val 2', ], [ [ @@ -743,27 +783,31 @@ public function providerRemoveByValueAndReindex(): array 'val 21', 'vat', 'test', - ], 0, + ], + 0, [ 'val 2', 'val 2', 'val 21', 'vat', 'test', - ], 'val 2', + ], + 'val 2', ], [ [ 'val 2', 'val 21', 'vat', - ], 3, + ], + 3, [ 'val 2', 'val 21', 'vat', null, - ], null, + ], + null, ], [ [ @@ -771,24 +815,28 @@ public function providerRemoveByValueAndReindex(): array 'val 21', 'vat', null, - ], null, + ], + null, [ 'val 2', 'val 21', 'vat', null, - ], 1, + ], + 1, ], [ [ 'val 1', 'val 2', - ], 'key 4', + ], + 'key 4', [ 'key 1' => 'val 1', 'key 2' => 'val 2', 'key 4' => 'val 4', - ], 'val 4', + ], + 'val 4', ], [ @@ -869,7 +917,8 @@ public function providerGet(): array 'sub2' => [ 'val2', 'val3', - ], 'sub4' => ['sub4sub' => 'val3'], + ], + 'sub4' => ['sub4sub' => 'val3'], ], 'key2' => 2, 'key4' => 1, @@ -895,7 +944,8 @@ public function providerGet(): array [ 'val2', 'val3', - ], $array, + ], + $array, 'key.sub2', ], [ @@ -978,7 +1028,8 @@ public function providerHas(): array 'sub2' => [ 'val2', 'val3', - ], 'sub4' => ['sub4sub' => 'val3'], + ], + 'sub4' => ['sub4sub' => 'val3'], ], 'key2' => 2, 'key4' => 1, @@ -1093,7 +1144,8 @@ public function providerSet(): array [ 'val2', 'val3', - ], $array, + ], + $array, 'key.sub2', ], [ @@ -1160,7 +1212,8 @@ public function providerRemove(): array 'sub2' => [ 'val2', 'val3', - ], 'sub4' => ['sub4sub' => 'val3'], + ], + 'sub4' => ['sub4sub' => 'val3'], ], 'key2' => 2, 'key4' => 1, @@ -1242,10 +1295,12 @@ public function dataReplaceByTemplate(): array [ 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', - ], [ + ], + [ '{{%KEY%}}' => 'vKey', '{{%TOKEN%}}' => 'vToken', - ], [ + ], + [ 'key' => 'vKey', 'token' => 'vToken', ], @@ -1254,7 +1309,8 @@ public function dataReplaceByTemplate(): array [ 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', - ], ['{{%KEY%}}' => 'vKey'], + ], + ['{{%KEY%}}' => 'vKey'], [ 'key' => 'vKey', 'token' => '{{%TOKEN%}}', @@ -1264,7 +1320,8 @@ public function dataReplaceByTemplate(): array [ 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', - ], ['{{%KEY%}}' => ''], + ], + ['{{%KEY%}}' => ''], [ 'key' => '', 'token' => '{{%TOKEN%}}', @@ -1274,7 +1331,8 @@ public function dataReplaceByTemplate(): array [ 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', - ], ['{{%KEY%}}' => null], + ], + ['{{%KEY%}}' => null], [ 'key' => '', 'token' => '{{%TOKEN%}}', @@ -1293,7 +1351,8 @@ public function dataReplaceByTemplate(): array ], ], 'step3' => ['val' => '{{%VALUE%}}'], - ], [ + ], + [ '{{%KEY%}}' => 'vKey', '{{%TOKEN%}}' => 'vToken', '{{%VALUE%}}' => 12, @@ -1317,7 +1376,8 @@ public function dataReplaceByTemplate(): array [ '{{%KEY%}}' => 'key', '{{%TOKEN%}}' => 'token', - ], ['sdasdas'], + ], + ['sdasdas'], ], [ ['sdaas'], @@ -1338,10 +1398,10 @@ public function testReplaceByTemplate(array $array, array $replace, array $exp): { $res = Arr::replaceByTemplate($array, $replace); -// var_dump($res); -// var_dump($exp); -// static::assertEquals($exp, $res); + // var_dump($res); + // var_dump($exp); + // static::assertEquals($exp, $res); static::assertJsonStringEqualsJsonString(\json_encode($exp), \json_encode($res)); -// static::assertEquals($exp, $res); + // static::assertEquals($exp, $res); } } From 3d1b7e8ab64a5f26b098ee0cc1297ba4a666b3fa Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 8 Feb 2022 18:29:09 +0500 Subject: [PATCH 30/95] refactor --- CHANGELOG.md | 6 +++ composer.json | 2 +- phpstan.neon.dist | 2 +- src/Exceptions/ConfigException.php | 11 +----- src/Exceptions/InvalidConfigException.php | 4 +- src/Exceptions/MissingConfigException.php | 13 ++---- src/Exceptions/MissingPropertyException.php | 8 +--- src/Global/base.php | 2 +- src/Helpers/Arr.php | 44 ++++++++++++++------- src/Helpers/Json.php | 4 +- tests/helpers/ArrTest.php | 29 +------------- 11 files changed, 50 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1251676..7019a34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.8.0 + +### Added + +- Add methods `toPostgresPoint`, `fromPostgresPoint` to `Arr` helper + ## v4.7.0 ### Added diff --git a/composer.json b/composer.json index 9507d27..673974c 100755 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "phpstan/phpstan": "~0.12.85", + "phpstan/phpstan": "^1.4", "squizlabs/php_codesniffer": "^3.6" }, "autoload": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 02e585c..34f2b4b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,6 +4,6 @@ parameters: - src checkMissingIterableValueType: false checkGenericClassInNonGenericObjectType: false - excludes_analyse: + excludePaths: - 'src/Types/Point.php' - 'src/Types/GeoPoint.php' diff --git a/src/Exceptions/ConfigException.php b/src/Exceptions/ConfigException.php index 6f54460..3952a03 100644 --- a/src/Exceptions/ConfigException.php +++ b/src/Exceptions/ConfigException.php @@ -11,22 +11,15 @@ */ class ConfigException extends Exception { - /** - * @var mixed - */ - protected $config; - /** * ConfigException constructor. * - * @param mixed|null $config + * @param ?array $config * @param string $message */ - public function __construct($message = 'Config Exception', $config = null) + public function __construct(string $message = 'Config Exception', protected ?array $config = null) { parent::__construct($message); - - $this->config = $config; } /** diff --git a/src/Exceptions/InvalidConfigException.php b/src/Exceptions/InvalidConfigException.php index c954ea2..44b6028 100644 --- a/src/Exceptions/InvalidConfigException.php +++ b/src/Exceptions/InvalidConfigException.php @@ -14,10 +14,10 @@ class InvalidConfigException extends ConfigException /** * InvalidConfigException constructor. * - * @param mixed|null $config + * @param ?array $config * @param string $message */ - public function __construct($config = null, $message = 'Invalid Configuration') + public function __construct(?array $config = null, $message = 'Invalid Configuration') { parent::__construct($message, $config); } diff --git a/src/Exceptions/MissingConfigException.php b/src/Exceptions/MissingConfigException.php index cf570ec..c18c071 100644 --- a/src/Exceptions/MissingConfigException.php +++ b/src/Exceptions/MissingConfigException.php @@ -11,20 +11,13 @@ */ class MissingConfigException extends ConfigException { - /** @var string|null */ - protected $needKey; - /** - * MissingConfigException constructor. - * - * @param mixed $config - * @param string $needKey + * @param ?array $config + * @param ?string $needKey * @param string $message */ - public function __construct($config = null, $needKey = null, $message = 'Missing Config') + public function __construct(?array $config = null, protected ?string $needKey = null, $message = 'Missing Config') { - $this->needKey = $needKey; - parent::__construct($message, $config); } } diff --git a/src/Exceptions/MissingPropertyException.php b/src/Exceptions/MissingPropertyException.php index b96085a..2a7a843 100644 --- a/src/Exceptions/MissingPropertyException.php +++ b/src/Exceptions/MissingPropertyException.php @@ -11,19 +11,15 @@ */ class MissingPropertyException extends ConfigException { - /** @var string|null */ - protected $property; - /** * MissingPropertyException constructor. * * @param null|string $message * @param null|string $property - * @param mixed $config + * @param ?array $config */ - public function __construct(?string $message = null, ?string $property = null, $config = null) + public function __construct(?string $message = null, protected ?string $property = null, ?array $config = null) { - $this->property = $property; parent::__construct( $message ?? ($this->getName() . ($this->property ? ': "' . $this->property . '"' : '')), $config diff --git a/src/Global/base.php b/src/Global/base.php index acd70a6..52fd806 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -221,7 +221,7 @@ function remoteStaticCallOrTrow(object|string|null $class, string $method, mixed return $class::$method(...$params); } - \Php\Support\Exceptions\MissingMethodException::throw("$class::$method"); + throw new \Php\Support\Exceptions\MissingMethodException("$class::$method"); } } diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index 30a2943..9e15091 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -5,6 +5,7 @@ namespace Php\Support\Helpers; use ArrayAccess; +use ArrayObject; use JsonSerializable; use Php\Support\Interfaces\Arrayable; use Php\Support\Interfaces\Jsonable; @@ -68,7 +69,7 @@ public static function toArray($items): array } } - return $res; + return (array)$res; } /** @@ -209,14 +210,14 @@ public static function toIndexedArray(array $array): array * * @param string|null $s * @param int $start - * @param null $end + * @param ?int $end * * @return array */ public static function fromPostgresArrayWithBraces( ?string $s, int $start = 0, - &$end = null, + ?int &$end = null, array $braces = [ '{', '}', @@ -284,11 +285,23 @@ public static function fromPostgresArrayWithBraces( return $return; } - public static function fromPostgresArray(?string $s, int $start = 0, &$end = null): array + /** + * @param string|null $s + * @param int $start + * @param ?int $end + * + * @return array + */ + public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end = null): array { return static::fromPostgresArrayWithBraces($s, $start, $end, ['{', '}']); } + /** + * @param string|null $value + * + * @return array|null + */ public static function fromPostgresPoint(?string $value): ?array { if (empty($value)) { @@ -312,7 +325,7 @@ public static function fromPostgresPoint(?string $value): ?array * * @return mixed */ - public static function get($array, ?string $key, $default = null) + public static function get(mixed $array, ?string $key, mixed $default = null): mixed { if (!static::accessible($array)) { return value($default); @@ -322,11 +335,12 @@ public static function get($array, ?string $key, $default = null) return $array; } + /** @var array|ArrayAccess $array */ if (static::exists($array, $key)) { return $array[$key]; } - if (strpos($key, '.') === false) { + if (!str_contains($key, '.')) { return $array[$key] ?? value($default); } @@ -348,7 +362,7 @@ public static function get($array, ?string $key, $default = null) * * @return bool */ - public static function accessible($value): bool + public static function accessible(mixed $value): bool { return is_array($value) || $value instanceof ArrayAccess; } @@ -361,7 +375,7 @@ public static function accessible($value): bool * * @return bool */ - public static function exists($array, $key): bool + public static function exists(ArrayAccess|array $array, string|int $key): bool { if ($array instanceof ArrayAccess) { return $array->offsetExists($key); @@ -378,7 +392,7 @@ public static function exists($array, $key): bool * * @return bool */ - public static function has($array, $keys): bool + public static function has(ArrayAccess|array $array, string|array $keys): bool { $keys = (array)$keys; @@ -410,13 +424,13 @@ public static function has($array, $keys): bool * * If no key is given to the method, the entire array will be replaced. * - * @param ?array $array + * @param array|ArrayObject|null $array * @param string $key * @param mixed $value * - * @return array|null + * @return array|ArrayObject|null */ - public static function set(&$array, string $key, $value): ?array + public static function set(array|ArrayObject|null &$array, string $key, mixed $value): array|ArrayObject|null { if ($array === null) { return $array; @@ -442,12 +456,12 @@ public static function set(&$array, string $key, $value): ?array /** * Remove one or many array items from a given array using "dot" notation. * - * @param array $array + * @param array|ArrayObject $array * @param array|string $keys * * @return void */ - public static function remove(&$array, $keys): void + public static function remove(array|ArrayObject &$array, array|string $keys): void { $original = &$array; $keys = (array)$keys; @@ -497,7 +511,7 @@ public static function replaceByTemplate(array $array, array $replace): array { $res = []; foreach ($array as $key => $item) { - $res[$key] = static::itemReplaceByTemplate($item, $replace); + $res[$key] = self::itemReplaceByTemplate($item, $replace); } return $res; } diff --git a/src/Helpers/Json.php b/src/Helpers/Json.php index 545ec1d..fac5341 100644 --- a/src/Helpers/Json.php +++ b/src/Helpers/Json.php @@ -41,7 +41,7 @@ public static function htmlEncode($value): ?string * @param int $options the encoding options. For more details please refer to * . Default is * `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`. - * @param int $depth + * @param int<1, max> $depth * * @return string|null */ @@ -59,7 +59,7 @@ public static function encode($value, $options = 320, int $depth = 512): ?string * @param null|string $json the JSON string to be decoded * @param bool $asArray whether to return objects in terms of associative arrays. * @param int $options - * @param int $depth + * @param int<1, max> $depth * * @return mixed|null */ diff --git a/tests/helpers/ArrTest.php b/tests/helpers/ArrTest.php index 616f5aa..3780608 100644 --- a/tests/helpers/ArrTest.php +++ b/tests/helpers/ArrTest.php @@ -435,7 +435,6 @@ public function testExists(): void static::assertFalse(Arr::exists($array, 'test.key1')); static::assertFalse(Arr::exists($array, 'te')); static::assertFalse(Arr::exists($array, '')); - static::assertFalse(Arr::exists($array, null)); static::assertFalse(Arr::exists($array, 1)); @@ -1066,32 +1065,6 @@ public function providerHas(): array $array, 'key.sub12', ], - [ - false, - $array, - null, - ], - [ - false, - null, - null, - ], - [ - false, - null, - [], - ], - [ - false, - null, - 0, - ], - [ - false, - '', - 0, - ], - [ true, $array, @@ -1275,7 +1248,7 @@ public function testRemove2(): void 'key2' => 2, 'key4' => 1, ]; - Arr::remove($array, null); + Arr::remove($array, []); static::assertEquals($array, Arr::get($array, null)); } From 7d91dc96c439e8f9612c8a6dcf8b71dc6bbc9c48 Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 8 Feb 2022 19:02:47 +0500 Subject: [PATCH 31/95] fix --- src/Helpers/Arr.php | 11 +++++++++-- tests/helpers/ArrTest.php | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index 9e15091..bdfe0ad 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -300,7 +300,7 @@ public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end /** * @param string|null $value * - * @return array|null + * @return ?array */ public static function fromPostgresPoint(?string $value): ?array { @@ -313,7 +313,14 @@ public static function fromPostgresPoint(?string $value): ?array return null; } - return explode(',', $string); + [ + $x, + $y, + ] = explode(',', $string); + return [ + (float)$x, + (float)$y, + ]; } /** diff --git a/tests/helpers/ArrTest.php b/tests/helpers/ArrTest.php index 3780608..a9cdc96 100644 --- a/tests/helpers/ArrTest.php +++ b/tests/helpers/ArrTest.php @@ -574,8 +574,8 @@ public function testFromPostgresArray(): void public function testFromPostgresPoint(): void { - static::assertEquals(['val1', '2342342'], Arr::fromPostgresPoint('(val1,2342342)')); - static::assertEquals(['12.3223', '0.3223',], Arr::fromPostgresPoint('(12.3223,0.3223)')); + static::assertEquals([32.323, 2342342.0], Arr::fromPostgresPoint('(32.323,2342342)')); + static::assertEquals([12.3223, 0.3223,], Arr::fromPostgresPoint('(12.3223,0.3223)')); static::assertNull(Arr::fromPostgresPoint('()')); static::assertNull(Arr::fromPostgresPoint(null)); static::assertNull(Arr::fromPostgresPoint('')); From ff46f96ec302fc09e66f0373b3cb09cb6f239022 Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 16 Apr 2022 23:28:30 +0500 Subject: [PATCH 32/95] feat: add new methods: truncate, slugify, seemsUTF8, removeAccents --- CHANGELOG.md | 10 + composer.json | 5 +- src/Helpers/Str.php | 101 ++- src/Helpers/URLify.php | 791 ++++++++++++++++++++++ tests/{helpers => Helpers}/ArrTest.php | 2 +- tests/{helpers => Helpers}/B64Test.php | 2 +- tests/{helpers => Helpers}/BitTest.php | 2 +- tests/Helpers/HasReflection.php | 19 + tests/{helpers => Helpers}/JsonTest.php | 2 +- tests/{helpers => Helpers}/NumberTest.php | 2 +- tests/{helpers => Helpers}/StrTest.php | 107 ++- 11 files changed, 1033 insertions(+), 10 deletions(-) create mode 100644 src/Helpers/URLify.php rename tests/{helpers => Helpers}/ArrTest.php (99%) rename tests/{helpers => Helpers}/B64Test.php (98%) rename tests/{helpers => Helpers}/BitTest.php (99%) create mode 100644 tests/Helpers/HasReflection.php rename tests/{helpers => Helpers}/JsonTest.php (99%) rename tests/{helpers => Helpers}/NumberTest.php (96%) rename tests/{helpers => Helpers}/StrTest.php (76%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7019a34..748f82b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.9.0 + +### Added + +- Add method `Str::truncate`: truncate a string to a specified length without cutting a word off. +- Add method `Str::slugify`: generate a string safe for use in URLs from any given string +- Add method `Str::seemsUTF8`: checks to see if a string is utf8 encoded +- Add method `Str::removeAccents`: converts all accent characters to ASCII characters +- Add method `URLify::downcode`: transliterates characters to their ASCII equivalents + ## v4.8.0 ### Added diff --git a/composer.json b/composer.json index 673974c..6ed3bfd 100755 --- a/composer.json +++ b/composer.json @@ -1,10 +1,11 @@ { "name": "efureev/support", - "description": "PHP Support Package", + "description": "PHP Support Package is a collection of useful functions and snippets", "license": "MIT", "type": "library", "keywords": [ "php", + "utility", "support", "helpers" ], @@ -20,7 +21,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^1.5", "squizlabs/php_codesniffer": "^3.6" }, "autoload": { diff --git a/src/Helpers/Str.php b/src/Helpers/Str.php index 8027707..e09f13a 100644 --- a/src/Helpers/Str.php +++ b/src/Helpers/Str.php @@ -321,6 +321,105 @@ public static function replaceByTemplate(string $str, array $replace) */ public static function isRegExp(string $regex): bool { - return empty($regex) ? false : @preg_match($regex, '') !== false; + return !empty($regex) && @preg_match($regex, '') !== false; + } + + /** + * Truncate a string to a specified length without cutting a word off + * + * @param string $str + * @param int $length + * @param string $append + * + * @return string + */ + public static function truncate(string $str, int $length, string $append = '...'): string + { + $ret = mb_substr($str, 0, $length); + $last_space = mb_strrpos($ret, ' '); + + if ($last_space !== false && $str !== $ret) { + $ret = mb_substr($ret, 0, $last_space); + } + + if ($ret !== $str) { + $ret .= $append; + } + + return $ret; + } + + + /** + * Generate a string safe for use in URLs from any given string. + * + * @param string $str + * @param string $separator + * @param bool $firstLetterOnly + * + * @return string + */ + public static function slugify(string $str, string $separator = '-', bool $firstLetterOnly = false): string + { + $slug = preg_replace('/([^a-z\d]+)/', $separator, mb_strtolower(self::removeAccents($str))); + if (empty($slug)) { + return ''; + } + + if ($firstLetterOnly) { + $digits = [ + 'zero', + 'one', + 'two', + 'three', + 'four', + 'five', + 'six', + 'seven', + 'eight', + 'nine', + ]; + + if (is_numeric(mb_substr($slug, 0, 1))) { + $slug = $digits[mb_substr($slug, 0, 1)] . mb_substr($slug, 1); + } + } + + return $slug; + } + + + /** + * Checks to see if a string is utf8 encoded. + * + * NOTE: This function checks for 5-Byte sequences, UTF8 + * has Bytes Sequences with a maximum length of 4. + * + * Written by Tony Ferrara + * + * @param string $string The string to be checked + * + * @return bool + */ + public static function seemsUTF8(string $string): bool + { + return URLify::seemsUTF8($string); + } + + /** + * Converts all accent characters to ASCII characters. + * + * @param string $str + * @param string $language + * + * @return string + */ + public static function removeAccents(string $str, string $language = ''): string + { + if (!preg_match('/[\x80-\xff]/', $str)) { + return $str; + } + + return URLify::downcode($str, $language); } } diff --git a/src/Helpers/URLify.php b/src/Helpers/URLify.php new file mode 100644 index 0000000..e4c2a9f --- /dev/null +++ b/src/Helpers/URLify.php @@ -0,0 +1,791 @@ + + * + * @see https://github.com/jbroadway/urlify/blob/master/URLify.php + */ + public static array $maps = [ + 'de' => [/* German */ + 'Ä' => 'Ae', + 'Ö' => 'Oe', + 'Ü' => 'Ue', + 'ä' => 'ae', + 'ö' => 'oe', + 'ü' => 'ue', + 'ß' => 'ss', + 'ẞ' => 'SS', + ], + 'latin' => [ + 'À' => 'A', + 'Á' => 'A', + 'Â' => 'A', + 'Ã' => 'A', + 'Ä' => 'A', + 'Å' => 'A', + 'Ă' => 'A', + 'Æ' => 'AE', + 'Ç' => + 'C', + 'È' => 'E', + 'É' => 'E', + 'Ê' => 'E', + 'Ë' => 'E', + 'Ì' => 'I', + 'Í' => 'I', + 'Î' => 'I', + 'Ï' => 'I', + 'Ð' => 'D', + 'Ñ' => 'N', + 'Ò' => 'O', + 'Ó' => 'O', + 'Ô' => 'O', + 'Õ' => 'O', + 'Ö' => + 'O', + 'Ő' => 'O', + 'Ø' => 'O', + 'Ș' => 'S', + 'Ț' => 'T', + 'Ù' => 'U', + 'Ú' => 'U', + 'Û' => 'U', + 'Ü' => 'U', + 'Ű' => 'U', + 'Ý' => 'Y', + 'Þ' => 'TH', + 'ß' => 'ss', + 'à' => 'a', + 'á' => 'a', + 'â' => 'a', + 'ã' => 'a', + 'ä' => + 'a', + 'å' => 'a', + 'ă' => 'a', + 'æ' => 'ae', + 'ç' => 'c', + 'è' => 'e', + 'é' => 'e', + 'ê' => 'e', + 'ë' => 'e', + 'ì' => 'i', + 'í' => 'i', + 'î' => 'i', + 'ï' => 'i', + 'ð' => 'd', + 'ñ' => 'n', + 'ò' => 'o', + 'ó' => + 'o', + 'ô' => 'o', + 'õ' => 'o', + 'ö' => 'o', + 'ő' => 'o', + 'ø' => 'o', + 'ș' => 's', + 'ț' => 't', + 'ù' => 'u', + 'ú' => 'u', + 'û' => 'u', + 'ü' => 'u', + 'ű' => 'u', + 'ý' => 'y', + 'þ' => 'th', + 'ÿ' => 'y', + ], + 'latin_symbols' => [ + '©' => '(c)', + '®' => '(r)', + ], + 'el' => [/* Greek */ + 'α' => 'a', + 'β' => 'b', + 'γ' => 'g', + 'δ' => 'd', + 'ε' => 'e', + 'ζ' => 'z', + 'η' => 'h', + 'θ' => '8', + 'ι' => 'i', + 'κ' => 'k', + 'λ' => 'l', + 'μ' => 'm', + 'ν' => 'n', + 'ξ' => '3', + 'ο' => 'o', + 'π' => 'p', + 'ρ' => 'r', + 'σ' => 's', + 'τ' => 't', + 'υ' => 'y', + 'φ' => 'f', + 'χ' => 'x', + 'ψ' => 'ps', + 'ω' => 'w', + 'ά' => 'a', + 'έ' => 'e', + 'ί' => 'i', + 'ό' => 'o', + 'ύ' => 'y', + 'ή' => 'h', + 'ώ' => 'w', + 'ς' => 's', + 'ϊ' => 'i', + 'ΰ' => 'y', + 'ϋ' => 'y', + 'ΐ' => 'i', + 'Α' => 'A', + 'Β' => 'B', + 'Γ' => 'G', + 'Δ' => 'D', + 'Ε' => 'E', + 'Ζ' => 'Z', + 'Η' => 'H', + 'Θ' => '8', + 'Ι' => 'I', + 'Κ' => 'K', + 'Λ' => 'L', + 'Μ' => 'M', + 'Ν' => 'N', + 'Ξ' => '3', + 'Ο' => 'O', + 'Π' => 'P', + 'Ρ' => 'R', + 'Σ' => 'S', + 'Τ' => 'T', + 'Υ' => 'Y', + 'Φ' => 'F', + 'Χ' => 'X', + 'Ψ' => 'PS', + 'Ω' => 'W', + 'Ά' => 'A', + 'Έ' => 'E', + 'Ί' => 'I', + 'Ό' => 'O', + 'Ύ' => 'Y', + 'Ή' => 'H', + 'Ώ' => 'W', + 'Ϊ' => 'I', + 'Ϋ' => 'Y', + ], + 'tr' => [/* Turkish */ + 'ş' => 's', + 'Ş' => 'S', + 'ı' => 'i', + 'İ' => 'I', + 'ç' => 'c', + 'Ç' => 'C', + 'ü' => 'u', + 'Ü' => 'U', + 'ö' => 'o', + 'Ö' => 'O', + 'ğ' => 'g', + 'Ğ' => 'G', + ], + 'ru' => [/* Russian */ + 'а' => 'a', + 'б' => 'b', + 'в' => 'v', + 'г' => 'g', + 'д' => 'd', + 'е' => 'e', + 'ё' => 'yo', + 'ж' => 'zh', + 'з' => 'z', + 'и' => 'i', + 'й' => 'j', + 'к' => 'k', + 'л' => 'l', + 'м' => 'm', + 'н' => 'n', + 'о' => 'o', + 'п' => 'p', + 'р' => 'r', + 'с' => 's', + 'т' => 't', + 'у' => 'u', + 'ф' => 'f', + 'х' => 'h', + 'ц' => 'c', + 'ч' => 'ch', + 'ш' => 'sh', + 'щ' => 'sh', + 'ъ' => '', + 'ы' => 'y', + 'ь' => '', + 'э' => 'e', + 'ю' => 'yu', + 'я' => 'ya', + 'А' => 'A', + 'Б' => 'B', + 'В' => 'V', + 'Г' => 'G', + 'Д' => 'D', + 'Е' => 'E', + 'Ё' => 'Yo', + 'Ж' => 'Zh', + 'З' => 'Z', + 'И' => 'I', + 'Й' => 'J', + 'К' => 'K', + 'Л' => 'L', + 'М' => 'M', + 'Н' => 'N', + 'О' => 'O', + 'П' => 'P', + 'Р' => 'R', + 'С' => 'S', + 'Т' => 'T', + 'У' => 'U', + 'Ф' => 'F', + 'Х' => 'H', + 'Ц' => 'C', + 'Ч' => 'Ch', + 'Ш' => 'Sh', + 'Щ' => 'Sh', + 'Ъ' => '', + 'Ы' => 'Y', + 'Ь' => '', + 'Э' => 'E', + 'Ю' => 'Yu', + 'Я' => 'Ya', + '№' => '', + ], + 'uk' => [/* Ukrainian */ + 'Є' => 'Ye', + 'І' => 'I', + 'Ї' => 'Yi', + 'Ґ' => 'G', + 'є' => 'ye', + 'і' => 'i', + 'ї' => 'yi', + 'ґ' => 'g', + ], + 'cs' => [/* Czech */ + 'č' => 'c', + 'ď' => 'd', + 'ě' => 'e', + 'ň' => 'n', + 'ř' => 'r', + 'š' => 's', + 'ť' => 't', + 'ů' => 'u', + 'ž' => 'z', + 'Č' => 'C', + 'Ď' => 'D', + 'Ě' => 'E', + 'Ň' => 'N', + 'Ř' => 'R', + 'Š' => 'S', + 'Ť' => 'T', + 'Ů' => 'U', + 'Ž' => 'Z', + ], + 'pl' => [/* Polish */ + 'ą' => 'a', + 'ć' => 'c', + 'ę' => 'e', + 'ł' => 'l', + 'ń' => 'n', + 'ó' => 'o', + 'ś' => 's', + 'ź' => 'z', + 'ż' => 'z', + 'Ą' => 'A', + 'Ć' => 'C', + 'Ę' => 'e', + 'Ł' => 'L', + 'Ń' => 'N', + 'Ó' => 'O', + 'Ś' => 'S', + 'Ź' => 'Z', + 'Ż' => 'Z', + ], + 'ro' => [/* Romanian */ + 'ă' => 'a', + 'â' => 'a', + 'î' => 'i', + 'ș' => 's', + 'ț' => 't', + 'Ţ' => 'T', + 'ţ' => 't', + ], + 'lv' => [/* Latvian */ + 'ā' => 'a', + 'č' => 'c', + 'ē' => 'e', + 'ģ' => 'g', + 'ī' => 'i', + 'ķ' => 'k', + 'ļ' => 'l', + 'ņ' => 'n', + 'š' => 's', + 'ū' => 'u', + 'ž' => 'z', + 'Ā' => 'A', + 'Č' => 'C', + 'Ē' => 'E', + 'Ģ' => 'G', + 'Ī' => 'i', + 'Ķ' => 'k', + 'Ļ' => 'L', + 'Ņ' => 'N', + 'Š' => 'S', + 'Ū' => 'u', + 'Ž' => 'Z', + ], + 'lt' => [/* Lithuanian */ + 'ą' => 'a', + 'č' => 'c', + 'ę' => 'e', + 'ė' => 'e', + 'į' => 'i', + 'š' => 's', + 'ų' => 'u', + 'ū' => 'u', + 'ž' => 'z', + 'Ą' => 'A', + 'Č' => 'C', + 'Ę' => 'E', + 'Ė' => 'E', + 'Į' => 'I', + 'Š' => 'S', + 'Ų' => 'U', + 'Ū' => 'U', + 'Ž' => 'Z', + ], + 'vn' => [/* Vietnamese */ + 'Á' => 'A', + 'À' => 'A', + 'Ả' => 'A', + 'Ã' => 'A', + 'Ạ' => 'A', + 'Ă' => 'A', + 'Ắ' => 'A', + 'Ằ' => 'A', + 'Ẳ' => 'A', + 'Ẵ' => 'A', + 'Ặ' => 'A', + 'Â' => 'A', + 'Ấ' => 'A', + 'Ầ' => 'A', + 'Ẩ' => 'A', + 'Ẫ' => 'A', + 'Ậ' => 'A', + 'á' => 'a', + 'à' => 'a', + 'ả' => 'a', + 'ã' => 'a', + 'ạ' => 'a', + 'ă' => 'a', + 'ắ' => 'a', + 'ằ' => 'a', + 'ẳ' => 'a', + 'ẵ' => 'a', + 'ặ' => 'a', + 'â' => 'a', + 'ấ' => 'a', + 'ầ' => 'a', + 'ẩ' => 'a', + 'ẫ' => 'a', + 'ậ' => 'a', + 'É' => 'E', + 'È' => 'E', + 'Ẻ' => 'E', + 'Ẽ' => 'E', + 'Ẹ' => 'E', + 'Ê' => 'E', + 'Ế' => 'E', + 'Ề' => 'E', + 'Ể' => 'E', + 'Ễ' => 'E', + 'Ệ' => 'E', + 'é' => 'e', + 'è' => 'e', + 'ẻ' => 'e', + 'ẽ' => 'e', + 'ẹ' => 'e', + 'ê' => 'e', + 'ế' => 'e', + 'ề' => 'e', + 'ể' => 'e', + 'ễ' => 'e', + 'ệ' => 'e', + 'Í' => 'I', + 'Ì' => 'I', + 'Ỉ' => 'I', + 'Ĩ' => 'I', + 'Ị' => 'I', + 'í' => 'i', + 'ì' => 'i', + 'ỉ' => 'i', + 'ĩ' => 'i', + 'ị' => 'i', + 'Ó' => 'O', + 'Ò' => 'O', + 'Ỏ' => 'O', + 'Õ' => 'O', + 'Ọ' => 'O', + 'Ô' => 'O', + 'Ố' => 'O', + 'Ồ' => 'O', + 'Ổ' => 'O', + 'Ỗ' => 'O', + 'Ộ' => 'O', + 'Ơ' => 'O', + 'Ớ' => 'O', + 'Ờ' => 'O', + 'Ở' => 'O', + 'Ỡ' => 'O', + 'Ợ' => 'O', + 'ó' => 'o', + 'ò' => 'o', + 'ỏ' => 'o', + 'õ' => 'o', + 'ọ' => 'o', + 'ô' => 'o', + 'ố' => 'o', + 'ồ' => 'o', + 'ổ' => 'o', + 'ỗ' => 'o', + 'ộ' => 'o', + 'ơ' => 'o', + 'ớ' => 'o', + 'ờ' => 'o', + 'ở' => 'o', + 'ỡ' => 'o', + 'ợ' => 'o', + 'Ú' => 'U', + 'Ù' => 'U', + 'Ủ' => 'U', + 'Ũ' => 'U', + 'Ụ' => 'U', + 'Ư' => 'U', + 'Ứ' => 'U', + 'Ừ' => 'U', + 'Ử' => 'U', + 'Ữ' => 'U', + 'Ự' => 'U', + 'ú' => 'u', + 'ù' => 'u', + 'ủ' => 'u', + 'ũ' => 'u', + 'ụ' => 'u', + 'ư' => 'u', + 'ứ' => 'u', + 'ừ' => 'u', + 'ử' => 'u', + 'ữ' => 'u', + 'ự' => 'u', + 'Ý' => 'Y', + 'Ỳ' => 'Y', + 'Ỷ' => 'Y', + 'Ỹ' => 'Y', + 'Ỵ' => 'Y', + 'ý' => 'y', + 'ỳ' => 'y', + 'ỷ' => 'y', + 'ỹ' => 'y', + 'ỵ' => 'y', + 'Đ' => 'D', + 'đ' => 'd', + ], + 'ar' => [/* Arabic */ + 'أ' => 'a', + 'ب' => 'b', + 'ت' => 't', + 'ث' => 'th', + 'ج' => 'g', + 'ح' => 'h', + 'خ' => 'kh', + 'د' => 'd', + 'ذ' => 'th', + 'ر' => 'r', + 'ز' => 'z', + 'س' => 's', + 'ش' => 'sh', + 'ص' => 's', + 'ض' => 'd', + 'ط' => 't', + 'ظ' => 'th', + 'ع' => 'aa', + 'غ' => 'gh', + 'ف' => 'f', + 'ق' => 'k', + 'ك' => 'k', + 'ل' => 'l', + 'م' => 'm', + 'ن' => 'n', + 'ه' => 'h', + 'و' => 'o', + 'ي' => 'y', + ], + 'sr' => [/* Serbian */ + 'ђ' => 'dj', + 'ј' => 'j', + 'љ' => 'lj', + 'њ' => 'nj', + 'ћ' => 'c', + 'џ' => 'dz', + 'đ' => 'dj', + 'Ђ' => 'Dj', + 'Ј' => 'j', + 'Љ' => 'Lj', + 'Њ' => 'Nj', + 'Ћ' => 'C', + 'Џ' => 'Dz', + 'Đ' => 'Dj', + ], + 'az' => [/* Azerbaijani */ + 'ç' => 'c', + 'ə' => 'e', + 'ğ' => 'g', + 'ı' => 'i', + 'ö' => 'o', + 'ş' => 's', + 'ü' => 'u', + 'Ç' => 'C', + 'Ə' => 'E', + 'Ğ' => 'G', + 'İ' => 'I', + 'Ö' => 'O', + 'Ş' => 'S', + 'Ü' => 'U', + ], + 'fi' => [/* Finnish */ + 'ä' => 'a', + 'ö' => 'o', + ], + 'fa' => [ /* Farsi */ + 'آ' => 'aa', + 'ا' => 'a', + 'ب' => 'b', + 'پ' => 'p', + 'ت' => 't', + 'ث' => 'th', + 'ج' => 'j', + 'چ' => 'ch', + 'ح' => 'h', + 'خ' => 'kh', + 'د' => 'd', + 'ذ' => 'z', + 'ر' => 'r', + 'ز' => 'z', + 'ژ' => 'zh', + 'س' => 's', + 'ش' => 'sh', + 'ص' => 's', + 'ض' => 'z', + 'ط' => 't', + 'ظ' => 'th', + 'ع' => 'aa', + 'غ' => 'gh', + 'ف' => 'f', + 'ق' => 'gh', + 'ك' => 'k', + 'گ' => 'g', + 'ل' => 'l', + 'م' => 'm', + 'ن' => 'n', + 'ه' => 'h', + 'و' => 'o', + 'ي' => 'y', + 'ی' => 'y', + 'ِ' => 'e', + 'ُ' => 'o', + 'َ' => 'a', + ], + ]; + + + /** + * Transliterates characters to their ASCII equivalents. + * + * Part of the URLify.php Project + * + * @see https://github.com/jbroadway/urlify/blob/master/URLify.php + * + * @param string $text Text that might have not-ASCII characters + * @param string $language Specifies a priority for a specific language. + * + * @return string Filtered string with replaced "nice" characters + */ + public static function downcode(string $text, string $language = ''): string + { + self::initLanguageMap($language); + + if (self::seemsUTF8($text)) { + if (preg_match_all(self::$regex, $text, $matches)) { + for ($i = 0, $iMax = count($matches[0]); $i < $iMax; $i++) { + $char = $matches[0][$i]; + if (isset(self::$map[$char])) { + $text = str_replace($char, self::$map[$char], $text); + } + } + } + } else { + // Not a UTF-8 string so we assume its ISO-8859-1 + $search = "\x80\x83\x8a\x8e\x9a\x9e\x9f\xa2\xa5\xb5\xc0\xc1\xc2\xc3\xc4\xc5\xc7\xc8\xc9\xca\xcb\xcc\xcd"; + $search .= "\xce\xcf\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xe0\xe1\xe2\xe3\xe4\xe5\xe7\xe8\xe9"; + $search .= "\xea\xeb\xec\xed\xee\xef\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xff"; + $text = strtr($text, $search, 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'); + + // These latin characters should be represented by two characters so + // we can't use strtr + $complexSearch = [ + "\x8c", + "\x9c", + "\xc6", + "\xd0", + "\xde", + "\xdf", + "\xe6", + "\xf0", + "\xfe", + ]; + $complexReplace = [ + 'OE', + 'oe', + 'AE', + 'DH', + 'TH', + 'ss', + 'ae', + 'dh', + 'th', + ]; + $text = str_replace($complexSearch, $complexReplace, $text); + } + + return $text; + } + + + /** + * Checks to see if a string is utf8 encoded. + * + * NOTE: This function checks for 5-Byte sequences, UTF8 + * has Bytes Sequences with a maximum length of 4. + * + * Written by Tony Ferrara + * + * @param string $string The string to be checked + * + * @return bool + */ + public static function seemsUTF8(string $string): bool + { + if (function_exists('mb_check_encoding')) { + // If mb-string is available, this is significantly faster than using PHP regexps. + return mb_check_encoding($string, 'UTF-8'); + } + + // @codeCoverageIgnoreStart + return self::seemsUTF8Regex($string); + // @codeCoverageIgnoreEnd + } + + /** + * A non-Mb-string UTF-8 checker. + * + * @param string $string + * + * @return bool + */ + protected static function seemsUTF8Regex(string $string): bool + { + // Obtained from http://stackoverflow.com/a/11709412/430062 with permission. + $regex = '/( + [\xC0-\xC1] # Invalid UTF-8 Bytes + | [\xF5-\xFF] # Invalid UTF-8 Bytes + | \xE0[\x80-\x9F] # Overlong encoding of prior code point + | \xF0[\x80-\x8F] # Overlong encoding of prior code point + | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start + | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start + | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start + | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle + | (? + * + * @see https://github.com/jbroadway/urlify/blob/master/URLify.php + */ + private static function initLanguageMap(string $language = ''): void + { + if (count(self::$map) > 0 && (($language === '') || ($language === self::$language))) { + return; + } + + // Is a specific map associated with $language? + if (isset(self::$maps[$language]) && is_array(self::$maps[$language])) { + // Move this map to end. This means it will have priority over others + $map = self::$maps[$language]; + unset(self::$maps[$language]); + self::$maps[$language] = $map; + } + + // Reset static vars + self::$language = $language; + self::$map = []; + self::$chars = ''; + + foreach (self::$maps as $map) { + foreach ($map as $orig => $conv) { + self::$map[$orig] = $conv; + self::$chars .= $orig; + } + } + + self::$regex = '/[' . self::$chars . ']/u'; + } +} diff --git a/tests/helpers/ArrTest.php b/tests/Helpers/ArrTest.php similarity index 99% rename from tests/helpers/ArrTest.php rename to tests/Helpers/ArrTest.php index a9cdc96..a65a3b2 100644 --- a/tests/helpers/ArrTest.php +++ b/tests/Helpers/ArrTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Helpers; use ArrayObject; use Php\Support\Helpers\Arr; diff --git a/tests/helpers/B64Test.php b/tests/Helpers/B64Test.php similarity index 98% rename from tests/helpers/B64Test.php rename to tests/Helpers/B64Test.php index 21ac32d..0db5afe 100644 --- a/tests/helpers/B64Test.php +++ b/tests/Helpers/B64Test.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Helpers; use Php\Support\Helpers\B64; use PHPUnit\Framework\TestCase; diff --git a/tests/helpers/BitTest.php b/tests/Helpers/BitTest.php similarity index 99% rename from tests/helpers/BitTest.php rename to tests/Helpers/BitTest.php index fd5fad7..32151a7 100644 --- a/tests/helpers/BitTest.php +++ b/tests/Helpers/BitTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Helpers; use Php\Support\Helpers\Bit; use PHPUnit\Framework\TestCase; diff --git a/tests/Helpers/HasReflection.php b/tests/Helpers/HasReflection.php new file mode 100644 index 0000000..3d686a4 --- /dev/null +++ b/tests/Helpers/HasReflection.php @@ -0,0 +1,19 @@ +getMethod($name); + $method->setAccessible(true); + return $method; + } +} diff --git a/tests/helpers/JsonTest.php b/tests/Helpers/JsonTest.php similarity index 99% rename from tests/helpers/JsonTest.php rename to tests/Helpers/JsonTest.php index 76a4456..2efc21a 100644 --- a/tests/helpers/JsonTest.php +++ b/tests/Helpers/JsonTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Helpers; use JsonException; use Php\Support\Helpers\Json; diff --git a/tests/helpers/NumberTest.php b/tests/Helpers/NumberTest.php similarity index 96% rename from tests/helpers/NumberTest.php rename to tests/Helpers/NumberTest.php index 4686108..9fe1909 100644 --- a/tests/helpers/NumberTest.php +++ b/tests/Helpers/NumberTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Helpers; use Php\Support\Helpers\Number; use PHPUnit\Framework\TestCase; diff --git a/tests/helpers/StrTest.php b/tests/Helpers/StrTest.php similarity index 76% rename from tests/helpers/StrTest.php rename to tests/Helpers/StrTest.php index 3bee5ca..5963c02 100644 --- a/tests/helpers/StrTest.php +++ b/tests/Helpers/StrTest.php @@ -2,9 +2,10 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Helpers; use Php\Support\Helpers\Str; +use Php\Support\Helpers\URLify; use PHPUnit\Framework\TestCase; /** @@ -12,6 +13,7 @@ */ final class StrTest extends TestCase { + use HasReflection; public function providerDataSnake(): array { @@ -668,6 +670,107 @@ public function dataRegExps(): array */ public function testIsRegExp(string $regexp, bool $result): void { - static::assertEquals($result, Str::isRegExp($regexp)); + self::assertEquals($result, Str::isRegExp($regexp)); } + + + /** + * @test + */ + public function truncate(): void + { + self::assertEquals( + 'The quick brown fox...', + Str::truncate('The quick brown fox jumps over the lazy dog', 24) + ); + self::assertEquals( + 'The quick brown fox>', + Str::truncate('The quick brown fox jumps over the lazy dog', 24, '>') + ); + self::assertEquals( + 'The quick brown fox jumps over the lazy dog', + Str::truncate('The quick brown fox jumps over the lazy dog', 55) + ); + self::assertEquals('Th...', Str::truncate('The quick brown fox jumps over the lazy dog', 2)); + self::assertEquals('The...', Str::truncate('The quick brown fox jumps over the lazy dog', 3)); + self::assertEquals('The...', Str::truncate('The quick brown fox jumps over the lazy dog', 7)); + } + + /** + * @test + */ + public function seemsUTF8(): void + { + // Test a valid UTF-8 sequence: "ÜTF-8 Fµñ". + $validUTF8 = "\xC3\x9CTF-8 F\xC2\xB5\xC3\xB1"; + self::assertTrue(Str::seemsUTF8($validUTF8)); + + self::assertTrue( + Str::seemsUTF8("\xEF\xBF\xBD this has \xEF\xBF\xBD\xEF\xBF\xBD some invalid UTF-8 \xEF\xBF\xBD") + ); + + // Test invalid UTF-8 sequences + $invalidUTF8 = "\xc3 this has \xe6\x9d some invalid UTF-8 \xe6"; + self::assertFalse(Str::seemsUTF8($invalidUTF8)); + + // And test some plain ASCII + self::assertTrue(Str::seemsUTF8('The quick brown fox jumps over the lazy dog')); + + // Test an invalid non-UTF-8 string. + if (function_exists('mb_convert_encoding')) { + mb_internal_encoding('UTF-8'); + // Converts the 'ç' UTF-8 character to UCS-2LE + $utf8Char = pack('n', 50087); + $ucsChar = mb_convert_encoding($utf8Char, 'UCS-2LE', 'UTF-8'); + + self::assertEquals( + $utf8Char, + 'ç', + 'This PHP system\'s internal character set is not properly set as UTF-8.' + ); + self::assertEquals($utf8Char, pack('n', 50087), 'Something is wrong with your ICU unicode library.'); + + // Test for not UTF-8. + self::assertFalse(Str::seemsUTF8($ucsChar)); + + // Test the worker method. + $method = self::setMethodAccessible(URLify::class, 'seemsUTF8Regex'); + self::assertFalse( + $method->invoke(null, $invalidUTF8), + self::class . '::seemsUTF8Regex did not properly detect invalid UTF-8.' + ); + self::assertTrue( + $method->invoke(null, $validUTF8), + self::class . '::seemsUTF8Regex did not properly detect valid UTF-8.' + ); + } + } + + /** + * @test + */ + public function slugify(): void + { + $this->assertEquals('a-simple-title', Str::slugify('A simple title')); + $this->assertEquals('this-post-it-has-a-dash', Str::slugify('This post -- it has a dash')); + $this->assertEquals('123-1251251', Str::slugify('123----1251251')); + $this->assertEquals('one23-1251251', Str::slugify('123----1251251', '-', true)); + + $this->assertEquals('a-simple-title', Str::slugify('A simple title', '-')); + $this->assertEquals('this-post-it-has-a-dash', Str::slugify('This post -- it has a dash', '-')); + $this->assertEquals('123-1251251', Str::slugify('123----1251251', '-')); + $this->assertEquals('one23-1251251', Str::slugify('123----1251251', '-', true)); + + $this->assertEquals('a_simple_title', Str::slugify('A simple title', '_')); + $this->assertEquals('this_post_it_has_a_dash', Str::slugify('This post -- it has a dash', '_')); + $this->assertEquals('123_1251251', Str::slugify('123----1251251', '_')); + $this->assertEquals('one23_1251251', Str::slugify('123----1251251', '_', true)); + + // Blank separator test + $this->assertEquals('asimpletitle', Str::slugify('A simple title', '')); + $this->assertEquals('thispostithasadash', Str::slugify('This post -- it has a dash', '')); + $this->assertEquals('1231251251', Str::slugify('123----1251251', '')); + $this->assertEquals('one231251251', Str::slugify('123----1251251', '', true)); + } + } From 691fddb04b5d22ba5c383d55961a5a25c6d42199 Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 16 Apr 2022 23:29:47 +0500 Subject: [PATCH 33/95] feat: add new methods: truncate, slugify, seemsUTF8, removeAccents --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 748f82b..b69f946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added -- Add method `Str::truncate`: truncate a string to a specified length without cutting a word off. +- Add method `Str::truncate`: truncate a string to a specified length without cutting a word off - Add method `Str::slugify`: generate a string safe for use in URLs from any given string - Add method `Str::seemsUTF8`: checks to see if a string is utf8 encoded - Add method `Str::removeAccents`: converts all accent characters to ASCII characters From e95a4299193163cab8f792ed945642feca00aae0 Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 16 Apr 2022 23:36:22 +0500 Subject: [PATCH 34/95] fix --- CHANGELOG.md | 2 +- readme.md | 181 +++++++++++++++++++++++++++------------------------ 2 files changed, 97 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b69f946..e54814a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,7 +68,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added -- Add new Helper Class: `Nubmer` +- Add new Helper Class: `Number` - Add method, working with integers: `Number::safeInt` ## v4.0.0 diff --git a/readme.md b/readme.md index b0386df..a2a2ca9 100644 --- a/readme.md +++ b/readme.md @@ -33,96 +33,107 @@ composer require efureev/support "^2.0" ## Content - Helpers - + Array - - accessible - - dataToArray - - exists - - fromPostgresArray - - fromPostgresPoint - - get - - has - - merge - - remove - - removeByValue - - set - - toArray - - toIndexedArray - - toPostgresArray - - toPostgresPoint - - replaceByTemplate - + String - - removeMultiSpace - - replaceByTemplate - - replaceStrTo - - toCamel - - toDelimited - - toKebab - - toLowerCamel - - toScreamingDelimited - - toScreamingSnake - - toSnake - + Json - - decode - - encode - - htmlEncode - + Bit - - addFlag - - checkFlag - - decBinPad - - exist - - grant - - removeFlag - + B64 - - decode - - decodeSafe - - encode - - encodeSafe + + Array + - accessible + - dataToArray + - exists + - fromPostgresArray + - fromPostgresPoint (^4.8.0) + - get + - has + - merge + - remove + - removeByValue + - set + - toArray + - toIndexedArray + - toPostgresArray + - toPostgresPoint (^4.8.0) + - replaceByTemplate + + String + - removeAccents (^4.9.0) + - removeMultiSpace + - replaceByTemplate + - replaceStrTo + - seemsUTF8 (^4.9.0) + - slugify (^4.9.0) + - toCamel + - toDelimited + - toKebab + - toLowerCamel + - toScreamingDelimited + - toScreamingSnake + - toSnake + - truncate (^4.9.0) + + Json + - decode + - encode + - htmlEncode + + Bit + - addFlag + - checkFlag + - decBinPad + - exist + - grant + - removeFlag + + B64 + - decode + - decodeSafe + - encode + - encodeSafe + + Number + - safeInt (^4.1.0) - Global functions - + classNamespace - + class_basename - + class_uses_recursive - + instance - + isTrue - + trait_uses_recursive - + value - + when + + does_trait_use (^4.4.0) + + classNamespace + + class_basename + + class_uses_recursive + + instance + + isTrue + + trait_uses_recursive + + value + + remoteCall (^4.3.1) + + remoteStaticCall (^4.3.1) + + remoteStaticCallOrTrow (^4.7.0) + + when - Exceptions - + ConfigException - + Exception - + InvalidArgumentException - + InvalidCallException - + InvalidConfigException - + InvalidParamException - + InvalidValueException - + JsonException - + MethodNotAllowedException - + MissingClassException - + MissingConfigException - + MissingPropertyException - + NotSupportedException - + UnknownMethodException - + UnknownPropertyException + + ConfigException + + Exception + + InvalidArgumentException + + InvalidCallException + + InvalidConfigException + + InvalidParamException + + InvalidValueException + + JsonException + + MethodNotAllowedException + + MissingClassException + + MissingConfigException + + MissingPropertyException + + MissingMethodException (^4.7.0) + + NotSupportedException + + UnknownMethodException + + UnknownPropertyException - Interfaces - + Arrayable - + Command - + Jsonable - + Prototype + + Arrayable + + Command + + Jsonable + + Prototype - Traits - + ArrayStorage - + ArrayStorageConfigurableTrait - + ConfigurableTrait - + ConsolePrint - + Maker - + Metable - + ReadOnlyProperties - + Singleton - + Thrower - + TraitBooter - + TraitInitializer - + Whener + + ArrayStorage + + ArrayStorageConfigurableTrait + + ConfigurableTrait + + ConsolePrint + + Maker + + Metable + + ReadOnlyProperties + + Singleton + + Thrower + + TraitBooter + + TraitInitializer + + Whener - Types - + GeoPoint - + Point + + GeoPoint + + Point ## Test From 5fe717dbeadec9c91ca8b4b75ce3039c9d0f274a Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 23 Jun 2022 16:46:25 +0500 Subject: [PATCH 35/95] feat: change `HasPrePostActions::getCallbackActions` --- src/Traits/HasPrePostActions.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Traits/HasPrePostActions.php b/src/Traits/HasPrePostActions.php index 0053b10..6241b7b 100644 --- a/src/Traits/HasPrePostActions.php +++ b/src/Traits/HasPrePostActions.php @@ -16,9 +16,11 @@ public function addCallbackAction(string $key, callable $action): self return $this; } - public function getCallbackActions(string $key): array + public function getCallbackActions(string $key = null): array { - return (array)($this->executeCallbacks[$key] ?? []); + return $key ? + $this->executeCallbacks[$key] ?? [] + : $this->executeCallbacks; } protected function runActions(string $actionGroup, ...$arguments): bool From 4569d0bc495f6bf9dd33af540bc38b266db7a157 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 13 Jul 2022 16:37:51 +0500 Subject: [PATCH 36/95] fix: global function `value` --- src/Global/base.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Global/base.php b/src/Global/base.php index 52fd806..c177b31 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -4,14 +4,15 @@ /** * Return the default value of the given value. * - * @param mixed $value + * @param $value + * @param ...$args * * @return mixed */ - function value($value) + function value($value, ...$args) { return $value instanceof Closure || (is_object($value) && is_callable($value)) - ? $value() + ? $value(...$args) : $value; } } From 0570243cbe6c9bca1e59c17419caddd519f28bb0 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 13 Jul 2022 16:38:52 +0500 Subject: [PATCH 37/95] fix: global function `value` --- src/Global/base.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Global/base.php b/src/Global/base.php index c177b31..f4badb0 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -4,12 +4,12 @@ /** * Return the default value of the given value. * - * @param $value - * @param ...$args + * @param mixed $value + * @param mixed ...$args * * @return mixed */ - function value($value, ...$args) + function value(mixed $value, mixed ...$args): mixed { return $value instanceof Closure || (is_object($value) && is_callable($value)) ? $value(...$args) From ad00aa4c801a40488a90f9ed6ce37b41404c76c6 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 13 Jul 2022 17:28:00 +0500 Subject: [PATCH 38/95] feat: add new section `Testings` with helpers --- composer.json | 4 +- src/Testing/AdditionalAssertionsTrait.php | 85 +++++++++++++++++++++++ src/Testing/TestingHelper.php | 32 +++++++++ 3 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/Testing/AdditionalAssertionsTrait.php create mode 100755 src/Testing/TestingHelper.php diff --git a/composer.json b/composer.json index 6ed3bfd..330edcb 100755 --- a/composer.json +++ b/composer.json @@ -21,8 +21,8 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "phpstan/phpstan": "^1.5", - "squizlabs/php_codesniffer": "^3.6" + "phpstan/phpstan": "^1.8", + "squizlabs/php_codesniffer": "^3.7" }, "autoload": { "files": [ diff --git a/src/Testing/AdditionalAssertionsTrait.php b/src/Testing/AdditionalAssertionsTrait.php new file mode 100644 index 0000000..916deb7 --- /dev/null +++ b/src/Testing/AdditionalAssertionsTrait.php @@ -0,0 +1,85 @@ + $class] as $class_iterate) { + $results[] = $trait_uses_recursive($class_iterate); + } + return array_values(array_merge(...$results)); + }; + + $uses = $class_uses_recursive($class); + $uses = array_flip($uses); + + foreach ((array)$expected_traits as $k => $trait_class) { + static::assertArrayHasKey( + $trait_class, + $uses, + $message === '' + ? 'Class does not uses passed traits' + : $message + ); + } + } +} diff --git a/src/Testing/TestingHelper.php b/src/Testing/TestingHelper.php new file mode 100755 index 0000000..7251532 --- /dev/null +++ b/src/Testing/TestingHelper.php @@ -0,0 +1,32 @@ +runProtectedMethod($sp, "registerService", T::class); + */ + public function runProtectedMethod(string|object $class, string $method, ...$params): mixed + { + $class = instance($class); + + $methodReflex = new \ReflectionMethod($class, $method); + $methodReflex->setAccessible(true); + return $methodReflex->invoke($class, ...$params); + } +} From 6d7611f5b427bb8ca269d533e107797d123e4406 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 13 Jul 2022 17:40:46 +0500 Subject: [PATCH 39/95] feat: add Testing::getProperty --- src/Testing/TestingHelper.php | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Testing/TestingHelper.php b/src/Testing/TestingHelper.php index 7251532..61ec430 100755 --- a/src/Testing/TestingHelper.php +++ b/src/Testing/TestingHelper.php @@ -4,7 +4,7 @@ namespace Php\Support\Testing; -use function instance; +use ReflectionClass; trait TestingHelper { @@ -21,12 +21,30 @@ trait TestingHelper * @example * $result = $this->runProtectedMethod($sp, "registerService", T::class); */ - public function runProtectedMethod(string|object $class, string $method, ...$params): mixed + protected function runProtectedMethod(string|object $class, string $method, ...$params): mixed { - $class = instance($class); - $methodReflex = new \ReflectionMethod($class, $method); $methodReflex->setAccessible(true); return $methodReflex->invoke($class, ...$params); } + + /** + * Get a instance property (public/private/protected) value. + * + * @param object|string $object + * @param string $property_name + * + * @return mixed + * @throws \ReflectionException + * + */ + protected function getProperty(object|string $object, string $propertyName): mixed + { + $reflection = new ReflectionClass($object); + + $property = $reflection->getProperty($propertyName); + $property->setAccessible(true); + + return $property->getValue($object); + } } From c065511d703ee3697a12b00cde9037d26a443b1b Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 14 Jul 2022 10:05:10 +0500 Subject: [PATCH 40/95] fix: make methods `getProperty`, `runProtectedMethod` - static --- src/Testing/TestingHelper.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Testing/TestingHelper.php b/src/Testing/TestingHelper.php index 61ec430..eb94311 100755 --- a/src/Testing/TestingHelper.php +++ b/src/Testing/TestingHelper.php @@ -19,9 +19,9 @@ trait TestingHelper * @throws \ReflectionException * * @example - * $result = $this->runProtectedMethod($sp, "registerService", T::class); + * $result = static::runProtectedMethod($sp, "registerService", T::class); */ - protected function runProtectedMethod(string|object $class, string $method, ...$params): mixed + protected static function runProtectedMethod(string|object $class, string $method, ...$params): mixed { $methodReflex = new \ReflectionMethod($class, $method); $methodReflex->setAccessible(true); @@ -32,13 +32,13 @@ protected function runProtectedMethod(string|object $class, string $method, ...$ * Get a instance property (public/private/protected) value. * * @param object|string $object - * @param string $property_name + * @param string $propertyName * * @return mixed * @throws \ReflectionException * */ - protected function getProperty(object|string $object, string $propertyName): mixed + protected static function getProperty(object|string $object, string $propertyName): mixed { $reflection = new ReflectionClass($object); From 767763f699ba405511f2a2ac4b740a18424ab2dd Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 17 Aug 2022 12:31:06 +0500 Subject: [PATCH 41/95] bump php version up to 8.1 --- .github/workflows/php.yml | 22 +-- composer.json | 2 +- src/Traits/ArrayStorage.php | 2 +- tests/Global/BaseTest.php | 264 ++++++++++++++++++++------ tests/Helpers/ArrTest.php | 22 ++- tests/Helpers/B64Test.php | 5 +- tests/Helpers/JsonTest.php | 10 +- tests/Helpers/NumberTest.php | 67 +++++-- tests/Helpers/StrTest.php | 43 ++++- tests/exceptions/InvalidParamTest.php | 1 - tests/traits/TraitBooterTest.php | 52 +++-- 11 files changed, 352 insertions(+), 138 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 2b78e91..0e6d3cf 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -3,15 +3,15 @@ name: PHP Package on: [ push ] jobs: -# phpcs: -# name: PHPCS -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v2 -# - name: PHPCS check -# uses: chekalsky/phpcs-action@v1 -# with: -# enable_warnings: true + # phpcs: + # name: PHPCS + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - name: PHPCS check + # uses: chekalsky/phpcs-action@v1 + # with: + # enable_warnings: true lint-changelog: name: Lint changelog file @@ -31,13 +31,13 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 - needs: [lint-changelog] + needs: [ lint-changelog ] strategy: fail-fast: false matrix: setup: [ 'basic', 'lowest', 'stable' ] - php: [ '8.0' ] + php: [ '8.0','8.1' ] steps: - uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index 330edcb..9744473 100755 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^8.0", + "php": "^8.0|^8.1", "ext-mbstring": "*" }, "require-dev": { diff --git a/src/Traits/ArrayStorage.php b/src/Traits/ArrayStorage.php index 8906137..6a4e37a 100644 --- a/src/Traits/ArrayStorage.php +++ b/src/Traits/ArrayStorage.php @@ -144,7 +144,7 @@ public function valueExists(string $name): bool * * @return mixed */ - public function offsetGet($key) + public function offsetGet($key): mixed { return $this->get($key); } diff --git a/tests/Global/BaseTest.php b/tests/Global/BaseTest.php index 9a6b6c8..0915b58 100644 --- a/tests/Global/BaseTest.php +++ b/tests/Global/BaseTest.php @@ -36,7 +36,7 @@ private static function values(): array 'empty' => '', 'emptyArray' => [], 'cls' => new class { - function __invoke() + public function __invoke() { return 'cls.test'; } @@ -64,45 +64,196 @@ public function testIsTrue(): void { foreach ( [ - ['val' => new \stdClass, 'res' => true, 'resNull' => true,], - ['val' => [1, 2], 'res' => true, 'resNull' => true], - ['val' => [1], 'res' => true, 'resNull' => true], - ['val' => [0], 'res' => true, 'resNull' => true], - ['val' => 1, 'res' => true, 'resNull' => true], - ['val' => 42, 'res' => true, 'resNull' => true], - ['val' => -42, 'res' => true, 'resNull' => true], - ['val' => 'true', 'res' => true, 'resNull' => true], - ['val' => '1', 'res' => true, 'resNull' => true], - ['val' => 'on', 'res' => true, 'resNull' => true], - ['val' => 'On', 'res' => true, 'resNull' => true], - ['val' => 'ON', 'res' => true, 'resNull' => true], - ['val' => 'yes', 'res' => true, 'resNull' => true], - ['val' => 'YES', 'res' => true, 'resNull' => true], - ['val' => 'TRUE', 'res' => true, 'resNull' => true], - - - ['val' => 'off', 'res' => false, 'resNull' => false], - ['val' => 'Off', 'res' => false, 'resNull' => false], - ['val' => 'OFF', 'res' => false, 'resNull' => false], - ['val' => 'no', 'res' => false, 'resNull' => false], - ['val' => 'ja', 'res' => false, 'resNull' => false], - ['val' => 'nein', 'res' => false, 'resNull' => false], - ['val' => 'нет', 'res' => false, 'resNull' => false], - ['val' => 'да', 'res' => false, 'resNull' => false], - ['val' => null, 'res' => false, 'resNull' => null], - ['val' => 0, 'res' => false, 'resNull' => false], - ['val' => 'false', 'res' => false, 'resNull' => false], - ['val' => 'FALSE', 'res' => false, 'resNull' => false], - ['val' => 'string', 'res' => false, 'resNull' => false], - ['val' => 'bool', 'res' => false, 'resNull' => false], - ['val' => '0.0', 'res' => false, 'resNull' => false], - ['val' => '4.2', 'res' => false, 'resNull' => false], - ['val' => '0', 'res' => false, 'resNull' => false], - ['val' => '', 'res' => false, 'resNull' => false], - ['val' => '[]', 'res' => false, 'resNull' => false], - ['val' => '{}', 'res' => false, 'resNull' => false], - ['val' => 'false', 'res' => false, 'resNull' => false], - ['val' => 'bar', 'res' => false, 'resNull' => false], + [ + 'val' => new \stdClass(), + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => [ + 1, + 2, + ], + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => [1], + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => [0], + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 1, + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 42, + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => -42, + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 'true', + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => '1', + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 'on', + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 'On', + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 'ON', + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 'yes', + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 'YES', + 'res' => true, + 'resNull' => true, + ], + [ + 'val' => 'TRUE', + 'res' => true, + 'resNull' => true, + ], + + + [ + 'val' => 'off', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'Off', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'OFF', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'no', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'ja', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'nein', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'нет', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'да', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => null, + 'res' => false, + 'resNull' => null, + ], + [ + 'val' => 0, + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'false', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'FALSE', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'string', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'bool', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => '0.0', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => '4.2', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => '0', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => '', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => '[]', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => '{}', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'false', + 'res' => false, + 'resNull' => false, + ], + [ + 'val' => 'bar', + 'res' => false, + 'resNull' => false, + ], ] as $data ) { @@ -157,10 +308,14 @@ public function testTraitUsesRecursive(): void static::assertEquals( [ - \Php\Support\Traits\Singleton::class => \Php\Support\Traits\Singleton::class, - \Php\Support\Traits\ArrayStorageConfigurableTrait::class => \Php\Support\Traits\ArrayStorageConfigurableTrait::class, - \Php\Support\Traits\ArrayStorage::class => \Php\Support\Traits\ArrayStorage::class, - \Php\Support\Traits\ConfigurableTrait::class => \Php\Support\Traits\ConfigurableTrait::class, + \Php\Support\Traits\Singleton::class => + \Php\Support\Traits\Singleton::class, + \Php\Support\Traits\ArrayStorageConfigurableTrait::class => + \Php\Support\Traits\ArrayStorageConfigurableTrait::class, + \Php\Support\Traits\ArrayStorage::class => + \Php\Support\Traits\ArrayStorage::class, + \Php\Support\Traits\ConfigurableTrait::class => + \Php\Support\Traits\ConfigurableTrait::class, ], $traits ); @@ -172,11 +327,16 @@ public function testClassUsesRecursive(): void static::assertEquals( [ - \Php\Support\Traits\Singleton::class => \Php\Support\Traits\Singleton::class, - \Php\Support\Traits\ArrayStorageConfigurableTrait::class => \Php\Support\Traits\ArrayStorageConfigurableTrait::class, - \Php\Support\Traits\ArrayStorage::class => \Php\Support\Traits\ArrayStorage::class, - \Php\Support\Traits\ConfigurableTrait::class => \Php\Support\Traits\ConfigurableTrait::class, - \Php\Support\Traits\Maker::class => \Php\Support\Traits\Maker::class, + \Php\Support\Traits\Singleton::class => + \Php\Support\Traits\Singleton::class, + \Php\Support\Traits\ArrayStorageConfigurableTrait::class => + \Php\Support\Traits\ArrayStorageConfigurableTrait::class, + \Php\Support\Traits\ArrayStorage::class => + \Php\Support\Traits\ArrayStorage::class, + \Php\Support\Traits\ConfigurableTrait::class => + \Php\Support\Traits\ConfigurableTrait::class, + \Php\Support\Traits\Maker::class => + \Php\Support\Traits\Maker::class, ], $traits ); @@ -189,17 +349,15 @@ public function testClassBasename(): void $name = class_basename(new \stdClass()); static::assertEquals('stdClass', $name); } - } class TraitUsesRecursiveClass { - - protected $username; - use \Php\Support\Traits\Singleton; use \Php\Support\Traits\ArrayStorageConfigurableTrait; + + protected $username; } class RecursiveClass extends TraitUsesRecursiveClass diff --git a/tests/Helpers/ArrTest.php b/tests/Helpers/ArrTest.php index a65a3b2..9c0845c 100644 --- a/tests/Helpers/ArrTest.php +++ b/tests/Helpers/ArrTest.php @@ -222,14 +222,15 @@ public function toJson($options = 320): ?string ], [ new class () implements \JsonSerializable { - private $data = [ + private $data = + [ '132', 12, 'test', ]; - public function jsonSerialize() + public function jsonSerialize(): mixed { return $this->data; } @@ -347,14 +348,15 @@ public function toJson($options = 320): ?string ], [ new class () implements \JsonSerializable { - private $data = [ + private $data = + [ '132', 12, 'test', ]; - public function jsonSerialize() + public function jsonSerialize(): mixed { return $this->data; } @@ -566,7 +568,15 @@ public function testFromPostgresArray(): void { static::assertEquals(['val1', 'test', 'null', '', 'null'], Arr::fromPostgresArray('{val1,test,null,,null}')); static::assertEquals( - ['val1', '1', '', '3', 'null', '', 'null'], + [ + 'val1', + '1', + '', + '3', + 'null', + '', + 'null', + ], Arr::fromPostgresArray('{val1,1,,3,null,,null}') ); static::assertEquals([], Arr::fromPostgresArray('{}')); @@ -575,7 +585,7 @@ public function testFromPostgresArray(): void public function testFromPostgresPoint(): void { static::assertEquals([32.323, 2342342.0], Arr::fromPostgresPoint('(32.323,2342342)')); - static::assertEquals([12.3223, 0.3223,], Arr::fromPostgresPoint('(12.3223,0.3223)')); + static::assertEquals([12.3223, 0.3223], Arr::fromPostgresPoint('(12.3223,0.3223)')); static::assertNull(Arr::fromPostgresPoint('()')); static::assertNull(Arr::fromPostgresPoint(null)); static::assertNull(Arr::fromPostgresPoint('')); diff --git a/tests/Helpers/B64Test.php b/tests/Helpers/B64Test.php index 0db5afe..7b0f341 100644 --- a/tests/Helpers/B64Test.php +++ b/tests/Helpers/B64Test.php @@ -24,8 +24,9 @@ final class B64Test extends TestCase '12Кириллик' => 'MTLQmtC40YDQuNC70LvQuNC6', "12Кир\nиллик\nen" => 'MTLQmtC40YAK0LjQu9C70LjQugplbg==', "12Кир\tиллик\ten" => 'MTLQmtC40YAJ0LjQu9C70LjQugllbg==', - "'πάντα χωρεῖ καὶ οὐδὲν μένει …'" => 'J8+AzqzOvc+EzrEgz4fPic+BzrXhv5YgzrrOseG9tiDOv+G9kM604b2yzr0gzrzOrc69zrXOuSDigKYn', - '🤪 🤪 😈' => '8J+kqiDwn6SqIPCfmIg=', + "'πάντα χωρεῖ καὶ οὐδὲν μένει …'" => + 'J8+AzqzOvc+EzrEgz4fPic+BzrXhv5YgzrrOseG9tiDOv+G9kM604b2yzr0gzrzOrc69zrXOuSDigKYn', + '🤪 🤪 😈' => '8J+kqiDwn6SqIPCfmIg=', ]; private static $emptyList = [ diff --git a/tests/Helpers/JsonTest.php b/tests/Helpers/JsonTest.php index 2efc21a..558f4cd 100644 --- a/tests/Helpers/JsonTest.php +++ b/tests/Helpers/JsonTest.php @@ -9,14 +9,13 @@ use PHPUnit\Framework\TestCase; use Throwable; - /** * Class JsonTest */ final class JsonTest extends TestCase { /** - * @throws ReflectionException + * @throws \ReflectionException */ public function testEncode(): void { @@ -97,11 +96,6 @@ public function testHtmlEncode(): void // JsonSerializable $data = new JsonModel(); self::assertSame('{"json":"serializable"}', Json::htmlEncode($data)); - - // $postsStack = new \SplStack(); - // $postsStack->push(new Post(915, 'record1')); - // $postsStack->push(new Post(456, 'record2')); - // self::assertSame('{"1":{"id":456,"title":"record2"},"0":{"id":915,"title":"record1"}}', Json::encode($postsStack)); } /** @@ -253,7 +247,7 @@ class JsonModel implements \JsonSerializable /** @var array */ public $data = ['json' => 'serializable']; - public function jsonSerialize() + public function jsonSerialize(): mixed { return $this->data; } diff --git a/tests/Helpers/NumberTest.php b/tests/Helpers/NumberTest.php index 9fe1909..5419e00 100644 --- a/tests/Helpers/NumberTest.php +++ b/tests/Helpers/NumberTest.php @@ -12,24 +12,61 @@ */ final class NumberTest extends TestCase { - - public function providerSafeInt(): array { return [ - ['1', 1], - [1, 1], - [0, 0], - [-1, -1], - [9007199254740991, '9007199254740991'], - [9007199254740992, '9007199254740992'], - [9007199254740990, 9007199254740990], - [-9007199254740991, '-9007199254740991'], - [-9007199254740992, '-9007199254740992'], - [-9007199254740990, -9007199254740990], - [-9007199254740992, '-9007199254740992'], - ['66ede6f7-1b11-4d01-8bbb-c6412b12eac3', '66ede6f7-1b11-4d01-8bbb-c6412b12eac3'], - ['test', 'test'], + [ + '1', + 1, + ], + [ + 1, + 1, + ], + [ + 0, + 0, + ], + [ + -1, + -1, + ], + [ + 9007199254740991, + '9007199254740991', + ], + [ + 9007199254740992, + '9007199254740992', + ], + [ + 9007199254740990, + 9007199254740990, + ], + [ + -9007199254740991, + '-9007199254740991', + ], + [ + -9007199254740992, + '-9007199254740992', + ], + [ + -9007199254740990, + -9007199254740990, + ], + [ + -9007199254740992, + '-9007199254740992', + ], + [ + '66ede6f7-1b11-4d01-8bbb-c6412b12eac3', + '66ede6f7-1b11-4d01-8bbb-c6412b12eac3', + ], + [ + 'test', + 'test', + ], ]; } diff --git a/tests/Helpers/StrTest.php b/tests/Helpers/StrTest.php index 5963c02..ec8395a 100644 --- a/tests/Helpers/StrTest.php +++ b/tests/Helpers/StrTest.php @@ -650,15 +650,39 @@ public function testReplaceByTemplate(string $str, array $replaced, string $exp) public function dataRegExps(): array { return [ - ['/^(\d+)$/', true], - ['/([A-Z])\w+/', true], - ['/\{(?[\w]+?)(:(?[\\\$^()+\w]+?))?}/', true], - - ['^(\d+)$', false], - ['\d+)$', false], - ['', false], - ['test', false], - ['/\{(?[\w]+?)(:(?[\\\$^()+\w]+?)?}/', false], + [ + '/^(\d+)$/', + true, + ], + [ + '/([A-Z])\w+/', + true, + ], + [ + '/\{(?[\w]+?)(:(?[\\\$^()+\w]+?))?}/', + true, + ], + + [ + '^(\d+)$', + false, + ], + [ + '\d+)$', + false, + ], + [ + '', + false, + ], + [ + 'test', + false, + ], + [ + '/\{(?[\w]+?)(:(?[\\\$^()+\w]+?)?}/', + false, + ], ]; } @@ -772,5 +796,4 @@ public function slugify(): void $this->assertEquals('1231251251', Str::slugify('123----1251251', '')); $this->assertEquals('one231251251', Str::slugify('123----1251251', '', true)); } - } diff --git a/tests/exceptions/InvalidParamTest.php b/tests/exceptions/InvalidParamTest.php index 344761a..c5c8e8c 100644 --- a/tests/exceptions/InvalidParamTest.php +++ b/tests/exceptions/InvalidParamTest.php @@ -12,7 +12,6 @@ */ final class InvalidParamTest extends TestCase { - public function testThrow() { try { diff --git a/tests/traits/TraitBooterTest.php b/tests/traits/TraitBooterTest.php index 073a586..52467cf 100644 --- a/tests/traits/TraitBooterTest.php +++ b/tests/traits/TraitBooterTest.php @@ -12,43 +12,35 @@ final class TraitBooterTest extends TestCase { public function testBootTrait(): void { - self::assertEquals('class', BootClass::$type); - $class = new BootClass(); + $class = new class { + use TraitBooter; + use BootTrait; + + public static $type = 'class'; + + public function __construct() + { + $this->bootIfNotBooted(); + // $this->initializeTraits(); + } + }; self::assertEquals('trait', $class::$type); } public function testInitTrait(): void { - $class = new InitClass(); - self::assertEquals('load initialize from InitTrait', $class->title); - } -} - -class BootClass -{ - use TraitBooter; - use BootTrait; - - public static $type = 'class'; + $class = new class { + use TraitInitializer; + use InitTrait; - public function __construct() - { - $this->bootIfNotBooted(); - // $this->initializeTraits(); + public $title = ''; - } -} - -class InitClass -{ - use TraitInitializer; - use InitTrait; - - public $title = ''; - - public function __construct() - { - $this->bootIfNotBooted(); + public function __construct() + { + $this->bootIfNotBooted(); + } + }; + self::assertEquals('load initialize from InitTrait', $class->title); } } From 412d40cc647105e14c8b838fdf5f4eed3c82c456 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 17 Aug 2022 12:34:01 +0500 Subject: [PATCH 42/95] doc: changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e54814a..4b54a71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.13.0 + +### Added + +- Add support `PHP 8.1` + ## v4.9.0 ### Added From a25961563e8cafc95d7222f9244c2789beefc7a6 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 17 Aug 2022 13:05:49 +0500 Subject: [PATCH 43/95] fix --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index a2a2ca9..f1d5811 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # PHP Support -![](https://img.shields.io/badge/php-^8.0-blue.svg) +![](https://img.shields.io/badge/php-^8.0|^8.1-blue.svg) ![PHP Package](https://github.com/efureev/php-support/workflows/PHP%20Package/badge.svg?branch=master) [![Build Status](https://travis-ci.org/efureev/php-support.svg?branch=master)](https://travis-ci.org/efureev/php-support) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a53fb85fd1ab46169758e10dd2d818cb)](https://app.codacy.com/app/efureev/php-support?utm_source=github.com&utm_medium=referral&utm_content=efureev/php-support&utm_campaign=Badge_Grade_Settings) @@ -12,10 +12,10 @@ ## Install -For php >= 8.0 +For php >= 8.0 (8.1) ```bash -composer require efureev/support "^4.0" +composer require efureev/support "^4.13" ``` For php >= 7.4 and <=8.0 From b643757d338d3b403d8572b5abbdbc5a95b1abb9 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 18 Sep 2022 13:34:16 +0500 Subject: [PATCH 44/95] feat: add `Number:isInteger` method --- .phpcs.xml | 2 +- CHANGELOG.md | 6 ++ src/Helpers/Number.php | 11 +++- tests/Helpers/NumberTest.php | 116 ++++++++++++++++++++++++++++++++++- 4 files changed, 129 insertions(+), 6 deletions(-) diff --git a/.phpcs.xml b/.phpcs.xml index 861f09e..f05a076 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -3,7 +3,7 @@ The PSR2 coding standard. src/ - + tests/ vendor resources database diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b54a71..538422d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.14.0 + +### Added + +- Add method `Number::isInteger` Allows you to determine whether the $value is an integer or not + ## v4.13.0 ### Added diff --git a/src/Helpers/Number.php b/src/Helpers/Number.php index dcaebb9..ad0e2f2 100644 --- a/src/Helpers/Number.php +++ b/src/Helpers/Number.php @@ -13,16 +13,21 @@ class Number /** * Convert large integer higher than Number.MAX_SAFE_INTEGER (JavaScript) to string. * - * @param int|string $value + * @param int|string $value * * @return int|string */ public static function safeInt(int|string $value): int|string { if (is_int($value) && ($value >= 9007199254740991 || $value <= -9007199254740991)) { - return (string) $value; + return (string)$value; } - return is_numeric($value) ? (int) $value : (string) $value; + return is_numeric($value) ? (int)$value : (string)$value; + } + + public static function isInteger(mixed $value): bool + { + return is_int($value) || (string)$value === (string)(int)($value); } } diff --git a/tests/Helpers/NumberTest.php b/tests/Helpers/NumberTest.php index 5419e00..da0586d 100644 --- a/tests/Helpers/NumberTest.php +++ b/tests/Helpers/NumberTest.php @@ -73,11 +73,123 @@ public function providerSafeInt(): array /** * @dataProvider providerSafeInt * - * @param int|string $value - * @param int|string $exp + * @param int|string $value + * @param int|string $exp */ public function testSafeInt(int|string $value, int|string $exp): void { self::assertTrue($exp === Number::safeInt($value)); } + + + public function providerIsInteger(): array + { + return [ + [ + '1', + true, + ], + [ + 1, + true, + ], + [ + 0, + true, + ], + [ + -1, + true, + ], + [ + 9007199254740991, + true, + ], + [ + 9007199254740992, + true, + ], + [ + 9007199254740990, + true, + ], + [ + -9007199254740991, + true, + ], + [ + -9007199254740992, + true, + ], + [ + -9007199254740990, + true, + ], + [ + -9007199254740992, + true, + ], + [ + '66ede6f7-1b11-4d01-8bbb-c6412b12eac3', + false, + ], + [ + 'test', + false, + ], + + [ + 23, + true, + ], + [ + '-23', + true, + ], + [ + '23', + true, + ], + [ + 23.3, + false, + ], + [ + '23.3', + false, + ], + [ + '23.3', + false, + ], + [ + '23,2', + false, + ], + [ + 'null', + false, + ], + [ + null, + false, + ], + [ + '', + false, + ], + ]; + } + + /** + * @dataProvider providerIsInteger + * + * @param int|string $value + * @param bool $exp + * @test + */ + public function isInteger(mixed $value, bool $exp): void + { + self::assertEquals($exp, Number::isInteger($value)); + } } From ef15ff1260b317dac6da2397fbc9913fcb0c483d Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 18 Sep 2022 13:37:27 +0500 Subject: [PATCH 45/95] feat: add `Number:isInteger` method --- .phpcs.xml | 2 +- src/Helpers/Number.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.phpcs.xml b/.phpcs.xml index f05a076..861f09e 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -3,7 +3,7 @@ The PSR2 coding standard. src/ - tests/ + vendor resources database diff --git a/src/Helpers/Number.php b/src/Helpers/Number.php index ad0e2f2..4c761db 100644 --- a/src/Helpers/Number.php +++ b/src/Helpers/Number.php @@ -28,6 +28,7 @@ public static function safeInt(int|string $value): int|string public static function isInteger(mixed $value): bool { + /** @phpstan-ignore-next-line */ return is_int($value) || (string)$value === (string)(int)($value); } } From 0dd47fefcff9d7f9a409e1ad60b444817b4cb6e6 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Mon, 21 Nov 2022 01:13:30 +0400 Subject: [PATCH 46/95] feat: add `mapValue` --- CHANGELOG.md | 6 ++++++ src/Global/base.php | 16 +++++++++++++- tests/Global/BaseTest.php | 2 +- tests/Global/MapValueTest.php | 39 +++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/Global/MapValueTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 538422d..3fa4e21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.15.0 + +### Added + +- Add global method `mapValue` Returns an array containing the results of applying func to the items of the $collection + ## v4.14.0 ### Added diff --git a/src/Global/base.php b/src/Global/base.php index f4badb0..5f217ec 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -17,6 +17,19 @@ function value(mixed $value, mixed ...$args): mixed } } +if (!function_exists('mapValue')) { + function mapValue(callable $fn, iterable $collection, mixed ...$args): array + { + $result = []; + + foreach ($collection as $key => $value) { + $result[$key] = $fn($value, $key, ...$args); + } + + return $result; + } +} + if (!function_exists('when')) { /** * Returns a value when a condition is truthy. @@ -222,7 +235,8 @@ function remoteStaticCallOrTrow(object|string|null $class, string $method, mixed return $class::$method(...$params); } - throw new \Php\Support\Exceptions\MissingMethodException("$class::$method"); + $strClass = is_object($class) ? $class::class : $class; + throw new \Php\Support\Exceptions\MissingMethodException("$strClass::$method"); } } diff --git a/tests/Global/BaseTest.php b/tests/Global/BaseTest.php index 0915b58..a589a25 100644 --- a/tests/Global/BaseTest.php +++ b/tests/Global/BaseTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Global; use Php\Support\Types\Point; use PHPUnit\Framework\TestCase; diff --git a/tests/Global/MapValueTest.php b/tests/Global/MapValueTest.php new file mode 100644 index 0000000..9c0d2d2 --- /dev/null +++ b/tests/Global/MapValueTest.php @@ -0,0 +1,39 @@ + mb_strtoupper($value); + $result = mapValue($fnColl, ['test', 'app']); + $expect = [ + 'TEST', + 'APP', + ]; + self::assertEquals($expect, $result); + } + + /** + * @test + */ + public function mapValueWithParams(): void + { + $fnColl = static fn(string $value, $key, string $prefix, string $suffix) => + $prefix . mb_strtoupper($value) . $suffix; + $result = mapValue($fnColl, ['test', 'app'], '- ', '.'); + $expect = [ + '- TEST.', + '- APP.', + ]; + self::assertEquals($expect, $result); + } +} From 2960b4de2b5513e99c24e68529b0f658719e59f3 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Mon, 21 Nov 2022 02:06:59 +0400 Subject: [PATCH 47/95] feat: add `eachValue` --- CHANGELOG.md | 1 + src/Global/base.php | 9 +++++++++ tests/Global/EachValueTest.php | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 tests/Global/EachValueTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa4e21..6fe3a1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added - Add global method `mapValue` Returns an array containing the results of applying func to the items of the $collection +- Add global method `eachValue` Apply a $fn to all the items of the $collection ## v4.14.0 diff --git a/src/Global/base.php b/src/Global/base.php index 5f217ec..74fe095 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -30,6 +30,15 @@ function mapValue(callable $fn, iterable $collection, mixed ...$args): array } } +if (!function_exists('eachValue')) { + function eachValue(callable $fn, iterable $collection, mixed ...$args): void + { + foreach ($collection as $key => $value) { + $fn($value, $key, ...$args); + } + } +} + if (!function_exists('when')) { /** * Returns a value when a condition is truthy. diff --git a/tests/Global/EachValueTest.php b/tests/Global/EachValueTest.php new file mode 100644 index 0000000..a204181 --- /dev/null +++ b/tests/Global/EachValueTest.php @@ -0,0 +1,25 @@ + Date: Fri, 16 Dec 2022 17:50:46 +0200 Subject: [PATCH 48/95] feat: add Collections --- .github/workflows/php.yml | 2 +- CHANGELOG.md | 9 + composer.json | 2 +- src/Global/base.php | 57 ++ src/Helpers/Arr.php | 136 ++-- src/Helpers/Bit.php | 14 +- src/Helpers/Json.php | 2 +- .../Collections/ArrayCollection.php | 706 ++++++++++++++++++ src/Structures/Collections/Collection.php | 84 +++ .../Collections/ReadableCollection.php | 290 +++++++ tests/Helpers/ArrTest.php | 119 +-- 11 files changed, 1312 insertions(+), 109 deletions(-) create mode 100644 src/Structures/Collections/ArrayCollection.php create mode 100644 src/Structures/Collections/Collection.php create mode 100644 src/Structures/Collections/ReadableCollection.php diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 0e6d3cf..d804889 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Lint changelog file uses: avto-dev/markdown-lint@v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe3a1a..2b61862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.16.0 + +### Added + +- Add global method `dataGet` +- Add helper method `Arr::collapse` +- Add helper method `Arr::prepend` +- Add Structures: `ArrayCollection` and its interfaces + ## v4.15.0 ### Added diff --git a/composer.json b/composer.json index 9744473..e423118 100755 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "phpstan/phpstan": "^1.8", + "phpstan/phpstan": "^1.9.3", "squizlabs/php_codesniffer": "^3.7" }, "autoload": { diff --git a/src/Global/base.php b/src/Global/base.php index 74fe095..514f325 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -1,5 +1,8 @@ $segment) { + unset($key[$i]); + + if ($segment === null) { + return $target; + } + + if ($segment === '*') { + if ($target instanceof ReadableCollection) { + $target = $target->all(); + } elseif (!is_iterable($target)) { + return value($default); + } + + $result = []; + + foreach ($target as $item) { + $result[] = dataGet($item, $key); + } + + return in_array('*', $key) ? Arr::collapse($result) : $result; + } + + if (Arr::accessible($target) && Arr::exists($target, $segment)) { + $target = $target[$segment]; + } elseif (is_object($target) && isset($target->{$segment})) { + $target = $target->{$segment}; + } else { + return value($default); + } + } + + return $target; + } +} + + if (!function_exists('mapValue')) { function mapValue(callable $fn, iterable $collection, mixed ...$args): array { diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index bdfe0ad..a66e526 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -9,15 +9,39 @@ use JsonSerializable; use Php\Support\Interfaces\Arrayable; use Php\Support\Interfaces\Jsonable; +use Php\Support\Structures\Collections\ReadableCollection; use Traversable; /** - * Class Arr - * - * @package Php\Support\Helpers + * @psalm-template TKey of array-key + * @psalm-template T */ class Arr { + /** + * Collapse an array of arrays into a single array. + * + * @param iterable $array + * @return array + * @psalm-return T[] + */ + public static function collapse(iterable $array): array + { + $results = []; + + foreach ($array as $values) { + if ($values instanceof ReadableCollection) { + $values = $values->all(); + } elseif (!is_array($values)) { + continue; + } + + $results[] = $values; + } + + return array_merge([], ...$results); + } + /** * Remove one element from array by value * @@ -27,7 +51,7 @@ class Arr * * @return string|int|null Index of removed element or null if don't exist */ - public static function removeByValue(array &$array, $val, $reindex = false) + public static function removeByValue(array &$array, mixed $val, bool $reindex = false): string|int|null { if (($key = array_search($val, $array, false)) !== false) { unset($array[$key]); @@ -43,33 +67,31 @@ public static function removeByValue(array &$array, $val, $reindex = false) * * @param mixed $items * - * @return array + * @return array */ - public static function toArray($items): array + public static function toArray(mixed $items): array { if (is_array($items)) { - $res = $items; - } else { - if ($items instanceof Arrayable) { - $res = $items->toArray(); - } else { - if ($items instanceof Jsonable) { - $res = Json::decode($items->toJson()); - } else { - if ($items instanceof JsonSerializable) { - $res = $items->jsonSerialize(); - } else { - if ($items instanceof Traversable) { - $res = iterator_to_array($items); - } else { - $res = (array)$items; - } - } - } - } + return $items; + } + + if ($items instanceof Arrayable) { + return $items->toArray(); + } + + if ($items instanceof Traversable) { + return iterator_to_array($items); + } + if ($items instanceof Jsonable) { + $res = Json::decode($items->toJson()); + return is_array($res) ? $res : []; + } + + if ($items instanceof JsonSerializable) { + return (array)$items->jsonSerialize(); } - return (array)$res; + return (array)$items; } /** @@ -79,7 +101,7 @@ public static function toArray($items): array * * @return array|mixed|null */ - public static function dataToArray($items) + public static function dataToArray(mixed $items): mixed { if (is_object($items)) { if ($items instanceof JsonSerializable) { @@ -126,7 +148,7 @@ public static function dataToArray($items) * * @return array the merged array (the original arrays are not changed.) */ - public static function merge($res, $b, $replaceArray = true): array + public static function merge(array $res, array $b, bool $replaceArray = true): array { foreach ($b as $key => $val) { if (is_int($key)) { @@ -196,33 +218,26 @@ public static function toIndexedArray(array $array): array return $array; } - /** - * Load from PG array to PHP array - * - * @param string|null $s - * @param int $start - * @param null $end - * - * @return array - */ /** * Load from PG array to PHP array * * @param string|null $s * @param int $start * @param ?int $end + * @param array $braces * * @return array */ public static function fromPostgresArrayWithBraces( ?string $s, - int $start = 0, - ?int &$end = null, - array $braces = [ + int $start = 0, + ?int &$end = null, + array $braces = [ '{', '}', ] - ): array { + ): array + { [ $braceOpen, $braceClose, @@ -233,9 +248,9 @@ public static function fromPostgresArrayWithBraces( $return = []; $string = false; - $quote = ''; - $len = strlen($s); - $v = ''; + $quote = ''; + $len = strlen($s); + $v = ''; for ($i = $start + 1; $i < $len; $i++) { $ch = $s[$i]; @@ -251,11 +266,11 @@ public static function fromPostgresArrayWithBraces( } else { if (!$string && $ch === ',') { $return[] = $v; - $v = ''; + $v = ''; } else { if (!$string && ($ch === '"' || $ch === "'")) { $string = true; - $quote = $ch; + $quote = $ch; } else { if ($string && $ch === $quote) { if ($s[$i - 1] === "\\") { @@ -327,12 +342,12 @@ public static function fromPostgresPoint(?string $value): ?array * Get an item from an array using "dot" notation. * * @param mixed $array - * @param null|string $key + * @param string|int|null $key * @param mixed $default * * @return mixed */ - public static function get(mixed $array, ?string $key, mixed $default = null): mixed + public static function get(mixed $array, string|int|null $key, mixed $default = null): mixed { if (!static::accessible($array)) { return value($default); @@ -347,7 +362,7 @@ public static function get(mixed $array, ?string $key, mixed $default = null): m return $array[$key]; } - if (!str_contains($key, '.')) { + if (is_int($key) || !str_contains($key, '.')) { return $array[$key] ?? value($default); } @@ -471,7 +486,7 @@ public static function set(array|ArrayObject|null &$array, string $key, mixed $v public static function remove(array|ArrayObject &$array, array|string $keys): void { $original = &$array; - $keys = (array)$keys; + $keys = (array)$keys; if (count($keys) === 0) { return; @@ -531,7 +546,7 @@ public static function replaceByTemplate(array $array, array $replace): array * * @return array|mixed */ - private static function itemReplaceByTemplate($item, array $replace) + private static function itemReplaceByTemplate(mixed $item, array $replace) { if (is_array($item)) { $item = self::replaceByTemplate($item, $replace); @@ -574,4 +589,23 @@ public static function fillKeysByValues(array $keys, array $values): array return $result; } + + /** + * Push an item onto the beginning of an array. + * + * @param array $array + * @param mixed $value + * @param mixed $key + * @return array + */ + public static function prepend(array $array, mixed $value, mixed $key = null): array + { + if (func_num_args() == 2) { + array_unshift($array, $value); + } else { + $array = [$key => $value] + $array; + } + + return $array; + } } diff --git a/src/Helpers/Bit.php b/src/Helpers/Bit.php index 7e7fe4b..261856e 100644 --- a/src/Helpers/Bit.php +++ b/src/Helpers/Bit.php @@ -15,27 +15,27 @@ class Bit { /** - * Remove bit from $value + * Remove a bit from $value * * @param int|string $value * @param int $bit * * @return int */ - public static function removeFlag($value, int $bit): int + public static function removeFlag(int|string $value, int $bit): int { return static::toInt($value) & ~$bit; } /** - * Set bit to $value + * Set a bit to $value * * @param int|string $value * @param int $bit * * @return int */ - public static function addFlag($value, int $bit): int + public static function addFlag(int|string $value, int $bit): int { return static::toInt($value) | $bit; } @@ -45,13 +45,13 @@ public static function addFlag($value, int $bit): int * * @return int */ - protected static function toInt($value): int + protected static function toInt(int|string $value): int { if (is_string($value)) { return (int)bindec($value); } - return (int)$value; + return $value; } /** @@ -62,7 +62,7 @@ protected static function toInt($value): int * * @return bool */ - public static function checkFlag($value, int $bit): bool + public static function checkFlag(int|string $value, int $bit): bool { return (static::toInt($value) & $bit) > 0; } diff --git a/src/Helpers/Json.php b/src/Helpers/Json.php index fac5341..eb3c911 100644 --- a/src/Helpers/Json.php +++ b/src/Helpers/Json.php @@ -63,7 +63,7 @@ public static function encode($value, $options = 320, int $depth = 512): ?string * * @return mixed|null */ - public static function decode(?string $json, $asArray = true, int $options = 0, int $depth = 512) + public static function decode(?string $json, bool $asArray = true, int $options = 0, int $depth = 512) { if ($json === null || $json === '') { return null; diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php new file mode 100644 index 0000000..492eab2 --- /dev/null +++ b/src/Structures/Collections/ArrayCollection.php @@ -0,0 +1,706 @@ + + * + * @psalm-consistent-constructor + */ +class ArrayCollection implements Collection, Stringable +{ + /** + * @var array + * @psalm-var array + */ + protected array $elements = []; + + /** + * @param array $elements + * @psalm-param array $elements + */ + public function __construct(array $elements = []) + { + $this->elements = $elements; + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return $this->elements; + } + + /** + * {@inheritDoc} + */ + public function all(): array + { + return $this->elements; + } + + /** + * {@inheritDoc} + * + * @return Traversable + * @psalm-return Traversable + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->elements); + } + + /** + * @param TKey $offset + * @return bool + */ + public function offsetExists(mixed $offset): bool + { + return $this->containsKey($offset); + } + + /** + * @param TKey $offset + * @return T|null + */ + public function offsetGet(mixed $offset): mixed + { + return $this->get($offset); + } + + /** + * @param int|string|null $offset + * @param T $value + * @psalm-param TKey|null $offset + */ + public function offsetSet(mixed $offset, mixed $value): void + { + if (!isset($offset)) { + $this->add($value); + + return; + } + + $this->set($offset, $value); + } + + /** + * @param TKey $offset + * @return void + */ + public function offsetUnset(mixed $offset): void + { + $this->remove($offset); + } + + /** + * {@inheritDoc} + * + * @return int<0, max> + */ + public function count(): int + { + return count($this->elements); + } + + /** + * {@inheritDoc} + */ + public function containsKey(int|string $key): bool + { + return isset($this->elements[$key]) || array_key_exists($key, $this->elements); + } + + /** + * {@inheritDoc} + */ + public function get(int|string $key): mixed + { + return $this->elements[$key] ?? null; + } + + + /** + * {@inheritDoc} + */ + public function set(int|string $key, mixed $value): void + { + $this->elements[$key] = $value; + } + + /** + * {@inheritDoc} + * + * @psalm-suppress InvalidPropertyAssignmentValue + */ + public function add(mixed $element): bool + { + $this->elements[] = $element; + + return true; + } + + /** + * {@inheritDoc} + */ + public function remove(int|string $key): mixed + { + if (!isset($this->elements[$key]) && !array_key_exists($key, $this->elements)) { + return null; + } + + $removed = $this->elements[$key]; + unset($this->elements[$key]); + + return $removed; + } + + + /** + * {@inheritDoc} + */ + public function isEmpty(): bool + { + return empty($this->elements); + } + + /** + * {@inheritDoc} + */ + public function getKeys(): array + { + return array_keys($this->elements); + } + + /** + * {@inheritDoc} + */ + public function getValues(): array + { + return array_values($this->elements); + } + + + /** + * {@inheritDoc} + * + * @template TMaybeContained + */ + public function contains(mixed $element): bool + { + return in_array($element, $this->elements, true); + } + + /** + * {@inheritDoc} + * + * @psalm-param Closure(T):U $func + * + * @return static + * @psalm-return static + * + * @psalm-template U + */ + public function map(Closure $func): static + { + return $this->createFrom(array_map($func, $this->elements)); + } + + + /** + * {@inheritDoc} + * + * @psalm-param null|Closure(T,TKey):U $func + * + * @return static + * @psalm-return static + * + * @psalm-template U + */ + public function mapByKey(string $keyName, ?string $valueName = null, ?Closure $func = null): static + { + $result = []; + foreach ($this->elements as $ind => $element) { + if ($valueName === null) { + $value = $element; + } else { + $value = $func ? $func($element, $ind) : $this->getProperty($element, $valueName); + } + $result[$this->getProperty($element, $keyName)] = $value; + } + return $this->createFrom($result); + } + + private function getProperty(mixed $target, string|int $keyName, bool $throwOnMiss = true): mixed + { + return match (true) { + is_array($target) || $target instanceof \ArrayAccess + => $throwOnMiss ? $target[$keyName] : ($target[$keyName] ?? null), + is_object($target) + => $throwOnMiss ? $target->$keyName : (property_exists($target, $keyName) ? $target->$keyName : null), + }; + } + + /** + * {@inheritDoc} + * + * @return static + * @psalm-return static + */ + public function filter(Closure $func): static + { + return $this->createFrom(array_filter($this->elements, $func, ARRAY_FILTER_USE_BOTH)); + } + + /** + * {@inheritDoc} + */ + public function each(Closure $func): bool + { + return $this->testForAll($func); + } + + /** + * {@inheritDoc} + */ + public function transform(Closure $func): static + { + $this->elements = array_map($func, $this->elements); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function merge(mixed $items): static + { + return $this->createFrom(array_merge($this->elements, Arr::toArray($items))); + } + + /** + * Creates a new instance from the specified elements. + * + * This method is provided for derived classes to specify how a new + * instance should be created when constructor semantics have changed. + * + * @param array $elements Elements. + * @psalm-param array $elements + * + * @return static + * @psalm-return static + * + * @psalm-template K of array-key + * @psalm-template V + */ + protected function createFrom(array $elements): static + { + return new static($elements); + } + + /** + * {@inheritDoc} + */ + public function clear(): void + { + $this->elements = []; + } + + /** + * {@inheritDoc} + */ + public function removeElement(mixed $element): bool + { + $key = array_search($element, $this->elements, true); + + if ($key === false) { + return false; + } + + unset($this->elements[$key]); + + return true; + } + + /** + * {@inheritDoc} + */ + public function first(): mixed + { + return reset($this->elements); + } + + /** + * {@inheritDoc} + */ + public function last(): mixed + { + return end($this->elements); + } + + /** + * {@inheritDoc} + */ + public function key(): int|string|null + { + return key($this->elements); + } + + /** + * {@inheritDoc} + */ + public function current(): mixed + { + return current($this->elements); + } + + /** + * {@inheritDoc} + */ + public function next(): mixed + { + return next($this->elements); + } + + /** + * {@inheritDoc} + */ + public function slice(int $offset, ?int $length = null): array + { + return array_slice($this->elements, $offset, $length, true); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $func): bool + { + foreach ($this->elements as $key => $element) { + if ($func($key, $element)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $func): array + { + $matches = $noMatches = []; + + foreach ($this->elements as $key => $element) { + if ($func($key, $element)) { + $matches[$key] = $element; + } else { + $noMatches[$key] = $element; + } + } + + return [ + $this->createFrom($matches), + $this->createFrom($noMatches), + ]; + } + + /** + * {@inheritDoc} + */ + public function testForAll(Closure $func): bool + { + foreach ($this->elements as $key => $element) { + if (!$func($key, $element)) { + return false; + } + } + + return true; + } + + /** + * {@inheritDoc} + * + * @psalm-param TMaybeContained $element + * + * @return string|int|false + * @psalm-return (TMaybeContained is T ? TKey|false : false) + * + * @template TMaybeContained + */ + public function indexOf(mixed $element): string|int|bool + { + return array_search($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function findFirst(Closure $func): mixed + { + foreach ($this->elements as $key => $element) { + if ($func($key, $element)) { + return $element; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function reduce(Closure $func, mixed $initial = null): mixed + { + return array_reduce($this->elements, $func, $initial); + } + + /** + * Collapse the collection of items into a single array. + * + * + * @return static + * @psalm-return static + */ + public function collapse(): static + { + return $this->createFrom(Arr::collapse($this->elements)); + } + + /** + * Push an element onto the beginning of the collection. + * + * @param T $value + * @param TKey $key + * @return static + */ + public function prepend(mixed $value, $key = null): static + { + $this->elements = Arr::prepend($this->elements, ...func_get_args()); + + return $this; + } + + + /** + * Push one or more elements onto the end of the collection. + * + * @param T ...$values + * @return static + */ + public function push(...$values): static + { + foreach ($values as $value) { + $this->elements[] = $value; + } + + return $this; + } + + /** + * Reverse elements order. + * + * @return static + */ + public function reverse(): static + { + return $this->createFrom(array_reverse($this->elements, true)); + } + + /** + * Chunk the collection into chunks of the given size. + * + * @param int $size + * + * @return static + */ + public function chunk(int $size): static + { + if ($size <= 0) { + return $this->createFrom([]); + } + + $chunks = []; + + foreach (array_chunk($this->elements, $size, true) as $chunk) { + $chunks[] = $this->createFrom($chunk); + } + + return $this->createFrom($chunks); + } + + /** + * Sort through each item with a callback. + * + * @param (callable(T, T): int)|null|int $func + * + * @return static + */ + public function sort(callable|int|null $func = null): static + { + $items = $this->elements; + + $func && is_callable($func) + ? uasort($items, $func) + : asort($items, $func ?? SORT_REGULAR); + + return $this->createFrom($items); + } + + + /** + * Sort items in descending order. + * + * @param int $options + * @return static + */ + public function sortDesc(int $options = SORT_REGULAR): static + { + $items = $this->elements; + + arsort($items, $options); + + return $this->createFrom($items); + } + + /** + * Sort the collection using the given callback. + * + * @param array|(callable(T, TKey): mixed)|string $callback + * @param int $options + * @param bool $descending + * @return static + */ + public function sortBy(array|string|callable $callback, int $options = SORT_REGULAR, bool $descending = false) + { + if (is_array($callback) && !is_callable($callback)) { + return $this->sortByMany($callback); + } + + $results = []; + + if (is_callable($callback)) { + // First we will loop through the items and get the comparator from a callback + // function which we were given. Then, we will sort the returned values and + // grab all the corresponding values for the sorted keys from this array. + foreach ($this->elements as $key => $value) { + $results[$key] = $callback($value, $key); + } + } + $descending ? arsort($results, $options) + : asort($results, $options); + + // Once we have sorted all of the keys in the array, we will loop through them + // and grab the corresponding model so we can set the underlying items list + // to the sorted version. Then we'll just return the collection instance. + foreach (array_keys($results) as $key) { + $results[$key] = $this->elements[$key]; + } + + return $this->createFrom($results); + } + + /** + * Sort the collection using multiple comparisons. + * + * @param array $comparisons + * @return static + */ + protected function sortByMany(array $comparisons = []): static + { + $items = $this->elements; + + uasort( + $items, + function ($a, $b) use ($comparisons) { + foreach ($comparisons as $comparison) { + $comparison = (array)$comparison; + + $prop = $comparison[0]; + + $ascending = Arr::get($comparison, 1, true) === true || + Arr::get($comparison, 1, true) === 'asc'; + + if (!is_string($prop) && is_callable($prop)) { + $result = $prop($a, $b); + } else { + $values = [ + dataGet($a, $prop), + dataGet($b, $prop), + ]; + + if (!$ascending) { + $values = array_reverse($values); + } + + $result = $values[0] <=> $values[1]; + } + + if ($result === 0) { + continue; + } + + return $result; + } + } + ); + + return $this->createFrom($items); + } + + /** + * Sort the collection keys. + * + * @param int $options + * @param bool $descending + * @return static + */ + public function sortKeys(int $options = SORT_REGULAR, bool $descending = false): static + { + $items = $this->elements; + + $descending ? krsort($items, $options) : ksort($items, $options); + + return $this->createFrom($items); + } + + public function __toString(): string + { + return self::class . '@' . spl_object_hash($this); + } +} diff --git a/src/Structures/Collections/Collection.php b/src/Structures/Collections/Collection.php new file mode 100644 index 0000000..e4f31b3 --- /dev/null +++ b/src/Structures/Collections/Collection.php @@ -0,0 +1,84 @@ +ordered map that can also be used + * like a list. + * + * A Collection has an internal iterator just like a PHP array. In addition, + * a Collection can be iterated with external iterators, which is preferable. + * To use an external iterator simply use the foreach language construct to + * iterate over the collection (which calls {@link getIterator()} internally) or + * explicitly retrieve an iterator though {@link getIterator()} which can then be + * used to iterate over the collection. + * You can not rely on the internal iterator of the collection being at a certain + * position unless you explicitly positioned it before. Prefer iteration with + * external iterators. + * + * @author Doctrine + * + * @psalm-template TKey of array-key + * @psalm-template T + * @template-extends ReadableCollection + * @template-extends ArrayAccess + */ +interface Collection extends ReadableCollection, ArrayAccess +{ + /** + * Adds an element at the end of the collection. + * + * @param mixed $element The element to add. + * @psalm-param T $element + * + * @return bool + */ + public function add(mixed $element): bool; + + /** + * Clears the collection, removing all elements. + * + * @return void + */ + public function clear(): void; + + /** + * Removes the element at the specified index from the collection. + * + * @param string|int $key The key/index of the element to remove. + * @psalm-param TKey $key + * + * @return mixed The removed element or NULL, if the collection did not contain the element. + * @psalm-return T|null + */ + public function remove(string|int $key): mixed; + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * @psalm-param T $element + * + * @return bool TRUE if this collection contained the specified element, FALSE otherwise. + */ + public function removeElement(mixed $element): bool; + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string|int $key The key/index of the element to set. + * @param mixed $value The element to set. + * @psalm-param TKey $key + * @psalm-param T $value + * + * @return void + */ + public function set(string|int $key, mixed $value): void; +} diff --git a/src/Structures/Collections/ReadableCollection.php b/src/Structures/Collections/ReadableCollection.php new file mode 100644 index 0000000..d6e8270 --- /dev/null +++ b/src/Structures/Collections/ReadableCollection.php @@ -0,0 +1,290 @@ + + */ +interface ReadableCollection extends Countable, IteratorAggregate +{ + /** + * Checks whether an element is contained in the collection. + * This is an O(n) operation, where n is the size of the collection. + * + * @param mixed $element The element to search for. + * @psalm-param TMaybeContained $element + * + * @return bool TRUE if the collection contains the element, FALSE otherwise. + * @psalm-return (TMaybeContained is T ? bool : false) + * + * @template TMaybeContained + */ + public function contains(mixed $element): bool; + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return bool TRUE if the collection is empty, FALSE otherwise. + */ + public function isEmpty(): bool; + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string|int $key The key/index to check for. + * @psalm-param TKey $key + * + * @return bool TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + public function containsKey(string|int $key): bool; + + /** + * Gets the element at the specified key/index. + * + * @param string|int $key The key/index of the element to retrieve. + * @psalm-param TKey $key + * + * @return mixed + * @psalm-return T|null + */ + public function get(string|int $key): mixed; + + /** + * Gets all keys/indices of the collection. + * + * @return int[]|string[] The keys/indices of the collection, in the order of the corresponding + * elements in the collection. + * @psalm-return list + */ + public function getKeys(): array; + + /** + * Gets all values of the collection. + * + * @return mixed[] The values of all elements in the collection, in the + * order they appear in the collection. + * @psalm-return list + */ + public function getValues(): array; + + /** + * Gets a native PHP array representation of the collection. + * + * @return mixed[] + * @psalm-return array + */ + public function toArray(): array; + + /** + * Gets a native PHP array of the elements. + * + * @return mixed[] + * @psalm-return array + */ + public function all(): array; + + /** + * Sets the internal iterator to the first element in the collection and returns this element. + * + * @return mixed + * @psalm-return T|false + */ + public function first(): mixed; + + /** + * Sets the internal iterator to the last element in the collection and returns this element. + * + * @return mixed + * @psalm-return T|false + */ + public function last(): mixed; + + /** + * Gets the key/index of the element at the current iterator position. + * + * @return int|string|null + * @psalm-return TKey|null + */ + public function key(): int|string|null; + + /** + * Gets the element of the collection at the current iterator position. + * + * @return mixed + * @psalm-return T|false + */ + public function current(): mixed; + + /** + * Moves the internal iterator position to the next element and returns this element. + * + * @return mixed + * @psalm-return T|false + */ + public function next(): mixed; + + /** + * Extracts a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset The offset to start from. + * @param int|null $length The maximum number of elements to return, or null for no limit. + * + * @return mixed[] + * @psalm-return array + */ + public function slice(int $offset, int|null $length = null): array; + + /** + * Tests for the existence of an element that satisfies the given predicate. + * + * @param Closure $func The predicate. + * @psalm-param Closure(TKey, T):bool $func + * + * @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + public function exists(Closure $func): bool; + + /** + * Returns all the elements of this collection that satisfy the predicate $func. + * The order of the elements is preserved. + * + * @param Closure $func The predicate used for filtering. + * @psalm-param Closure(T, TKey):bool $func + * + * @return ReadableCollection A collection with the results of the filter operation. + * @psalm-return ReadableCollection + */ + public function filter(Closure $func): ReadableCollection; + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @psalm-param Closure(T):U $func + * + * @return ReadableCollection + * @psalm-return ReadableCollection + * + * @psalm-template U + */ + public function map(Closure $func): ReadableCollection; + + /** + * Returns a new collection with Key = $keyName and the elements returned by the function if it exists. + * + * @param string $keyName + * @param ?string $valueName + * @psalm-param null|Closure(T,TKey):U $func + * + * @return ReadableCollection + * @psalm-return ReadableCollection + * + * @psalm-template U + */ + public function mapByKey(string $keyName, ?string $valueName = null, ?Closure $func = null): ReadableCollection; + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $func The predicate on which to partition. + * @psalm-param Closure(TKey, T):bool $func + * + * @return ReadableCollection[] An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + * @psalm-return array{0: ReadableCollection, 1: ReadableCollection} + */ + public function partition(Closure $func): array; + + /** + * Tests whether the given predicate $func holds for all elements of this collection. + * + * @param Closure $func The predicate. + * @psalm-param Closure(TKey, T):bool $func + * + * @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public function testForAll(Closure $func): bool; + + /** + * Applies the given function to each element of the Collection. Returns the same Collection. + * + * @param Closure $func The predicate. + * @psalm-param Closure(TKey, T):bool $func + */ + public function each(Closure $func): bool; + + /** + * Transform each item in the collection using a callback. + * + * @param Closure $func The predicate. + * @psalm-param Closure(TKey, T):void $func + */ + public function transform(Closure $func): static; + + + /** + * Merge the collection with the given items. + * + * @param \iterable|Arrayable $items + * @return static + */ + public function merge(mixed $items): static; + + /** + * Gets the index/key of a given element. The comparison of two elements is strict, + * that means not only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element The element to search for. + * @psalm-param TMaybeContained $element + * + * @return int|string|bool The key/index of the element or FALSE if the element was not found. + * @psalm-return (TMaybeContained is T ? TKey|false : false) + * + * @template TMaybeContained + */ + public function indexOf(mixed $element): string|int|bool; + + /** + * Returns the first element of this collection that satisfies the predicate $func. + * + * @param Closure $func The predicate. + * @psalm-param Closure(TKey, T):bool $func + * + * @return mixed The first element respecting the predicate, + * null if no element respects the predicate. + * @psalm-return T|null + */ + public function findFirst(Closure $func): mixed; + + /** + * Applies iteratively the given function to each element in the collection, + * so as to reduce the collection to a single value. + * + * @psalm-param Closure(TReturn|TInitial|null, T):(TInitial|TReturn) $func + * @psalm-param TInitial|null $initial + * + * @return mixed + * @psalm-return TReturn|TInitial|null + * + * @psalm-template TReturn + * @psalm-template TInitial + */ + public function reduce(Closure $func, mixed $initial = null): mixed; +} diff --git a/tests/Helpers/ArrTest.php b/tests/Helpers/ArrTest.php index 9c0845c..ac9720d 100644 --- a/tests/Helpers/ArrTest.php +++ b/tests/Helpers/ArrTest.php @@ -8,6 +8,7 @@ use Php\Support\Helpers\Arr; use Php\Support\Helpers\Json; use Php\Support\Interfaces\Jsonable; +use Php\Support\Structures\Collections\ArrayCollection; use PHPUnit\Framework\TestCase; /** @@ -119,7 +120,7 @@ public function toArray(): array return $this->data; } }; - $jsonableClass = new class () implements \Php\Support\Interfaces\Jsonable { + $jsonableClass = new class () implements \Php\Support\Interfaces\Jsonable { private $data = [ '32', 12, @@ -160,24 +161,24 @@ public function toJson($options = 320): ?string ], [ [ - 'test' => 1, - 0 => 14, + 'test' => 1, + 0 => 14, 'nested' => [ - 'cl' => $arrayableClass, + 'cl' => $arrayableClass, 'cl2' => $arrayableClass, - '1' => [ + '1' => [ 1, 2, $jsonableClass, ], ], - 'csl' => $arrayableClass, + 'csl' => $arrayableClass, ], [ - 'test' => 1, - 0 => 14, + 'test' => 1, + 0 => 14, 'nested' => [ - 'cl' => [ + 'cl' => [ '1', 2, 'test', @@ -187,7 +188,7 @@ public function toJson($options = 320): ?string 2, 'test', ], - '1' => [ + '1' => [ 1, 2, [ @@ -197,7 +198,7 @@ public function toJson($options = 320): ?string ], ], ], - 'csl' => [ + 'csl' => [ '1', 2, 'test', @@ -223,12 +224,12 @@ public function toJson($options = 320): ?string [ new class () implements \JsonSerializable { private $data = - [ + [ - '132', - 12, - 'test', - ]; + '132', + 12, + 'test', + ]; public function jsonSerialize(): mixed { @@ -283,7 +284,7 @@ public function toArray(): array return $this->data; } }; - $jsonableClass = new class () implements \Php\Support\Interfaces\Jsonable { + $jsonableClass = new class () implements \Php\Support\Interfaces\Jsonable { private $data = [ '32', 12, @@ -349,12 +350,12 @@ public function toJson($options = 320): ?string [ new class () implements \JsonSerializable { private $data = - [ + [ - '132', - 12, - 'test', - ]; + '132', + 12, + 'test', + ]; public function jsonSerialize(): mixed { @@ -425,8 +426,8 @@ public function testExists(): void { $array = [ 'key1' => 'val1', - 2 => 'val2', - 0 => 'val0', + 2 => 'val2', + 0 => 'val0', 'test' => 'test', ]; @@ -456,9 +457,9 @@ public function testExists(): void public function testToIndexedArray(): void { $array = [ - 'key1' => 'val1', - 'test' => 'test', - 'nested' => [ + 'key1' => 'val1', + 'test' => 'test', + 'nested' => [ 'n1' => 'test1', 'n2' => 'test2', ], @@ -508,9 +509,9 @@ public function testToPostgresArray(): void '{val1,test,null,,null}', Arr::ToPostgresArray( [ - 'key1' => 'val1', - 'test' => 'test', - 'nested' => null, + 'key1' => 'val1', + 'test' => 'test', + 'nested' => null, 'indexed1' => '', 'indexed2' => null, ] @@ -921,7 +922,7 @@ public function testRemoveByValueAndReindex($expArray, $expIdx, $array, $val): v public function providerGet(): array { $array = [ - 'key' => [ + 'key' => [ 'sub1' => 'val1', 'sub2' => [ 'val2', @@ -1032,7 +1033,7 @@ public function testGet($expVal, $array, $key): void public function providerHas(): array { $array = [ - 'key' => [ + 'key' => [ 'sub1' => 'val1', 'sub2' => [ 'val2', @@ -1190,7 +1191,7 @@ public function testSet2(): void public function providerRemove(): array { $array = [ - 'key' => [ + 'key' => [ 'sub1' => 'val1', 'sub2' => [ 'val2', @@ -1276,79 +1277,79 @@ public function dataReplaceByTemplate(): array ], [ [ - 'key' => '{{%KEY%}}', + 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', ], [ - '{{%KEY%}}' => 'vKey', + '{{%KEY%}}' => 'vKey', '{{%TOKEN%}}' => 'vToken', ], [ - 'key' => 'vKey', + 'key' => 'vKey', 'token' => 'vToken', ], ], [ [ - 'key' => '{{%KEY%}}', + 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', ], ['{{%KEY%}}' => 'vKey'], [ - 'key' => 'vKey', + 'key' => 'vKey', 'token' => '{{%TOKEN%}}', ], ], [ [ - 'key' => '{{%KEY%}}', + 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', ], ['{{%KEY%}}' => ''], [ - 'key' => '', + 'key' => '', 'token' => '{{%TOKEN%}}', ], ], [ [ - 'key' => '{{%KEY%}}', + 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', ], ['{{%KEY%}}' => null], [ - 'key' => '', + 'key' => '', 'token' => '{{%TOKEN%}}', ], ], [ [ 'step1' => [ - 'key' => '{{%KEY%}}', + 'key' => '{{%KEY%}}', 'token' => '{{%TOKEN%}}', ], 'step2' => [ 'subStep2' => [ 'token' => '{{%TOKEN%}}', - 'key' => '{{%KEY%}}', + 'key' => '{{%KEY%}}', ], ], 'step3' => ['val' => '{{%VALUE%}}'], ], [ - '{{%KEY%}}' => 'vKey', + '{{%KEY%}}' => 'vKey', '{{%TOKEN%}}' => 'vToken', '{{%VALUE%}}' => 12, ], [ 'step1' => [ - 'key' => 'vKey', + 'key' => 'vKey', 'token' => 'vToken', ], 'step2' => [ 'subStep2' => [ 'token' => 'vToken', - 'key' => 'vKey', + 'key' => 'vKey', ], ], 'step3' => ['val' => '12'], @@ -1357,7 +1358,7 @@ public function dataReplaceByTemplate(): array [ ['sdasdas'], [ - '{{%KEY%}}' => 'key', + '{{%KEY%}}' => 'key', '{{%TOKEN%}}' => 'token', ], ['sdasdas'], @@ -1387,4 +1388,26 @@ public function testReplaceByTemplate(array $array, array $replace, array $exp): static::assertJsonStringEqualsJsonString(\json_encode($exp), \json_encode($res)); // static::assertEquals($exp, $res); } + + /** + * @test + */ + public function collapse(): void + { + $list = [new ArrayCollection([1, 2, 3]), 4, 5, 6, [7, 8, 9]]; + + self::assertEquals([1, 2, 3, 7, 8, 9], Arr::collapse($list)); + } + + /** + * @test + */ + public function prepend(): void + { + $list = [1, 2, 3]; + self::assertEquals([5, 1, 2, 3], Arr::prepend($list, 5)); + + $list = ['One' => 1, 'Two' => 2]; + self::assertEquals(['Five' => 5, 'One' => 1, 'Two' => 2], Arr::prepend($list, 5, 'Five')); + } } From 0ae8fc155d5addb0f72938369409605a31f380d3 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 16 Dec 2022 17:53:29 +0200 Subject: [PATCH 49/95] fix --- composer.json | 2 -- phpstan.neon.dist | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e423118..21ab5b6 100755 --- a/composer.json +++ b/composer.json @@ -47,12 +47,10 @@ "phpstan": "@php ./vendor/bin/phpstan analyze -c ./phpstan.neon.dist --no-progress --ansi", "test": [ "@phpstan", - "@phpcs", "@phpunit" ], "test-cover": [ "@phpstan", - "@phpcs", "@phpunit-cover" ] } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 34f2b4b..4bc4288 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,3 +7,4 @@ parameters: excludePaths: - 'src/Types/Point.php' - 'src/Types/GeoPoint.php' + - 'src/Structures/Collections/ArrayCollection.php' From cffff6e304640bd1a0f3016bad5c1851efb53d23 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 16 Dec 2022 17:54:11 +0200 Subject: [PATCH 50/95] ci: add php 8.2 --- .github/workflows/php.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index d804889..3d8d9d9 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -37,7 +37,7 @@ jobs: fail-fast: false matrix: setup: [ 'basic', 'lowest', 'stable' ] - php: [ '8.0','8.1' ] + php: [ '8.0', '8.1' ,'8.2' ] steps: - uses: actions/checkout@v2 From df3b8c59387d3818d7bf2c9d094dc11c01606c04 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 16 Dec 2022 18:02:48 +0200 Subject: [PATCH 51/95] docs: update --- readme.md | 208 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 109 insertions(+), 99 deletions(-) diff --git a/readme.md b/readme.md index f1d5811..db4a9df 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # PHP Support -![](https://img.shields.io/badge/php-^8.0|^8.1-blue.svg) +![](https://img.shields.io/badge/php-8.0|8.1|8.2-blue.svg) ![PHP Package](https://github.com/efureev/php-support/workflows/PHP%20Package/badge.svg?branch=master) [![Build Status](https://travis-ci.org/efureev/php-support.svg?branch=master)](https://travis-ci.org/efureev/php-support) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a53fb85fd1ab46169758e10dd2d818cb)](https://app.codacy.com/app/efureev/php-support?utm_source=github.com&utm_medium=referral&utm_content=efureev/php-support&utm_campaign=Badge_Grade_Settings) @@ -12,10 +12,10 @@ ## Install -For php >= 8.0 (8.1) +For php >= 8.0 (8.1, 8.2) ```bash -composer require efureev/support "^4.13" +composer require efureev/support "^4.16" ``` For php >= 7.4 and <=8.0 @@ -33,107 +33,117 @@ composer require efureev/support "^2.0" ## Content - Helpers - + Array - - accessible - - dataToArray - - exists - - fromPostgresArray - - fromPostgresPoint (^4.8.0) - - get - - has - - merge - - remove - - removeByValue - - set - - toArray - - toIndexedArray - - toPostgresArray - - toPostgresPoint (^4.8.0) - - replaceByTemplate - + String - - removeAccents (^4.9.0) - - removeMultiSpace - - replaceByTemplate - - replaceStrTo - - seemsUTF8 (^4.9.0) - - slugify (^4.9.0) - - toCamel - - toDelimited - - toKebab - - toLowerCamel - - toScreamingDelimited - - toScreamingSnake - - toSnake - - truncate (^4.9.0) - + Json - - decode - - encode - - htmlEncode - + Bit - - addFlag - - checkFlag - - decBinPad - - exist - - grant - - removeFlag - + B64 - - decode - - decodeSafe - - encode - - encodeSafe - + Number - - safeInt (^4.1.0) + + Array + + collapse (^4.16.0) + + prepend (^4.16.0) + + - accessible + - dataToArray + - exists + - fromPostgresArray + - fromPostgresPoint (^4.8.0) + - get + - has + - merge + - remove + - removeByValue + - replaceByTemplate + - set + - toArray + - toIndexedArray + - toPostgresArray + - toPostgresPoint (^4.8.0) + + String + - removeAccents (^4.9.0) + - removeMultiSpace + - replaceByTemplate + - replaceStrTo + - seemsUTF8 (^4.9.0) + - slugify (^4.9.0) + - toCamel + - toDelimited + - toKebab + - toLowerCamel + - toScreamingDelimited + - toScreamingSnake + - toSnake + - truncate (^4.9.0) + + Json + - decode + - encode + - htmlEncode + + Bit + - addFlag + - checkFlag + - decBinPad + - exist + - grant + - removeFlag + + B64 + - decode + - decodeSafe + - encode + - encodeSafe + + Number + - isInteger (^4.14.0) + - safeInt (^4.1.0) + - Global functions - + does_trait_use (^4.4.0) - + classNamespace - + class_basename - + class_uses_recursive - + instance - + isTrue - + trait_uses_recursive - + value - + remoteCall (^4.3.1) - + remoteStaticCall (^4.3.1) - + remoteStaticCallOrTrow (^4.7.0) - + when + + classNamespace + + class_basename + + class_uses_recursive + + dataGet (^4.16.0) + + does_trait_use (^4.4.0) + + eachValue (^4.15.0) + + instance + + isTrue + + mapValue (^4.15.0) + + remoteCall (^4.3.1) + + remoteStaticCall (^4.3.1) + + remoteStaticCallOrTrow (^4.7.0) + + trait_uses_recursive + + value + + when - Exceptions - + ConfigException - + Exception - + InvalidArgumentException - + InvalidCallException - + InvalidConfigException - + InvalidParamException - + InvalidValueException - + JsonException - + MethodNotAllowedException - + MissingClassException - + MissingConfigException - + MissingPropertyException - + MissingMethodException (^4.7.0) - + NotSupportedException - + UnknownMethodException - + UnknownPropertyException + + ConfigException + + Exception + + InvalidArgumentException + + InvalidCallException + + InvalidConfigException + + InvalidParamException + + InvalidValueException + + JsonException + + MethodNotAllowedException + + MissingClassException + + MissingConfigException + + MissingPropertyException + + MissingMethodException (^4.7.0) + + NotSupportedException + + UnknownMethodException + + UnknownPropertyException - Interfaces - + Arrayable - + Command - + Jsonable - + Prototype + + Arrayable + + Command + + Jsonable + + Prototype +- Structures + - Collections (^4.16.0) - Traits - + ArrayStorage - + ArrayStorageConfigurableTrait - + ConfigurableTrait - + ConsolePrint - + Maker - + Metable - + ReadOnlyProperties - + Singleton - + Thrower - + TraitBooter - + TraitInitializer - + Whener + + ArrayStorage + + ArrayStorageConfigurableTrait + + ConfigurableTrait + + ConsolePrint + + Maker + + Metable + + ReadOnlyProperties + + Singleton + + Thrower + + TraitBooter + + TraitInitializer + + Whener - Types - + GeoPoint - + Point + + GeoPoint + + Point ## Test From f0dd45c8781414bf1222b2da070e004f320a8905 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Wed, 11 Jan 2023 19:10:00 +0200 Subject: [PATCH 52/95] fix --- .github/workflows/php.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 3d8d9d9..5dadcb1 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -40,7 +40,7 @@ jobs: php: [ '8.0', '8.1' ,'8.2' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use PHP ${{ matrix.php }} uses: shivammathur/setup-php@v2 # Action page: @@ -52,10 +52,10 @@ jobs: - name: Get Composer Cache Directory # Docs: id: composer-cache run: | - echo "::set-output name=dir::$(composer config cache-files-dir)" + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} From 9cef54d9cf32ba4328c6181eccc7f82be4372687 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 24 Feb 2023 22:52:43 +0200 Subject: [PATCH 53/95] up --- .github/workflows/php.yml | 2 +- CHANGELOG.md | 10 ++++ composer.json | 8 +-- phpunit.xml | 51 +++++++++---------- src/Helpers/Arr.php | 21 ++++---- .../Collections/ArrayCollection.php | 1 + tests/traits/ArrayStorageTest.php | 30 +++-------- 7 files changed, 56 insertions(+), 67 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 5dadcb1..bca2b42 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -37,7 +37,7 @@ jobs: fail-fast: false matrix: setup: [ 'basic', 'lowest', 'stable' ] - php: [ '8.0', '8.1' ,'8.2' ] + php: [ '8.1' ,'8.2' ] steps: - uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b61862..55c14f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,16 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher - Add method `Number::isInteger` Allows you to determine whether the $value is an integer or not +## v4.15.0 + +### Added + +- Add support `PHP 8.2` + +### Removed + +- Remove support `PHP 8.0` + ## v4.13.0 ### Added diff --git a/composer.json b/composer.json index 21ab5b6..7739769 100755 --- a/composer.json +++ b/composer.json @@ -16,13 +16,13 @@ } ], "require": { - "php": "^8.0|^8.1", + "php": "^8.1|^8.2", "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^9.5", - "phpstan/phpstan": "^1.9.3", - "squizlabs/php_codesniffer": "^3.7" + "phpunit/phpunit": "^10.0.11", + "phpstan/phpstan": "^1.10.2", + "squizlabs/php_codesniffer": "^3.7.2" }, "autoload": { "files": [ diff --git a/phpunit.xml b/phpunit.xml index 1071b9a..e9dfc8d 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,30 +1,25 @@ - - - - ./tests - - - - - ./src - - - - - - - - - + + + + ./src + + + + + + + + + + + ./tests + + + + + diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index a66e526..c2e0871 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -230,14 +230,13 @@ public static function toIndexedArray(array $array): array */ public static function fromPostgresArrayWithBraces( ?string $s, - int $start = 0, - ?int &$end = null, - array $braces = [ + int $start = 0, + ?int &$end = null, + array $braces = [ '{', '}', ] - ): array - { + ): array { [ $braceOpen, $braceClose, @@ -248,9 +247,9 @@ public static function fromPostgresArrayWithBraces( $return = []; $string = false; - $quote = ''; - $len = strlen($s); - $v = ''; + $quote = ''; + $len = strlen($s); + $v = ''; for ($i = $start + 1; $i < $len; $i++) { $ch = $s[$i]; @@ -266,11 +265,11 @@ public static function fromPostgresArrayWithBraces( } else { if (!$string && $ch === ',') { $return[] = $v; - $v = ''; + $v = ''; } else { if (!$string && ($ch === '"' || $ch === "'")) { $string = true; - $quote = $ch; + $quote = $ch; } else { if ($string && $ch === $quote) { if ($s[$i - 1] === "\\") { @@ -486,7 +485,7 @@ public static function set(array|ArrayObject|null &$array, string $key, mixed $v public static function remove(array|ArrayObject &$array, array|string $keys): void { $original = &$array; - $keys = (array)$keys; + $keys = (array)$keys; if (count($keys) === 0) { return; diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 492eab2..74defdb 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -9,6 +9,7 @@ use Php\Support\Helpers\Arr; use Stringable; use Traversable; + use function array_chunk; use function array_filter; use function array_key_exists; diff --git a/tests/traits/ArrayStorageTest.php b/tests/traits/ArrayStorageTest.php index 5b39cac..9b02c74 100644 --- a/tests/traits/ArrayStorageTest.php +++ b/tests/traits/ArrayStorageTest.php @@ -31,7 +31,7 @@ public function testSetAndGet(): void public function testDeepData(): void { $config = new ArrayStorageClassTest(); - $key = 'test.sub.key'; + $key = 'test.sub.key'; $config->$key = 1; @@ -53,7 +53,7 @@ public function testGetData(): void $expected = [ 'test' => [ - 'sub' => [ + 'sub' => [ 'key' => 1, 'val' => 'value', ], @@ -74,7 +74,7 @@ public function testUnset(): void static::assertTrue(isset($config->name)); static::assertNull($config->name); - $key = 'test.sub.key'; + $key = 'test.sub.key'; $config->$key = 1; static::assertEquals(1, $config->$key); @@ -83,22 +83,15 @@ public function testUnset(): void static::assertFalse(isset($config->$key)); - $this->expectNotice(); - static::assertNull($config->$key); - unset($config->{'tst.sdf'}); static::assertFalse(isset($config->{'tst.sdf'})); - - $this->expectNotice(); - static::assertNull($config->$key); } public function testAbsent(): void { $config = new ArrayStorageClassTest(); - $this->expectNotice(); static::assertNull($config->test); } @@ -138,7 +131,7 @@ public function testValueExist(): void public function testOffsetExists(): void { - $config = new ArrayStorageClassTest(); + $config = new ArrayStorageClassTest(); $config->test2 = 'test2'; static::assertTrue($config->offsetExists('test2')); @@ -151,7 +144,7 @@ public function testOffsetExists(): void public function testOffsetGet(): void { - $config = new ArrayStorageClassTest(); + $config = new ArrayStorageClassTest(); $config->test2 = 'test2'; static::assertEquals('test2', $config->offsetGet('test2')); @@ -159,14 +152,11 @@ public function testOffsetGet(): void $config->null = null; static::assertNull($config['null']); - - $this->expectNotice(); - static::assertNull($config['null2']); } public function testOffsetSet(): void { - $config = new ArrayStorageClassTest(); + $config = new ArrayStorageClassTest(); $config['test2'] = 'val2'; static::assertEquals('val2', $config->test2); @@ -175,14 +165,11 @@ public function testOffsetSet(): void $config['null'] = null; static::assertNull($config['null']); - - $this->expectNotice(); - static::assertNull($config['null2']); } public function testOffsetUnset(): void { - $config = new ArrayStorageClassTest(); + $config = new ArrayStorageClassTest(); $config['test2'] = 'val2'; static::assertEquals('val2', $config->test2); @@ -190,9 +177,6 @@ public function testOffsetUnset(): void static::assertEquals('val2', $config['test2']); unset($config['test2']); - - $this->expectNotice(); - static::assertNull($config['test2']); } } From 2c19bca5c4bf477c225ac79b82f1d17467b9788e Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 24 Feb 2023 22:55:55 +0200 Subject: [PATCH 54/95] up --- CHANGELOG.md | 20 ++++++++++---------- readme.md | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55c14f0..db0bb6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.17.0 + +### Added + +- Add support `PHP 8.2` + +### Removed + +- Remove support `PHP 8.0` + ## v4.16.0 ### Added @@ -26,16 +36,6 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher - Add method `Number::isInteger` Allows you to determine whether the $value is an integer or not -## v4.15.0 - -### Added - -- Add support `PHP 8.2` - -### Removed - -- Remove support `PHP 8.0` - ## v4.13.0 ### Added diff --git a/readme.md b/readme.md index db4a9df..f2218f6 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # PHP Support -![](https://img.shields.io/badge/php-8.0|8.1|8.2-blue.svg) +![](https://img.shields.io/badge/php-8.1|8.2-blue.svg) ![PHP Package](https://github.com/efureev/php-support/workflows/PHP%20Package/badge.svg?branch=master) [![Build Status](https://travis-ci.org/efureev/php-support.svg?branch=master)](https://travis-ci.org/efureev/php-support) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a53fb85fd1ab46169758e10dd2d818cb)](https://app.codacy.com/app/efureev/php-support?utm_source=github.com&utm_medium=referral&utm_content=efureev/php-support&utm_campaign=Badge_Grade_Settings) @@ -12,7 +12,7 @@ ## Install -For php >= 8.0 (8.1, 8.2) +For php >= 8.1 (8.1, 8.2) ```bash composer require efureev/support "^4.16" From 4b4ca8bf599531f3785b1e9fc5f6ad48942687a0 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Thu, 2 Mar 2023 10:47:02 +0200 Subject: [PATCH 55/95] feat: Collection::filter --- CHANGELOG.md | 6 ++++++ src/Structures/Collections/ArrayCollection.php | 2 +- src/Structures/Collections/ReadableCollection.php | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db0bb6b..7258731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.17.1 + +### Changed + +- `Collection::filter(Closure $func = null)` - The argument `$func` may be `null` + ## v4.17.0 ### Added diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 74defdb..2517175 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -282,7 +282,7 @@ private function getProperty(mixed $target, string|int $keyName, bool $throwOnMi * @return static * @psalm-return static */ - public function filter(Closure $func): static + public function filter(Closure $func = null): static { return $this->createFrom(array_filter($this->elements, $func, ARRAY_FILTER_USE_BOTH)); } diff --git a/src/Structures/Collections/ReadableCollection.php b/src/Structures/Collections/ReadableCollection.php index d6e8270..a85db79 100644 --- a/src/Structures/Collections/ReadableCollection.php +++ b/src/Structures/Collections/ReadableCollection.php @@ -162,13 +162,13 @@ public function exists(Closure $func): bool; * Returns all the elements of this collection that satisfy the predicate $func. * The order of the elements is preserved. * - * @param Closure $func The predicate used for filtering. - * @psalm-param Closure(T, TKey):bool $func + * @param null|Closure $func The predicate used for filtering. + * @psalm-param null|Closure(T, TKey):bool $func * * @return ReadableCollection A collection with the results of the filter operation. * @psalm-return ReadableCollection */ - public function filter(Closure $func): ReadableCollection; + public function filter(Closure $func = null): ReadableCollection; /** * Applies the given function to each element in the collection and returns From 9d212bcb414ad77358a0be38135ce92fcf0a59f5 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Thu, 2 Mar 2023 10:56:12 +0200 Subject: [PATCH 56/95] feat: add Collection::reject --- CHANGELOG.md | 6 ++++++ src/Structures/Collections/ArrayCollection.php | 11 +++++++++++ src/Structures/Collections/ReadableCollection.php | 11 +++++++++++ 3 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7258731..8eaaa98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.18.0 + +### Added + +- Add function `Collection::reject` + ## v4.17.1 ### Changed diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 2517175..7e2872d 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -287,6 +287,17 @@ public function filter(Closure $func = null): static return $this->createFrom(array_filter($this->elements, $func, ARRAY_FILTER_USE_BOTH)); } + /** + * {@inheritDoc} + * + * @return static + * @psalm-return static + */ + public function reject(Closure $callback): static + { + return $this->filter(static fn($value, $key) => !$callback($value, $key)); + } + /** * {@inheritDoc} */ diff --git a/src/Structures/Collections/ReadableCollection.php b/src/Structures/Collections/ReadableCollection.php index a85db79..9ae3fb5 100644 --- a/src/Structures/Collections/ReadableCollection.php +++ b/src/Structures/Collections/ReadableCollection.php @@ -170,6 +170,17 @@ public function exists(Closure $func): bool; */ public function filter(Closure $func = null): ReadableCollection; + /** + * Create a collection of all elements that do not pass a given truth test. + * + * @param Closure $func The predicate used for filtering. + * @psalm-param Closure(T, TKey):bool $func + * + * @return ReadableCollection A collection with the results of the filter operation. + * @psalm-return ReadableCollection + */ + public function reject(Closure $callback): ReadableCollection; + /** * Applies the given function to each element in the collection and returns * a new collection with the elements returned by the function. From 3cab7b6a667dc41de812272bee7b7a447f44e2a8 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Mon, 27 Mar 2023 16:05:57 +0300 Subject: [PATCH 57/95] fix --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eaaa98..5c23101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.19.* + +### Added + +- Something + ## v4.18.0 ### Added From 77b4d739179808bab0b8f74e98e476f4721e44ed Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Mon, 27 Mar 2023 16:50:01 +0300 Subject: [PATCH 58/95] fix --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c2b9f0..7ff256d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,11 +14,11 @@ jobs: uses: actions/checkout@master - name: Create Release id: create_release - uses: actions/create-release@v1 + uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: startsWith(github.ref, 'refs/tags/') with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} + name: Release ${{ github.ref }} draft: false prerelease: false From 40b6c13cbcc27541330f3e12e0e03df7a1c918e7 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 21 Apr 2023 10:54:46 +0300 Subject: [PATCH 59/95] feat: add Enum`s traits --- CHANGELOG.md | 12 +- readme.md | 122 +++++++++++---------- src/Enums/WithEnhances.php | 32 ++++++ src/Enums/WithEnhancesForStrings.php | 39 +++++++ tests/enums/WithEnhancesForStringsTest.php | 79 +++++++++++++ tests/enums/data/StringsEnum.php | 18 +++ 6 files changed, 245 insertions(+), 57 deletions(-) create mode 100644 src/Enums/WithEnhances.php create mode 100644 src/Enums/WithEnhancesForStrings.php create mode 100644 tests/enums/WithEnhancesForStringsTest.php create mode 100644 tests/enums/data/StringsEnum.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c23101..ff4c47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. -## v4.19.* +## v4.19.0 ### Added -- Something +- Add traits for + Enums: [WithEnhances.php](./src/Enums/WithEnhances.php), [WithEnhancesForStrings](./src/Enums/WithEnhancesForStrings.php) + with following methods: + - `casesToString`- Returns string of Enum's names or values + - `casesToEscapeString`- Returns string of Enum's escaped names or values + - `values`- Returns list of Enum's values + - `names` - Returns list of Enum's names + - `hasValue` - Check if the Enum has provided Value + - `hasName` - Check if the Enum has provided Name ## v4.18.0 diff --git a/readme.md b/readme.md index f2218f6..b7b3fee 100644 --- a/readme.md +++ b/readme.md @@ -15,7 +15,7 @@ For php >= 8.1 (8.1, 8.2) ```bash -composer require efureev/support "^4.16" +composer require efureev/support "^4.19" ``` For php >= 7.4 and <=8.0 @@ -33,60 +33,59 @@ composer require efureev/support "^2.0" ## Content - Helpers - + Array - + collapse (^4.16.0) - + prepend (^4.16.0) - - - accessible - - dataToArray - - exists - - fromPostgresArray - - fromPostgresPoint (^4.8.0) - - get - - has - - merge - - remove - - removeByValue - - replaceByTemplate - - set - - toArray - - toIndexedArray - - toPostgresArray - - toPostgresPoint (^4.8.0) - + String - - removeAccents (^4.9.0) - - removeMultiSpace - - replaceByTemplate - - replaceStrTo - - seemsUTF8 (^4.9.0) - - slugify (^4.9.0) - - toCamel - - toDelimited - - toKebab - - toLowerCamel - - toScreamingDelimited - - toScreamingSnake - - toSnake - - truncate (^4.9.0) - + Json - - decode - - encode - - htmlEncode - + Bit - - addFlag - - checkFlag - - decBinPad - - exist - - grant - - removeFlag - + B64 - - decode - - decodeSafe - - encode - - encodeSafe - + Number - - isInteger (^4.14.0) - - safeInt (^4.1.0) + - Array + - collapse (^4.16.0) + - prepend (^4.16.0) + - accessible + - dataToArray + - exists + - fromPostgresArray + - fromPostgresPoint (^4.8.0) + - get + - has + - merge + - remove + - removeByValue + - replaceByTemplate + - set + - toArray + - toIndexedArray + - toPostgresArray + - toPostgresPoint (^4.8.0) + + String + - removeAccents (^4.9.0) + - removeMultiSpace + - replaceByTemplate + - replaceStrTo + - seemsUTF8 (^4.9.0) + - slugify (^4.9.0) + - toCamel + - toDelimited + - toKebab + - toLowerCamel + - toScreamingDelimited + - toScreamingSnake + - toSnake + - truncate (^4.9.0) + + Json + - decode + - encode + - htmlEncode + + Bit + - addFlag + - checkFlag + - decBinPad + - exist + - grant + - removeFlag + + B64 + - decode + - decodeSafe + - encode + - encodeSafe + + Number + - isInteger (^4.14.0) + - safeInt (^4.1.0) - Global functions + classNamespace @@ -104,6 +103,15 @@ composer require efureev/support "^2.0" + trait_uses_recursive + value + when + +- Enums (^4.19.0) + - casesToEscapeString + - casesToString + - hasName + - hasValue + - names + - values + - Exceptions + ConfigException + Exception @@ -121,13 +129,16 @@ composer require efureev/support "^2.0" + NotSupportedException + UnknownMethodException + UnknownPropertyException + - Interfaces + Arrayable + Command + Jsonable + Prototype + - Structures - Collections (^4.16.0) + - Traits + ArrayStorage + ArrayStorageConfigurableTrait @@ -141,6 +152,7 @@ composer require efureev/support "^2.0" + TraitBooter + TraitInitializer + Whener + - Types + GeoPoint + Point diff --git a/src/Enums/WithEnhances.php b/src/Enums/WithEnhances.php new file mode 100644 index 0000000..9e16b05 --- /dev/null +++ b/src/Enums/WithEnhances.php @@ -0,0 +1,32 @@ + $enumItem->value, self::cases()); + } + + public static function names(): array + { + return array_map(static fn(self $enumItem) => $enumItem->name, self::cases()); + } +} diff --git a/src/Enums/WithEnhancesForStrings.php b/src/Enums/WithEnhancesForStrings.php new file mode 100644 index 0000000..9eb60c9 --- /dev/null +++ b/src/Enums/WithEnhancesForStrings.php @@ -0,0 +1,39 @@ + "{$enumItem->value}"; + } + + return self::casesToStringBase($decorator, $delimiter); + } + + public static function casesToEscapeString(string $delimiter = ', ', callable $decorator = null): string + { + return static::casesToString($delimiter, static fn(self $enumItem) => "'$enumItem->value'"); + } + + public static function hasValue(string $value): bool + { + return in_array($value, static::values(), true); + } + + public static function hasName(string $value): bool + { + return in_array($value, static::names(), true); + } +} diff --git a/tests/enums/WithEnhancesForStringsTest.php b/tests/enums/WithEnhancesForStringsTest.php new file mode 100644 index 0000000..2c507d3 --- /dev/null +++ b/tests/enums/WithEnhancesForStringsTest.php @@ -0,0 +1,79 @@ + Date: Fri, 21 Apr 2023 10:55:13 +0300 Subject: [PATCH 60/95] feat: add Enum`s traits --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 1fb8642..13299c0 100755 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ clover.xml summary.log per-mutator.md infection.log + +/.phpunit.cache From 8af744c7fc56a163993ac2e15b22c6904cf5fae6 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 21 Apr 2023 10:57:32 +0300 Subject: [PATCH 61/95] feat: add Enum`s traits --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff4c47c..f4d8065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,12 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher - Add traits for Enums: [WithEnhances.php](./src/Enums/WithEnhances.php), [WithEnhancesForStrings](./src/Enums/WithEnhancesForStrings.php) with following methods: - - `casesToString`- Returns string of Enum's names or values - - `casesToEscapeString`- Returns string of Enum's escaped names or values - - `values`- Returns list of Enum's values - - `names` - Returns list of Enum's names - - `hasValue` - Check if the Enum has provided Value - - `hasName` - Check if the Enum has provided Name + - `casesToString`- Returns string of Enum's names or values + - `casesToEscapeString`- Returns string of Enum's escaped names or values + - `values`- Returns list of Enum's values + - `names` - Returns list of Enum's names + - `hasValue` - Check if the Enum has provided Value + - `hasName` - Check if the Enum has provided Name ## v4.18.0 From 0320fcf2b171e54d35bac2b0c207fc79e1552c5f Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 21 Apr 2023 11:16:17 +0300 Subject: [PATCH 62/95] fix: tests --- .github/workflows/php.yml | 2 +- composer.json | 4 ++-- phpunit.xml | 10 +++++---- .../Collections/ReadableCollection.php | 5 +++-- tests/Helpers/ArrTest.php | 22 ++++++++----------- tests/Helpers/NumberTest.php | 4 ++-- tests/Helpers/StrTest.php | 16 +++++++------- 7 files changed, 31 insertions(+), 32 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index bca2b42..c99fac8 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -87,4 +87,4 @@ jobs: # Docs: https://getcomposer.org/doc/articles/scripts.md - name: Run test suite - run: composer test + run: XDEBUG_MODE=coverage composer test diff --git a/composer.json b/composer.json index 7739769..c7e2c55 100755 --- a/composer.json +++ b/composer.json @@ -20,8 +20,8 @@ "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^10.0.11", - "phpstan/phpstan": "^1.10.2", + "phpunit/phpunit": "^10.1.1", + "phpstan/phpstan": "^1.10.14", "squizlabs/php_codesniffer": "^3.7.2" }, "autoload": { diff --git a/phpunit.xml b/phpunit.xml index e9dfc8d..02cedd4 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,12 +1,9 @@ - - ./src - @@ -22,4 +19,9 @@ + + + ./src + + diff --git a/src/Structures/Collections/ReadableCollection.php b/src/Structures/Collections/ReadableCollection.php index 9ae3fb5..4168cae 100644 --- a/src/Structures/Collections/ReadableCollection.php +++ b/src/Structures/Collections/ReadableCollection.php @@ -173,8 +173,8 @@ public function filter(Closure $func = null): ReadableCollection; /** * Create a collection of all elements that do not pass a given truth test. * - * @param Closure $func The predicate used for filtering. - * @psalm-param Closure(T, TKey):bool $func + * @param Closure $callback The predicate used for filtering. + * @psalm-param Closure(T, TKey):bool $callback * * @return ReadableCollection A collection with the results of the filter operation. * @psalm-return ReadableCollection @@ -255,6 +255,7 @@ public function transform(Closure $func): static; * @param \iterable|Arrayable $items * @return static */ + // @phpstan-ignore-next-line public function merge(mixed $items): static; /** diff --git a/tests/Helpers/ArrTest.php b/tests/Helpers/ArrTest.php index ac9720d..b960f65 100644 --- a/tests/Helpers/ArrTest.php +++ b/tests/Helpers/ArrTest.php @@ -106,7 +106,7 @@ public function testMerge(): void } - public function providerDataToArray(): array + public static function providerDataToArray(): array { $arrayableClass = new class () implements \Php\Support\Interfaces\Arrayable { private $data = [ @@ -257,8 +257,6 @@ public function jsonSerialize(): mixed * * @param mixed $items * @param $exp - * - * @throws \Php\Support\Exceptions\JsonException */ public function testDataToArray($items, $exp): void { @@ -270,7 +268,7 @@ public function testDataToArray($items, $exp): void /** * @return array */ - public function providerToArray(): array + public static function providerToArray(): array { $arrayableClass = new class () implements \Php\Support\Interfaces\Arrayable { private $data = [ @@ -383,8 +381,6 @@ public function jsonSerialize(): mixed * * @param $items * @param $exp - * - * @throws \Php\Support\Exceptions\JsonException */ public function testToArray($items, $exp): void { @@ -595,7 +591,7 @@ public function testFromPostgresPoint(): void /** * @return array */ - public function providerRemoveByValue(): array + public static function providerRemoveByValue(): array { return [ [ @@ -756,7 +752,7 @@ public function testRemoveByValue($expArray, $expIdx, $array, $val): void /** * @return array */ - public function providerRemoveByValueAndReindex(): array + public static function providerRemoveByValueAndReindex(): array { return [ [ @@ -919,7 +915,7 @@ public function testRemoveByValueAndReindex($expArray, $expIdx, $array, $val): v /** * @return array */ - public function providerGet(): array + public static function providerGet(): array { $array = [ 'key' => [ @@ -1030,7 +1026,7 @@ public function testGet($expVal, $array, $key): void /** * @return array */ - public function providerHas(): array + public static function providerHas(): array { $array = [ 'key' => [ @@ -1104,7 +1100,7 @@ public function testHas($expVal, $array, $key): void /** * @return array */ - public function providerSet(): array + public static function providerSet(): array { $array = []; @@ -1188,7 +1184,7 @@ public function testSet2(): void /** * @return array */ - public function providerRemove(): array + public static function providerRemove(): array { $array = [ 'key' => [ @@ -1267,7 +1263,7 @@ public function testRemove2(): void /** * @return array */ - public function dataReplaceByTemplate(): array + public static function dataReplaceByTemplate(): array { return [ [ diff --git a/tests/Helpers/NumberTest.php b/tests/Helpers/NumberTest.php index da0586d..c53b4d1 100644 --- a/tests/Helpers/NumberTest.php +++ b/tests/Helpers/NumberTest.php @@ -12,7 +12,7 @@ */ final class NumberTest extends TestCase { - public function providerSafeInt(): array + public static function providerSafeInt(): array { return [ [ @@ -82,7 +82,7 @@ public function testSafeInt(int|string $value, int|string $exp): void } - public function providerIsInteger(): array + public static function providerIsInteger(): array { return [ [ diff --git a/tests/Helpers/StrTest.php b/tests/Helpers/StrTest.php index ec8395a..5f14ada 100644 --- a/tests/Helpers/StrTest.php +++ b/tests/Helpers/StrTest.php @@ -15,7 +15,7 @@ final class StrTest extends TestCase { use HasReflection; - public function providerDataSnake(): array + public static function providerDataSnake(): array { return [ [ @@ -118,7 +118,7 @@ public function testSnake($str, $exp): void static::assertEquals($exp, $result); } - public function providerToScreamingSnake(): array + public static function providerToScreamingSnake(): array { return [ [ @@ -221,7 +221,7 @@ public function testToScreamingSnake($str, $exp): void static::assertEquals($exp, $result); } - public function providerDataKebab(): array + public static function providerDataKebab(): array { return [ [ @@ -325,7 +325,7 @@ public function testToKebab($str, $exp): void } - public function providerCamel(): array + public static function providerCamel(): array { return [ [ @@ -396,7 +396,7 @@ public function testToCamel($str, $exp): void static::assertEquals($exp, $result); } - public function providerLowerCamel(): array + public static function providerLowerCamel(): array { return [ [ @@ -487,7 +487,7 @@ public function testToCache(): void /** * @return array */ - public function dataReplaceStrTo(): array + public static function dataReplaceStrTo(): array { return [ [ @@ -602,7 +602,7 @@ public function testReplaceStrTo(string $val, int $fromStart, int $fromEnd, stri /** * @return array */ - public function dataReplaceByTemplate(): array + public static function dataReplaceByTemplate(): array { return [ [ @@ -647,7 +647,7 @@ public function testReplaceByTemplate(string $str, array $replaced, string $exp) static::assertEquals($exp, $result); } - public function dataRegExps(): array + public static function dataRegExps(): array { return [ [ From 22dcd50da64db870b5455384c69a6cfe04cd29e2 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 21 Apr 2023 12:30:03 +0300 Subject: [PATCH 63/95] fix: phpdoc --- src/Enums/WithEnhances.php | 7 ++++++- src/Enums/WithEnhancesForStrings.php | 6 +----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Enums/WithEnhances.php b/src/Enums/WithEnhances.php index 9e16b05..1af5118 100644 --- a/src/Enums/WithEnhances.php +++ b/src/Enums/WithEnhances.php @@ -5,7 +5,7 @@ namespace Php\Support\Enums; /** - * @mixin \BackedEnum + * @mixin \UnitEnum */ trait WithEnhances { @@ -29,4 +29,9 @@ public static function names(): array { return array_map(static fn(self $enumItem) => $enumItem->name, self::cases()); } + + public static function hasName(string $value): bool + { + return in_array($value, static::names(), true); + } } diff --git a/src/Enums/WithEnhancesForStrings.php b/src/Enums/WithEnhancesForStrings.php index 9eb60c9..54b060c 100644 --- a/src/Enums/WithEnhancesForStrings.php +++ b/src/Enums/WithEnhancesForStrings.php @@ -6,6 +6,7 @@ /** * @method static string[] values(); + * @mixin \BackedEnum */ trait WithEnhancesForStrings { @@ -31,9 +32,4 @@ public static function hasValue(string $value): bool { return in_array($value, static::values(), true); } - - public static function hasName(string $value): bool - { - return in_array($value, static::names(), true); - } } From 7c9e3938a32c87f718daf6f81b31f8bb939b165a Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Wed, 31 May 2023 17:27:57 +0300 Subject: [PATCH 64/95] feat [Str] add `slugifyWithFormat` --- CHANGELOG.md | 20 ++++++++++++++++++++ src/Helpers/Str.php | 11 ++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4d8065..2ed1e44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,26 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added +- Add traits for + Enums: [WithEnhances.php](./src/Enums/WithEnhances.php), [WithEnhancesForStrings](./src/Enums/WithEnhancesForStrings.php) + with following methods: + - `casesToString`- Returns string of Enum's names or values + - `casesToEscapeString`- Returns string of Enum's escaped names or values + - `values`- Returns list of Enum's values + - `names` - Returns list of Enum's names + - `hasValue` - Check if the Enum has provided Value + - `hasName` - Check if the Enum has provided Name + +## v4.20.0 + +### Added + +- Add method `slugifyWithFormat` into Str + +## v4.19.0 + +### Added + - Add traits for Enums: [WithEnhances.php](./src/Enums/WithEnhances.php), [WithEnhancesForStrings](./src/Enums/WithEnhancesForStrings.php) with following methods: diff --git a/src/Helpers/Str.php b/src/Helpers/Str.php index e09f13a..d66724d 100644 --- a/src/Helpers/Str.php +++ b/src/Helpers/Str.php @@ -361,7 +361,16 @@ public static function truncate(string $str, int $length, string $append = '...' */ public static function slugify(string $str, string $separator = '-', bool $firstLetterOnly = false): string { - $slug = preg_replace('/([^a-z\d]+)/', $separator, mb_strtolower(self::removeAccents($str))); + return self::slugifyWithFormat($str, $separator, '([^a-z\d]+)', $firstLetterOnly); + } + + public static function slugifyWithFormat( + string $str, + string $separator = '-', + string $format = '([^a-z\d]+)', + bool $firstLetterOnly = false + ): string { + $slug = preg_replace("/$format/", $separator, mb_strtolower(self::removeAccents($str))); if (empty($slug)) { return ''; } From e7fb26a34b86ea43d6356f906b9fbe3eac1fa1f2 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Wed, 31 May 2023 17:28:35 +0300 Subject: [PATCH 65/95] feat [Str] add `slugifyWithFormat` --- CHANGELOG.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ed1e44..47c7276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,20 +4,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. -## v4.19.0 - -### Added - -- Add traits for - Enums: [WithEnhances.php](./src/Enums/WithEnhances.php), [WithEnhancesForStrings](./src/Enums/WithEnhancesForStrings.php) - with following methods: - - `casesToString`- Returns string of Enum's names or values - - `casesToEscapeString`- Returns string of Enum's escaped names or values - - `values`- Returns list of Enum's values - - `names` - Returns list of Enum's names - - `hasValue` - Check if the Enum has provided Value - - `hasName` - Check if the Enum has provided Name - ## v4.20.0 ### Added From 9cd7fb419e67c9279b191c27c9bd21701ff9c1fe Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Thu, 20 Jul 2023 13:15:05 +0300 Subject: [PATCH 66/95] feat: add whereInstanceOf --- CHANGELOG.md | 8 +++++++- src/Structures/Collections/ArrayCollection.php | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47c7276..aa472af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.21.0 + +### Added + +- Add method `whereInstanceOf` into `ArrayCollection` + ## v4.20.0 ### Added -- Add method `slugifyWithFormat` into Str +- Add method `slugifyWithFormat` into `Str` ## v4.19.0 diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 7e2872d..a977c33 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -287,6 +287,21 @@ public function filter(Closure $func = null): static return $this->createFrom(array_filter($this->elements, $func, ARRAY_FILTER_USE_BOTH)); } + public function whereInstanceOf(string|array $type): static + { + return $this->filter( + static function ($value) use ($type) { + foreach ((array)$type as $classType) { + if ($value instanceof $classType) { + return true; + } + } + + return false; + } + ); + } + /** * {@inheritDoc} * From ad638ef572b4d20bcc33f39ec250b940ff29454c Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Thu, 20 Jul 2023 16:20:42 +0300 Subject: [PATCH 67/95] fix: Collection: each --- src/Structures/Collections/ArrayCollection.php | 10 ++++++++-- src/Structures/Collections/ReadableCollection.php | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index a977c33..7fa952c 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -316,9 +316,15 @@ public function reject(Closure $callback): static /** * {@inheritDoc} */ - public function each(Closure $func): bool + public function each(callable $func): static { - return $this->testForAll($func); + foreach ($this as $key => $item) { + if ($func($item, $key) === false) { + break; + } + } + + return $this; } /** diff --git a/src/Structures/Collections/ReadableCollection.php b/src/Structures/Collections/ReadableCollection.php index 4168cae..3d9d5bc 100644 --- a/src/Structures/Collections/ReadableCollection.php +++ b/src/Structures/Collections/ReadableCollection.php @@ -235,10 +235,10 @@ public function testForAll(Closure $func): bool; /** * Applies the given function to each element of the Collection. Returns the same Collection. * - * @param Closure $func The predicate. - * @psalm-param Closure(TKey, T):bool $func + * @param callable $func The predicate. + * @psalm-param callable(TKey, T):bool $func */ - public function each(Closure $func): bool; + public function each(callable $func): static; /** * Transform each item in the collection using a callback. From c99f391668aafbd916e3c902bdbbaa76156e1efa Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 21 Jul 2023 10:09:20 +0300 Subject: [PATCH 68/95] feat: add mapInto --- CHANGELOG.md | 6 ++++++ src/Structures/Collections/ArrayCollection.php | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa472af..28c1f61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## master + +### Added + +- Add method `mapInto` into `ArrayCollection` + ## v4.21.0 ### Added diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 7fa952c..5452274 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -241,6 +241,16 @@ public function map(Closure $func): static return $this->createFrom(array_map($func, $this->elements)); } + /** + * Map the values into a new class. + * + * @param string $class + * @return static + */ + public function mapInto(string $class): static + { + return $this->map(static fn($value) => new $class($value)); + } /** * {@inheritDoc} From b2f332c4a17d89c04277affbcb401c5cc9543f40 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 21 Jul 2023 10:11:08 +0300 Subject: [PATCH 69/95] fix --- src/Structures/Collections/ArrayCollection.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 5452274..036d786 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -245,11 +245,12 @@ public function map(Closure $func): static * Map the values into a new class. * * @param string $class + * @param mixed ...$params * @return static */ - public function mapInto(string $class): static + public function mapInto(string $class, mixed ...$params): static { - return $this->map(static fn($value) => new $class($value)); + return $this->map(static fn($value) => new $class($value, ...$params)); } /** From 6febc96c8209ef20d4ef1f64a690e91524e361dd Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 21 Jul 2023 11:25:16 +0300 Subject: [PATCH 70/95] release: v4.22.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28c1f61..a147b40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. -## master +## v4.22.0 ### Added From 4b9129d872d1ee9f104297af54c0362a9b001b2c Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Thu, 27 Jul 2023 18:07:19 +0300 Subject: [PATCH 71/95] fix --- src/Traits/ArrayStorage.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Traits/ArrayStorage.php b/src/Traits/ArrayStorage.php index 6a4e37a..69a4a55 100644 --- a/src/Traits/ArrayStorage.php +++ b/src/Traits/ArrayStorage.php @@ -5,7 +5,6 @@ namespace Php\Support\Traits; use ArrayAccess; -use Php\Support\Exceptions\JsonException; use Php\Support\Helpers\Arr; use Php\Support\Helpers\Json; @@ -137,6 +136,13 @@ public function valueExists(string $name): bool return $this->propertyExists($name) || Arr::has($this->data, $name); } + public function disableErrorOnNull(): static + { + $this->showErrorOnGetIfNull = false; + + return $this; + } + /** * Get an item at a given offset. * @@ -192,7 +198,6 @@ public function getData(): array /** * @return string - * @throws JsonException */ public function __toString(): string { From e3e9888a6eaac0d5840a6f128a3b5e117f1e293d Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 4 Aug 2023 15:23:07 +0300 Subject: [PATCH 72/95] feat: add Str::trimSuffix, Str::trimPrefix --- CHANGELOG.md | 6 ++++++ src/Helpers/Str.php | 18 ++++++++++++++++++ tests/Helpers/StrTest.php | 32 ++++++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a147b40..44e2fd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.23.0 + +### Added + +- Add methods `trimPrefix`, `trimSuffix` into `Str` + ## v4.22.0 ### Added diff --git a/src/Helpers/Str.php b/src/Helpers/Str.php index d66724d..7395619 100644 --- a/src/Helpers/Str.php +++ b/src/Helpers/Str.php @@ -431,4 +431,22 @@ public static function removeAccents(string $str, string $language = ''): string return URLify::downcode($str, $language); } + + public static function trimPrefix(string $str, string $prefix): string + { + if (str_starts_with($str, $prefix)) { + return mb_substr($str, mb_strlen($prefix)); + } + + return $str; + } + + public static function trimSuffix(string $str, string $suffix): string + { + if (str_ends_with($str, $suffix)) { + return mb_substr($str, 0, mb_strlen($str) - mb_strlen($suffix)); + } + + return $str; + } } diff --git a/tests/Helpers/StrTest.php b/tests/Helpers/StrTest.php index 5f14ada..0e185e6 100644 --- a/tests/Helpers/StrTest.php +++ b/tests/Helpers/StrTest.php @@ -221,7 +221,7 @@ public function testToScreamingSnake($str, $exp): void static::assertEquals($exp, $result); } - public static function providerDataKebab(): array + public static function providerDataKebab(): array { return [ [ @@ -613,7 +613,7 @@ public static function dataReplaceByTemplate(): array [ '"{{%KEY%}}-{{%TOKEN%}}" - test', [ - '{{%KEY%}}' => 'key', + '{{%KEY%}}' => 'key', '{{%TOKEN%}}' => 'token', ], '"key-token" - test', @@ -621,7 +621,7 @@ public static function dataReplaceByTemplate(): array [ 'sdasdas', [ - '{{%KEY%}}' => 'key', + '{{%KEY%}}' => 'key', '{{%TOKEN%}}' => 'token', ], 'sdasdas', @@ -745,7 +745,7 @@ public function seemsUTF8(): void mb_internal_encoding('UTF-8'); // Converts the 'ç' UTF-8 character to UCS-2LE $utf8Char = pack('n', 50087); - $ucsChar = mb_convert_encoding($utf8Char, 'UCS-2LE', 'UTF-8'); + $ucsChar = mb_convert_encoding($utf8Char, 'UCS-2LE', 'UTF-8'); self::assertEquals( $utf8Char, @@ -796,4 +796,28 @@ public function slugify(): void $this->assertEquals('1231251251', Str::slugify('123----1251251', '')); $this->assertEquals('one231251251', Str::slugify('123----1251251', '', true)); } + + /** + * @test + */ + public function trimPrefix(): void + { + $this->assertEquals('title', Str::trimPrefix('a-simple:title', 'a-simple:')); + $this->assertEquals('a-simple:title', Str::trimPrefix('a-simple:title', '')); + $this->assertEquals('a-simple:title', Str::trimPrefix('a-simple:title', 'asdas')); + $this->assertEquals('a-simple:title', Str::trimPrefix('a-simple:title', 'a-sdas')); + $this->assertEquals('', Str::trimPrefix('', 'a-simple:')); + } + + /** + * @test + */ + public function trimSuffix(): void + { + $this->assertEquals('a-simple:', Str::trimSuffix('a-simple:title', 'title')); + $this->assertEquals('a-simple:title', Str::trimSuffix('a-simple:title', '')); + $this->assertEquals('a-simple:title', Str::trimSuffix('a-simple:title', 'asdas')); + $this->assertEquals('a-simple:title', Str::trimSuffix('a-simple:title', 'a-sdas')); + $this->assertEquals('', Str::trimSuffix('', 'a-simple:')); + } } From 030c1eede4e256c0cb8ebb6d6a64d3422deebbe9 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 8 Sep 2023 16:05:30 +0300 Subject: [PATCH 73/95] feat: add separator --- CHANGELOG.md | 6 ++++++ src/Helpers/Arr.php | 23 ++++++++++------------- tests/Helpers/ArrTest.php | 9 +++++++++ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e2fd8..9a9d44c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.24.0 + +### Added + +- Add an argument `separator` to methods `Arr::set`, `Arr::get`, `Arr::has` + ## v4.23.0 ### Added diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index c2e0871..b780963 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -343,10 +343,9 @@ public static function fromPostgresPoint(?string $value): ?array * @param mixed $array * @param string|int|null $key * @param mixed $default - * - * @return mixed + * @param non-empty-string $separator */ - public static function get(mixed $array, string|int|null $key, mixed $default = null): mixed + public static function get(mixed $array, string|int|null $key, mixed $default = null, string $separator = '.'): mixed { if (!static::accessible($array)) { return value($default); @@ -361,11 +360,11 @@ public static function get(mixed $array, string|int|null $key, mixed $default = return $array[$key]; } - if (is_int($key) || !str_contains($key, '.')) { + if (is_int($key) || !str_contains($key, $separator)) { return $array[$key] ?? value($default); } - foreach (explode('.', $key) as $segment) { + foreach (explode($separator, $key) as $segment) { if (static::accessible($array) && static::exists($array, $segment)) { $array = $array[$segment]; } else { @@ -410,10 +409,9 @@ public static function exists(ArrayAccess|array $array, string|int $key): bool * * @param ArrayAccess|array $array * @param string|array $keys - * - * @return bool + * @param non-empty-string $separator */ - public static function has(ArrayAccess|array $array, string|array $keys): bool + public static function has(ArrayAccess|array $array, string|array $keys, string $separator = '.'): bool { $keys = (array)$keys; @@ -428,7 +426,7 @@ public static function has(ArrayAccess|array $array, string|array $keys): bool continue; } - foreach (explode('.', $key) as $segment) { + foreach (explode($separator, $key) as $segment) { if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { $subKeyArray = $subKeyArray[$segment]; } else { @@ -448,16 +446,15 @@ public static function has(ArrayAccess|array $array, string|array $keys): bool * @param array|ArrayObject|null $array * @param string $key * @param mixed $value - * - * @return array|ArrayObject|null + * @param non-empty-string $separator */ - public static function set(array|ArrayObject|null &$array, string $key, mixed $value): array|ArrayObject|null + public static function set(array|ArrayObject|null &$array, string $key, mixed $value, string $separator = '.'): array|ArrayObject|null { if ($array === null) { return $array; } - $keys = explode('.', $key); + $keys = explode($separator, $key); while (count($keys) > 1) { $key = array_shift($keys); diff --git a/tests/Helpers/ArrTest.php b/tests/Helpers/ArrTest.php index b960f65..ec4a27f 100644 --- a/tests/Helpers/ArrTest.php +++ b/tests/Helpers/ArrTest.php @@ -1181,6 +1181,15 @@ public function testSet2(): void static::assertEquals(['' => 1], Arr::set($array, '', 1)); } + public function testSetWithDivider(): void + { + $array = ['key' => ['sub2' => 1]]; + $expVal = 121; + Arr::set($array, 'key/sub3/sub4sub', $expVal, '/'); + + static::assertEquals(['key' => ['sub2' => 1, 'sub3' => ['sub4sub' => 121]]], $array); + } + /** * @return array */ From 49300df5aa5908526392a2a2164933204f665555 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Sun, 12 Nov 2023 11:44:30 +0200 Subject: [PATCH 74/95] feat: add new features into Collections, Array --- CHANGELOG.md | 26 +++- src/Helpers/Arr.php | 86 ++++++++++-- .../Collections/ArrayCollection.php | 132 +++++++++++++++++- src/Structures/Collections/Collection.php | 30 ++++ 4 files changed, 253 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a9d44c..1e85ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.25.0 + +### Added + +- `Arr::random` - Get one or a specified number of random values from an array +- `ArrayCollection::random` - Get one or a specified number of items randomly from the collection +- `ArrayCollection::clone` - Clone elements and returns Collection +- `ArrayCollection::groupBy` - Group an associative array by a field or using a callback + +### Changed + +- `ArrayCollection::map` - works with keys now +- `ArrayCollection::createFrom` - receives Collections + ## v4.24.0 ### Added @@ -41,12 +55,12 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher - Add traits for Enums: [WithEnhances.php](./src/Enums/WithEnhances.php), [WithEnhancesForStrings](./src/Enums/WithEnhancesForStrings.php) with following methods: - - `casesToString`- Returns string of Enum's names or values - - `casesToEscapeString`- Returns string of Enum's escaped names or values - - `values`- Returns list of Enum's values - - `names` - Returns list of Enum's names - - `hasValue` - Check if the Enum has provided Value - - `hasName` - Check if the Enum has provided Name + - `casesToString`- Returns string of Enum's names or values + - `casesToEscapeString`- Returns string of Enum's escaped names or values + - `values`- Returns list of Enum's values + - `names` - Returns list of Enum's names + - `hasValue` - Check if the Enum has provided Value + - `hasName` - Check if the Enum has provided Name ## v4.18.0 diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index b780963..85ef752 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -12,6 +12,25 @@ use Php\Support\Structures\Collections\ReadableCollection; use Traversable; +use function array_diff_assoc; +use function array_merge; +use function array_rand; +use function array_search; +use function array_shift; +use function array_unique; +use function array_unshift; +use function array_values; +use function explode; +use function func_num_args; +use function is_array; +use function is_int; +use function is_numeric; +use function is_object; +use function iterator_to_array; +use function mb_substr; +use function str_contains; +use function str_replace; + /** * @psalm-template TKey of array-key * @psalm-template T @@ -448,8 +467,12 @@ public static function has(ArrayAccess|array $array, string|array $keys, string * @param mixed $value * @param non-empty-string $separator */ - public static function set(array|ArrayObject|null &$array, string $key, mixed $value, string $separator = '.'): array|ArrayObject|null - { + public static function set( + array|ArrayObject|null &$array, + string $key, + mixed $value, + string $separator = '.' + ): array|ArrayObject|null { if ($array === null) { return $array; } @@ -545,11 +568,11 @@ public static function replaceByTemplate(array $array, array $replace): array private static function itemReplaceByTemplate(mixed $item, array $replace) { if (is_array($item)) { - $item = self::replaceByTemplate($item, $replace); - } else { - if (is_string($item)) { - $item = Str::replaceByTemplate($item, $replace); - } + return self::replaceByTemplate($item, $replace); + } + + if (is_string($item)) { + return Str::replaceByTemplate($item, $replace); } return $item; @@ -596,7 +619,7 @@ public static function fillKeysByValues(array $keys, array $values): array */ public static function prepend(array $array, mixed $value, mixed $key = null): array { - if (func_num_args() == 2) { + if (func_num_args() === 2) { array_unshift($array, $value); } else { $array = [$key => $value] + $array; @@ -604,4 +627,51 @@ public static function prepend(array $array, mixed $value, mixed $key = null): a return $array; } + + /** + * Get one or a specified number of random values from an array. + * + * @param array $array + * @param int|null $number + * @param bool $preserveKeys + * @return mixed + * + * @throws \InvalidArgumentException + */ + public static function random(array $array, ?int $number = null, bool $preserveKeys = false): mixed + { + $requested = $number ?? 1; + + $count = count($array); + + if ($requested > $count) { + throw new \InvalidArgumentException( + "You requested {$requested} items, but there are only {$count} items available." + ); + } + + if ($number === null) { + return $array[array_rand($array)]; + } + + if ($number === 0) { + return []; + } + + $keys = array_rand($array, $number); + + $results = []; + + if ($preserveKeys) { + foreach ((array)$keys as $key) { + $results[$key] = $array[$key]; + } + } else { + foreach ((array)$keys as $key) { + $results[] = $array[$key]; + } + } + + return $results; + } } diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 036d786..02ff496 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -26,6 +26,7 @@ use function end; use function in_array; use function is_array; +use function is_callable; use function is_object; use function key; use function next; @@ -50,12 +51,16 @@ class ArrayCollection implements Collection, Stringable protected array $elements = []; /** - * @param array $elements + * @param array|Collection $elements * @psalm-param array $elements */ - public function __construct(array $elements = []) + public function __construct(array|Collection $elements = []) { - $this->elements = $elements; + if ($elements instanceof Collection) { + $this->elements = $elements->all(); + } else { + $this->elements = $elements; + } } /** @@ -238,7 +243,10 @@ public function contains(mixed $element): bool */ public function map(Closure $func): static { - return $this->createFrom(array_map($func, $this->elements)); + $keys = array_keys($this->elements); + $map = array_map($func, $this->elements, $keys); + + return $this->createFrom(array_combine($keys, $map)); } /** @@ -362,8 +370,8 @@ public function merge(mixed $items): static * This method is provided for derived classes to specify how a new * instance should be created when constructor semantics have changed. * - * @param array $elements Elements. - * @psalm-param array $elements + * @param array|Collection $elements Elements. + * @psalm-param array|Collection $elements * * @return static * @psalm-return static @@ -371,7 +379,7 @@ public function merge(mixed $items): static * @psalm-template K of array-key * @psalm-template V */ - protected function createFrom(array $elements): static + protected function createFrom(array|Collection $elements): static { return new static($elements); } @@ -608,6 +616,27 @@ public function chunk(int $size): static return $this->createFrom($chunks); } + public function clone(): static + { + return $this->createFrom($this); + } + + /** + * Push all the given items onto the collection. + * + * @param iterable $source + */ + public function concat(iterable $source): static + { + $result = $this->clone(); + + foreach ($source as $item) { + $result->push($item); + } + + return $result; + } + /** * Sort through each item with a callback. * @@ -727,6 +756,29 @@ function ($a, $b) use ($comparisons) { return $this->createFrom($items); } + /** + * Get one or a specified number of items randomly from the collection. + * + * @param (callable(self): int)|int|null $number + * @param bool $preserveKeys + * + * @return static|T + * + * @throws \InvalidArgumentException + */ + public function random(callable|int|null $number = null, bool $preserveKeys = false): mixed + { + if ($number === null) { + return Arr::random($this->elements); + } + + if (is_callable($number)) { + return new static(Arr::random($this->elements, $number($this), $preserveKeys)); + } + + return new static(Arr::random($this->elements, $number, $preserveKeys)); + } + /** * Sort the collection keys. * @@ -743,6 +795,72 @@ public function sortKeys(int $options = SORT_REGULAR, bool $descending = false): return $this->createFrom($items); } + protected function useAsCallable(mixed $value): bool + { + return !is_string($value) && is_callable($value); + } + + protected function valueRetriever(mixed $value): callable + { + if ($this->useAsCallable($value)) { + return $value; + } + + return fn($item) => Arr::get($item, $value); + } + + /** + * Group an associative array by a field or using a callback. + * + * @param (callable(T, TKey): array-key)|array|string $groupBy + * @param bool $preserveKeys + * @psalm-return static> + * @return static> + */ + public function groupBy(callable|array|string $groupBy, bool $preserveKeys = false): static + { + if (is_array($groupBy) && !$this->useAsCallable($groupBy)) { + $nextGroups = $groupBy; + + $groupBy = array_shift($nextGroups); + } + + $groupBy = $this->valueRetriever($groupBy); + + $results = []; + + foreach ($this->elements as $key => $value) { + $groupKeys = $groupBy($value, $key); + + if (!is_array($groupKeys)) { + $groupKeys = [$groupKeys]; + } + + foreach ($groupKeys as $groupKey) { + $groupKey = match (true) { + is_bool($groupKey) => (int)$groupKey, + $groupKey instanceof \BackedEnum => $groupKey->value, + $groupKey instanceof \Stringable => (string)$groupKey, + default => $groupKey, + }; + + if (!array_key_exists($groupKey, $results)) { + $results[$groupKey] = $this->createFrom([]); + } + + $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); + } + } + + $result = $this->createFrom($results); + + if (!empty($nextGroups)) { + return $result->map(fn(Collection $item) => $item->groupBy($nextGroups, $preserveKeys)); + } + + return $result; + } + public function __toString(): string { return self::class . '@' . spl_object_hash($this); diff --git a/src/Structures/Collections/Collection.php b/src/Structures/Collections/Collection.php index e4f31b3..a3d1380 100644 --- a/src/Structures/Collections/Collection.php +++ b/src/Structures/Collections/Collection.php @@ -81,4 +81,34 @@ public function removeElement(mixed $element): bool; * @return void */ public function set(string|int $key, mixed $value): void; + + /** + * Push all the given items onto the collection. + * + * @param iterable $source + */ + public function concat(iterable $source): static; + + public function clone(): static; + + /** + * Get one or a specified number of items randomly from the collection. + * + * @param (callable(self): int)|int|null $number + * @param bool $preserveKeys + * + * @return static|T + * + * @throws \InvalidArgumentException + */ + public function random(callable|int|null $number = null, bool $preserveKeys = false): mixed; + + /** + * Group an associative array by a field or using a callback. + * + * @param (callable(T, TKey): array-key)|array|string $groupBy + * @param bool $preserveKeys + * @psalm-param (callable(T, TKey): array-key)|array|string $groupBy + */ + public function groupBy(callable|array|string $groupBy, bool $preserveKeys = false): static; } From 3caeb8985ccba42ef4a6eaf6bfcce002496b1bcb Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Sun, 12 Nov 2023 11:47:40 +0200 Subject: [PATCH 75/95] fix --- readme.md | 72 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/readme.md b/readme.md index b7b3fee..173b90a 100644 --- a/readme.md +++ b/readme.md @@ -33,7 +33,7 @@ composer require efureev/support "^2.0" ## Content - Helpers - - Array + + Array - collapse (^4.16.0) - prepend (^4.16.0) - accessible @@ -44,6 +44,7 @@ composer require efureev/support "^2.0" - get - has - merge + - random (^4.25.0) - remove - removeByValue - replaceByTemplate @@ -52,40 +53,40 @@ composer require efureev/support "^2.0" - toIndexedArray - toPostgresArray - toPostgresPoint (^4.8.0) - + String - - removeAccents (^4.9.0) - - removeMultiSpace - - replaceByTemplate - - replaceStrTo - - seemsUTF8 (^4.9.0) - - slugify (^4.9.0) - - toCamel - - toDelimited - - toKebab - - toLowerCamel - - toScreamingDelimited - - toScreamingSnake - - toSnake - - truncate (^4.9.0) - + Json - - decode - - encode - - htmlEncode - + Bit - - addFlag - - checkFlag - - decBinPad - - exist - - grant - - removeFlag - + B64 - - decode - - decodeSafe - - encode - - encodeSafe - + Number - - isInteger (^4.14.0) - - safeInt (^4.1.0) + + String + - removeAccents (^4.9.0) + - removeMultiSpace + - replaceByTemplate + - replaceStrTo + - seemsUTF8 (^4.9.0) + - slugify (^4.9.0) + - toCamel + - toDelimited + - toKebab + - toLowerCamel + - toScreamingDelimited + - toScreamingSnake + - toSnake + - truncate (^4.9.0) + + Json + - decode + - encode + - htmlEncode + + Bit + - addFlag + - checkFlag + - decBinPad + - exist + - grant + - removeFlag + + B64 + - decode + - decodeSafe + - encode + - encodeSafe + + Number + - isInteger (^4.14.0) + - safeInt (^4.1.0) - Global functions + classNamespace @@ -138,6 +139,7 @@ composer require efureev/support "^2.0" - Structures - Collections (^4.16.0) + - ArrayCollections - Traits + ArrayStorage From a528f37f9cdee6805adf7eeb446eb11eec303c94 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Sun, 12 Nov 2023 12:35:53 +0200 Subject: [PATCH 76/95] fixg --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e85ddc..c334c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,12 +55,12 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher - Add traits for Enums: [WithEnhances.php](./src/Enums/WithEnhances.php), [WithEnhancesForStrings](./src/Enums/WithEnhancesForStrings.php) with following methods: - - `casesToString`- Returns string of Enum's names or values - - `casesToEscapeString`- Returns string of Enum's escaped names or values - - `values`- Returns list of Enum's values - - `names` - Returns list of Enum's names - - `hasValue` - Check if the Enum has provided Value - - `hasName` - Check if the Enum has provided Name + - `casesToString`- Returns string of Enum's names or values + - `casesToEscapeString`- Returns string of Enum's escaped names or values + - `values`- Returns list of Enum's values + - `names` - Returns list of Enum's names + - `hasValue` - Check if the Enum has provided Value + - `hasName` - Check if the Enum has provided Name ## v4.18.0 From 312fccaeb657520eebe50df22a68ef71e70f1a06 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Wed, 31 Jan 2024 12:29:16 +0200 Subject: [PATCH 77/95] feat: add PHP 8.3 support --- CHANGELOG.md | 8 ++++++++ composer.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c334c39..669a005 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. + +## v4.26.0 + +### Added + +- Add support `PHP 8.3` + + ## v4.25.0 ### Added diff --git a/composer.json b/composer.json index c7e2c55..aba478a 100755 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^8.1|^8.2", + "php": "^8.1|^8.2|^8.3", "ext-mbstring": "*" }, "require-dev": { From f82b381ddbd1479ee394ab2504278d4418609306 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Wed, 31 Jan 2024 12:33:50 +0200 Subject: [PATCH 78/95] ci: update GH workflow --- .github/workflows/php.yml | 8 ++++---- .github/workflows/release.yml | 2 +- CHANGELOG.md | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index c99fac8..0dd15e2 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Lint changelog file uses: avto-dev/markdown-lint@v1 with: @@ -37,10 +37,10 @@ jobs: fail-fast: false matrix: setup: [ 'basic', 'lowest', 'stable' ] - php: [ '8.1' ,'8.2' ] + php: [ '8.1' ,'8.2', '8.3' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use PHP ${{ matrix.php }} uses: shivammathur/setup-php@v2 # Action page: @@ -55,7 +55,7 @@ jobs: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ff256d..8c2be78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@master + uses: actions/checkout@4 - name: Create Release id: create_release uses: softprops/action-gh-release@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 669a005..344d2fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. - ## v4.26.0 ### Added - Add support `PHP 8.3` - ## v4.25.0 ### Added From 5a8ef6eb93ebf002916e357d306fe19282602619 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 1 Mar 2024 16:39:00 +0200 Subject: [PATCH 79/95] feat: add some functions for Enum`s trait` --- CHANGELOG.md | 6 ++++++ src/Enums/WithEnhances.php | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 344d2fc..a1ca382 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.27.0 + +### Added + +- Add func for Enum's traits: `WithEnhances::toKeyValueArray`, `WithEnhances::toValueKeyArray` + ## v4.26.0 ### Added diff --git a/src/Enums/WithEnhances.php b/src/Enums/WithEnhances.php index 1af5118..1135f69 100644 --- a/src/Enums/WithEnhances.php +++ b/src/Enums/WithEnhances.php @@ -34,4 +34,24 @@ public static function hasName(string $value): bool { return in_array($value, static::names(), true); } + + public static function toKeyValueArray(): array + { + $list = []; + foreach (self::cases() as $case) { + $list[$case->value] = $case->name; + } + + return $list; + } + + public static function toValueKeyArray(): array + { + $list = []; + foreach (self::cases() as $case) { + $list[$case->value] = $case->name; + } + + return $list; + } } From 7b5e4d0840eff5ef8262e899b81982de19ac6eea Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 1 Mar 2024 16:54:43 +0200 Subject: [PATCH 80/95] feat: add Arr::map --- CHANGELOG.md | 1 + src/Helpers/Arr.php | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1ca382..9e141ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added - Add func for Enum's traits: `WithEnhances::toKeyValueArray`, `WithEnhances::toValueKeyArray` +- Add `Arr::map` ## v4.26.0 diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index 85ef752..e309d39 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -674,4 +674,12 @@ public static function random(array $array, ?int $number = null, bool $preserveK return $results; } + + public static function map(array $elements, \Closure $func): array + { + $keys = array_keys($elements); + $map = array_map($func, $elements, $keys); + + return array_combine($keys, $map); + } } From 7282d18014197be34905cf500b845c42fc39f3c6 Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 1 Mar 2024 16:59:07 +0200 Subject: [PATCH 81/95] feat: add Arr::map --- CHANGELOG.md | 687 +++++++++++++++++++++++++++++++++++++++++++- src/Helpers/Arr.php | 685 ------------------------------------------- 2 files changed, 686 insertions(+), 686 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e141ab..6a7d49f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,692 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ## v4.26.0 -### Added +### Added + * @psalm-return T[] + */ + public static function collapse(iterable $array): array + { + $results = []; + + foreach ($array as $values) { + if ($values instanceof ReadableCollection) { + $values = $values->all(); + } elseif (!is_array($values)) { + continue; + } + + $results[] = $values; + } + + return array_merge([], ...$results); + } + + /** + * Remove one element from array by value + * + * @param array $array + * @param mixed $val If $val is a string, the comparison is done in a case-sensitive manner. + * @param bool $reindex + * + * @return string|int|null Index of removed element or null if don't exist + */ + public static function removeByValue(array &$array, mixed $val, bool $reindex = false): string|int|null + { + if (($key = array_search($val, $array, false)) !== false) { + unset($array[$key]); + } + if ($reindex) { + $array = array_values($array); + } + return $key ?: null; + } + + /** + * Simple variable to array + * + * @param mixed $items + * + * @return array + */ + public static function toArray(mixed $items): array + { + if (is_array($items)) { + return $items; + } + + if ($items instanceof Arrayable) { + return $items->toArray(); + } + + if ($items instanceof Traversable) { + return iterator_to_array($items); + } + if ($items instanceof Jsonable) { + $res = Json::decode($items->toJson()); + return is_array($res) ? $res : []; + } + + if ($items instanceof JsonSerializable) { + return (array)$items->jsonSerialize(); + } + + return (array)$items; + } + + /** + * Nested variable data to array + * + * @param mixed $items + * + * @return array|mixed|null + */ + public static function dataToArray(mixed $items): mixed + { + if (is_object($items)) { + if ($items instanceof JsonSerializable) { + return static::dataToArray($items->jsonSerialize()); + } + + if ($items instanceof Jsonable) { + return Json::decode($items->toJson()); + } + + if ($items instanceof Arrayable) { + $items = $items->toArray(); + } elseif ($items instanceof Traversable) { + $items = iterator_to_array($items); + } else { + $result = []; + if (is_iterable($items)) { + foreach ($items as $name => $value) { + $result[$name] = $value; + } + } + $items = $result; + } + } + + if (!is_array($items)) { + return $items; + } + + foreach ($items as $key => &$value) { + if (is_array($value) || is_object($value)) { + $value = static::dataToArray($value); + } + } + + return $items; + } + + /** + * @param array $res array to be merged to + * @param array $b array to be merged from. You can specify additional + * arrays via third argument, fourth argument etc. + * @param bool $replaceArray Replace or Add values into Array, if key existed. + * + * @return array the merged array (the original arrays are not changed.) + */ + public static function merge(array $res, array $b, bool $replaceArray = true): array + { + foreach ($b as $key => $val) { + if (is_int($key)) { + if (isset($res[$key])) { + $res[] = $val; + } else { + $res[$key] = $val; + } + } else { + if (is_array($val) && isset($res[$key]) && is_array($res[$key])) { + $res[$key] = ($replaceArray ? $val : self::merge($res[$key], $val, $replaceArray)); + } else { + $res[$key] = $val; + } + } + } + + return $res; + } + + /** + * Changes PHP array to default Postgres array format + * + * @param array $array + * + * @return string + */ + public static function toPostgresArray(array $array): string + { + if (!$json = Json::encode(self::toIndexedArray($array), JSON_UNESCAPED_UNICODE)) { + return '{}'; + } + + return str_replace(['[', ']', '"'], ['{', '}', ''], $json); + } + + public static function toPostgresPoint(array $array): ?string + { + if (count($array) !== 2) { + return null; + } + + [ + $x, + $y, + ] = $array; + + return '(' . $x . ',' . $y . ')'; + } + + /** + * Remove named keys from arrays + * + * @param array $array + * + * @return array + */ + public static function toIndexedArray(array $array): array + { + $array = array_values($array); + foreach ($array as &$value) { + if (is_array($value)) { + $value = static::toIndexedArray($value); + } + } + + return $array; + } + + /** + * Load from PG array to PHP array + * + * @param string|null $s + * @param int $start + * @param ?int $end + * @param array $braces + * + * @return array + */ + public static function fromPostgresArrayWithBraces( + ?string $s, + int $start = 0, + ?int &$end = null, + array $braces = [ + '{', + '}', + ] + ): array { + [ + $braceOpen, + $braceClose, + ] = $braces; + if (empty($s) || $s[0] !== $braceOpen) { + return []; + } + + $return = []; + $string = false; + $quote = ''; + $len = strlen($s); + $v = ''; + + for ($i = $start + 1; $i < $len; $i++) { + $ch = $s[$i]; + if (!$string && $ch === $braceClose) { + if ($v !== '' || !empty($return)) { + $return[] = $v; + } + $end = $i; + break; + } else { + if (!$string && $ch === $braceOpen) { + $v = self::fromPostgresArray($s, (int)$i, $i); + } else { + if (!$string && $ch === ',') { + $return[] = $v; + $v = ''; + } else { + if (!$string && ($ch === '"' || $ch === "'")) { + $string = true; + $quote = $ch; + } else { + if ($string && $ch === $quote) { + if ($s[$i - 1] === "\\") { + $v = substr($v, 0, -1) . $ch; + } else { + $string = false; + } + } else { + $v .= $ch; + } + } + } + } + } + } + + foreach ($return as &$r) { + if (is_numeric($r)) { + if (ctype_digit((string)$r)) { + $r = (int)$r; + } else { + $r = (float)$r; + } + } + } + + return $return; + } + + /** + * @param string|null $s + * @param int $start + * @param ?int $end + * + * @return array + */ + public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end = null): array + { + return static::fromPostgresArrayWithBraces($s, $start, $end, ['{', '}']); + } + + /** + * @param string|null $value + * + * @return ?array + */ + public static function fromPostgresPoint(?string $value): ?array + { + if (empty($value)) { + return null; + } + + $string = mb_substr($value, 1, -1); + if (empty($string)) { + return null; + } + + [ + $x, + $y, + ] = explode(',', $string); + return [ + (float)$x, + (float)$y, + ]; + } + + /** + * Get an item from an array using "dot" notation. + * + * @param mixed $array + * @param string|int|null $key + * @param mixed $default + * @param non-empty-string $separator + */ + public static function get(mixed $array, string|int|null $key, mixed $default = null, string $separator = '.'): mixed + { + if (!static::accessible($array)) { + return value($default); + } + + if ($key === null) { + return $array; + } + + /** @var array|ArrayAccess $array */ + if (static::exists($array, $key)) { + return $array[$key]; + } + + if (is_int($key) || !str_contains($key, $separator)) { + return $array[$key] ?? value($default); + } + + foreach (explode($separator, $key) as $segment) { + if (static::accessible($array) && static::exists($array, $segment)) { + $array = $array[$segment]; + } else { + return value($default); + } + } + + return $array; + } + + /** + * Determine whether the given value is array accessible. + * + * @param mixed $value + * + * @return bool + */ + public static function accessible(mixed $value): bool + { + return is_array($value) || $value instanceof ArrayAccess; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param ArrayAccess|array $array + * @param string|int $key + * + * @return bool + */ + public static function exists(ArrayAccess|array $array, string|int $key): bool + { + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + + return array_key_exists($key, $array); + } + + /** + * Check if an item or items exist in an array using "dot" notation. + * + * @param ArrayAccess|array $array + * @param string|array $keys + * @param non-empty-string $separator + */ + public static function has(ArrayAccess|array $array, string|array $keys, string $separator = '.'): bool + { + $keys = (array)$keys; + + if (!$array || $keys === []) { + return false; + } + + foreach ($keys as $key) { + $subKeyArray = $array; + + if (static::exists($array, $key)) { + continue; + } + + foreach (explode($separator, $key) as $segment) { + if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { + $subKeyArray = $subKeyArray[$segment]; + } else { + return false; + } + } + } + + return true; + } + + /** + * Set an array item to a given value using "dot" notation. + * + * If no key is given to the method, the entire array will be replaced. + * + * @param array|ArrayObject|null $array + * @param string $key + * @param mixed $value + * @param non-empty-string $separator + */ + public static function set( + array|ArrayObject|null &$array, + string $key, + mixed $value, + string $separator = '.' + ): array|ArrayObject|null { + if ($array === null) { + return $array; + } + + $keys = explode($separator, $key); + + while (count($keys) > 1) { + $key = array_shift($keys); + + if (!isset($array[$key]) || !is_array($array[$key])) { + $array[$key] = []; + } + + $array = &$array[$key]; + } + + $array[array_shift($keys)] = $value; + + return $array; + } + + /** + * Remove one or many array items from a given array using "dot" notation. + * + * @param array|ArrayObject $array + * @param array|string $keys + * + * @return void + */ + public static function remove(array|ArrayObject &$array, array|string $keys): void + { + $original = &$array; + $keys = (array)$keys; + + if (count($keys) === 0) { + return; + } + + foreach ($keys as $key) { + // if the exact key exists in the top-level, remove it + if (static::exists($array, $key)) { + unset($array[$key]); + + continue; + } + + $parts = explode('.', $key); + + // clean up before each pass + $array = &$original; + + while (count($parts) > 1) { + $part = array_shift($parts); + + if (isset($array[$part]) && is_array($array[$part])) { + $array = &$array[$part]; + } else { + continue 2; + } + } + + unset($array[array_shift($parts)]); + } + } + + /** + * Replace templates into array + * Key = search value + * Value = replace value + * + * @param array $array + * @param array $replace + * + * @return array + */ + public static function replaceByTemplate(array $array, array $replace): array + { + $res = []; + foreach ($array as $key => $item) { + $res[$key] = self::itemReplaceByTemplate($item, $replace); + } + return $res; + } + + /** + * Replace templates into item + * + * @param mixed $item + * @param array $replace + * + * @return array|mixed + */ + private static function itemReplaceByTemplate(mixed $item, array $replace) + { + if (is_array($item)) { + return self::replaceByTemplate($item, $replace); + } + + if (is_string($item)) { + return Str::replaceByTemplate($item, $replace); + } + + return $item; + } + + /** + * Find duplicates into an array + * + * @param array $array + * + * @return array + */ + public static function duplicates(array $array): array + { + return array_unique(array_diff_assoc($array, array_unique($array))); + } + + /** + * Fill a keyed array by values from another array + * + * @param array $keys + * @param array $values + * + * @return array + */ + public static function fillKeysByValues(array $keys, array $values): array + { + $result = []; + + foreach ($keys as $key => $keyName) { + $result[$keyName] = $values[$key] ?? null; + } + + return $result; + } + + /** + * Push an item onto the beginning of an array. + * + * @param array $array + * @param mixed $value + * @param mixed $key + * @return array + */ + public static function prepend(array $array, mixed $value, mixed $key = null): array + { + if (func_num_args() === 2) { + array_unshift($array, $value); + } else { + $array = [$key => $value] + $array; + } + + return $array; + } + + /** + * Get one or a specified number of random values from an array. + * + * @param array $array + * @param int|null $number + * @param bool $preserveKeys + * @return mixed + * + * @throws \InvalidArgumentException + */ + public static function random(array $array, ?int $number = null, bool $preserveKeys = false): mixed + { + $requested = $number ?? 1; + + $count = count($array); + + if ($requested > $count) { + throw new \InvalidArgumentException( + "You requested {$requested} items, but there are only {$count} items available." + ); + } + + if ($number === null) { + return $array[array_rand($array)]; + } + + if ($number === 0) { + return []; + } + + $keys = array_rand($array, $number); + + $results = []; + + if ($preserveKeys) { + foreach ((array)$keys as $key) { + $results[$key] = $array[$key]; + } + } else { + foreach ((array)$keys as $key) { + $results[] = $array[$key]; + } + } + + return $results; + } + + public static function map(array $elements, \Closure $func): array + { + $keys = array_keys($elements); + $map = array_map($func, $elements, $keys); + + return array_combine($keys, $map); + } + } + - Add support `PHP 8.3` diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index e309d39..e69de29 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -1,685 +0,0 @@ - - * @psalm-return T[] - */ - public static function collapse(iterable $array): array - { - $results = []; - - foreach ($array as $values) { - if ($values instanceof ReadableCollection) { - $values = $values->all(); - } elseif (!is_array($values)) { - continue; - } - - $results[] = $values; - } - - return array_merge([], ...$results); - } - - /** - * Remove one element from array by value - * - * @param array $array - * @param mixed $val If $val is a string, the comparison is done in a case-sensitive manner. - * @param bool $reindex - * - * @return string|int|null Index of removed element or null if don't exist - */ - public static function removeByValue(array &$array, mixed $val, bool $reindex = false): string|int|null - { - if (($key = array_search($val, $array, false)) !== false) { - unset($array[$key]); - } - if ($reindex) { - $array = array_values($array); - } - return $key ?: null; - } - - /** - * Simple variable to array - * - * @param mixed $items - * - * @return array - */ - public static function toArray(mixed $items): array - { - if (is_array($items)) { - return $items; - } - - if ($items instanceof Arrayable) { - return $items->toArray(); - } - - if ($items instanceof Traversable) { - return iterator_to_array($items); - } - if ($items instanceof Jsonable) { - $res = Json::decode($items->toJson()); - return is_array($res) ? $res : []; - } - - if ($items instanceof JsonSerializable) { - return (array)$items->jsonSerialize(); - } - - return (array)$items; - } - - /** - * Nested variable data to array - * - * @param mixed $items - * - * @return array|mixed|null - */ - public static function dataToArray(mixed $items): mixed - { - if (is_object($items)) { - if ($items instanceof JsonSerializable) { - return static::dataToArray($items->jsonSerialize()); - } - - if ($items instanceof Jsonable) { - return Json::decode($items->toJson()); - } - - if ($items instanceof Arrayable) { - $items = $items->toArray(); - } elseif ($items instanceof Traversable) { - $items = iterator_to_array($items); - } else { - $result = []; - if (is_iterable($items)) { - foreach ($items as $name => $value) { - $result[$name] = $value; - } - } - $items = $result; - } - } - - if (!is_array($items)) { - return $items; - } - - foreach ($items as $key => &$value) { - if (is_array($value) || is_object($value)) { - $value = static::dataToArray($value); - } - } - - return $items; - } - - /** - * @param array $res array to be merged to - * @param array $b array to be merged from. You can specify additional - * arrays via third argument, fourth argument etc. - * @param bool $replaceArray Replace or Add values into Array, if key existed. - * - * @return array the merged array (the original arrays are not changed.) - */ - public static function merge(array $res, array $b, bool $replaceArray = true): array - { - foreach ($b as $key => $val) { - if (is_int($key)) { - if (isset($res[$key])) { - $res[] = $val; - } else { - $res[$key] = $val; - } - } else { - if (is_array($val) && isset($res[$key]) && is_array($res[$key])) { - $res[$key] = ($replaceArray ? $val : self::merge($res[$key], $val, $replaceArray)); - } else { - $res[$key] = $val; - } - } - } - - return $res; - } - - /** - * Changes PHP array to default Postgres array format - * - * @param array $array - * - * @return string - */ - public static function toPostgresArray(array $array): string - { - if (!$json = Json::encode(self::toIndexedArray($array), JSON_UNESCAPED_UNICODE)) { - return '{}'; - } - - return str_replace(['[', ']', '"'], ['{', '}', ''], $json); - } - - public static function toPostgresPoint(array $array): ?string - { - if (count($array) !== 2) { - return null; - } - - [ - $x, - $y, - ] = $array; - - return '(' . $x . ',' . $y . ')'; - } - - /** - * Remove named keys from arrays - * - * @param array $array - * - * @return array - */ - public static function toIndexedArray(array $array): array - { - $array = array_values($array); - foreach ($array as &$value) { - if (is_array($value)) { - $value = static::toIndexedArray($value); - } - } - - return $array; - } - - /** - * Load from PG array to PHP array - * - * @param string|null $s - * @param int $start - * @param ?int $end - * @param array $braces - * - * @return array - */ - public static function fromPostgresArrayWithBraces( - ?string $s, - int $start = 0, - ?int &$end = null, - array $braces = [ - '{', - '}', - ] - ): array { - [ - $braceOpen, - $braceClose, - ] = $braces; - if (empty($s) || $s[0] !== $braceOpen) { - return []; - } - - $return = []; - $string = false; - $quote = ''; - $len = strlen($s); - $v = ''; - - for ($i = $start + 1; $i < $len; $i++) { - $ch = $s[$i]; - if (!$string && $ch === $braceClose) { - if ($v !== '' || !empty($return)) { - $return[] = $v; - } - $end = $i; - break; - } else { - if (!$string && $ch === $braceOpen) { - $v = self::fromPostgresArray($s, (int)$i, $i); - } else { - if (!$string && $ch === ',') { - $return[] = $v; - $v = ''; - } else { - if (!$string && ($ch === '"' || $ch === "'")) { - $string = true; - $quote = $ch; - } else { - if ($string && $ch === $quote) { - if ($s[$i - 1] === "\\") { - $v = substr($v, 0, -1) . $ch; - } else { - $string = false; - } - } else { - $v .= $ch; - } - } - } - } - } - } - - foreach ($return as &$r) { - if (is_numeric($r)) { - if (ctype_digit((string)$r)) { - $r = (int)$r; - } else { - $r = (float)$r; - } - } - } - - return $return; - } - - /** - * @param string|null $s - * @param int $start - * @param ?int $end - * - * @return array - */ - public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end = null): array - { - return static::fromPostgresArrayWithBraces($s, $start, $end, ['{', '}']); - } - - /** - * @param string|null $value - * - * @return ?array - */ - public static function fromPostgresPoint(?string $value): ?array - { - if (empty($value)) { - return null; - } - - $string = mb_substr($value, 1, -1); - if (empty($string)) { - return null; - } - - [ - $x, - $y, - ] = explode(',', $string); - return [ - (float)$x, - (float)$y, - ]; - } - - /** - * Get an item from an array using "dot" notation. - * - * @param mixed $array - * @param string|int|null $key - * @param mixed $default - * @param non-empty-string $separator - */ - public static function get(mixed $array, string|int|null $key, mixed $default = null, string $separator = '.'): mixed - { - if (!static::accessible($array)) { - return value($default); - } - - if ($key === null) { - return $array; - } - - /** @var array|ArrayAccess $array */ - if (static::exists($array, $key)) { - return $array[$key]; - } - - if (is_int($key) || !str_contains($key, $separator)) { - return $array[$key] ?? value($default); - } - - foreach (explode($separator, $key) as $segment) { - if (static::accessible($array) && static::exists($array, $segment)) { - $array = $array[$segment]; - } else { - return value($default); - } - } - - return $array; - } - - /** - * Determine whether the given value is array accessible. - * - * @param mixed $value - * - * @return bool - */ - public static function accessible(mixed $value): bool - { - return is_array($value) || $value instanceof ArrayAccess; - } - - /** - * Determine if the given key exists in the provided array. - * - * @param ArrayAccess|array $array - * @param string|int $key - * - * @return bool - */ - public static function exists(ArrayAccess|array $array, string|int $key): bool - { - if ($array instanceof ArrayAccess) { - return $array->offsetExists($key); - } - - return array_key_exists($key, $array); - } - - /** - * Check if an item or items exist in an array using "dot" notation. - * - * @param ArrayAccess|array $array - * @param string|array $keys - * @param non-empty-string $separator - */ - public static function has(ArrayAccess|array $array, string|array $keys, string $separator = '.'): bool - { - $keys = (array)$keys; - - if (!$array || $keys === []) { - return false; - } - - foreach ($keys as $key) { - $subKeyArray = $array; - - if (static::exists($array, $key)) { - continue; - } - - foreach (explode($separator, $key) as $segment) { - if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { - $subKeyArray = $subKeyArray[$segment]; - } else { - return false; - } - } - } - - return true; - } - - /** - * Set an array item to a given value using "dot" notation. - * - * If no key is given to the method, the entire array will be replaced. - * - * @param array|ArrayObject|null $array - * @param string $key - * @param mixed $value - * @param non-empty-string $separator - */ - public static function set( - array|ArrayObject|null &$array, - string $key, - mixed $value, - string $separator = '.' - ): array|ArrayObject|null { - if ($array === null) { - return $array; - } - - $keys = explode($separator, $key); - - while (count($keys) > 1) { - $key = array_shift($keys); - - if (!isset($array[$key]) || !is_array($array[$key])) { - $array[$key] = []; - } - - $array = &$array[$key]; - } - - $array[array_shift($keys)] = $value; - - return $array; - } - - /** - * Remove one or many array items from a given array using "dot" notation. - * - * @param array|ArrayObject $array - * @param array|string $keys - * - * @return void - */ - public static function remove(array|ArrayObject &$array, array|string $keys): void - { - $original = &$array; - $keys = (array)$keys; - - if (count($keys) === 0) { - return; - } - - foreach ($keys as $key) { - // if the exact key exists in the top-level, remove it - if (static::exists($array, $key)) { - unset($array[$key]); - - continue; - } - - $parts = explode('.', $key); - - // clean up before each pass - $array = &$original; - - while (count($parts) > 1) { - $part = array_shift($parts); - - if (isset($array[$part]) && is_array($array[$part])) { - $array = &$array[$part]; - } else { - continue 2; - } - } - - unset($array[array_shift($parts)]); - } - } - - /** - * Replace templates into array - * Key = search value - * Value = replace value - * - * @param array $array - * @param array $replace - * - * @return array - */ - public static function replaceByTemplate(array $array, array $replace): array - { - $res = []; - foreach ($array as $key => $item) { - $res[$key] = self::itemReplaceByTemplate($item, $replace); - } - return $res; - } - - /** - * Replace templates into item - * - * @param mixed $item - * @param array $replace - * - * @return array|mixed - */ - private static function itemReplaceByTemplate(mixed $item, array $replace) - { - if (is_array($item)) { - return self::replaceByTemplate($item, $replace); - } - - if (is_string($item)) { - return Str::replaceByTemplate($item, $replace); - } - - return $item; - } - - /** - * Find duplicates into an array - * - * @param array $array - * - * @return array - */ - public static function duplicates(array $array): array - { - return array_unique(array_diff_assoc($array, array_unique($array))); - } - - /** - * Fill a keyed array by values from another array - * - * @param array $keys - * @param array $values - * - * @return array - */ - public static function fillKeysByValues(array $keys, array $values): array - { - $result = []; - - foreach ($keys as $key => $keyName) { - $result[$keyName] = $values[$key] ?? null; - } - - return $result; - } - - /** - * Push an item onto the beginning of an array. - * - * @param array $array - * @param mixed $value - * @param mixed $key - * @return array - */ - public static function prepend(array $array, mixed $value, mixed $key = null): array - { - if (func_num_args() === 2) { - array_unshift($array, $value); - } else { - $array = [$key => $value] + $array; - } - - return $array; - } - - /** - * Get one or a specified number of random values from an array. - * - * @param array $array - * @param int|null $number - * @param bool $preserveKeys - * @return mixed - * - * @throws \InvalidArgumentException - */ - public static function random(array $array, ?int $number = null, bool $preserveKeys = false): mixed - { - $requested = $number ?? 1; - - $count = count($array); - - if ($requested > $count) { - throw new \InvalidArgumentException( - "You requested {$requested} items, but there are only {$count} items available." - ); - } - - if ($number === null) { - return $array[array_rand($array)]; - } - - if ($number === 0) { - return []; - } - - $keys = array_rand($array, $number); - - $results = []; - - if ($preserveKeys) { - foreach ((array)$keys as $key) { - $results[$key] = $array[$key]; - } - } else { - foreach ((array)$keys as $key) { - $results[] = $array[$key]; - } - } - - return $results; - } - - public static function map(array $elements, \Closure $func): array - { - $keys = array_keys($elements); - $map = array_map($func, $elements, $keys); - - return array_combine($keys, $map); - } -} From e53026ce6dd40195231caa96a706118415acdafb Mon Sep 17 00:00:00 2001 From: Fureev Eugene Date: Fri, 1 Mar 2024 17:06:48 +0200 Subject: [PATCH 82/95] fix --- CHANGELOG.md | 687 +------------------------------------------- src/Helpers/Arr.php | 685 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 686 insertions(+), 686 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a7d49f..9e141ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,692 +13,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ## v4.26.0 -### Added - * @psalm-return T[] - */ - public static function collapse(iterable $array): array - { - $results = []; - - foreach ($array as $values) { - if ($values instanceof ReadableCollection) { - $values = $values->all(); - } elseif (!is_array($values)) { - continue; - } - - $results[] = $values; - } - - return array_merge([], ...$results); - } - - /** - * Remove one element from array by value - * - * @param array $array - * @param mixed $val If $val is a string, the comparison is done in a case-sensitive manner. - * @param bool $reindex - * - * @return string|int|null Index of removed element or null if don't exist - */ - public static function removeByValue(array &$array, mixed $val, bool $reindex = false): string|int|null - { - if (($key = array_search($val, $array, false)) !== false) { - unset($array[$key]); - } - if ($reindex) { - $array = array_values($array); - } - return $key ?: null; - } - - /** - * Simple variable to array - * - * @param mixed $items - * - * @return array - */ - public static function toArray(mixed $items): array - { - if (is_array($items)) { - return $items; - } - - if ($items instanceof Arrayable) { - return $items->toArray(); - } - - if ($items instanceof Traversable) { - return iterator_to_array($items); - } - if ($items instanceof Jsonable) { - $res = Json::decode($items->toJson()); - return is_array($res) ? $res : []; - } - - if ($items instanceof JsonSerializable) { - return (array)$items->jsonSerialize(); - } - - return (array)$items; - } - - /** - * Nested variable data to array - * - * @param mixed $items - * - * @return array|mixed|null - */ - public static function dataToArray(mixed $items): mixed - { - if (is_object($items)) { - if ($items instanceof JsonSerializable) { - return static::dataToArray($items->jsonSerialize()); - } - - if ($items instanceof Jsonable) { - return Json::decode($items->toJson()); - } - - if ($items instanceof Arrayable) { - $items = $items->toArray(); - } elseif ($items instanceof Traversable) { - $items = iterator_to_array($items); - } else { - $result = []; - if (is_iterable($items)) { - foreach ($items as $name => $value) { - $result[$name] = $value; - } - } - $items = $result; - } - } - - if (!is_array($items)) { - return $items; - } - - foreach ($items as $key => &$value) { - if (is_array($value) || is_object($value)) { - $value = static::dataToArray($value); - } - } - - return $items; - } - - /** - * @param array $res array to be merged to - * @param array $b array to be merged from. You can specify additional - * arrays via third argument, fourth argument etc. - * @param bool $replaceArray Replace or Add values into Array, if key existed. - * - * @return array the merged array (the original arrays are not changed.) - */ - public static function merge(array $res, array $b, bool $replaceArray = true): array - { - foreach ($b as $key => $val) { - if (is_int($key)) { - if (isset($res[$key])) { - $res[] = $val; - } else { - $res[$key] = $val; - } - } else { - if (is_array($val) && isset($res[$key]) && is_array($res[$key])) { - $res[$key] = ($replaceArray ? $val : self::merge($res[$key], $val, $replaceArray)); - } else { - $res[$key] = $val; - } - } - } - - return $res; - } - - /** - * Changes PHP array to default Postgres array format - * - * @param array $array - * - * @return string - */ - public static function toPostgresArray(array $array): string - { - if (!$json = Json::encode(self::toIndexedArray($array), JSON_UNESCAPED_UNICODE)) { - return '{}'; - } - - return str_replace(['[', ']', '"'], ['{', '}', ''], $json); - } - - public static function toPostgresPoint(array $array): ?string - { - if (count($array) !== 2) { - return null; - } - - [ - $x, - $y, - ] = $array; - - return '(' . $x . ',' . $y . ')'; - } - - /** - * Remove named keys from arrays - * - * @param array $array - * - * @return array - */ - public static function toIndexedArray(array $array): array - { - $array = array_values($array); - foreach ($array as &$value) { - if (is_array($value)) { - $value = static::toIndexedArray($value); - } - } - - return $array; - } - - /** - * Load from PG array to PHP array - * - * @param string|null $s - * @param int $start - * @param ?int $end - * @param array $braces - * - * @return array - */ - public static function fromPostgresArrayWithBraces( - ?string $s, - int $start = 0, - ?int &$end = null, - array $braces = [ - '{', - '}', - ] - ): array { - [ - $braceOpen, - $braceClose, - ] = $braces; - if (empty($s) || $s[0] !== $braceOpen) { - return []; - } - - $return = []; - $string = false; - $quote = ''; - $len = strlen($s); - $v = ''; - - for ($i = $start + 1; $i < $len; $i++) { - $ch = $s[$i]; - if (!$string && $ch === $braceClose) { - if ($v !== '' || !empty($return)) { - $return[] = $v; - } - $end = $i; - break; - } else { - if (!$string && $ch === $braceOpen) { - $v = self::fromPostgresArray($s, (int)$i, $i); - } else { - if (!$string && $ch === ',') { - $return[] = $v; - $v = ''; - } else { - if (!$string && ($ch === '"' || $ch === "'")) { - $string = true; - $quote = $ch; - } else { - if ($string && $ch === $quote) { - if ($s[$i - 1] === "\\") { - $v = substr($v, 0, -1) . $ch; - } else { - $string = false; - } - } else { - $v .= $ch; - } - } - } - } - } - } - - foreach ($return as &$r) { - if (is_numeric($r)) { - if (ctype_digit((string)$r)) { - $r = (int)$r; - } else { - $r = (float)$r; - } - } - } - - return $return; - } - - /** - * @param string|null $s - * @param int $start - * @param ?int $end - * - * @return array - */ - public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end = null): array - { - return static::fromPostgresArrayWithBraces($s, $start, $end, ['{', '}']); - } - - /** - * @param string|null $value - * - * @return ?array - */ - public static function fromPostgresPoint(?string $value): ?array - { - if (empty($value)) { - return null; - } - - $string = mb_substr($value, 1, -1); - if (empty($string)) { - return null; - } - - [ - $x, - $y, - ] = explode(',', $string); - return [ - (float)$x, - (float)$y, - ]; - } - - /** - * Get an item from an array using "dot" notation. - * - * @param mixed $array - * @param string|int|null $key - * @param mixed $default - * @param non-empty-string $separator - */ - public static function get(mixed $array, string|int|null $key, mixed $default = null, string $separator = '.'): mixed - { - if (!static::accessible($array)) { - return value($default); - } - - if ($key === null) { - return $array; - } - - /** @var array|ArrayAccess $array */ - if (static::exists($array, $key)) { - return $array[$key]; - } - - if (is_int($key) || !str_contains($key, $separator)) { - return $array[$key] ?? value($default); - } - - foreach (explode($separator, $key) as $segment) { - if (static::accessible($array) && static::exists($array, $segment)) { - $array = $array[$segment]; - } else { - return value($default); - } - } - - return $array; - } - - /** - * Determine whether the given value is array accessible. - * - * @param mixed $value - * - * @return bool - */ - public static function accessible(mixed $value): bool - { - return is_array($value) || $value instanceof ArrayAccess; - } - - /** - * Determine if the given key exists in the provided array. - * - * @param ArrayAccess|array $array - * @param string|int $key - * - * @return bool - */ - public static function exists(ArrayAccess|array $array, string|int $key): bool - { - if ($array instanceof ArrayAccess) { - return $array->offsetExists($key); - } - - return array_key_exists($key, $array); - } - - /** - * Check if an item or items exist in an array using "dot" notation. - * - * @param ArrayAccess|array $array - * @param string|array $keys - * @param non-empty-string $separator - */ - public static function has(ArrayAccess|array $array, string|array $keys, string $separator = '.'): bool - { - $keys = (array)$keys; - - if (!$array || $keys === []) { - return false; - } - - foreach ($keys as $key) { - $subKeyArray = $array; - - if (static::exists($array, $key)) { - continue; - } - - foreach (explode($separator, $key) as $segment) { - if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { - $subKeyArray = $subKeyArray[$segment]; - } else { - return false; - } - } - } - - return true; - } - - /** - * Set an array item to a given value using "dot" notation. - * - * If no key is given to the method, the entire array will be replaced. - * - * @param array|ArrayObject|null $array - * @param string $key - * @param mixed $value - * @param non-empty-string $separator - */ - public static function set( - array|ArrayObject|null &$array, - string $key, - mixed $value, - string $separator = '.' - ): array|ArrayObject|null { - if ($array === null) { - return $array; - } - - $keys = explode($separator, $key); - - while (count($keys) > 1) { - $key = array_shift($keys); - - if (!isset($array[$key]) || !is_array($array[$key])) { - $array[$key] = []; - } - - $array = &$array[$key]; - } - - $array[array_shift($keys)] = $value; - - return $array; - } - - /** - * Remove one or many array items from a given array using "dot" notation. - * - * @param array|ArrayObject $array - * @param array|string $keys - * - * @return void - */ - public static function remove(array|ArrayObject &$array, array|string $keys): void - { - $original = &$array; - $keys = (array)$keys; - - if (count($keys) === 0) { - return; - } - - foreach ($keys as $key) { - // if the exact key exists in the top-level, remove it - if (static::exists($array, $key)) { - unset($array[$key]); - - continue; - } - - $parts = explode('.', $key); - - // clean up before each pass - $array = &$original; - - while (count($parts) > 1) { - $part = array_shift($parts); - - if (isset($array[$part]) && is_array($array[$part])) { - $array = &$array[$part]; - } else { - continue 2; - } - } - - unset($array[array_shift($parts)]); - } - } - - /** - * Replace templates into array - * Key = search value - * Value = replace value - * - * @param array $array - * @param array $replace - * - * @return array - */ - public static function replaceByTemplate(array $array, array $replace): array - { - $res = []; - foreach ($array as $key => $item) { - $res[$key] = self::itemReplaceByTemplate($item, $replace); - } - return $res; - } - - /** - * Replace templates into item - * - * @param mixed $item - * @param array $replace - * - * @return array|mixed - */ - private static function itemReplaceByTemplate(mixed $item, array $replace) - { - if (is_array($item)) { - return self::replaceByTemplate($item, $replace); - } - - if (is_string($item)) { - return Str::replaceByTemplate($item, $replace); - } - - return $item; - } - - /** - * Find duplicates into an array - * - * @param array $array - * - * @return array - */ - public static function duplicates(array $array): array - { - return array_unique(array_diff_assoc($array, array_unique($array))); - } - - /** - * Fill a keyed array by values from another array - * - * @param array $keys - * @param array $values - * - * @return array - */ - public static function fillKeysByValues(array $keys, array $values): array - { - $result = []; - - foreach ($keys as $key => $keyName) { - $result[$keyName] = $values[$key] ?? null; - } - - return $result; - } - - /** - * Push an item onto the beginning of an array. - * - * @param array $array - * @param mixed $value - * @param mixed $key - * @return array - */ - public static function prepend(array $array, mixed $value, mixed $key = null): array - { - if (func_num_args() === 2) { - array_unshift($array, $value); - } else { - $array = [$key => $value] + $array; - } - - return $array; - } - - /** - * Get one or a specified number of random values from an array. - * - * @param array $array - * @param int|null $number - * @param bool $preserveKeys - * @return mixed - * - * @throws \InvalidArgumentException - */ - public static function random(array $array, ?int $number = null, bool $preserveKeys = false): mixed - { - $requested = $number ?? 1; - - $count = count($array); - - if ($requested > $count) { - throw new \InvalidArgumentException( - "You requested {$requested} items, but there are only {$count} items available." - ); - } - - if ($number === null) { - return $array[array_rand($array)]; - } - - if ($number === 0) { - return []; - } - - $keys = array_rand($array, $number); - - $results = []; - - if ($preserveKeys) { - foreach ((array)$keys as $key) { - $results[$key] = $array[$key]; - } - } else { - foreach ((array)$keys as $key) { - $results[] = $array[$key]; - } - } - - return $results; - } - - public static function map(array $elements, \Closure $func): array - { - $keys = array_keys($elements); - $map = array_map($func, $elements, $keys); - - return array_combine($keys, $map); - } - } - +### Added - Add support `PHP 8.3` diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index e69de29..e309d39 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -0,0 +1,685 @@ + + * @psalm-return T[] + */ + public static function collapse(iterable $array): array + { + $results = []; + + foreach ($array as $values) { + if ($values instanceof ReadableCollection) { + $values = $values->all(); + } elseif (!is_array($values)) { + continue; + } + + $results[] = $values; + } + + return array_merge([], ...$results); + } + + /** + * Remove one element from array by value + * + * @param array $array + * @param mixed $val If $val is a string, the comparison is done in a case-sensitive manner. + * @param bool $reindex + * + * @return string|int|null Index of removed element or null if don't exist + */ + public static function removeByValue(array &$array, mixed $val, bool $reindex = false): string|int|null + { + if (($key = array_search($val, $array, false)) !== false) { + unset($array[$key]); + } + if ($reindex) { + $array = array_values($array); + } + return $key ?: null; + } + + /** + * Simple variable to array + * + * @param mixed $items + * + * @return array + */ + public static function toArray(mixed $items): array + { + if (is_array($items)) { + return $items; + } + + if ($items instanceof Arrayable) { + return $items->toArray(); + } + + if ($items instanceof Traversable) { + return iterator_to_array($items); + } + if ($items instanceof Jsonable) { + $res = Json::decode($items->toJson()); + return is_array($res) ? $res : []; + } + + if ($items instanceof JsonSerializable) { + return (array)$items->jsonSerialize(); + } + + return (array)$items; + } + + /** + * Nested variable data to array + * + * @param mixed $items + * + * @return array|mixed|null + */ + public static function dataToArray(mixed $items): mixed + { + if (is_object($items)) { + if ($items instanceof JsonSerializable) { + return static::dataToArray($items->jsonSerialize()); + } + + if ($items instanceof Jsonable) { + return Json::decode($items->toJson()); + } + + if ($items instanceof Arrayable) { + $items = $items->toArray(); + } elseif ($items instanceof Traversable) { + $items = iterator_to_array($items); + } else { + $result = []; + if (is_iterable($items)) { + foreach ($items as $name => $value) { + $result[$name] = $value; + } + } + $items = $result; + } + } + + if (!is_array($items)) { + return $items; + } + + foreach ($items as $key => &$value) { + if (is_array($value) || is_object($value)) { + $value = static::dataToArray($value); + } + } + + return $items; + } + + /** + * @param array $res array to be merged to + * @param array $b array to be merged from. You can specify additional + * arrays via third argument, fourth argument etc. + * @param bool $replaceArray Replace or Add values into Array, if key existed. + * + * @return array the merged array (the original arrays are not changed.) + */ + public static function merge(array $res, array $b, bool $replaceArray = true): array + { + foreach ($b as $key => $val) { + if (is_int($key)) { + if (isset($res[$key])) { + $res[] = $val; + } else { + $res[$key] = $val; + } + } else { + if (is_array($val) && isset($res[$key]) && is_array($res[$key])) { + $res[$key] = ($replaceArray ? $val : self::merge($res[$key], $val, $replaceArray)); + } else { + $res[$key] = $val; + } + } + } + + return $res; + } + + /** + * Changes PHP array to default Postgres array format + * + * @param array $array + * + * @return string + */ + public static function toPostgresArray(array $array): string + { + if (!$json = Json::encode(self::toIndexedArray($array), JSON_UNESCAPED_UNICODE)) { + return '{}'; + } + + return str_replace(['[', ']', '"'], ['{', '}', ''], $json); + } + + public static function toPostgresPoint(array $array): ?string + { + if (count($array) !== 2) { + return null; + } + + [ + $x, + $y, + ] = $array; + + return '(' . $x . ',' . $y . ')'; + } + + /** + * Remove named keys from arrays + * + * @param array $array + * + * @return array + */ + public static function toIndexedArray(array $array): array + { + $array = array_values($array); + foreach ($array as &$value) { + if (is_array($value)) { + $value = static::toIndexedArray($value); + } + } + + return $array; + } + + /** + * Load from PG array to PHP array + * + * @param string|null $s + * @param int $start + * @param ?int $end + * @param array $braces + * + * @return array + */ + public static function fromPostgresArrayWithBraces( + ?string $s, + int $start = 0, + ?int &$end = null, + array $braces = [ + '{', + '}', + ] + ): array { + [ + $braceOpen, + $braceClose, + ] = $braces; + if (empty($s) || $s[0] !== $braceOpen) { + return []; + } + + $return = []; + $string = false; + $quote = ''; + $len = strlen($s); + $v = ''; + + for ($i = $start + 1; $i < $len; $i++) { + $ch = $s[$i]; + if (!$string && $ch === $braceClose) { + if ($v !== '' || !empty($return)) { + $return[] = $v; + } + $end = $i; + break; + } else { + if (!$string && $ch === $braceOpen) { + $v = self::fromPostgresArray($s, (int)$i, $i); + } else { + if (!$string && $ch === ',') { + $return[] = $v; + $v = ''; + } else { + if (!$string && ($ch === '"' || $ch === "'")) { + $string = true; + $quote = $ch; + } else { + if ($string && $ch === $quote) { + if ($s[$i - 1] === "\\") { + $v = substr($v, 0, -1) . $ch; + } else { + $string = false; + } + } else { + $v .= $ch; + } + } + } + } + } + } + + foreach ($return as &$r) { + if (is_numeric($r)) { + if (ctype_digit((string)$r)) { + $r = (int)$r; + } else { + $r = (float)$r; + } + } + } + + return $return; + } + + /** + * @param string|null $s + * @param int $start + * @param ?int $end + * + * @return array + */ + public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end = null): array + { + return static::fromPostgresArrayWithBraces($s, $start, $end, ['{', '}']); + } + + /** + * @param string|null $value + * + * @return ?array + */ + public static function fromPostgresPoint(?string $value): ?array + { + if (empty($value)) { + return null; + } + + $string = mb_substr($value, 1, -1); + if (empty($string)) { + return null; + } + + [ + $x, + $y, + ] = explode(',', $string); + return [ + (float)$x, + (float)$y, + ]; + } + + /** + * Get an item from an array using "dot" notation. + * + * @param mixed $array + * @param string|int|null $key + * @param mixed $default + * @param non-empty-string $separator + */ + public static function get(mixed $array, string|int|null $key, mixed $default = null, string $separator = '.'): mixed + { + if (!static::accessible($array)) { + return value($default); + } + + if ($key === null) { + return $array; + } + + /** @var array|ArrayAccess $array */ + if (static::exists($array, $key)) { + return $array[$key]; + } + + if (is_int($key) || !str_contains($key, $separator)) { + return $array[$key] ?? value($default); + } + + foreach (explode($separator, $key) as $segment) { + if (static::accessible($array) && static::exists($array, $segment)) { + $array = $array[$segment]; + } else { + return value($default); + } + } + + return $array; + } + + /** + * Determine whether the given value is array accessible. + * + * @param mixed $value + * + * @return bool + */ + public static function accessible(mixed $value): bool + { + return is_array($value) || $value instanceof ArrayAccess; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param ArrayAccess|array $array + * @param string|int $key + * + * @return bool + */ + public static function exists(ArrayAccess|array $array, string|int $key): bool + { + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + + return array_key_exists($key, $array); + } + + /** + * Check if an item or items exist in an array using "dot" notation. + * + * @param ArrayAccess|array $array + * @param string|array $keys + * @param non-empty-string $separator + */ + public static function has(ArrayAccess|array $array, string|array $keys, string $separator = '.'): bool + { + $keys = (array)$keys; + + if (!$array || $keys === []) { + return false; + } + + foreach ($keys as $key) { + $subKeyArray = $array; + + if (static::exists($array, $key)) { + continue; + } + + foreach (explode($separator, $key) as $segment) { + if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { + $subKeyArray = $subKeyArray[$segment]; + } else { + return false; + } + } + } + + return true; + } + + /** + * Set an array item to a given value using "dot" notation. + * + * If no key is given to the method, the entire array will be replaced. + * + * @param array|ArrayObject|null $array + * @param string $key + * @param mixed $value + * @param non-empty-string $separator + */ + public static function set( + array|ArrayObject|null &$array, + string $key, + mixed $value, + string $separator = '.' + ): array|ArrayObject|null { + if ($array === null) { + return $array; + } + + $keys = explode($separator, $key); + + while (count($keys) > 1) { + $key = array_shift($keys); + + if (!isset($array[$key]) || !is_array($array[$key])) { + $array[$key] = []; + } + + $array = &$array[$key]; + } + + $array[array_shift($keys)] = $value; + + return $array; + } + + /** + * Remove one or many array items from a given array using "dot" notation. + * + * @param array|ArrayObject $array + * @param array|string $keys + * + * @return void + */ + public static function remove(array|ArrayObject &$array, array|string $keys): void + { + $original = &$array; + $keys = (array)$keys; + + if (count($keys) === 0) { + return; + } + + foreach ($keys as $key) { + // if the exact key exists in the top-level, remove it + if (static::exists($array, $key)) { + unset($array[$key]); + + continue; + } + + $parts = explode('.', $key); + + // clean up before each pass + $array = &$original; + + while (count($parts) > 1) { + $part = array_shift($parts); + + if (isset($array[$part]) && is_array($array[$part])) { + $array = &$array[$part]; + } else { + continue 2; + } + } + + unset($array[array_shift($parts)]); + } + } + + /** + * Replace templates into array + * Key = search value + * Value = replace value + * + * @param array $array + * @param array $replace + * + * @return array + */ + public static function replaceByTemplate(array $array, array $replace): array + { + $res = []; + foreach ($array as $key => $item) { + $res[$key] = self::itemReplaceByTemplate($item, $replace); + } + return $res; + } + + /** + * Replace templates into item + * + * @param mixed $item + * @param array $replace + * + * @return array|mixed + */ + private static function itemReplaceByTemplate(mixed $item, array $replace) + { + if (is_array($item)) { + return self::replaceByTemplate($item, $replace); + } + + if (is_string($item)) { + return Str::replaceByTemplate($item, $replace); + } + + return $item; + } + + /** + * Find duplicates into an array + * + * @param array $array + * + * @return array + */ + public static function duplicates(array $array): array + { + return array_unique(array_diff_assoc($array, array_unique($array))); + } + + /** + * Fill a keyed array by values from another array + * + * @param array $keys + * @param array $values + * + * @return array + */ + public static function fillKeysByValues(array $keys, array $values): array + { + $result = []; + + foreach ($keys as $key => $keyName) { + $result[$keyName] = $values[$key] ?? null; + } + + return $result; + } + + /** + * Push an item onto the beginning of an array. + * + * @param array $array + * @param mixed $value + * @param mixed $key + * @return array + */ + public static function prepend(array $array, mixed $value, mixed $key = null): array + { + if (func_num_args() === 2) { + array_unshift($array, $value); + } else { + $array = [$key => $value] + $array; + } + + return $array; + } + + /** + * Get one or a specified number of random values from an array. + * + * @param array $array + * @param int|null $number + * @param bool $preserveKeys + * @return mixed + * + * @throws \InvalidArgumentException + */ + public static function random(array $array, ?int $number = null, bool $preserveKeys = false): mixed + { + $requested = $number ?? 1; + + $count = count($array); + + if ($requested > $count) { + throw new \InvalidArgumentException( + "You requested {$requested} items, but there are only {$count} items available." + ); + } + + if ($number === null) { + return $array[array_rand($array)]; + } + + if ($number === 0) { + return []; + } + + $keys = array_rand($array, $number); + + $results = []; + + if ($preserveKeys) { + foreach ((array)$keys as $key) { + $results[$key] = $array[$key]; + } + } else { + foreach ((array)$keys as $key) { + $results[] = $array[$key]; + } + } + + return $results; + } + + public static function map(array $elements, \Closure $func): array + { + $keys = array_keys($elements); + $map = array_map($func, $elements, $keys); + + return array_combine($keys, $map); + } +} From c011e9bee12a96b2b359faa398055e5169f0cb7f Mon Sep 17 00:00:00 2001 From: Eugene Fureev Date: Thu, 5 Sep 2024 15:37:42 +0300 Subject: [PATCH 83/95] fix --- src/Enums/WithEnhancesForStrings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Enums/WithEnhancesForStrings.php b/src/Enums/WithEnhancesForStrings.php index 54b060c..3367a07 100644 --- a/src/Enums/WithEnhancesForStrings.php +++ b/src/Enums/WithEnhancesForStrings.php @@ -23,7 +23,7 @@ public static function casesToString(string $delimiter = ', ', callable $decorat return self::casesToStringBase($decorator, $delimiter); } - public static function casesToEscapeString(string $delimiter = ', ', callable $decorator = null): string + public static function casesToEscapeString(string $delimiter = ', '): string { return static::casesToString($delimiter, static fn(self $enumItem) => "'$enumItem->value'"); } From a847cd1e1dd212398690e77eed6824853c1201fa Mon Sep 17 00:00:00 2001 From: Eugene Fureev Date: Thu, 19 Sep 2024 16:04:09 +0300 Subject: [PATCH 84/95] feat: add some global functions --- CHANGELOG.md | 7 +++++ src/Global/base.php | 67 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e141ab..a0df71b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v4.28.0 + +### Added + +- Add global functions: `attributeToGetterMethod`, `attributeToSetterMethod`, + `findGetterMethod`, `public_property_exists`, `get_property_value` + ## v4.27.0 ### Added diff --git a/src/Global/base.php b/src/Global/base.php index 514f325..2c2a8b0 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -1,6 +1,7 @@ $method(...$params); + return null; + } +} + + +if (!function_exists('public_property_exists')) { + /** + * Returns existing public method (name) or null if missing + */ + function public_property_exists(object $instance, string $attribute): ?string + { + $property = Str::toLowerCamel($attribute); + $vars = get_object_vars($instance); + + return array_key_exists($property, $vars) ? $property : null; + } +} + + +if (!function_exists('get_property_value')) { + /** + * Returns a value from public property or null + */ + function get_property_value(object $instance, string $attribute): mixed + { + $property = public_property_exists($instance, $attribute); + if ($property) { + return $instance->$property; } return null; From b2bfbd864ad3e9d14b5e0da9bacbc9ac271a4705 Mon Sep 17 00:00:00 2001 From: Eugene Fureev Date: Thu, 19 Sep 2024 16:06:34 +0300 Subject: [PATCH 85/95] fix --- src/Global/base.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Global/base.php b/src/Global/base.php index 2c2a8b0..612e142 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -307,6 +307,29 @@ function remoteStaticCallOrTrow(object|string|null $class, string $method, mixed } } +if (!function_exists('remoteCall')) { + /** + * Returns result of an object's method if it exists in the object. + * + * @param object|null $class + * @param string $method + * @param mixed ...$params + * + * @return mixed + */ + function remoteCall(?object $class, string $method, mixed ...$params): mixed + { + if (!$class) { + return null; + } + if (method_exists($class, $method)) { + return $class->$method(...$params); + } + + return null; + } +} + if (!function_exists('attributeToGetterMethod')) { /** * Returns getter-method's name or null by an attribute From e80cbb9245ec7578a7fa49a9af521fc8e435b74f Mon Sep 17 00:00:00 2001 From: Eugene Fureev Date: Thu, 19 Sep 2024 16:07:46 +0300 Subject: [PATCH 86/95] fix --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0df71b..2f002e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,7 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added -- Add global functions: `attributeToGetterMethod`, `attributeToSetterMethod`, - `findGetterMethod`, `public_property_exists`, `get_property_value` +- Add global functions: `attributeToGetterMethod`, `attributeToSetterMethod`, `findGetterMethod`, `public_property_exists`, `get_property_value` ## v4.27.0 From 688e133c09e83ee643e8a99ae342519b4491c265 Mon Sep 17 00:00:00 2001 From: Eugene Fureev Date: Thu, 19 Sep 2024 16:15:03 +0300 Subject: [PATCH 87/95] ci: fix --- .github/workflows/release.yml | 7 +++---- src/Global/base.php | 18 ------------------ 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c2be78..14a52cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,13 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@4 + uses: actions/checkout@v4 - name: Create Release - id: create_release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - if: startsWith(github.ref, 'refs/tags/') with: name: Release ${{ github.ref }} draft: false diff --git a/src/Global/base.php b/src/Global/base.php index 612e142..b347653 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -255,12 +255,6 @@ function class_uses_recursive($class): array if (!function_exists('remoteStaticCall')) { /** * Returns result of an object's method if it exists in the object. - * - * @param string|object|null $class - * @param string $method - * @param mixed ...$params - * - * @return mixed */ function remoteStaticCall(object|string|null $class, string $method, mixed ...$params): mixed { @@ -282,12 +276,6 @@ function remoteStaticCall(object|string|null $class, string $method, mixed ...$p if (!function_exists('remoteStaticCall')) { /** * Returns result of an object's method if it exists in the object or trow exception. - * - * @param string|object|null $class - * @param string $method - * @param mixed ...$params - * - * @return mixed */ function remoteStaticCallOrTrow(object|string|null $class, string $method, mixed ...$params): mixed { @@ -310,12 +298,6 @@ function remoteStaticCallOrTrow(object|string|null $class, string $method, mixed if (!function_exists('remoteCall')) { /** * Returns result of an object's method if it exists in the object. - * - * @param object|null $class - * @param string $method - * @param mixed ...$params - * - * @return mixed */ function remoteCall(?object $class, string $method, mixed ...$params): mixed { From 766130fe6bf36308f2de480e18cbc7f188718e33 Mon Sep 17 00:00:00 2001 From: Eugene Fureev Date: Thu, 19 Sep 2024 16:43:12 +0300 Subject: [PATCH 88/95] fix --- src/Global/base.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Global/base.php b/src/Global/base.php index b347653..ef51ba2 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -361,11 +361,11 @@ function public_property_exists(object $instance, string $attribute): ?string } -if (!function_exists('get_property_value')) { +if (!function_exists('getPropertyValue')) { /** * Returns a value from public property or null */ - function get_property_value(object $instance, string $attribute): mixed + function getPropertyValue(object $instance, string $attribute): mixed { $property = public_property_exists($instance, $attribute); if ($property) { From e233170b7429f8feeb72c8ac037196dc185328df Mon Sep 17 00:00:00 2001 From: fureev Date: Wed, 25 Dec 2024 11:40:35 +0200 Subject: [PATCH 89/95] fix --- .travis.yml | 51 -------- CHANGELOG.md | 6 + composer.json | 31 +++-- phpstan.neon.dist => phpstan.neon | 8 +- phpunit.xml | 2 +- src/ConditionalHandler.php | 24 +++- src/Enums/WithEnhancesForStrings.php | 2 +- src/Exceptions/Exception.php | 6 +- src/Exceptions/InvalidArgumentException.php | 6 +- src/Exceptions/MissingMethodException.php | 18 ++- src/Global/base.php | 87 +++++-------- src/Helpers/Arr.php | 115 +++++++++--------- src/Helpers/B64.php | 4 +- src/Helpers/Bit.php | 6 +- src/Helpers/Number.php | 1 - src/Helpers/Str.php | 19 +-- src/Helpers/URLify.php | 4 + src/Interfaces/Arrayable.php | 7 +- .../Collections/ArrayCollection.php | 4 +- src/Structures/Collections/Collection.php | 37 +++--- .../Collections/ReadableCollection.php | 113 ++++++++--------- src/Traits/ArrayStorage.php | 62 ++-------- src/Traits/ArrayStorageConfigurableTrait.php | 8 +- src/Traits/ConfigurableTrait.php | 24 +--- src/Traits/ConsolePrint.php | 10 +- src/Traits/Metable.php | 4 +- src/Traits/Whener.php | 9 +- tests/Global/BaseTest.php | 6 - tests/Global/EachValueTest.php | 5 +- tests/Global/MapValueTest.php | 9 +- tests/Helpers/ArrTest.php | 113 ++--------------- tests/Helpers/JsonTest.php | 2 +- tests/Helpers/NumberTest.php | 18 +-- tests/Helpers/StrTest.php | 84 +++---------- tests/enums/WithEnhancesForStringsTest.php | 25 ++-- tests/traits/ArrayStorageTest.php | 1 - tests/traits/TraitWhenerTest.php | 22 ++++ 37 files changed, 334 insertions(+), 619 deletions(-) delete mode 100644 .travis.yml rename phpstan.neon.dist => phpstan.neon (60%) create mode 100644 tests/traits/TraitWhenerTest.php diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9eea3d4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -language: php - -cache: - directories: - - $HOME/.composer/cache - -env: - global: - - setup=basic - - coverage=false - -before_install: - - composer install - - composer global require hirak/prestissimo --update-no-dev - - if [[ $coverage = 'false' ]]; then phpenv config-rm xdebug.ini || true; fi - - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - - chmod +x ./cc-test-reporter - - ./cc-test-reporter before-build - -install: - - if [[ $setup = 'basic' ]]; then travis_retry composer update --prefer-dist --no-interaction --no-suggest; fi - - if [[ $setup = 'stable' ]]; then travis_retry composer update --prefer-dist --no-interaction --no-suggest --prefer-stable; fi - - if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --no-suggest --prefer-lowest; fi - - composer dump-autoload -o - -script: - - if [[ $coverage = 'true' ]]; then composer test-cover; else composer test; fi - -after_script: - - if [[ $coverage = 'true' ]]; then ./cc-test-reporter after-build --coverage-input-type clover --exit-code $TRAVIS_TEST_RESULT - -after_success: - - if [[ $coverage = 'true' ]]; then bash <(curl -s https://codecov.io/bash); fi - -matrix: - include: - # - php: 7.4 - # env: setup=lowest - - php: 7.4 - env: setup=lowest - - - php: 7.4 - env: setup=stable - - - php: 8.0 - env: setup=stable - - - php: nightly - - allow_failures: - - php: nightly diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f002e0..7d7977f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v5.0.0 + +### Added + +- Add `PHP 8.4` support + ## v4.28.0 ### Added diff --git a/composer.json b/composer.json index aba478a..68eb080 100755 --- a/composer.json +++ b/composer.json @@ -16,35 +16,42 @@ } ], "require": { - "php": "^8.1|^8.2|^8.3", + "php": "^8.4", "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^10.1.1", - "phpstan/phpstan": "^1.10.14", - "squizlabs/php_codesniffer": "^3.7.2" + "ergebnis/composer-normalize": "^2.45", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^11.5", + "squizlabs/php_codesniffer": "^3.11", + "symfony/var-dumper": "^7.2" }, "autoload": { - "files": [ - "src/Global/base.php" - ], "psr-4": { "Php\\Support\\": "src/" - } + }, + "files": [ + "src/Global/base.php" + ] }, "autoload-dev": { "psr-4": { "Php\\Support\\Tests\\": "tests/" } }, + "config": { + "allow-plugins": { + "ergebnis/composer-normalize": true + } + }, "scripts": { - "phpcs": "@php ./vendor/bin/phpcs", "cs-fix": "@php ./vendor/bin/phpcbf", - "phpunit": "@php ./vendor/bin/phpunit --no-coverage --testdox --colors=always", - "phpunit-test": "@php ./vendor/bin/phpunit --no-coverage --testdox --colors=always", "infection": "@php ./vendor/bin/infection --coverage=./storage/coverage --threads=4", + "phpcs": "@php ./vendor/bin/phpcs", + "phpstan": "@php ./vendor/bin/phpstan analyze -c phpstan.neon --no-progress --ansi", + "phpunit": "@php ./vendor/bin/phpunit --no-coverage --testdox --colors=always", "phpunit-cover": "@php ./vendor/bin/phpunit --coverage-text", - "phpstan": "@php ./vendor/bin/phpstan analyze -c ./phpstan.neon.dist --no-progress --ansi", + "phpunit-test": "@php ./vendor/bin/phpunit --no-coverage --testdox --colors=always", "test": [ "@phpstan", "@phpunit" diff --git a/phpstan.neon.dist b/phpstan.neon similarity index 60% rename from phpstan.neon.dist rename to phpstan.neon index 4bc4288..483dec6 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon @@ -1,9 +1,11 @@ parameters: - level: 'max' + level: '6' paths: - src - checkMissingIterableValueType: false - checkGenericClassInNonGenericObjectType: false +# - tests + ignoreErrors: + - + identifier: trait.unused excludePaths: - 'src/Types/Point.php' - 'src/Types/GeoPoint.php' diff --git a/phpunit.xml b/phpunit.xml index 02cedd4..6b0174f 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,7 +1,7 @@ diff --git a/src/ConditionalHandler.php b/src/ConditionalHandler.php index 4c40ed2..1c6e183 100644 --- a/src/ConditionalHandler.php +++ b/src/ConditionalHandler.php @@ -30,12 +30,24 @@ */ final class ConditionalHandler { + /** + * @var array + * @phpstan-ignore missingType.iterableValue + */ private array $params = []; + /** + * @param Closure(mixed ...): mixed $handler + * @param bool|(Closure(mixed ...): bool) $condition + */ public function __construct(private Closure $handler, private bool|Closure $condition = true) { } + /** + * @param (Closure(mixed ...): bool)|bool $fn + * @return ConditionalHandler + */ public function handleIf(Closure|bool $fn): self { $this->condition = $fn; @@ -52,6 +64,9 @@ private function resolveCondition(): bool return $this->condition; } + /** + * @param mixed ...$params + */ public function resolve(mixed ...$params): mixed { $this->params = $params; @@ -65,14 +80,17 @@ public function resolve(mixed ...$params): mixed /** * @param mixed ...$params - * - * @return mixed */ - public function __invoke(mixed ...$params) + public function __invoke(mixed ...$params): mixed { return $this->resolve(...$params); } + /** + * @param Closure(mixed ...): mixed $fn + * @param bool|(Closure(mixed ...): bool) $condition + * @return ConditionalHandler + */ public static function make(Closure $fn, bool|Closure $condition = true): self { return new self($fn, $condition); diff --git a/src/Enums/WithEnhancesForStrings.php b/src/Enums/WithEnhancesForStrings.php index 3367a07..a506b3d 100644 --- a/src/Enums/WithEnhancesForStrings.php +++ b/src/Enums/WithEnhancesForStrings.php @@ -14,7 +14,7 @@ trait WithEnhancesForStrings casesToString as casesToStringBase; } - public static function casesToString(string $delimiter = ', ', callable $decorator = null): string + public static function casesToString(string $delimiter = ', ', ?callable $decorator = null): string { if ($decorator === null) { $decorator = static fn(self $enumItem) => "{$enumItem->value}"; diff --git a/src/Exceptions/Exception.php b/src/Exceptions/Exception.php index e9da400..fce1e1f 100644 --- a/src/Exceptions/Exception.php +++ b/src/Exceptions/Exception.php @@ -21,11 +21,11 @@ class Exception extends \Exception /** * Exception constructor. * - * @param null|string $message + * @param ?string $message * @param int $code - * @param Throwable|null $previous + * @param ?Throwable $previous */ - public function __construct(?string $message = null, $code = 0, Throwable $previous = null) + public function __construct(?string $message = null, int $code = 0, ?Throwable $previous = null) { parent::__construct($message ?? $this->getName(), $code, $previous); } diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php index d4a7ec0..814e2ba 100644 --- a/src/Exceptions/InvalidArgumentException.php +++ b/src/Exceptions/InvalidArgumentException.php @@ -16,11 +16,11 @@ class InvalidArgumentException extends \InvalidArgumentException /** * Exception constructor. * - * @param null|string $message + * @param ?string $message * @param int $code - * @param Throwable|null $previous + * @param ?Throwable $previous */ - public function __construct(?string $message = null, $code = 0, Throwable $previous = null) + public function __construct(?string $message = null, int $code = 0, ?Throwable $previous = null) { parent::__construct($message ?? $this->getName(), $code, $previous); } diff --git a/src/Exceptions/MissingMethodException.php b/src/Exceptions/MissingMethodException.php index 225244c..91f58fb 100644 --- a/src/Exceptions/MissingMethodException.php +++ b/src/Exceptions/MissingMethodException.php @@ -11,8 +11,14 @@ class MissingMethodException extends BadMethodCallException { use Thrower; - public function __construct(protected ?string $method = null, string $message = null) - { + public function __construct( + protected ?string $method = null { + get { + return $this->method; + } + }, + ?string $message = null + ) { parent::__construct( $message ?? ($this->getName() . ($this->method ? ': "' . $this->method . '"' : '')) @@ -26,12 +32,4 @@ public function getName(): string { return 'Missing method'; } - - /** - * @return null|string - */ - public function getMethod(): ?string - { - return $this->method; - } } diff --git a/src/Global/base.php b/src/Global/base.php index ef51ba2..f0fe00e 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -7,11 +7,6 @@ if (!function_exists('value')) { /** * Return the default value of the given value. - * - * @param mixed $value - * @param mixed ...$args - * - * @return mixed */ function value(mixed $value, mixed ...$args): mixed { @@ -76,6 +71,14 @@ function dataGet(mixed $target, string|array|int|null $key, mixed $default = nul if (!function_exists('mapValue')) { + /** + * @template TKey of array-key + * @template TValue + * @param callable $fn + * @param iterable $collection + * @param mixed ...$args + * @return array + */ function mapValue(callable $fn, iterable $collection, mixed ...$args): array { $result = []; @@ -89,6 +92,14 @@ function mapValue(callable $fn, iterable $collection, mixed ...$args): array } if (!function_exists('eachValue')) { + /** + * @template TKey of array-key + * @template TValue + * @param callable $fn + * @param iterable $collection + * @param mixed ...$args + * @return void + */ function eachValue(callable $fn, iterable $collection, mixed ...$args): void { foreach ($collection as $key => $value) { @@ -100,14 +111,8 @@ function eachValue(callable $fn, iterable $collection, mixed ...$args): void if (!function_exists('when')) { /** * Returns a value when a condition is truthy. - * - * @param mixed|bool|\Closure $condition - * @param mixed|\Closure $value - * @param mixed|\Closure|null $default - * - * @return mixed */ - function when($condition, $value, $default = null) + function when(mixed $condition, mixed $value, mixed $default = null): mixed { if ($result = value($condition)) { return $value instanceof Closure ? $value($result) : $value; @@ -118,12 +123,7 @@ function when($condition, $value, $default = null) } if (!function_exists('classNamespace')) { - /** - * @param object|string $class - * - * @return string - */ - function classNamespace($class): string + function classNamespace(object|string $class): string { if (is_object($class)) { $class = get_class($class); @@ -136,13 +136,8 @@ function classNamespace($class): string if (!function_exists('isTrue')) { /** * Returns bool value of a value - * - * @param mixed $val - * @param bool $return_null - * - * @return bool|null */ - function isTrue($val, bool $return_null = false): ?bool + function isTrue(mixed $val, bool $return_null = false): ?bool { if ($val === null && $return_null) { return null; @@ -156,13 +151,7 @@ function isTrue($val, bool $return_null = false): ?bool } if (!function_exists('instance')) { - /** - * @param string|object $instance - * @param mixed ...$params - * - * @return mixed - */ - function instance($instance, ...$params): mixed + function instance(string|object|null $instance, mixed ...$params): ?object { if (is_object($instance)) { return $instance; @@ -179,12 +168,8 @@ function instance($instance, ...$params): mixed if (!function_exists('class_basename')) { /** * Get the class "basename" of the given object / class. - * - * @param string|object $class - * - * @return string */ - function class_basename($class) + function class_basename(string|object $class): string { $class = is_object($class) ? get_class($class) : $class; @@ -196,9 +181,7 @@ function class_basename($class) /** * Returns all traits used by a trait and its traits. * - * @param string $trait - * - * @return array + * @return string[] */ function trait_uses_recursive(string $trait): array { @@ -206,7 +189,7 @@ function trait_uses_recursive(string $trait): array return []; } - foreach ((array)$traits as $trt) { + foreach ($traits as $trt) { $traits += trait_uses_recursive($trt); } @@ -215,12 +198,6 @@ function trait_uses_recursive(string $trait): array } if (!function_exists('does_trait_use')) { - /** - * @param string $class - * @param string $trait - * - * @return bool - */ function does_trait_use(string $class, string $trait): bool { return isset(trait_uses_recursive($class)[$trait]); @@ -231,14 +208,12 @@ function does_trait_use(string $class, string $trait): bool /** * Returns all traits used by a class, its parent classes and trait of their traits. * - * @param object|string $class - * - * @return array + * @return string[] */ - function class_uses_recursive($class): array + function class_uses_recursive(object|string $class): array { if (is_object($class)) { - $class = get_class($class); + $class = $class::class; } $results = []; @@ -262,10 +237,7 @@ function remoteStaticCall(object|string|null $class, string $method, mixed ...$p return null; } - if ( - (is_object($class) || (is_string($class) && class_exists($class))) && - method_exists($class, $method) - ) { + if ((is_object($class) || class_exists($class)) && method_exists($class, $method)) { return $class::$method(...$params); } @@ -283,10 +255,7 @@ function remoteStaticCallOrTrow(object|string|null $class, string $method, mixed throw new RuntimeException('Target Class is absent'); } - if ( - (is_object($class) || (is_string($class) && class_exists($class))) && - method_exists($class, $method) - ) { + if ((is_object($class) || class_exists($class)) && method_exists($class, $method)) { return $class::$method(...$params); } diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index e309d39..0624083 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -4,6 +4,7 @@ namespace Php\Support\Helpers; +use Closure; use ArrayAccess; use ArrayObject; use JsonSerializable; @@ -32,17 +33,16 @@ use function str_replace; /** - * @psalm-template TKey of array-key - * @psalm-template T + * @template TKey of array-key + * @template T */ class Arr { /** * Collapse an array of arrays into a single array. * - * @param iterable $array - * @return array - * @psalm-return T[] + * @param iterable $array + * @return array */ public static function collapse(iterable $array): array { @@ -64,11 +64,11 @@ public static function collapse(iterable $array): array /** * Remove one element from array by value * - * @param array $array + * @param array $array * @param mixed $val If $val is a string, the comparison is done in a case-sensitive manner. * @param bool $reindex * - * @return string|int|null Index of removed element or null if don't exist + * @return string|int|null Index of removed element or null if it don't exist */ public static function removeByValue(array &$array, mixed $val, bool $reindex = false): string|int|null { @@ -86,7 +86,7 @@ public static function removeByValue(array &$array, mixed $val, bool $reindex = * * @param mixed $items * - * @return array + * @return T[]|array */ public static function toArray(mixed $items): array { @@ -160,12 +160,12 @@ public static function dataToArray(mixed $items): mixed } /** - * @param array $res array to be merged to - * @param array $b array to be merged from. You can specify additional + * @param array $res array to be merged to + * @param array $b array to be merged from. You can specify additional * arrays via third argument, fourth argument etc. * @param bool $replaceArray Replace or Add values into Array, if key existed. * - * @return array the merged array (the original arrays are not changed.) + * @return array the merged array (the original arrays are not changed.) */ public static function merge(array $res, array $b, bool $replaceArray = true): array { @@ -191,7 +191,7 @@ public static function merge(array $res, array $b, bool $replaceArray = true): a /** * Changes PHP array to default Postgres array format * - * @param array $array + * @param array $array * * @return string */ @@ -204,6 +204,10 @@ public static function toPostgresArray(array $array): string return str_replace(['[', ']', '"'], ['{', '}', ''], $json); } + /** + * @param int[] $array + * @return ?string + */ public static function toPostgresPoint(array $array): ?string { if (count($array) !== 2) { @@ -221,9 +225,9 @@ public static function toPostgresPoint(array $array): ?string /** * Remove named keys from arrays * - * @param array $array + * @param array $array * - * @return array + * @return array */ public static function toIndexedArray(array $array): array { @@ -243,9 +247,9 @@ public static function toIndexedArray(array $array): array * @param string|null $s * @param int $start * @param ?int $end - * @param array $braces + * @param array{string,string} $braces * - * @return array + * @return float[] */ public static function fromPostgresArrayWithBraces( ?string $s, @@ -323,7 +327,7 @@ public static function fromPostgresArrayWithBraces( * @param int $start * @param ?int $end * - * @return array + * @return float[] */ public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end = null): array { @@ -331,9 +335,9 @@ public static function fromPostgresArray(?string $s, int $start = 0, ?int &$end } /** - * @param string|null $value + * @param ?string $value * - * @return ?array + * @return ?array{float,float} */ public static function fromPostgresPoint(?string $value): ?array { @@ -374,7 +378,6 @@ public static function get(mixed $array, string|int|null $key, mixed $default = return $array; } - /** @var array|ArrayAccess $array */ if (static::exists($array, $key)) { return $array[$key]; } @@ -409,7 +412,7 @@ public static function accessible(mixed $value): bool /** * Determine if the given key exists in the provided array. * - * @param ArrayAccess|array $array + * @param ArrayAccess|array $array * @param string|int $key * * @return bool @@ -426,9 +429,10 @@ public static function exists(ArrayAccess|array $array, string|int $key): bool /** * Check if an item or items exist in an array using "dot" notation. * - * @param ArrayAccess|array $array - * @param string|array $keys + * @param ArrayAccess|array $array + * @param string|string[] $keys * @param non-empty-string $separator + * @return bool */ public static function has(ArrayAccess|array $array, string|array $keys, string $separator = '.'): bool { @@ -462,21 +466,19 @@ public static function has(ArrayAccess|array $array, string|array $keys, string * * If no key is given to the method, the entire array will be replaced. * - * @param array|ArrayObject|null $array + * @param array|ArrayObject|array $array + * @param-out array|ArrayObject|array $array * @param string $key * @param mixed $value * @param non-empty-string $separator + * @return T[]|array|ArrayObject */ public static function set( - array|ArrayObject|null &$array, + array|ArrayObject &$array, string $key, mixed $value, string $separator = '.' - ): array|ArrayObject|null { - if ($array === null) { - return $array; - } - + ): array|ArrayObject { $keys = explode($separator, $key); while (count($keys) > 1) { @@ -497,8 +499,8 @@ public static function set( /** * Remove one or many array items from a given array using "dot" notation. * - * @param array|ArrayObject $array - * @param array|string $keys + * @param array|ArrayObject $array + * @param string[]|string $keys * * @return void */ @@ -543,29 +545,25 @@ public static function remove(array|ArrayObject &$array, array|string $keys): vo * Key = search value * Value = replace value * - * @param array $array - * @param array $replace + * @param array $array + * @param array $replace * - * @return array + * @return array */ public static function replaceByTemplate(array $array, array $replace): array { - $res = []; - foreach ($array as $key => $item) { - $res[$key] = self::itemReplaceByTemplate($item, $replace); - } - return $res; + return array_map(static fn($item) => self::itemReplaceByTemplate($item, $replace), $array); } /** * Replace templates into item * * @param mixed $item - * @param array $replace + * @param array $replace * - * @return array|mixed + * @return string|string[]|mixed */ - private static function itemReplaceByTemplate(mixed $item, array $replace) + private static function itemReplaceByTemplate(mixed $item, array $replace): mixed { if (is_array($item)) { return self::replaceByTemplate($item, $replace); @@ -581,9 +579,9 @@ private static function itemReplaceByTemplate(mixed $item, array $replace) /** * Find duplicates into an array * - * @param array $array + * @param array $array * - * @return array + * @return array */ public static function duplicates(array $array): array { @@ -593,10 +591,10 @@ public static function duplicates(array $array): array /** * Fill a keyed array by values from another array * - * @param array $keys - * @param array $values + * @param array $keys + * @param array $values * - * @return array + * @return array */ public static function fillKeysByValues(array $keys, array $values): array { @@ -612,12 +610,12 @@ public static function fillKeysByValues(array $keys, array $values): array /** * Push an item onto the beginning of an array. * - * @param array $array + * @param array $array * @param mixed $value - * @param mixed $key - * @return array + * @param string|int|null $key + * @return array */ - public static function prepend(array $array, mixed $value, mixed $key = null): array + public static function prepend(array $array, mixed $value, string|int|null $key = null): array { if (func_num_args() === 2) { array_unshift($array, $value); @@ -631,10 +629,10 @@ public static function prepend(array $array, mixed $value, mixed $key = null): a /** * Get one or a specified number of random values from an array. * - * @param array $array + * @param array $array * @param int|null $number * @param bool $preserveKeys - * @return mixed + * @return T[]|array * * @throws \InvalidArgumentException */ @@ -675,7 +673,14 @@ public static function random(array $array, ?int $number = null, bool $preserveK return $results; } - public static function map(array $elements, \Closure $func): array + /** + * @template U + * @param array $elements + * @param Closure $func + * @phpstan-param Closure(array): U[] $func + * @return array + */ + public static function map(array $elements, Closure $func): array { $keys = array_keys($elements); $map = array_map($func, $elements, $keys); diff --git a/src/Helpers/B64.php b/src/Helpers/B64.php index bff24ce..4d0d2b0 100644 --- a/src/Helpers/B64.php +++ b/src/Helpers/B64.php @@ -16,14 +16,14 @@ class B64 * * @var string */ - private const LAST_THREE_STANDARD = '+/='; + private const string LAST_THREE_STANDARD = '+/='; /** * The last three characters from the alphabet of the URL-safe implementation * * @var string */ - private const LAST_THREE_URL_SAFE = '-_~'; + private const string LAST_THREE_URL_SAFE = '-_~'; /** * Encodes the supplied data to Base64 diff --git a/src/Helpers/Bit.php b/src/Helpers/Bit.php index 261856e..aac8780 100644 --- a/src/Helpers/Bit.php +++ b/src/Helpers/Bit.php @@ -71,7 +71,7 @@ public static function checkFlag(int|string $value, int $bit): bool /** * Check a bit is existing in flag`s list * - * @param array $list + * @param int[] $list * @param int $bit * * @return bool @@ -84,13 +84,13 @@ public static function exist(array $list, int $bit): bool /** * Return value of sum of all bits in list * - * @param array $list + * @param int[] $list * * @return int */ public static function grant(array $list): int { - return array_reduce($list, fn($prev, $next) => $prev | $next, 0); + return array_reduce($list, fn(int $prev, int $next) => $prev | $next, 0); } /** diff --git a/src/Helpers/Number.php b/src/Helpers/Number.php index 4c761db..ad0e2f2 100644 --- a/src/Helpers/Number.php +++ b/src/Helpers/Number.php @@ -28,7 +28,6 @@ public static function safeInt(int|string $value): int|string public static function isInteger(mixed $value): bool { - /** @phpstan-ignore-next-line */ return is_int($value) || (string)$value === (string)(int)($value); } } diff --git a/src/Helpers/Str.php b/src/Helpers/Str.php index 7395619..e9e1275 100644 --- a/src/Helpers/Str.php +++ b/src/Helpers/Str.php @@ -11,25 +11,14 @@ use function preg_replace; use function trim; -/** - * Class Str - * - * @package Php\Support\Helpers - */ class Str { /** * The cache of delimited converted-cased words. * - * @var array + * @var array */ protected static array $delimitedCache = []; - /** - * The cache of camel-cased words. - * - * @var array - */ - protected static array $camelCache = []; /** * Converts a string to snake_case @@ -305,11 +294,11 @@ public static function replaceStrTo(string $str, int $from_start, int $from_end, * Value = replace value * * @param string $str - * @param array $replace + * @param array $replace * - * @return mixed + * @return string|string[] */ - public static function replaceByTemplate(string $str, array $replace) + public static function replaceByTemplate(string $str, array $replace): array|string { return str_replace(array_keys($replace), array_values($replace), $str); } diff --git a/src/Helpers/URLify.php b/src/Helpers/URLify.php index e4c2a9f..aba6f6f 100644 --- a/src/Helpers/URLify.php +++ b/src/Helpers/URLify.php @@ -17,6 +17,7 @@ class URLify * The character map for the designated language * * @see https://github.com/jbroadway/urlify/blob/master/URLify.php + * @var array */ private static array $map = []; @@ -42,6 +43,8 @@ class URLify * Part of the URLify.php Project * * @see https://github.com/jbroadway/urlify/blob/master/URLify.php + * + * @var array> */ public static array $maps = [ 'de' => [/* German */ @@ -767,6 +770,7 @@ private static function initLanguageMap(string $language = ''): void } // Is a specific map associated with $language? + // @phpstan-ignore booleanAnd.rightAlwaysTrue if (isset(self::$maps[$language]) && is_array(self::$maps[$language])) { // Move this map to end. This means it will have priority over others $map = self::$maps[$language]; diff --git a/src/Interfaces/Arrayable.php b/src/Interfaces/Arrayable.php index 331814c..d36184d 100644 --- a/src/Interfaces/Arrayable.php +++ b/src/Interfaces/Arrayable.php @@ -5,14 +5,13 @@ namespace Php\Support\Interfaces; /** - * Interface Arrayable - * - * @package Php\Support\Interfaces + * @template TKey of array-key + * @template TValue */ interface Arrayable { /** - * @return array + * @return array */ public function toArray(): array; } diff --git a/src/Structures/Collections/ArrayCollection.php b/src/Structures/Collections/ArrayCollection.php index 02ff496..ed6e8de 100644 --- a/src/Structures/Collections/ArrayCollection.php +++ b/src/Structures/Collections/ArrayCollection.php @@ -359,7 +359,7 @@ public function transform(Closure $func): static /** * {@inheritDoc} */ - public function merge(mixed $items): static + public function merge(iterable $items): static { return $this->createFrom(array_merge($this->elements, Arr::toArray($items))); } @@ -812,7 +812,7 @@ protected function valueRetriever(mixed $value): callable /** * Group an associative array by a field or using a callback. * - * @param (callable(T, TKey): array-key)|array|string $groupBy + * @param (callable(T, TKey): array-key)|string[]|string $groupBy * @param bool $preserveKeys * @psalm-return static> * @return static> diff --git a/src/Structures/Collections/Collection.php b/src/Structures/Collections/Collection.php index a3d1380..842f98b 100644 --- a/src/Structures/Collections/Collection.php +++ b/src/Structures/Collections/Collection.php @@ -25,10 +25,10 @@ * * @author Doctrine * - * @psalm-template TKey of array-key - * @psalm-template T - * @template-extends ReadableCollection - * @template-extends ArrayAccess + * @phpstan-template TKey of array-key + * @phpstan-template TValue + * @template-extends ReadableCollection + * @template-extends ArrayAccess */ interface Collection extends ReadableCollection, ArrayAccess { @@ -36,16 +36,12 @@ interface Collection extends ReadableCollection, ArrayAccess * Adds an element at the end of the collection. * * @param mixed $element The element to add. - * @psalm-param T $element - * - * @return bool + * @phpstan-param TValue $element */ public function add(mixed $element): bool; /** * Clears the collection, removing all elements. - * - * @return void */ public function clear(): void; @@ -53,10 +49,10 @@ public function clear(): void; * Removes the element at the specified index from the collection. * * @param string|int $key The key/index of the element to remove. - * @psalm-param TKey $key + * @phpstan-param TKey $key * * @return mixed The removed element or NULL, if the collection did not contain the element. - * @psalm-return T|null + * @phpstan-return ?TValue */ public function remove(string|int $key): mixed; @@ -64,7 +60,7 @@ public function remove(string|int $key): mixed; * Removes the specified element from the collection, if it is found. * * @param mixed $element The element to remove. - * @psalm-param T $element + * @phpstan-param TValue $element * * @return bool TRUE if this collection contained the specified element, FALSE otherwise. */ @@ -75,17 +71,15 @@ public function removeElement(mixed $element): bool; * * @param string|int $key The key/index of the element to set. * @param mixed $value The element to set. - * @psalm-param TKey $key - * @psalm-param T $value - * - * @return void + * @phpstan-param TKey $key + * @phpstan-param TValue $value */ public function set(string|int $key, mixed $value): void; /** * Push all the given items onto the collection. * - * @param iterable $source + * @param iterable $source */ public function concat(iterable $source): static; @@ -94,10 +88,9 @@ public function clone(): static; /** * Get one or a specified number of items randomly from the collection. * - * @param (callable(self): int)|int|null $number - * @param bool $preserveKeys + * @param (callable(self): int)|int|null $number * - * @return static|T + * @return static|TValue * * @throws \InvalidArgumentException */ @@ -106,9 +99,9 @@ public function random(callable|int|null $number = null, bool $preserveKeys = fa /** * Group an associative array by a field or using a callback. * - * @param (callable(T, TKey): array-key)|array|string $groupBy + * @param (callable(TValue, TKey): array-key)|string[]|string $groupBy * @param bool $preserveKeys - * @psalm-param (callable(T, TKey): array-key)|array|string $groupBy + * @phpstan-param (callable(TValue, TKey): array-key)|string[]|string $groupBy */ public function groupBy(callable|array|string $groupBy, bool $preserveKeys = false): static; } diff --git a/src/Structures/Collections/ReadableCollection.php b/src/Structures/Collections/ReadableCollection.php index 3d9d5bc..6d45bdb 100644 --- a/src/Structures/Collections/ReadableCollection.php +++ b/src/Structures/Collections/ReadableCollection.php @@ -7,12 +7,11 @@ use Closure; use Countable; use IteratorAggregate; -use Php\Support\Interfaces\Arrayable; /** - * @psalm-template TKey of array-key - * @template-covariant T - * @template-extends IteratorAggregate + * @template TKey of array-key + * @template TValue + * @template-extends IteratorAggregate */ interface ReadableCollection extends Countable, IteratorAggregate { @@ -21,10 +20,10 @@ interface ReadableCollection extends Countable, IteratorAggregate * This is an O(n) operation, where n is the size of the collection. * * @param mixed $element The element to search for. - * @psalm-param TMaybeContained $element + * @phpstan-param TMaybeContained $element * * @return bool TRUE if the collection contains the element, FALSE otherwise. - * @psalm-return (TMaybeContained is T ? bool : false) + * @phpstan-return (TMaybeContained is TValue ? bool : false) * * @template TMaybeContained */ @@ -41,7 +40,7 @@ public function isEmpty(): bool; * Checks whether the collection contains an element with the specified key/index. * * @param string|int $key The key/index to check for. - * @psalm-param TKey $key + * @phpstan-param TKey $key * * @return bool TRUE if the collection contains an element with the specified key/index, * FALSE otherwise. @@ -52,10 +51,10 @@ public function containsKey(string|int $key): bool; * Gets the element at the specified key/index. * * @param string|int $key The key/index of the element to retrieve. - * @psalm-param TKey $key + * @phpstan-param TKey $key * * @return mixed - * @psalm-return T|null + * @phpstan-return ?TValue */ public function get(string|int $key): mixed; @@ -64,32 +63,32 @@ public function get(string|int $key): mixed; * * @return int[]|string[] The keys/indices of the collection, in the order of the corresponding * elements in the collection. - * @psalm-return list + * @phpstan-return TKey[] */ public function getKeys(): array; /** * Gets all values of the collection. * - * @return mixed[] The values of all elements in the collection, in the + * @return array The values of all elements in the collection, in the * order they appear in the collection. - * @psalm-return list + * @phpstan-return TValue[] */ public function getValues(): array; /** * Gets a native PHP array representation of the collection. * - * @return mixed[] - * @psalm-return array + * @return array + * @phpstan-return array */ public function toArray(): array; /** * Gets a native PHP array of the elements. * - * @return mixed[] - * @psalm-return array + * @return array + * @phpstan-return array */ public function all(): array; @@ -97,7 +96,7 @@ public function all(): array; * Sets the internal iterator to the first element in the collection and returns this element. * * @return mixed - * @psalm-return T|false + * @phpstan-return TValue|false */ public function first(): mixed; @@ -105,7 +104,7 @@ public function first(): mixed; * Sets the internal iterator to the last element in the collection and returns this element. * * @return mixed - * @psalm-return T|false + * @phpstan-return TValue|false */ public function last(): mixed; @@ -113,23 +112,21 @@ public function last(): mixed; * Gets the key/index of the element at the current iterator position. * * @return int|string|null - * @psalm-return TKey|null + * @phpstan-return ?TKey */ public function key(): int|string|null; /** * Gets the element of the collection at the current iterator position. * - * @return mixed - * @psalm-return T|false + * @phpstan-return TValue|false */ public function current(): mixed; /** * Moves the internal iterator position to the next element and returns this element. * - * @return mixed - * @psalm-return T|false + * @phpstan-return TValue|false */ public function next(): mixed; @@ -143,16 +140,16 @@ public function next(): mixed; * @param int $offset The offset to start from. * @param int|null $length The maximum number of elements to return, or null for no limit. * - * @return mixed[] - * @psalm-return array + * @return array + * @phpstan-return array */ - public function slice(int $offset, int|null $length = null): array; + public function slice(int $offset, ?int $length = null): array; /** * Tests for the existence of an element that satisfies the given predicate. * * @param Closure $func The predicate. - * @psalm-param Closure(TKey, T):bool $func + * @phpstan-param Closure(TKey, TValue):bool $func * * @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise. */ @@ -162,22 +159,22 @@ public function exists(Closure $func): bool; * Returns all the elements of this collection that satisfy the predicate $func. * The order of the elements is preserved. * - * @param null|Closure $func The predicate used for filtering. - * @psalm-param null|Closure(T, TKey):bool $func + * @param ?Closure $func The predicate used for filtering. + * @phpstan-param null|Closure(TValue, TKey):bool $func * - * @return ReadableCollection A collection with the results of the filter operation. - * @psalm-return ReadableCollection + * @return ReadableCollection A collection with the results of the filter operation. + * @phpstan-return ReadableCollection */ - public function filter(Closure $func = null): ReadableCollection; + public function filter(?Closure $func = null): ReadableCollection; /** * Create a collection of all elements that do not pass a given truth test. * * @param Closure $callback The predicate used for filtering. - * @psalm-param Closure(T, TKey):bool $callback + * @phpstan-param Closure(TValue, TKey):bool $callback * * @return ReadableCollection A collection with the results of the filter operation. - * @psalm-return ReadableCollection + * @phpstan-return ReadableCollection */ public function reject(Closure $callback): ReadableCollection; @@ -185,12 +182,12 @@ public function reject(Closure $callback): ReadableCollection; * Applies the given function to each element in the collection and returns * a new collection with the elements returned by the function. * - * @psalm-param Closure(T):U $func + * @phpstan-param Closure(TValue):U $func * * @return ReadableCollection - * @psalm-return ReadableCollection + * @phpstan-return ReadableCollection * - * @psalm-template U + * @phpstan-template U */ public function map(Closure $func): ReadableCollection; @@ -199,12 +196,12 @@ public function map(Closure $func): ReadableCollection; * * @param string $keyName * @param ?string $valueName - * @psalm-param null|Closure(T,TKey):U $func + * @phpstan-param null|Closure(TValue,TKey):U $func * * @return ReadableCollection - * @psalm-return ReadableCollection + * @phpstan-return ReadableCollection * - * @psalm-template U + * @phpstan-template U */ public function mapByKey(string $keyName, ?string $valueName = null, ?Closure $func = null): ReadableCollection; @@ -213,12 +210,12 @@ public function mapByKey(string $keyName, ?string $valueName = null, ?Closure $f * Keys are preserved in the resulting collections. * * @param Closure $func The predicate on which to partition. - * @psalm-param Closure(TKey, T):bool $func + * @phpstan-param Closure(TKey, TValue):bool $func * * @return ReadableCollection[] An array with two elements. The first element contains the collection * of elements where the predicate returned TRUE, the second element * contains the collection of elements where the predicate returned FALSE. - * @psalm-return array{0: ReadableCollection, 1: ReadableCollection} + * @phpstan-return array{0: ReadableCollection, 1: ReadableCollection} */ public function partition(Closure $func): array; @@ -226,7 +223,7 @@ public function partition(Closure $func): array; * Tests whether the given predicate $func holds for all elements of this collection. * * @param Closure $func The predicate. - * @psalm-param Closure(TKey, T):bool $func + * @phpstan-param Closure(TKey, TValue):bool $func * * @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. */ @@ -236,7 +233,7 @@ public function testForAll(Closure $func): bool; * Applies the given function to each element of the Collection. Returns the same Collection. * * @param callable $func The predicate. - * @psalm-param callable(TKey, T):bool $func + * @phpstan-param callable(TKey, TValue):bool $func */ public function each(callable $func): static; @@ -244,7 +241,7 @@ public function each(callable $func): static; * Transform each item in the collection using a callback. * * @param Closure $func The predicate. - * @psalm-param Closure(TKey, T):void $func + * @phpstan-param Closure(TKey, TValue):void $func */ public function transform(Closure $func): static; @@ -252,11 +249,9 @@ public function transform(Closure $func): static; /** * Merge the collection with the given items. * - * @param \iterable|Arrayable $items - * @return static + * @param iterable $items */ - // @phpstan-ignore-next-line - public function merge(mixed $items): static; + public function merge(iterable $items): static; /** * Gets the index/key of a given element. The comparison of two elements is strict, @@ -264,10 +259,10 @@ public function merge(mixed $items): static; * For objects this means reference equality. * * @param mixed $element The element to search for. - * @psalm-param TMaybeContained $element + * @phpstan-param TMaybeContained $element * * @return int|string|bool The key/index of the element or FALSE if the element was not found. - * @psalm-return (TMaybeContained is T ? TKey|false : false) + * @phpstan-return (TMaybeContained is TValue ? TKey|false : false) * * @template TMaybeContained */ @@ -277,26 +272,26 @@ public function indexOf(mixed $element): string|int|bool; * Returns the first element of this collection that satisfies the predicate $func. * * @param Closure $func The predicate. - * @psalm-param Closure(TKey, T):bool $func + * @phpstan-param Closure(TKey, TValue):bool $func * * @return mixed The first element respecting the predicate, * null if no element respects the predicate. - * @psalm-return T|null + * @phpstan-return ?TValue */ public function findFirst(Closure $func): mixed; /** * Applies iteratively the given function to each element in the collection, - * so as to reduce the collection to a single value. + * to reduce the collection to a single value. * - * @psalm-param Closure(TReturn|TInitial|null, T):(TInitial|TReturn) $func - * @psalm-param TInitial|null $initial + * @phpstan-param Closure(TReturn|TInitial|null, TValue):(TInitial|TReturn) $func + * @phpstan-param TInitial|null $initial * * @return mixed - * @psalm-return TReturn|TInitial|null + * @phpstan-return TReturn|TInitial|null * - * @psalm-template TReturn - * @psalm-template TInitial + * @phpstan-template TReturn + * @phpstan-template TInitial */ public function reduce(Closure $func, mixed $initial = null): mixed; } diff --git a/src/Traits/ArrayStorage.php b/src/Traits/ArrayStorage.php index 69a4a55..7202e7c 100644 --- a/src/Traits/ArrayStorage.php +++ b/src/Traits/ArrayStorage.php @@ -12,7 +12,10 @@ * Class ArrayStorage * * @package Php\Support\Traits - * @mixin ArrayAccess + * @template TKey of array-key + * @template TValue + * @implements ArrayAccess + * @mixin ArrayAccess */ trait ArrayStorage // implements ArrayAccess, Arrayable { @@ -32,11 +35,7 @@ public function __get(string $name) return $this->get($name); } - /** - * @param string $name - * @param mixed $value - */ - public function __set(string $name, $value): void + public function __set(string $name, mixed $value): void { $this->set($name, $value); } @@ -46,7 +45,7 @@ public function __set(string $name, $value): void * * @return mixed|null */ - public function get(string $name) + public function get(string $name): mixed { if ($this->propertyExists($name)) { return $this->$name; @@ -77,11 +76,7 @@ protected function propertyExists(string $name): bool return $name !== 'data' && property_exists($this, $name); } - /** - * @param string $name - * @param $value - */ - public function set(string $name, $value): void + public function set(string $name, mixed $value): void { if ($this->propertyExists($name)) { $this->$name = $value; @@ -116,21 +111,12 @@ public function __unset(string $name) /** * Determine if an item exists at an offset. - * - * @param string $key - * - * @return bool */ - public function offsetExists($key): bool + public function offsetExists(mixed $key): bool { return $this->valueExists($key); } - /** - * @param string $name - * - * @return bool - */ public function valueExists(string $name): bool { return $this->propertyExists($name) || Arr::has($this->data, $name); @@ -145,37 +131,24 @@ public function disableErrorOnNull(): static /** * Get an item at a given offset. - * - * @param mixed $key - * - * @return mixed */ - public function offsetGet($key): mixed + public function offsetGet(mixed $key): mixed { return $this->get($key); } /** * Set the item at a given offset. - * - * @param mixed $key - * @param mixed $value - * - * @return void */ - public function offsetSet($key, $value): void + public function offsetSet(mixed $key, mixed $value): void { $this->set($key, $value); } /** * Unset the item at a given offset. - * - * @param string $key - * - * @return void */ - public function offsetUnset($key): void + public function offsetUnset(mixed $key): void { unset($this->$key); } @@ -185,30 +158,21 @@ public function offsetUnset($key): void */ public function count(): int { - return count($this->getData()); + return count($this->data); } - /** - * @return array - */ public function getData(): array { return $this->data; } - /** - * @return string - */ public function __toString(): string { return (string)Json::encode($this->toArray()); } - /** - * @return array - */ public function toArray(): array { - return $this->getData(); + return $this->data; } } diff --git a/src/Traits/ArrayStorageConfigurableTrait.php b/src/Traits/ArrayStorageConfigurableTrait.php index eb7bf81..4e79d0e 100644 --- a/src/Traits/ArrayStorageConfigurableTrait.php +++ b/src/Traits/ArrayStorageConfigurableTrait.php @@ -16,13 +16,7 @@ trait ArrayStorageConfigurableTrait ArrayStorage::propertyExists insteadof ConfigurableTrait; } - /** - * @param string $key - * @param $value - * - * @return bool - */ - protected function setPropConfigurable(string $key, $value): bool + protected function setPropConfigurable(string $key, mixed $value): bool { $this->set($key, $value); diff --git a/src/Traits/ConfigurableTrait.php b/src/Traits/ConfigurableTrait.php index d6fffa3..e248e25 100644 --- a/src/Traits/ConfigurableTrait.php +++ b/src/Traits/ConfigurableTrait.php @@ -28,13 +28,7 @@ public function configurable($attributes, ?bool $exceptOnMiss = true) return $this; } - /** - * @param string $key - * @param $value - * - * @return bool - */ - protected function applyValue(string $key, $value): bool + protected function applyValue(string $key, mixed $value): bool { if (!$res = $this->callMethod($key, $value)) { $res = $this->setPropConfigurable($key, $value); @@ -42,13 +36,7 @@ protected function applyValue(string $key, $value): bool return $res; } - /** - * @param string $key - * @param $value - * - * @return bool - */ - protected function setPropConfigurable(string $key, $value): bool + protected function setPropConfigurable(string $key, mixed $value): bool { if ($this->propertyExists($key)) { $this->{$key} = $value; @@ -68,13 +56,7 @@ protected function propertyExists(string $key): bool return property_exists($this, $key); } - /** - * @param string $key - * @param $value - * - * @return bool - */ - protected function callMethod(string $key, $value): bool + protected function callMethod(string $key, mixed $value): bool { if (method_exists($this, $method = 'set' . ucfirst($key))) { $this->$method($value); diff --git a/src/Traits/ConsolePrint.php b/src/Traits/ConsolePrint.php index 92b858c..97c3be4 100644 --- a/src/Traits/ConsolePrint.php +++ b/src/Traits/ConsolePrint.php @@ -10,21 +10,15 @@ */ trait ConsolePrint { - /** - * @param mixed $msg - * @param bool $newLine - */ - public function print($msg, bool $newLine = true): void + public function print(mixed $msg, bool $newLine = true): void { fwrite(STDOUT, print_r($msg, true) . ($newLine ? PHP_EOL : '')); } /** - * @param mixed $msg - * @param bool $newLine * @codeCoverageIgnore */ - public function printError($msg, bool $newLine = true): void + public function printError(mixed $msg, bool $newLine = true): void { fwrite(STDERR, print_r($msg, true) . ($newLine ? PHP_EOL : '')); } diff --git a/src/Traits/Metable.php b/src/Traits/Metable.php index 03a61fb..e82739b 100755 --- a/src/Traits/Metable.php +++ b/src/Traits/Metable.php @@ -13,7 +13,7 @@ trait Metable { /** - * The meta data for the element. + * The metadata for the element. * * @var array */ @@ -35,7 +35,7 @@ public function meta(): array * * @return mixed */ - public function metaAttribute(string $key, mixed $default = null) + public function metaAttribute(string $key, mixed $default = null): mixed { return Arr::get($this->meta, $key, $default); } diff --git a/src/Traits/Whener.php b/src/Traits/Whener.php index 3f23611..8f47809 100755 --- a/src/Traits/Whener.php +++ b/src/Traits/Whener.php @@ -10,14 +10,7 @@ */ trait Whener { - /** - * @param mixed $value - * @param callable $callback - * @param null|callable $default - * - * @return $this - */ - public function when($value, callable $callback, ?callable $default = null): self + public function when(mixed $value, callable $callback, ?callable $default = null): mixed { if ($value) { return $callback($this, $value) ?: $this; diff --git a/tests/Global/BaseTest.php b/tests/Global/BaseTest.php index a589a25..734a06f 100644 --- a/tests/Global/BaseTest.php +++ b/tests/Global/BaseTest.php @@ -283,12 +283,6 @@ public function testInstance(): void foreach ( [ - 1, - 0, - -1, - 12.21, - true, - false, null, '1', 'true', diff --git a/tests/Global/EachValueTest.php b/tests/Global/EachValueTest.php index a204181..a9f6276 100644 --- a/tests/Global/EachValueTest.php +++ b/tests/Global/EachValueTest.php @@ -4,13 +4,12 @@ namespace Php\Support\Tests\Global; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; final class EachValueTest extends TestCase { - /** - * @test - */ + #[Test] public function eachValue(): void { $result = []; diff --git a/tests/Global/MapValueTest.php b/tests/Global/MapValueTest.php index 9c0d2d2..d59517d 100644 --- a/tests/Global/MapValueTest.php +++ b/tests/Global/MapValueTest.php @@ -4,13 +4,12 @@ namespace Php\Support\Tests\Global; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; final class MapValueTest extends TestCase { - /** - * @test - */ + #[Test] public function mapValue(): void { $fnColl = static fn(string $value) => mb_strtoupper($value); @@ -22,9 +21,7 @@ public function mapValue(): void self::assertEquals($expect, $result); } - /** - * @test - */ + #[Test] public function mapValueWithParams(): void { $fnColl = static fn(string $value, $key, string $prefix, string $suffix) => diff --git a/tests/Helpers/ArrTest.php b/tests/Helpers/ArrTest.php index ec4a27f..0ec1602 100644 --- a/tests/Helpers/ArrTest.php +++ b/tests/Helpers/ArrTest.php @@ -9,6 +9,8 @@ use Php\Support\Helpers\Json; use Php\Support\Interfaces\Jsonable; use Php\Support\Structures\Collections\ArrayCollection; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; /** @@ -252,12 +254,7 @@ public function jsonSerialize(): mixed ]; } - /** - * @dataProvider providerDataToArray - * - * @param mixed $items - * @param $exp - */ + #[DataProvider('providerDataToArray')] public function testDataToArray($items, $exp): void { $result = Arr::dataToArray($items); @@ -265,9 +262,6 @@ public function testDataToArray($items, $exp): void static::assertEquals($exp, $result); } - /** - * @return array - */ public static function providerToArray(): array { $arrayableClass = new class () implements \Php\Support\Interfaces\Arrayable { @@ -376,12 +370,7 @@ public function jsonSerialize(): mixed ]; } - /** - * @dataProvider providerToArray - * - * @param $items - * @param $exp - */ + #[DataProvider('providerToArray')] public function testToArray($items, $exp): void { $result = Arr::toArray($items); @@ -588,9 +577,6 @@ public function testFromPostgresPoint(): void static::assertNull(Arr::fromPostgresPoint('')); } - /** - * @return array - */ public static function providerRemoveByValue(): array { return [ @@ -734,14 +720,7 @@ public static function providerRemoveByValue(): array ]; } - /** - * @dataProvider providerRemoveByValue - * - * @param $expArray - * @param $expIdx - * @param $array - * @param $val - */ + #[DataProvider('providerRemoveByValue')] public function testRemoveByValue($expArray, $expIdx, $array, $val): void { $idx = Arr::removeByValue($array, $val); @@ -749,9 +728,6 @@ public function testRemoveByValue($expArray, $expIdx, $array, $val): void static::assertEquals($expIdx, $idx); } - /** - * @return array - */ public static function providerRemoveByValueAndReindex(): array { return [ @@ -896,14 +872,7 @@ public static function providerRemoveByValueAndReindex(): array ]; } - /** - * @dataProvider providerRemoveByValueAndReindex - * - * @param $expArray - * @param $expIdx - * @param $array - * @param $val - */ + #[DataProvider('providerRemoveByValueAndReindex')] public function testRemoveByValueAndReindex($expArray, $expIdx, $array, $val): void { $idx = Arr::removeByValue($array, $val, true); @@ -1005,13 +974,7 @@ public static function providerGet(): array ]; } - /** - * @dataProvider providerGet - * - * @param $expVal - * @param $array - * @param $key - */ + #[DataProvider('providerGet')] public function testGet($expVal, $array, $key): void { $val = Arr::get($array, $key); @@ -1023,9 +986,6 @@ public function testGet($expVal, $array, $key): void static::assertEquals($expVal ?? 'test', $val); } - /** - * @return array - */ public static function providerHas(): array { $array = [ @@ -1085,21 +1045,12 @@ public static function providerHas(): array ]; } - /** - * @dataProvider providerHas - * - * @param $expVal - * @param $array - * @param $key - */ + #[DataProvider('providerHas')] public function testHas($expVal, $array, $key): void { static::assertEquals($expVal, Arr::has($array, $key)); } - /** - * @return array - */ public static function providerSet(): array { $array = []; @@ -1148,23 +1099,10 @@ public static function providerSet(): array $array, 'key3', ], - [ - null, - null, - '2', - ], - ]; } - /** - * @dataProvider providerSet - * - * @param $expVal - * @param $array - * @param $key - * @param $val - */ + #[DataProvider('providerSet')] public function testSet($expVal, $array, $key): void { Arr::set($array, $key, $expVal); @@ -1174,9 +1112,6 @@ public function testSet($expVal, $array, $key): void public function testSet2(): void { - $array = null; - static::assertNull(Arr::set($array, '', 1)); - $array = []; static::assertEquals(['' => 1], Arr::set($array, '', 1)); } @@ -1190,9 +1125,6 @@ public function testSetWithDivider(): void static::assertEquals(['key' => ['sub2' => 1, 'sub3' => ['sub4sub' => 121]]], $array); } - /** - * @return array - */ public static function providerRemove(): array { $array = [ @@ -1244,13 +1176,7 @@ public static function providerRemove(): array ]; } - - /** - * @dataProvider providerRemove - * - * @param $array - * @param $key - */ + #[DataProvider('providerRemove')] public function testRemove($array, $key): void { Arr::remove($array, $key); @@ -1269,9 +1195,6 @@ public function testRemove2(): void static::assertEquals($array, Arr::get($array, null)); } - /** - * @return array - */ public static function dataReplaceByTemplate(): array { return [ @@ -1376,13 +1299,7 @@ public static function dataReplaceByTemplate(): array ]; } - /** - * @dataProvider dataReplaceByTemplate - * - * @param $array - * @param $replace - * @param $exp - */ + #[DataProvider('dataReplaceByTemplate')] public function testReplaceByTemplate(array $array, array $replace, array $exp): void { $res = Arr::replaceByTemplate($array, $replace); @@ -1394,9 +1311,7 @@ public function testReplaceByTemplate(array $array, array $replace, array $exp): // static::assertEquals($exp, $res); } - /** - * @test - */ + #[Test] public function collapse(): void { $list = [new ArrayCollection([1, 2, 3]), 4, 5, 6, [7, 8, 9]]; @@ -1404,9 +1319,7 @@ public function collapse(): void self::assertEquals([1, 2, 3, 7, 8, 9], Arr::collapse($list)); } - /** - * @test - */ + #[Test] public function prepend(): void { $list = [1, 2, 3]; diff --git a/tests/Helpers/JsonTest.php b/tests/Helpers/JsonTest.php index 558f4cd..1c484e3 100644 --- a/tests/Helpers/JsonTest.php +++ b/tests/Helpers/JsonTest.php @@ -222,7 +222,7 @@ public function jsonSerialize() }*/ /** - * @dataProvider providerToArray + * dataProvider providerToArray * * @param $items * @param $exp diff --git a/tests/Helpers/NumberTest.php b/tests/Helpers/NumberTest.php index c53b4d1..3f57381 100644 --- a/tests/Helpers/NumberTest.php +++ b/tests/Helpers/NumberTest.php @@ -5,6 +5,8 @@ namespace Php\Support\Tests\Helpers; use Php\Support\Helpers\Number; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; /** @@ -70,12 +72,7 @@ public static function providerSafeInt(): array ]; } - /** - * @dataProvider providerSafeInt - * - * @param int|string $value - * @param int|string $exp - */ + #[DataProvider('providerSafeInt')] public function testSafeInt(int|string $value, int|string $exp): void { self::assertTrue($exp === Number::safeInt($value)); @@ -181,13 +178,8 @@ public static function providerIsInteger(): array ]; } - /** - * @dataProvider providerIsInteger - * - * @param int|string $value - * @param bool $exp - * @test - */ + #[DataProvider('providerIsInteger')] + #[Test] public function isInteger(mixed $value, bool $exp): void { self::assertEquals($exp, Number::isInteger($value)); diff --git a/tests/Helpers/StrTest.php b/tests/Helpers/StrTest.php index 0e185e6..f936678 100644 --- a/tests/Helpers/StrTest.php +++ b/tests/Helpers/StrTest.php @@ -6,6 +6,8 @@ use Php\Support\Helpers\Str; use Php\Support\Helpers\URLify; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; /** @@ -105,12 +107,7 @@ public static function providerDataSnake(): array ]; } - /** - * @dataProvider providerDataSnake - * - * @param $exp - * @param $str - */ + #[DataProvider('providerDataSnake')] public function testSnake($str, $exp): void { $result = Str::toSnake($str); @@ -208,12 +205,7 @@ public static function providerToScreamingSnake(): array ]; } - /** - * @dataProvider providerToScreamingSnake - * - * @param $exp - * @param $str - */ + #[DataProvider('providerToScreamingSnake')] public function testToScreamingSnake($str, $exp): void { $result = Str::toScreamingSnake($str); @@ -311,12 +303,7 @@ public static function providerDataKebab(): array ]; } - /** - * @dataProvider providerDataKebab - * - * @param $exp - * @param $str - */ + #[DataProvider('providerDataKebab')] public function testToKebab($str, $exp): void { $result = Str::toKebab($str); @@ -383,12 +370,8 @@ public static function providerCamel(): array ]; } - /** - * @dataProvider providerCamel - * - * @param $exp - * @param $str - */ + + #[DataProvider('providerCamel')] public function testToCamel($str, $exp): void { $result = Str::toCamel($str); @@ -454,12 +437,7 @@ public static function providerLowerCamel(): array ]; } - /** - * @dataProvider providerLowerCamel - * - * @param $exp - * @param $str - */ + #[DataProvider('providerLowerCamel')] public function testToLowerCamel($str, $exp): void { $result = Str::toLowerCamel($str); @@ -584,15 +562,7 @@ public static function dataReplaceStrTo(): array ]; } - /** - * @dataProvider dataReplaceStrTo - * - * @param string $val - * @param int $fromStart - * @param int $fromEnd - * @param string $to - * @param string $exp - */ + #[DataProvider('dataReplaceStrTo')] public function testReplaceStrTo(string $val, int $fromStart, int $fromEnd, string $to, string $exp): void { $result = Str::replaceStrTo($val, $fromStart, $fromEnd, $to); @@ -634,13 +604,8 @@ public static function dataReplaceByTemplate(): array ]; } - /** - * @dataProvider dataReplaceByTemplate - * - * @param string $str - * @param array $replaced - * @param string $exp - */ + + #[DataProvider('dataReplaceByTemplate')] public function testReplaceByTemplate(string $str, array $replaced, string $exp): void { $result = Str::replaceByTemplate($str, $replaced); @@ -686,21 +651,14 @@ public static function dataRegExps(): array ]; } - /** - * @dataProvider dataRegExps - * - * @param string $regexp - * @param bool $result - */ + #[DataProvider('dataRegExps')] public function testIsRegExp(string $regexp, bool $result): void { self::assertEquals($result, Str::isRegExp($regexp)); } - /** - * @test - */ + #[Test] public function truncate(): void { self::assertEquals( @@ -720,9 +678,7 @@ public function truncate(): void self::assertEquals('The...', Str::truncate('The quick brown fox jumps over the lazy dog', 7)); } - /** - * @test - */ + #[Test] public function seemsUTF8(): void { // Test a valid UTF-8 sequence: "ÜTF-8 Fµñ". @@ -770,9 +726,7 @@ public function seemsUTF8(): void } } - /** - * @test - */ + #[Test] public function slugify(): void { $this->assertEquals('a-simple-title', Str::slugify('A simple title')); @@ -797,9 +751,7 @@ public function slugify(): void $this->assertEquals('one231251251', Str::slugify('123----1251251', '', true)); } - /** - * @test - */ + #[Test] public function trimPrefix(): void { $this->assertEquals('title', Str::trimPrefix('a-simple:title', 'a-simple:')); @@ -809,9 +761,7 @@ public function trimPrefix(): void $this->assertEquals('', Str::trimPrefix('', 'a-simple:')); } - /** - * @test - */ + #[Test] public function trimSuffix(): void { $this->assertEquals('a-simple:', Str::trimSuffix('a-simple:title', 'title')); diff --git a/tests/enums/WithEnhancesForStringsTest.php b/tests/enums/WithEnhancesForStringsTest.php index 2c507d3..f140bc3 100644 --- a/tests/enums/WithEnhancesForStringsTest.php +++ b/tests/enums/WithEnhancesForStringsTest.php @@ -5,6 +5,7 @@ namespace Php\Support\Tests\enums; use Php\Support\Tests\enums\data\StringsEnum; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; /** @@ -12,41 +13,31 @@ */ final class WithEnhancesForStringsTest extends TestCase { - /** - * @test - */ + #[Test] public function casesToEscapeString(): void { self::assertEquals("'short', 'long', 'empty'", StringsEnum::casesToEscapeString()); } - /** - * @test - */ + #[Test] public function casesToString(): void { self::assertEquals('short, long, empty', StringsEnum::casesToString()); } - /** - * @test - */ + #[Test] public function values(): void { self::assertEquals(['short', 'long', 'empty'], StringsEnum::values()); } - /** - * @test - */ + #[Test] public function names(): void { self::assertEquals(['SHORT', 'LONG', 'EMPTY'], StringsEnum::names()); } - /** - * @test - */ + #[Test] public function hasValue(): void { self::assertFalse(StringsEnum::hasValue('---')); @@ -60,9 +51,7 @@ public function hasValue(): void self::assertTrue(StringsEnum::hasValue('empty')); } - /** - * @test - */ + #[Test] public function hasName(): void { self::assertFalse(StringsEnum::hasName('---')); diff --git a/tests/traits/ArrayStorageTest.php b/tests/traits/ArrayStorageTest.php index 9b02c74..4d7d53f 100644 --- a/tests/traits/ArrayStorageTest.php +++ b/tests/traits/ArrayStorageTest.php @@ -48,7 +48,6 @@ public function testGetData(): void $config->{'test.sub.key'} = 1; $config->{'test.sub.val'} = 'value'; - $config->{'test.next'} = 'next value'; $expected = [ diff --git a/tests/traits/TraitWhenerTest.php b/tests/traits/TraitWhenerTest.php new file mode 100644 index 0000000..d227fd3 --- /dev/null +++ b/tests/traits/TraitWhenerTest.php @@ -0,0 +1,22 @@ +when(true, fn()=>1)); + } +} \ No newline at end of file From cd3b5854b80be60d9ac00332f163da7bb7188dd0 Mon Sep 17 00:00:00 2001 From: fureev Date: Wed, 25 Dec 2024 14:57:14 +0200 Subject: [PATCH 90/95] feat: add trait `UseStorage`. --- CHANGELOG.md | 7 + src/Enums/WithEnhances.php | 14 +- src/Exceptions/ConfigException.php | 16 +- src/Exceptions/Exception.php | 2 - src/Exceptions/InvalidArgumentException.php | 9 - src/Exceptions/InvalidCallException.php | 4 - src/Exceptions/InvalidConfigException.php | 10 +- src/Exceptions/InvalidParamException.php | 32 +-- src/Exceptions/InvalidValueException.php | 4 - src/Exceptions/MethodNotAllowedException.php | 13 +- src/Exceptions/MissingClassException.php | 13 +- src/Exceptions/MissingConfigException.php | 7 +- src/Exceptions/MissingMethodException.php | 7 +- src/Exceptions/MissingPropertyException.php | 24 +-- src/Exceptions/NotSupportedException.php | 13 +- src/Exceptions/UnknownMethodException.php | 23 +- src/Exceptions/UnknownPropertyException.php | 27 +-- src/Global/base.php | 13 ++ src/Helpers/Json.php | 13 +- src/Helpers/Str.php | 64 ------ src/Storage.php | 110 ++++++++++ src/Traits/ArrayStorage.php | 178 ---------------- src/Traits/ArrayStorageConfigurableTrait.php | 25 --- src/Traits/ConfigurableTrait.php | 39 ++-- src/Traits/Maker.php | 1 - src/Traits/Metable.php | 24 +-- src/Traits/ReadOnlyProperties.php | 2 +- src/Traits/Singleton.php | 7 +- src/Traits/Thrower.php | 23 +- src/Traits/TraitBooter.php | 13 +- src/Traits/TraitInitializer.php | 3 +- src/Traits/UseConfigurabeStorage.php | 28 +++ src/Traits/UseErrorsBox.php | 7 +- src/Traits/UseSetter.php | 25 +++ src/Traits/UseStorage.php | 96 +++++++++ tests/Global/BaseTest.php | 201 +++++++++--------- tests/Helpers/JsonTest.php | 34 +-- tests/StorageTest.php | 150 +++++++++++++ tests/{traits => Traits}/ConfigurableTest.php | 4 +- tests/{traits => Traits}/ConsolePrintTest.php | 2 +- tests/{traits => Traits}/InterceptFilter.php | 2 +- tests/{traits => Traits}/MakerTest.php | 2 +- tests/{traits => Traits}/MetableTest.php | 2 +- tests/{traits => Traits}/SingletonTest.php | 2 +- tests/{traits => Traits}/TraitBooterTest.php | 2 +- tests/{traits => Traits}/TraitWhenerTest.php | 2 +- tests/exceptions/InvalidParamTest.php | 6 +- tests/traits/ArrayStorageConfigurableTest.php | 58 ----- tests/traits/ArrayStorageTest.php | 190 ----------------- 49 files changed, 615 insertions(+), 938 deletions(-) create mode 100644 src/Storage.php delete mode 100644 src/Traits/ArrayStorage.php delete mode 100644 src/Traits/ArrayStorageConfigurableTrait.php create mode 100644 src/Traits/UseConfigurabeStorage.php create mode 100755 src/Traits/UseSetter.php create mode 100644 src/Traits/UseStorage.php create mode 100644 tests/StorageTest.php rename tests/{traits => Traits}/ConfigurableTest.php (96%) rename tests/{traits => Traits}/ConsolePrintTest.php (97%) rename tests/{traits => Traits}/InterceptFilter.php (94%) rename tests/{traits => Traits}/MakerTest.php (96%) rename tests/{traits => Traits}/MetableTest.php (98%) rename tests/{traits => Traits}/SingletonTest.php (98%) rename tests/{traits => Traits}/TraitBooterTest.php (97%) rename tests/{traits => Traits}/TraitWhenerTest.php (91%) delete mode 100644 tests/traits/ArrayStorageConfigurableTest.php delete mode 100644 tests/traits/ArrayStorageTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d7977f..2efccab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ The format is based on [Keep a Changelog][keepachangelog] and this project adher ### Added - Add `PHP 8.4` support +- Add `UseStorage` trait. Trait `ArrayStorage` is deprecated now +- Add `UseConfigurabeStorage` trait. Trait `ArrayStorageConfigurableTrait` is deprecated now + +### Removed + +- Remove Trait `ArrayStorage`. Use `UseStorage` instead +- Remove Trait `ArrayStorageConfigurableTrait`. Use `UseConfigurabeStorage` instead ## v4.28.0 diff --git a/src/Enums/WithEnhances.php b/src/Enums/WithEnhances.php index 1135f69..e4b50c6 100644 --- a/src/Enums/WithEnhances.php +++ b/src/Enums/WithEnhances.php @@ -5,6 +5,7 @@ namespace Php\Support\Enums; /** + * @template TValue * @mixin \UnitEnum */ trait WithEnhances @@ -18,13 +19,16 @@ public static function casesToString(callable $decorator, string $delimiter = ', } /** - * @return mixed[] + * @return TValue[] */ public static function values(): array { return array_map(static fn(self $enumItem) => $enumItem->value, self::cases()); } + /** + * @return string[] + */ public static function names(): array { return array_map(static fn(self $enumItem) => $enumItem->name, self::cases()); @@ -35,16 +39,22 @@ public static function hasName(string $value): bool return in_array($value, static::names(), true); } + /** + * @return array + */ public static function toKeyValueArray(): array { $list = []; foreach (self::cases() as $case) { - $list[$case->value] = $case->name; + $list[$case->name] = $case->value; } return $list; } + /** + * @return array + */ public static function toValueKeyArray(): array { $list = []; diff --git a/src/Exceptions/ConfigException.php b/src/Exceptions/ConfigException.php index 3952a03..e7932d2 100644 --- a/src/Exceptions/ConfigException.php +++ b/src/Exceptions/ConfigException.php @@ -6,27 +6,15 @@ /** * Class ConfigException - * - * @package Php\Support\Exceptions */ class ConfigException extends Exception { /** - * ConfigException constructor. - * - * @param ?array $config * @param string $message + * @param array $config */ - public function __construct(string $message = 'Config Exception', protected ?array $config = null) + public function __construct(string $message = 'Config Exception', protected(set) array $config = []) { parent::__construct($message); } - - /** - * @return array|null - */ - public function getConfig(): ?array - { - return $this->config; - } } diff --git a/src/Exceptions/Exception.php b/src/Exceptions/Exception.php index fce1e1f..d387860 100644 --- a/src/Exceptions/Exception.php +++ b/src/Exceptions/Exception.php @@ -10,8 +10,6 @@ /** * Class Exception - * - * @package Php\Support\Exceptions */ class Exception extends \Exception { diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php index 814e2ba..8493632 100644 --- a/src/Exceptions/InvalidArgumentException.php +++ b/src/Exceptions/InvalidArgumentException.php @@ -8,18 +8,9 @@ /** * Class InvalidArgumentException - * - * @package Php\Support\Exceptions */ class InvalidArgumentException extends \InvalidArgumentException { - /** - * Exception constructor. - * - * @param ?string $message - * @param int $code - * @param ?Throwable $previous - */ public function __construct(?string $message = null, int $code = 0, ?Throwable $previous = null) { parent::__construct($message ?? $this->getName(), $code, $previous); diff --git a/src/Exceptions/InvalidCallException.php b/src/Exceptions/InvalidCallException.php index 507d846..ef9c186 100644 --- a/src/Exceptions/InvalidCallException.php +++ b/src/Exceptions/InvalidCallException.php @@ -8,13 +8,9 @@ /** * Class InvalidCallException - * @package Php\Support\Exceptions */ class InvalidCallException extends BadMethodCallException { - /** - * @return string - */ public function getName(): string { return 'Invalid Call'; diff --git a/src/Exceptions/InvalidConfigException.php b/src/Exceptions/InvalidConfigException.php index 44b6028..63070d9 100644 --- a/src/Exceptions/InvalidConfigException.php +++ b/src/Exceptions/InvalidConfigException.php @@ -6,18 +6,10 @@ /** * Class InvalidConfigException - * - * @package Php\Support\Exceptions */ class InvalidConfigException extends ConfigException { - /** - * InvalidConfigException constructor. - * - * @param ?array $config - * @param string $message - */ - public function __construct(?array $config = null, $message = 'Invalid Configuration') + public function __construct(array $config = [], string $message = 'Invalid Configuration') { parent::__construct($message, $config); } diff --git a/src/Exceptions/InvalidParamException.php b/src/Exceptions/InvalidParamException.php index 8c2faec..2f9989a 100644 --- a/src/Exceptions/InvalidParamException.php +++ b/src/Exceptions/InvalidParamException.php @@ -6,41 +6,17 @@ use LogicException; -/** - * Class InvalidParamException - * - * @package Php\Support\Exceptions - */ class InvalidParamException extends LogicException { - /** @var string|null */ - protected $param; - - /** - * InvalidParamException constructor. - * - * @param null|string $param - * @param null|string $message - */ - public function __construct(?string $message = null, ?string $param = null) + public function __construct(?string $message = null, public private(set) readonly ?string $name = null) { - $this->param = $param; - parent::__construct($message ?? sprintf('Invalid Parameter' . ($this->param ? ': %s' : ''), $this->param)); + parent::__construct( + $message ?? sprintf('Invalid Parameter' . ($this->name ? ': %s' : ''), $this->name) + ); } - /** - * @return string - */ public function getName(): string { return 'Invalid Parameter'; } - - /** - * @return null|string - */ - public function getParam(): ?string - { - return $this->param; - } } diff --git a/src/Exceptions/InvalidValueException.php b/src/Exceptions/InvalidValueException.php index c9af7f6..9a9c270 100644 --- a/src/Exceptions/InvalidValueException.php +++ b/src/Exceptions/InvalidValueException.php @@ -8,13 +8,9 @@ /** * Class InvalidValueException - * @package Php\Support\Exceptions */ class InvalidValueException extends UnexpectedValueException { - /** - * @return string - */ public function getName(): string { return 'Invalid Return Value'; diff --git a/src/Exceptions/MethodNotAllowedException.php b/src/Exceptions/MethodNotAllowedException.php index f5d0775..e44f789 100644 --- a/src/Exceptions/MethodNotAllowedException.php +++ b/src/Exceptions/MethodNotAllowedException.php @@ -6,21 +6,10 @@ /** * Class MethodNotAllowedException - * - * @package Php\Support\Exceptions */ class MethodNotAllowedException extends Exception { - /** @var string */ - protected $reason; - - /** - * MethodNotAllowedException constructor. - * - * @param string $reason - * @param string $message - */ - public function __construct(string $reason, $message = 'Method Not Allowed') + public function __construct(protected string $reason, string $message = 'Method Not Allowed') { $this->reason = $reason; diff --git a/src/Exceptions/MissingClassException.php b/src/Exceptions/MissingClassException.php index 9429db9..c57c58a 100644 --- a/src/Exceptions/MissingClassException.php +++ b/src/Exceptions/MissingClassException.php @@ -6,20 +6,11 @@ /** * Class MissingClassException - * - * @package Php\Support\Exceptions */ class MissingClassException extends Exception { - /** - * MissingClassException constructor. - * - * @param string|null $className - * @param string $message - */ - public function __construct($className = null, $message = 'Missing Class') + public function __construct(string $class, string $message = 'Missing Class') { - $message .= $className ? (': ' . $className) : ''; - parent::__construct($message); + parent::__construct($message . ": $class"); } } diff --git a/src/Exceptions/MissingConfigException.php b/src/Exceptions/MissingConfigException.php index c18c071..1fe1dc9 100644 --- a/src/Exceptions/MissingConfigException.php +++ b/src/Exceptions/MissingConfigException.php @@ -11,12 +11,7 @@ */ class MissingConfigException extends ConfigException { - /** - * @param ?array $config - * @param ?string $needKey - * @param string $message - */ - public function __construct(?array $config = null, protected ?string $needKey = null, $message = 'Missing Config') + public function __construct(array $config = [], protected ?string $needKey = null, string $message = 'Missing Config') { parent::__construct($message, $config); } diff --git a/src/Exceptions/MissingMethodException.php b/src/Exceptions/MissingMethodException.php index 91f58fb..ba57a16 100644 --- a/src/Exceptions/MissingMethodException.php +++ b/src/Exceptions/MissingMethodException.php @@ -12,17 +12,14 @@ class MissingMethodException extends BadMethodCallException use Thrower; public function __construct( - protected ?string $method = null { + protected string $method { get { return $this->method; } }, ?string $message = null ) { - parent::__construct( - $message ?? - ($this->getName() . ($this->method ? ': "' . $this->method . '"' : '')) - ); + parent::__construct($message ?? ($this->getName() . ": $this->method")); } /** diff --git a/src/Exceptions/MissingPropertyException.php b/src/Exceptions/MissingPropertyException.php index 2a7a843..41832b2 100644 --- a/src/Exceptions/MissingPropertyException.php +++ b/src/Exceptions/MissingPropertyException.php @@ -6,24 +6,12 @@ /** * Class MissingPropertyException - * - * @package Php\Support\Exceptions */ class MissingPropertyException extends ConfigException { - /** - * MissingPropertyException constructor. - * - * @param null|string $message - * @param null|string $property - * @param ?array $config - */ - public function __construct(?string $message = null, protected ?string $property = null, ?array $config = null) + public function __construct(protected(set) ?string $property, ?string $message = null, array $config = []) { - parent::__construct( - $message ?? ($this->getName() . ($this->property ? ': "' . $this->property . '"' : '')), - $config - ); + parent::__construct($message ?? ($this->getName() . ": '$this->property'"), $config); } /** @@ -33,12 +21,4 @@ public function getName(): string { return 'Missing property'; } - - /** - * @return null|string - */ - public function getProperty(): ?string - { - return $this->property; - } } diff --git a/src/Exceptions/NotSupportedException.php b/src/Exceptions/NotSupportedException.php index 4a9b9ef..0fc4c26 100644 --- a/src/Exceptions/NotSupportedException.php +++ b/src/Exceptions/NotSupportedException.php @@ -6,20 +6,13 @@ /** * Class NotSupportedException - * - * @package Php\Support\Exceptions */ class NotSupportedException extends Exception { - /** - * MissingClassException constructor. - * - * @param string|null $className - * @param string $message - */ - public function __construct($className = null, $message = 'Not Supported') + public function __construct(?string $className = null, string $message = 'Not Supported') { - $message .= $className ? (': ' . $className) : ''; + $message .= $className ? ": $className" : ''; + parent::__construct($message); } } diff --git a/src/Exceptions/UnknownMethodException.php b/src/Exceptions/UnknownMethodException.php index 11d6885..ada67d3 100644 --- a/src/Exceptions/UnknownMethodException.php +++ b/src/Exceptions/UnknownMethodException.php @@ -8,24 +8,13 @@ /** * Class UnknownMethodException - * - * @package Php\Support\Exceptions */ class UnknownMethodException extends BadMethodCallException { - /** @var string|null */ - protected $method; - - /** - * UnknownMethodException constructor. - * - * @param null|string $method - * @param null|string $message - */ - public function __construct(?string $message = null, ?string $method = null) + public function __construct(protected(set) string $method, ?string $message = null) { $this->method = $method; - parent::__construct($message ?? ($this->getName() . ($this->method ? ': "' . $this->method . '"' : ''))); + parent::__construct($message ?? ($this->getName() . ": $this->method ")); } /** @@ -35,12 +24,4 @@ public function getName(): string { return 'Unknown method'; } - - /** - * @return null|string - */ - public function getMethod(): ?string - { - return $this->method; - } } diff --git a/src/Exceptions/UnknownPropertyException.php b/src/Exceptions/UnknownPropertyException.php index 8f81bb2..9a882ba 100644 --- a/src/Exceptions/UnknownPropertyException.php +++ b/src/Exceptions/UnknownPropertyException.php @@ -6,39 +6,16 @@ /** * Class UnknownPropertyException - * - * @package Php\Support\Exceptions */ class UnknownPropertyException extends Exception { - /** @var string|null */ - protected $property; - - /** - * UnknownPropertyException constructor. - * - * @param null|string $property - * @param null|string $message - */ - public function __construct(?string $message = null, ?string $property = null) + public function __construct(protected(set) string $property, ?string $message = null) { - $this->property = $property; - parent::__construct($message ?? ($this->getName() . ($this->property ? ': "' . $this->property . '"' : ''))); + parent::__construct($message ?? ($this->getName() . ": $this->property")); } - /** - * @return string - */ public function getName(): string { return 'Unknown property'; } - - /** - * @return null|string - */ - public function getProperty(): ?string - { - return $this->property; - } } diff --git a/src/Global/base.php b/src/Global/base.php index f0fe00e..bc38836 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -315,6 +315,19 @@ function findGetterMethod(object $instance, string $attribute): ?string } } +if (!function_exists('findSetterMethodByProp')) { + /** + * Returns getter-method's name or null by an attribute + */ + function findSetterMethodByProp(object $instance, string $attribute): ?string + { + if (method_exists($instance, $method = attributeToSetterMethod($attribute))) { + return $method; + } + + return null; + } +} if (!function_exists('public_property_exists')) { /** diff --git a/src/Helpers/Json.php b/src/Helpers/Json.php index eb3c911..5b1457e 100644 --- a/src/Helpers/Json.php +++ b/src/Helpers/Json.php @@ -7,10 +7,6 @@ use function json_decode; use function json_encode; -/** - * Class Json - * @package Php\Support\Helpers - */ class Json { /** @@ -45,11 +41,12 @@ public static function htmlEncode($value): ?string * * @return string|null */ - public static function encode($value, $options = 320, int $depth = 512): ?string + public static function encode($value, int $options = 320, int $depth = 512): ?string { $value = Arr::dataToArray($value); $json = json_encode($value, $options, $depth); + return $json ?: null; } @@ -69,6 +66,12 @@ public static function decode(?string $json, bool $asArray = true, int $options return null; } + // @see https://www.php.net/manual/en/json.constants.php#constant.json-invalid-utf8-ignore + $validateOpts = Bit::checkFlag($options, JSON_INVALID_UTF8_IGNORE) ? JSON_INVALID_UTF8_IGNORE : 0; + if (!json_validate($json, $depth, $validateOpts)) { + return null; + } + return json_decode($json, $asArray, $depth, $options); } } diff --git a/src/Helpers/Str.php b/src/Helpers/Str.php index e9e1275..743b4f8 100644 --- a/src/Helpers/Str.php +++ b/src/Helpers/Str.php @@ -22,10 +22,6 @@ class Str /** * Converts a string to snake_case - * - * @param string $str - * - * @return string */ public static function toSnake(string $str): string { @@ -34,11 +30,6 @@ public static function toSnake(string $str): string /** * Converts a string to delimited.snake.case (in this case `del = '.'`) - * - * @param string $str - * @param string $delimiter - * - * @return string */ public static function toDelimited(string $str, string $delimiter): string { @@ -48,12 +39,6 @@ public static function toDelimited(string $str, string $delimiter): string /** * Converts a string to SCREAMING.DELIMITED.SNAKE.CASE (in this case `del = '.'; screaming = true`) or * delimited.snake.case (in this case `del = '.'; screaming = false`) - * - * @param string $str - * @param string $delimiter - * @param bool $screaming - * - * @return string */ public static function toScreamingDelimited(string $str, string $delimiter, bool $screaming): string { @@ -139,11 +124,6 @@ public static function removeMultiSpace(string $str): string return is_string($res) ? $res : $str; } - /** - * @param string $str - * - * @return string - */ private static function addWordBoundariesToNumbers(string $str): string { $res = preg_replace('/([a-zA-Z])(\d+)([a-zA-Z]?)/u', '$1 $2 $3', $str); @@ -152,10 +132,6 @@ private static function addWordBoundariesToNumbers(string $str): string /** * Converts a string to SCREAMING_SNAKE_CASE - * - * @param string $str - * - * @return string */ public static function toScreamingSnake(string $str): string { @@ -164,10 +140,6 @@ public static function toScreamingSnake(string $str): string /** * Converts a string to kebab-case - * - * @param string $str - * - * @return string */ public static function toKebab(string $str): string { @@ -176,10 +148,6 @@ public static function toKebab(string $str): string /** * Converts a string to CamelCase - * - * @param string $str - * - * @return string */ public static function toCamel(string $str): string { @@ -188,11 +156,6 @@ public static function toCamel(string $str): string /** * Converts a string to CamelCase - * - * @param string $str - * @param bool $initCase - * - * @return string */ public static function toCamelInitCase(string $str, bool $initCase): string { @@ -246,10 +209,6 @@ public static function toCamelInitCase(string $str, bool $initCase): string /** * Converts a string to lowerCamelCase - * - * @param string $str - * - * @return string */ public static function toLowerCamel(string $str): string { @@ -262,13 +221,6 @@ public static function toLowerCamel(string $str): string /** * Replace substr by start and finish indents - * - * @param string $str - * @param int $from_start - * @param int $from_end - * @param string $toStr - * - * @return string */ public static function replaceStrTo(string $str, int $from_start, int $from_end, string $toStr = '*'): string { @@ -303,11 +255,6 @@ public static function replaceByTemplate(string $str, array $replace): array|str return str_replace(array_keys($replace), array_values($replace), $str); } - /** - * @param string $regex - * - * @return bool - */ public static function isRegExp(string $regex): bool { return !empty($regex) && @preg_match($regex, '') !== false; @@ -315,12 +262,6 @@ public static function isRegExp(string $regex): bool /** * Truncate a string to a specified length without cutting a word off - * - * @param string $str - * @param int $length - * @param string $append - * - * @return string */ public static function truncate(string $str, int $length, string $append = '...'): string { @@ -406,11 +347,6 @@ public static function seemsUTF8(string $string): bool /** * Converts all accent characters to ASCII characters. - * - * @param string $str - * @param string $language - * - * @return string */ public static function removeAccents(string $str, string $language = ''): string { diff --git a/src/Storage.php b/src/Storage.php new file mode 100644 index 0000000..c976842 --- /dev/null +++ b/src/Storage.php @@ -0,0 +1,110 @@ + + * @mixin ArrayAccess + */ +class Storage implements ArrayAccess, Countable, JsonSerializable +{ + /** @var array */ + public private(set) array $data = []; + + /** + * @param array $init + */ + public function __construct(array $init = []) + { + $this->data = $init; + } + + public function set(string $key, mixed $value): void + { + Arr::set($this->data, $key, $value); + } + + public function remove(string $key): void + { + Arr::remove($this->data, $key); + } + + public function get(string $key, mixed $default = null): mixed + { + return Arr::get($this->data, $key, $default); + } + + public function exist(string $key): bool + { + return Arr::has($this->data, $key); + } + + public function __isset(string $name): bool + { + return $this->exist($name); + } + + public function __get(string $name): mixed + { + return $this->get($name); + } + + public function __set(string $name, mixed $value): void + { + $this->set($name, $value); + } + + public function __unset(string $name): void + { + $this->remove($name); + } + + public function offsetExists(mixed $offset): bool + { + return $this->exist($offset); + } + + public function offsetGet(mixed $offset): mixed + { + return $this->get($offset); + } + + public function offsetSet(mixed $offset, mixed $value): void + { + $this->set($offset, $value); + } + + public function offsetUnset(mixed $offset): void + { + $this->remove($offset); + } + + public function count(): int + { + return count($this->data); + } + + public function __toString(): string + { + return (string)Json::encode($this->data); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return $this->data; + } +} diff --git a/src/Traits/ArrayStorage.php b/src/Traits/ArrayStorage.php deleted file mode 100644 index 7202e7c..0000000 --- a/src/Traits/ArrayStorage.php +++ /dev/null @@ -1,178 +0,0 @@ - - * @mixin ArrayAccess - */ -trait ArrayStorage // implements ArrayAccess, Arrayable -{ - /** @var array */ - private array $data = []; - - /** @var bool Показывать ошибку не взятие из get */ - protected bool $showErrorOnGetIfNull = true; - - /** - * @param string $name - * - * @return mixed|null - */ - public function __get(string $name) - { - return $this->get($name); - } - - public function __set(string $name, mixed $value): void - { - $this->set($name, $value); - } - - /** - * @param string $name - * - * @return mixed|null - */ - public function get(string $name): mixed - { - if ($this->propertyExists($name)) { - return $this->$name; - } - - if (Arr::has($this->data, $name)) { - return Arr::get($this->data, $name); - } - - if ($this->showErrorOnGetIfNull) { - $trace = debug_backtrace(); - trigger_error( - "Undefined property in __get(): $name in file {$trace[0]['file']} in line {$trace[0]['line']}", - E_USER_NOTICE - ); - } - - return null; - } - - /** - * @param string $name - * - * @return bool - */ - protected function propertyExists(string $name): bool - { - return $name !== 'data' && property_exists($this, $name); - } - - public function set(string $name, mixed $value): void - { - if ($this->propertyExists($name)) { - $this->$name = $value; - return; - } - - Arr::set($this->data, $name, $value); - } - - /** - * @param string $name - * - * @return bool - */ - public function __isset(string $name): bool - { - return $this->propertyExists($name) || Arr::has($this->data, $name); - } - - /** - * @param string $name - */ - public function __unset(string $name) - { - if ($this->propertyExists($name)) { - $this->$name = null; - return; - } - - Arr::remove($this->data, $name); - } - - /** - * Determine if an item exists at an offset. - */ - public function offsetExists(mixed $key): bool - { - return $this->valueExists($key); - } - - public function valueExists(string $name): bool - { - return $this->propertyExists($name) || Arr::has($this->data, $name); - } - - public function disableErrorOnNull(): static - { - $this->showErrorOnGetIfNull = false; - - return $this; - } - - /** - * Get an item at a given offset. - */ - public function offsetGet(mixed $key): mixed - { - return $this->get($key); - } - - /** - * Set the item at a given offset. - */ - public function offsetSet(mixed $key, mixed $value): void - { - $this->set($key, $value); - } - - /** - * Unset the item at a given offset. - */ - public function offsetUnset(mixed $key): void - { - unset($this->$key); - } - - /** - * @return int - */ - public function count(): int - { - return count($this->data); - } - - public function getData(): array - { - return $this->data; - } - - public function __toString(): string - { - return (string)Json::encode($this->toArray()); - } - - public function toArray(): array - { - return $this->data; - } -} diff --git a/src/Traits/ArrayStorageConfigurableTrait.php b/src/Traits/ArrayStorageConfigurableTrait.php deleted file mode 100644 index 4e79d0e..0000000 --- a/src/Traits/ArrayStorageConfigurableTrait.php +++ /dev/null @@ -1,25 +0,0 @@ -set($key, $value); - - return true; - } -} diff --git a/src/Traits/ConfigurableTrait.php b/src/Traits/ConfigurableTrait.php index e248e25..8036537 100644 --- a/src/Traits/ConfigurableTrait.php +++ b/src/Traits/ConfigurableTrait.php @@ -6,22 +6,18 @@ use Php\Support\Exceptions\InvalidParamException; /** - * Trait ConfigurableTrait - * @package Php\Support\Traits + * @template TKey of array-key + * @template TValue + * @implements ArrayAccess + * @mixin ArrayAccess */ trait ConfigurableTrait { - /** - * @param array|ArrayAccess $attributes - * @param bool $exceptOnMiss - * - * @return self - */ - public function configurable($attributes, ?bool $exceptOnMiss = true) + public function configurable(array|ArrayAccess $attributes, bool $throwOnMissingProp = true): static { foreach ($attributes as $key => $value) { - if (!$this->applyValue($key, $value) && $exceptOnMiss) { - throw new InvalidParamException("Property $key is absent at class: " . get_class($this)); + if (!$this->applyValue($key, $value) && $throwOnMissingProp) { + throw new InvalidParamException("Property $key is absent at class: " . $this::class); } } @@ -30,35 +26,28 @@ public function configurable($attributes, ?bool $exceptOnMiss = true) protected function applyValue(string $key, mixed $value): bool { - if (!$res = $this->callMethod($key, $value)) { - $res = $this->setPropConfigurable($key, $value); - } - return $res; + return $this->callSetterProp($key, $value) || $this->setPropValue($key, $value); } - protected function setPropConfigurable(string $key, mixed $value): bool + protected function setPropValue(string $key, mixed $value): bool { if ($this->propertyExists($key)) { $this->{$key} = $value; + return true; } return false; } - /** - * @param string $key - * - * @return bool - */ - protected function propertyExists(string $key): bool + protected function propertyExists(string $name): bool { - return property_exists($this, $key); + return property_exists($this, $name); } - protected function callMethod(string $key, mixed $value): bool + protected function callSetterProp(string $key, mixed $value): bool { - if (method_exists($this, $method = 'set' . ucfirst($key))) { + if ($method = findSetterMethodByProp($this, $key)) { $this->$method($value); return true; diff --git a/src/Traits/Maker.php b/src/Traits/Maker.php index 0901408..b0d0504 100755 --- a/src/Traits/Maker.php +++ b/src/Traits/Maker.php @@ -6,7 +6,6 @@ /** * Trait Maker - * @package Php\Support\Traits */ trait Maker { diff --git a/src/Traits/Metable.php b/src/Traits/Metable.php index e82739b..ea170e4 100755 --- a/src/Traits/Metable.php +++ b/src/Traits/Metable.php @@ -7,46 +7,32 @@ use Php\Support\Helpers\Arr; /** - * Trait Metable - * @package Php\Support\Traits + * @template TValue */ trait Metable { /** * The metadata for the element. * - * @var array + * @var array */ protected array $meta = []; /** * Get additional meta information to merge with the element payload. * - * @return array + * @return array */ public function meta(): array { return $this->meta; } - /** - * @param string $key - * @param mixed $default - * - * @return mixed - */ public function metaAttribute(string $key, mixed $default = null): mixed { return Arr::get($this->meta, $key, $default); } - /** - * @param string $key - * @param mixed $value - * @param bool $removeNull - * - * @return $this - */ public function setMetaAttribute(string $key, mixed $value, bool $removeNull = false): static { if ($value !== null || !$removeNull) { @@ -59,9 +45,7 @@ public function setMetaAttribute(string $key, mixed $value, bool $removeNull = f /** * Set additional meta information for the element. * - * @param array $meta - * - * @return $this + * @param array $meta */ public function withMeta(array $meta): static { diff --git a/src/Traits/ReadOnlyProperties.php b/src/Traits/ReadOnlyProperties.php index e183c5d..d5ee2db 100755 --- a/src/Traits/ReadOnlyProperties.php +++ b/src/Traits/ReadOnlyProperties.php @@ -8,7 +8,7 @@ trait ReadOnlyProperties { - public function __get($key) + public function __get(string $key): mixed { if (property_exists($this, $key)) { return $this->$key; diff --git a/src/Traits/Singleton.php b/src/Traits/Singleton.php index 9318e19..23dedc8 100644 --- a/src/Traits/Singleton.php +++ b/src/Traits/Singleton.php @@ -8,13 +8,11 @@ /** * Trait Singleton - * - * @package Php\Support\Traits */ trait Singleton { /** - * @var array + * @var array */ protected static array $instances = []; @@ -25,9 +23,6 @@ protected function __construct() { } - /** - * @return Singleton - */ public static function getInstance(): self { $cls = static::class; diff --git a/src/Traits/Thrower.php b/src/Traits/Thrower.php index fb6127d..daa7cba 100755 --- a/src/Traits/Thrower.php +++ b/src/Traits/Thrower.php @@ -6,40 +6,23 @@ /** * Trait Thrower - * @package Php\Support\Traits */ trait Thrower { - /** - * Throw Exception - * - * @param mixed ...$arguments - */ - public static function throw(...$arguments): void + public static function throw(mixed ...$arguments): void { // @phpstan-ignore-next-line throw new static(...$arguments); } - - /** - * @param mixed $value - * @param mixed ...$arguments - */ - public static function throwIf(mixed $value, ...$arguments): void + public static function throwIf(mixed $value, mixed ...$arguments): void { if ($value) { static::throw(...$arguments); } } - /** - * @param mixed $value - * @param mixed ...$arguments - * - * @return bool - */ - public static function throwIfReturn(mixed $value, ...$arguments): bool + public static function throwIfReturn(mixed $value, mixed ...$arguments): bool { static::throwIf($value, ...$arguments); diff --git a/src/Traits/TraitBooter.php b/src/Traits/TraitBooter.php index 7abaaa4..a218e84 100755 --- a/src/Traits/TraitBooter.php +++ b/src/Traits/TraitBooter.php @@ -6,21 +6,20 @@ /** * Trait TraitBooter - * @package Php\Support\Traits */ trait TraitBooter { /** * The array of booted classes. * - * @var array + * @var class-string[] */ protected static array $booted = []; /** * The array of trait initializers that will be called on each new instance. * - * @return array + * @return class-string[] */ protected static function bootTraits(): array { @@ -54,8 +53,6 @@ protected function bootIfNotBooted(): void /** * Perform any actions required before the instance boots. - * - * @return void */ protected static function booting(): void { @@ -64,8 +61,6 @@ protected static function booting(): void /** * Bootstrap the instance and its traits. - * - * @return void */ protected static function boot(): void { @@ -74,8 +69,6 @@ protected static function boot(): void /** * Perform any actions required after the instance boots. - * - * @return void */ protected static function booted(): void { @@ -84,8 +77,6 @@ protected static function booted(): void /** * Clear the list of booted models so they will be re-booted. - * - * @return void */ public static function clearBooted(): void { diff --git a/src/Traits/TraitInitializer.php b/src/Traits/TraitInitializer.php index 80867e6..424f6e9 100755 --- a/src/Traits/TraitInitializer.php +++ b/src/Traits/TraitInitializer.php @@ -6,7 +6,6 @@ /** * Trait TraitInitializer - * @package Php\Support\Traits */ trait TraitInitializer { @@ -18,7 +17,7 @@ trait TraitInitializer /** * The array of trait initializers that will be called on each new instance. * - * @var array + * @var array */ protected static array $traitInitializers = []; diff --git a/src/Traits/UseConfigurabeStorage.php b/src/Traits/UseConfigurabeStorage.php new file mode 100644 index 0000000..23fecbf --- /dev/null +++ b/src/Traits/UseConfigurabeStorage.php @@ -0,0 +1,28 @@ + + * @mixin ArrayAccess + */ +trait UseConfigurabeStorage +{ + use UseStorage; + use ConfigurableTrait { + UseStorage::propertyExists insteadof ConfigurableTrait; + } + + protected function configureProps(string $key, mixed $value): bool + { + $this->set($key, $value); + + return true; + } +} diff --git a/src/Traits/UseErrorsBox.php b/src/Traits/UseErrorsBox.php index 3293e97..0790373 100644 --- a/src/Traits/UseErrorsBox.php +++ b/src/Traits/UseErrorsBox.php @@ -5,16 +5,13 @@ namespace Php\Support\Traits; /** - * Trait UseErrorsBox - * @package Php\Support\Traits - * * Use errors into your class */ trait UseErrorsBox { private array $errors = []; - public function setError($message): self + public function setError(string|\Throwable $message): static { if ($message instanceof \Exception) { $message = $message->getMessage(); @@ -35,7 +32,7 @@ public function errors(): array return $this->errors; } - public function clearErrors(): self + public function clearErrors(): static { $this->errors = []; diff --git a/src/Traits/UseSetter.php b/src/Traits/UseSetter.php new file mode 100755 index 0000000..09812be --- /dev/null +++ b/src/Traits/UseSetter.php @@ -0,0 +1,25 @@ +$method($value); + + return true; + } + + if (property_exists($this, $key)) { + return $this->$key; + } + + throw new MissingPropertyException(null, $key); + } +} diff --git a/src/Traits/UseStorage.php b/src/Traits/UseStorage.php new file mode 100644 index 0000000..9f727bd --- /dev/null +++ b/src/Traits/UseStorage.php @@ -0,0 +1,96 @@ + + * @mixin ArrayAccess + */ +trait UseStorage +{ + private Storage $storage; + + protected function propertyExists(string $name): bool + { + return $name !== 'storage' && property_exists($this, $name); + } + + + public function set(string $name, mixed $value): void + { + if ($this->propertyExists($name)) { + $this->$name = $value; + return; + } + + $this->storage->set($name, $value); + } + + public function get(string $name, mixed $default = null): mixed + { + if ($this->propertyExists($name)) { + return $this->$name; + } + + return $this->storage->get($name, $default); + } + + public function __get(string $name): mixed + { + return $this->get($name); + } + + public function __set(string $name, mixed $value): void + { + $this->set($name, $value); + } + + public function __isset(string $name): bool + { + return $this->propertyExists($name) || $this->storage->exist($name); + } + + public function __unset(string $name): void + { + if ($this->propertyExists($name)) { + $this->$name = null; + return; + } + + $this->storage->remove($name); + } + + public function offsetExists(mixed $key): bool + { + return $this->propExists($key); + } + + public function propExists(string $name): bool + { + return $this->propertyExists($name) || $this->storage->exist($name); + } + + public function offsetGet(mixed $key): mixed + { + return $this->get($key); + } + + public function offsetSet(mixed $key, mixed $value): void + { + $this->set($key, $value); + } + + public function offsetUnset(mixed $key): void + { + unset($this->$key); + } +} diff --git a/tests/Global/BaseTest.php b/tests/Global/BaseTest.php index 734a06f..b5da4a3 100644 --- a/tests/Global/BaseTest.php +++ b/tests/Global/BaseTest.php @@ -18,30 +18,30 @@ final class BaseTest extends TestCase private static function values(): array { return [ - 'key' => 'value 2', - 'int1' => 2, - 'int2' => -12, - 'array' => [ + 'key' => 'value 2', + 'int1' => 2, + 'int2' => -12, + 'array' => [ 1, 2, 3, 4, 5, ], - 'string' => 'string value', - 'null' => null, - 'false' => false, - 'true' => true, - 'float' => 12.31, - 'empty' => '', + 'string' => 'string value', + 'null' => null, + 'false' => false, + 'true' => true, + 'float' => 12.31, + 'empty' => '', 'emptyArray' => [], - 'cls' => new class { + 'cls' => new class { public function __invoke() { return 'cls.test'; } }, - 'fn' => static function () { + 'fn' => static function () { return 'fn.test'; }, ]; @@ -65,193 +65,193 @@ public function testIsTrue(): void foreach ( [ [ - 'val' => new \stdClass(), - 'res' => true, + 'val' => new \stdClass(), + 'res' => true, 'resNull' => true, ], [ - 'val' => [ + 'val' => [ 1, 2, ], - 'res' => true, + 'res' => true, 'resNull' => true, ], [ - 'val' => [1], - 'res' => true, + 'val' => [1], + 'res' => true, 'resNull' => true, ], [ - 'val' => [0], - 'res' => true, + 'val' => [0], + 'res' => true, 'resNull' => true, ], [ - 'val' => 1, - 'res' => true, + 'val' => 1, + 'res' => true, 'resNull' => true, ], [ - 'val' => 42, - 'res' => true, + 'val' => 42, + 'res' => true, 'resNull' => true, ], [ - 'val' => -42, - 'res' => true, + 'val' => -42, + 'res' => true, 'resNull' => true, ], [ - 'val' => 'true', - 'res' => true, + 'val' => 'true', + 'res' => true, 'resNull' => true, ], [ - 'val' => '1', - 'res' => true, + 'val' => '1', + 'res' => true, 'resNull' => true, ], [ - 'val' => 'on', - 'res' => true, + 'val' => 'on', + 'res' => true, 'resNull' => true, ], [ - 'val' => 'On', - 'res' => true, + 'val' => 'On', + 'res' => true, 'resNull' => true, ], [ - 'val' => 'ON', - 'res' => true, + 'val' => 'ON', + 'res' => true, 'resNull' => true, ], [ - 'val' => 'yes', - 'res' => true, + 'val' => 'yes', + 'res' => true, 'resNull' => true, ], [ - 'val' => 'YES', - 'res' => true, + 'val' => 'YES', + 'res' => true, 'resNull' => true, ], [ - 'val' => 'TRUE', - 'res' => true, + 'val' => 'TRUE', + 'res' => true, 'resNull' => true, ], [ - 'val' => 'off', - 'res' => false, + 'val' => 'off', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'Off', - 'res' => false, + 'val' => 'Off', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'OFF', - 'res' => false, + 'val' => 'OFF', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'no', - 'res' => false, + 'val' => 'no', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'ja', - 'res' => false, + 'val' => 'ja', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'nein', - 'res' => false, + 'val' => 'nein', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'нет', - 'res' => false, + 'val' => 'нет', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'да', - 'res' => false, + 'val' => 'да', + 'res' => false, 'resNull' => false, ], [ - 'val' => null, - 'res' => false, + 'val' => null, + 'res' => false, 'resNull' => null, ], [ - 'val' => 0, - 'res' => false, + 'val' => 0, + 'res' => false, 'resNull' => false, ], [ - 'val' => 'false', - 'res' => false, + 'val' => 'false', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'FALSE', - 'res' => false, + 'val' => 'FALSE', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'string', - 'res' => false, + 'val' => 'string', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'bool', - 'res' => false, + 'val' => 'bool', + 'res' => false, 'resNull' => false, ], [ - 'val' => '0.0', - 'res' => false, + 'val' => '0.0', + 'res' => false, 'resNull' => false, ], [ - 'val' => '4.2', - 'res' => false, + 'val' => '4.2', + 'res' => false, 'resNull' => false, ], [ - 'val' => '0', - 'res' => false, + 'val' => '0', + 'res' => false, 'resNull' => false, ], [ - 'val' => '', - 'res' => false, + 'val' => '', + 'res' => false, 'resNull' => false, ], [ - 'val' => '[]', - 'res' => false, + 'val' => '[]', + 'res' => false, 'resNull' => false, ], [ - 'val' => '{}', - 'res' => false, + 'val' => '{}', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'false', - 'res' => false, + 'val' => 'false', + 'res' => false, 'resNull' => false, ], [ - 'val' => 'bar', - 'res' => false, + 'val' => 'bar', + 'res' => false, 'resNull' => false, ], @@ -302,14 +302,10 @@ public function testTraitUsesRecursive(): void static::assertEquals( [ - \Php\Support\Traits\Singleton::class => - \Php\Support\Traits\Singleton::class, - \Php\Support\Traits\ArrayStorageConfigurableTrait::class => - \Php\Support\Traits\ArrayStorageConfigurableTrait::class, - \Php\Support\Traits\ArrayStorage::class => - \Php\Support\Traits\ArrayStorage::class, - \Php\Support\Traits\ConfigurableTrait::class => - \Php\Support\Traits\ConfigurableTrait::class, + \Php\Support\Traits\Singleton::class => \Php\Support\Traits\Singleton::class, + \Php\Support\Traits\UseConfigurabeStorage::class => \Php\Support\Traits\UseConfigurabeStorage::class, + \Php\Support\Traits\UseStorage::class => \Php\Support\Traits\UseStorage::class, + \Php\Support\Traits\ConfigurableTrait::class => \Php\Support\Traits\ConfigurableTrait::class, ], $traits ); @@ -321,16 +317,11 @@ public function testClassUsesRecursive(): void static::assertEquals( [ - \Php\Support\Traits\Singleton::class => - \Php\Support\Traits\Singleton::class, - \Php\Support\Traits\ArrayStorageConfigurableTrait::class => - \Php\Support\Traits\ArrayStorageConfigurableTrait::class, - \Php\Support\Traits\ArrayStorage::class => - \Php\Support\Traits\ArrayStorage::class, - \Php\Support\Traits\ConfigurableTrait::class => - \Php\Support\Traits\ConfigurableTrait::class, - \Php\Support\Traits\Maker::class => - \Php\Support\Traits\Maker::class, + \Php\Support\Traits\Singleton::class => \Php\Support\Traits\Singleton::class, + \Php\Support\Traits\UseConfigurabeStorage::class => \Php\Support\Traits\UseConfigurabeStorage::class, + \Php\Support\Traits\UseStorage::class => \Php\Support\Traits\UseStorage::class, + \Php\Support\Traits\ConfigurableTrait::class => \Php\Support\Traits\ConfigurableTrait::class, + \Php\Support\Traits\Maker::class => \Php\Support\Traits\Maker::class, ], $traits ); @@ -349,7 +340,7 @@ public function testClassBasename(): void class TraitUsesRecursiveClass { use \Php\Support\Traits\Singleton; - use \Php\Support\Traits\ArrayStorageConfigurableTrait; + use \Php\Support\Traits\UseConfigurabeStorage; protected $username; } diff --git a/tests/Helpers/JsonTest.php b/tests/Helpers/JsonTest.php index 1c484e3..3a239bf 100644 --- a/tests/Helpers/JsonTest.php +++ b/tests/Helpers/JsonTest.php @@ -109,6 +109,7 @@ public function testDecode(): void // basic data decoding $json = '"1"'; self::assertSame('1', Json::decode($json)); + self::assertSame('1', Json::decode($json, true, JSON_INVALID_UTF8_IGNORE)); // array decoding $json = '{"a":1,"b":2}'; self::assertSame(['a' => 1, 'b' => 2], Json::decode($json)); @@ -118,16 +119,16 @@ public function testDecode(): void self::assertEquals([], Json::decode("[]", true, JSON_THROW_ON_ERROR, 2)); // exception $json = '{"a":1,"b":2'; - $this->expectException(JsonException::class); - Json::decode($json, true, JSON_THROW_ON_ERROR); +// $this->expectException(JsonException::class); + self::assertNull(Json::decode($json, true, JSON_THROW_ON_ERROR)); } /** */ public function testDecodeInvalidParamException(): void { - $this->expectException(JsonException::class); - $this->expectExceptionMessage('Syntax error'); +// $this->expectException(JsonException::class); +// $this->expectExceptionMessage('Syntax error'); $res = Json::decode('sa', true, JSON_THROW_ON_ERROR); self::assertNull($res); @@ -146,28 +147,9 @@ public function testDecodeInvalidParamException2(): void */ public function testHandleJsonError(): void { - try { - $json = "{'a': '1'}"; - Json::decode($json, true, JSON_THROW_ON_ERROR); - } catch (Throwable $exception) { - self::assertInstanceOf(\JsonException::class, $exception); - self::assertSame('Syntax error', $exception->getMessage()); - } - - try { - $fp = fopen('php://stdin', 'rb'); - $data = ['a' => $fp]; - Json::encode($data); - fclose($fp); - } catch (Throwable $exception) { - self::assertInstanceOf(\JsonException::class, $exception); - if (PHP_VERSION_ID >= 50500) { - self::assertSame( - 'Type is not supported', - $exception->getMessage() - ); - } - } + $json = "{'a': '1'}"; + static::assertNull(Json::decode($json, )); + static::assertNull(Json::decode($json, true, JSON_THROW_ON_ERROR)); } diff --git a/tests/StorageTest.php b/tests/StorageTest.php new file mode 100644 index 0000000..739d78a --- /dev/null +++ b/tests/StorageTest.php @@ -0,0 +1,150 @@ +jsonSerialize()); + } + + #[Test] + public function setSimpleProp(): void + { + $storage = new Storage(); + $storage->test = 'name'; + + self::assertEquals(['test' => 'name'], $storage->jsonSerialize()); + } + + #[Test] + public function getSimpleProp(): void + { + $storage = new Storage(); + $storage->test = 'name'; + + self::assertEquals('name', $storage->test); + } + + #[Test] + public function setPathProp(): void + { + $storage = new Storage(); + $storage->{'first.second'} = 'name'; + + self::assertEquals(['first' => ['second' => 'name']], $storage->jsonSerialize()); + } + + #[Test] + public function getPathProp(): void + { + $storage = new Storage(); + $storage->{'first.second'} = 'name'; + $storage->{'first.second2'} = 'test'; + + self::assertEquals(['second' => 'name', 'second2' => 'test'], $storage->{'first'}); + self::assertEquals('name', $storage->{'first.second'}); + self::assertEquals('test', $storage->{'first.second2'}); + } + + #[Test] + public function setFn(): void + { + $storage = new Storage(); + $storage->set('first.second', 'name'); + + self::assertEquals(['first' => ['second' => 'name']], $storage->jsonSerialize()); + } + + #[Test] + public function getFn(): void + { + $storage = new Storage(); + $storage->set('first.second', 'name'); + $storage->set('first.second2', 1); + + self::assertEquals(['second' => 'name', 'second2' => 1], $storage->get('first')); + self::assertEquals('name', $storage->get('first.second')); + self::assertEquals(1, $storage->get('first.second2')); + } + + #[Test] + public function remove(): void + { + $storage = new Storage(); + $storage->set('first.second', 'name'); + $storage->set('first.second2', 1); + + $storage->remove('first'); + self::assertEquals([], $storage->jsonSerialize()); + + $storage->set('first.second', 'name'); + $storage->set('first.second2', 1); + + $storage->remove('first.second'); + + self::assertEquals(['second2' => 1], $storage->get('first')); + self::assertEquals(1, $storage->get('first.second2')); + self::assertNull($storage->get('first.second')); + } + + #[Test] + public function unsetFn(): void + { + $storage = new Storage(); + $storage->set('first.second', 'name'); + $storage->set('first.second2', 1); + + unset($storage->{'first.second'}); + + self::assertEquals(['second2' => 1], $storage->get('first')); + self::assertEquals(1, $storage->get('first.second2')); + self::assertNull($storage->get('first.second')); + } + + #[Test] + public function exist(): void + { + $storage = new Storage(); + self::assertFalse($storage->exist('first')); + $storage->set('first', 1); + + self::assertTrue($storage->exist('first')); + } + + #[Test] + public function issetFn(): void + { + $storage = new Storage(); + self::assertFalse(isset($storage->first)); + $storage->set('first', 1); + + self::assertTrue(isset($storage->first)); + } + + #[Test] + public function offsets(): void + { + $storage = new Storage(); + self::assertFalse(isset($storage['first'])); + $storage['first'] = 1; + + self::assertTrue(isset($storage['first'])); + self::assertEquals(1, $storage['first']); + + unset($storage['first']); + self::assertNull($storage['first']); + self::assertEquals([], $storage->jsonSerialize()); + + } +} \ No newline at end of file diff --git a/tests/traits/ConfigurableTest.php b/tests/Traits/ConfigurableTest.php similarity index 96% rename from tests/traits/ConfigurableTest.php rename to tests/Traits/ConfigurableTest.php index 4592308..0da9aad 100644 --- a/tests/traits/ConfigurableTest.php +++ b/tests/Traits/ConfigurableTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Traits; use Php\Support\Exceptions\InvalidParamException; use PHPUnit\Framework\TestCase; @@ -53,7 +53,7 @@ public function testConfigurableThrow(): void $cls->configurable(['prop' => 'success', 'test' => 'fake']); } catch (\Throwable $exception) { $this->assertInstanceOf(InvalidParamException::class, $exception); - $this->assertNull($exception->getParam()); + $this->assertNull($exception->name); } } diff --git a/tests/traits/ConsolePrintTest.php b/tests/Traits/ConsolePrintTest.php similarity index 97% rename from tests/traits/ConsolePrintTest.php rename to tests/Traits/ConsolePrintTest.php index 86e766b..3d8300a 100644 --- a/tests/traits/ConsolePrintTest.php +++ b/tests/Traits/ConsolePrintTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests\traits; +namespace Php\Support\Tests\Traits; use Php\Support\Traits\ConsolePrint; use PHPUnit\Framework\TestCase; diff --git a/tests/traits/InterceptFilter.php b/tests/Traits/InterceptFilter.php similarity index 94% rename from tests/traits/InterceptFilter.php rename to tests/Traits/InterceptFilter.php index a8231ec..e8cd607 100644 --- a/tests/traits/InterceptFilter.php +++ b/tests/Traits/InterceptFilter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests\traits; +namespace Php\Support\Tests\Traits; /** * Class InterceptFilter diff --git a/tests/traits/MakerTest.php b/tests/Traits/MakerTest.php similarity index 96% rename from tests/traits/MakerTest.php rename to tests/Traits/MakerTest.php index 50ac6ef..b1e96ca 100644 --- a/tests/traits/MakerTest.php +++ b/tests/Traits/MakerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Traits; use PHPUnit\Framework\TestCase; diff --git a/tests/traits/MetableTest.php b/tests/Traits/MetableTest.php similarity index 98% rename from tests/traits/MetableTest.php rename to tests/Traits/MetableTest.php index 8a19e9b..3ecaccf 100644 --- a/tests/traits/MetableTest.php +++ b/tests/Traits/MetableTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Traits; use PHPUnit\Framework\TestCase; diff --git a/tests/traits/SingletonTest.php b/tests/Traits/SingletonTest.php similarity index 98% rename from tests/traits/SingletonTest.php rename to tests/Traits/SingletonTest.php index bb841e6..322f471 100644 --- a/tests/traits/SingletonTest.php +++ b/tests/Traits/SingletonTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Traits; use Php\Support\Exceptions\Exception; use PHPUnit\Framework\TestCase; diff --git a/tests/traits/TraitBooterTest.php b/tests/Traits/TraitBooterTest.php similarity index 97% rename from tests/traits/TraitBooterTest.php rename to tests/Traits/TraitBooterTest.php index 52467cf..c56ade5 100644 --- a/tests/traits/TraitBooterTest.php +++ b/tests/Traits/TraitBooterTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Traits; use Php\Support\Traits\TraitBooter; use Php\Support\Traits\TraitInitializer; diff --git a/tests/traits/TraitWhenerTest.php b/tests/Traits/TraitWhenerTest.php similarity index 91% rename from tests/traits/TraitWhenerTest.php rename to tests/Traits/TraitWhenerTest.php index d227fd3..98d67fa 100644 --- a/tests/traits/TraitWhenerTest.php +++ b/tests/Traits/TraitWhenerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Traits; use Php\Support\Traits\TraitBooter; use Php\Support\Traits\TraitInitializer; diff --git a/tests/exceptions/InvalidParamTest.php b/tests/exceptions/InvalidParamTest.php index c5c8e8c..e4c0b52 100644 --- a/tests/exceptions/InvalidParamTest.php +++ b/tests/exceptions/InvalidParamTest.php @@ -28,14 +28,14 @@ public function testThrow() $this->assertInstanceOf(InvalidParamException::class, $e); $this->assertSame('Invalid Parameter', $e->getMessage()); $this->assertSame('Invalid Parameter', $e->getName()); - $this->assertNull($e->getParam()); + $this->assertNull($e->name); } try { throw new InvalidParamException('Invalid Param', 'prop'); } catch (InvalidParamException $e) { $this->assertInstanceOf(InvalidParamException::class, $e); - $this->assertSame('prop', $e->getParam()); + $this->assertSame('prop', $e->name); $this->assertSame('Invalid Param', $e->getMessage()); $this->assertSame('Invalid Parameter', $e->getName()); } @@ -44,7 +44,7 @@ public function testThrow() throw new InvalidParamException(null, 'prop'); } catch (InvalidParamException $e) { $this->assertInstanceOf(InvalidParamException::class, $e); - $this->assertSame('prop', $e->getParam()); + $this->assertSame('prop', $e->name); $this->assertSame('Invalid Parameter', $e->getName()); $this->assertSame('Invalid Parameter: prop', $e->getMessage()); } diff --git a/tests/traits/ArrayStorageConfigurableTest.php b/tests/traits/ArrayStorageConfigurableTest.php deleted file mode 100644 index 04bd88a..0000000 --- a/tests/traits/ArrayStorageConfigurableTest.php +++ /dev/null @@ -1,58 +0,0 @@ - 'Damn', - 'id' => 'test', - 'k' => 1, - 's.k.d' => 2, - 'remote' => true, - ] - ); - - static::assertEquals('Damn', $config->name); - static::assertEquals('test', $config->id); - static::assertEquals(true, $config->remote); - - static::assertEquals(1, $config->get('k')); - static::assertEquals(2, $config->get('s.k.d')); - static::assertIsArray($config->getData()); - static::assertNotEmpty($config->getData()); - - static::assertTrue(property_exists($config, 'name')); - // $this->assertTrue(property_exists($config, 'id')); - static::assertEquals('test', $config->get('id')); - static::assertTrue(isset($config->id)); - static::assertTrue(isset($config->k)); - } -} - - -/** - * Class ArrayStorageConfigurableClassTest - */ -class ArrayStorageConfigurableClassTest -{ - use \Php\Support\Traits\ArrayStorageConfigurableTrait; - use \Php\Support\Traits\Maker; - - protected $name; - - public function __construct(array $data = []) - { - $this->configurable($data); - } -} diff --git a/tests/traits/ArrayStorageTest.php b/tests/traits/ArrayStorageTest.php deleted file mode 100644 index 4d7d53f..0000000 --- a/tests/traits/ArrayStorageTest.php +++ /dev/null @@ -1,190 +0,0 @@ -name = 'test'; - static::assertEquals('test', $config->name); - - $config->test = 'test in test'; - static::assertEquals('test in test', $config->test); - - $config->data = 1; - static::assertEquals(1, $config->data); - - $config->null = null; - static::assertNull($config->null); - } - - public function testDeepData(): void - { - $config = new ArrayStorageClassTest(); - $key = 'test.sub.key'; - - $config->$key = 1; - - static::assertEquals(1, $config->$key); - - $config->{'upper.sad.as'} = 'test'; - - static::assertEquals('test', $config->{'upper.sad.as'}); - } - - public function testGetData(): void - { - $config = new ArrayStorageClassTest(); - - $config->{'test.sub.key'} = 1; - $config->{'test.sub.val'} = 'value'; - $config->{'test.next'} = 'next value'; - - $expected = [ - 'test' => [ - 'sub' => [ - 'key' => 1, - 'val' => 'value', - ], - 'next' => 'next value', - ], - ]; - - static::assertEquals($expected['test'], $config->test); - static::assertEquals($expected, $config->getData()); - } - - public function testUnset(): void - { - $config = new ArrayStorageClassTest(); - - $config->name = 'name'; - unset($config->name); - static::assertTrue(isset($config->name)); - static::assertNull($config->name); - - $key = 'test.sub.key'; - $config->$key = 1; - - static::assertEquals(1, $config->$key); - - unset($config->$key); - - static::assertFalse(isset($config->$key)); - - unset($config->{'tst.sdf'}); - - static::assertFalse(isset($config->{'tst.sdf'})); - } - - public function testAbsent(): void - { - $config = new ArrayStorageClassTest(); - - static::assertNull($config->test); - } - - public function testIsset(): void - { - $config = new ArrayStorageClassTest(); - - static::assertFalse(isset($config->test)); - - $config->test2 = 'test2'; - static::assertTrue(isset($config->test2)); - - $config->null = null; - static::assertTrue(isset($config->null)); - - $config->name = 'name'; - static::assertTrue(isset($config->name)); - - static::assertFalse(isset($config->{'nullable.one.1'})); - } - - public function testValueExist(): void - { - $config = new ArrayStorageClassTest(); - - $config->test = 'test2'; - - static::assertTrue($config->valueExists('name')); - static::assertTrue($config->valueExists('test')); - static::assertFalse($config->valueExists('data')); - static::assertFalse($config->valueExists('test2')); - unset($config->test); - - static::assertFalse($config->valueExists('test')); - } - - - public function testOffsetExists(): void - { - $config = new ArrayStorageClassTest(); - $config->test2 = 'test2'; - - static::assertTrue($config->offsetExists('test2')); - static::assertTrue(isset($config['test2'])); - - $config->null = null; - static::assertTrue($config->offsetExists('null')); - static::assertFalse($config->offsetExists('null2')); - } - - public function testOffsetGet(): void - { - $config = new ArrayStorageClassTest(); - $config->test2 = 'test2'; - - static::assertEquals('test2', $config->offsetGet('test2')); - static::assertEquals('test2', $config['test2']); - - $config->null = null; - static::assertNull($config['null']); - } - - public function testOffsetSet(): void - { - $config = new ArrayStorageClassTest(); - $config['test2'] = 'val2'; - - static::assertEquals('val2', $config->test2); - static::assertEquals('val2', $config->offsetGet('test2')); - static::assertEquals('val2', $config['test2']); - - $config['null'] = null; - static::assertNull($config['null']); - } - - public function testOffsetUnset(): void - { - $config = new ArrayStorageClassTest(); - $config['test2'] = 'val2'; - - static::assertEquals('val2', $config->test2); - static::assertEquals('val2', $config->offsetGet('test2')); - static::assertEquals('val2', $config['test2']); - - unset($config['test2']); - } -} - -/** - * Class ArrayStorageClassTest - */ -class ArrayStorageClassTest implements \ArrayAccess -{ - use \Php\Support\Traits\ArrayStorage; - - protected $name; -} From b8cab074f394ef440886b4b7b41a8383df8bc439 Mon Sep 17 00:00:00 2001 From: fureev Date: Wed, 25 Dec 2024 15:04:34 +0200 Subject: [PATCH 91/95] ci: fix for php 8.4 --- .github/workflows/php.yml | 2 +- src/Exceptions/MissingPropertyException.php | 2 +- src/Exceptions/UnknownMethodException.php | 2 +- .../WithEnhancesForStringsTest.php | 4 +- tests/{enums => Enums}/data/StringsEnum.php | 2 +- .../ExceptionTest.php | 2 +- .../InvalidArgumentTest.php | 2 +- .../InvalidParamTest.php | 2 +- .../MissingClassTest.php | 15 +----- tests/Exceptions/MissingPropertyTest.php | 26 +++++++++ .../NotSupportedTest.php | 2 +- tests/Exceptions/UnknownMethodTest.php | 25 +++++++++ tests/Exceptions/UnknownPropertyTest.php | 26 +++++++++ tests/exceptions/MissingPropertyTest.php | 53 ------------------- tests/exceptions/UnknownMethodTest.php | 42 --------------- tests/exceptions/UnknownPropertyTest.php | 42 --------------- 16 files changed, 88 insertions(+), 161 deletions(-) rename tests/{enums => Enums}/WithEnhancesForStringsTest.php (95%) rename tests/{enums => Enums}/data/StringsEnum.php (84%) rename tests/{exceptions => Exceptions}/ExceptionTest.php (93%) rename tests/{exceptions => Exceptions}/InvalidArgumentTest.php (95%) rename tests/{exceptions => Exceptions}/InvalidParamTest.php (97%) rename tests/{exceptions => Exceptions}/MissingClassTest.php (62%) create mode 100644 tests/Exceptions/MissingPropertyTest.php rename tests/{exceptions => Exceptions}/NotSupportedTest.php (97%) create mode 100644 tests/Exceptions/UnknownMethodTest.php create mode 100644 tests/Exceptions/UnknownPropertyTest.php delete mode 100644 tests/exceptions/MissingPropertyTest.php delete mode 100644 tests/exceptions/UnknownMethodTest.php delete mode 100644 tests/exceptions/UnknownPropertyTest.php diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 0dd15e2..4104904 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -37,7 +37,7 @@ jobs: fail-fast: false matrix: setup: [ 'basic', 'lowest', 'stable' ] - php: [ '8.1' ,'8.2', '8.3' ] + php: [ '8.4' ] steps: - uses: actions/checkout@v4 diff --git a/src/Exceptions/MissingPropertyException.php b/src/Exceptions/MissingPropertyException.php index 41832b2..8ebb21b 100644 --- a/src/Exceptions/MissingPropertyException.php +++ b/src/Exceptions/MissingPropertyException.php @@ -11,7 +11,7 @@ class MissingPropertyException extends ConfigException { public function __construct(protected(set) ?string $property, ?string $message = null, array $config = []) { - parent::__construct($message ?? ($this->getName() . ": '$this->property'"), $config); + parent::__construct($message ?? ($this->getName() . ": $this->property"), $config); } /** diff --git a/src/Exceptions/UnknownMethodException.php b/src/Exceptions/UnknownMethodException.php index ada67d3..8eb84e9 100644 --- a/src/Exceptions/UnknownMethodException.php +++ b/src/Exceptions/UnknownMethodException.php @@ -14,7 +14,7 @@ class UnknownMethodException extends BadMethodCallException public function __construct(protected(set) string $method, ?string $message = null) { $this->method = $method; - parent::__construct($message ?? ($this->getName() . ": $this->method ")); + parent::__construct($message ?? ($this->getName() . ": $this->method")); } /** diff --git a/tests/enums/WithEnhancesForStringsTest.php b/tests/Enums/WithEnhancesForStringsTest.php similarity index 95% rename from tests/enums/WithEnhancesForStringsTest.php rename to tests/Enums/WithEnhancesForStringsTest.php index f140bc3..3d3c5df 100644 --- a/tests/enums/WithEnhancesForStringsTest.php +++ b/tests/Enums/WithEnhancesForStringsTest.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Php\Support\Tests\enums; +namespace Php\Support\Tests\Enums; -use Php\Support\Tests\enums\data\StringsEnum; +use Php\Support\Tests\Enums\data\StringsEnum; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; diff --git a/tests/enums/data/StringsEnum.php b/tests/Enums/data/StringsEnum.php similarity index 84% rename from tests/enums/data/StringsEnum.php rename to tests/Enums/data/StringsEnum.php index 4206d54..9212c48 100644 --- a/tests/enums/data/StringsEnum.php +++ b/tests/Enums/data/StringsEnum.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests\enums\data; +namespace Php\Support\Tests\Enums\data; use Php\Support\Enums\WithEnhancesForStrings; diff --git a/tests/exceptions/ExceptionTest.php b/tests/Exceptions/ExceptionTest.php similarity index 93% rename from tests/exceptions/ExceptionTest.php rename to tests/Exceptions/ExceptionTest.php index eec5290..4897e3b 100644 --- a/tests/exceptions/ExceptionTest.php +++ b/tests/Exceptions/ExceptionTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Exceptions; use Php\Support\Exceptions\Exception; use PHPUnit\Framework\TestCase; diff --git a/tests/exceptions/InvalidArgumentTest.php b/tests/Exceptions/InvalidArgumentTest.php similarity index 95% rename from tests/exceptions/InvalidArgumentTest.php rename to tests/Exceptions/InvalidArgumentTest.php index df80c56..3f20d2d 100644 --- a/tests/exceptions/InvalidArgumentTest.php +++ b/tests/Exceptions/InvalidArgumentTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Exceptions; use Php\Support\Exceptions\InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/exceptions/InvalidParamTest.php b/tests/Exceptions/InvalidParamTest.php similarity index 97% rename from tests/exceptions/InvalidParamTest.php rename to tests/Exceptions/InvalidParamTest.php index e4c0b52..0d89862 100644 --- a/tests/exceptions/InvalidParamTest.php +++ b/tests/Exceptions/InvalidParamTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Exceptions; use Php\Support\Exceptions\InvalidParamException; use PHPUnit\Framework\TestCase; diff --git a/tests/exceptions/MissingClassTest.php b/tests/Exceptions/MissingClassTest.php similarity index 62% rename from tests/exceptions/MissingClassTest.php rename to tests/Exceptions/MissingClassTest.php index 68417f0..eb64497 100644 --- a/tests/exceptions/MissingClassTest.php +++ b/tests/Exceptions/MissingClassTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Exceptions; use Php\Support\Exceptions\MissingClassException; use PHPUnit\Framework\TestCase; @@ -15,13 +15,6 @@ final class MissingClassTest extends TestCase { public function testThrow() { - try { - throw new MissingClassException(); - } catch (Throwable $e) { - $this->assertInstanceOf(MissingClassException::class, $e); - $this->assertSame('Missing Class', $e->getMessage()); - } - try { throw new MissingClassException(MissingClassException::class); } catch (Throwable $e) { @@ -29,12 +22,6 @@ public function testThrow() $this->assertSame('Missing Class: ' . MissingClassException::class, $e->getMessage()); } - try { - throw new MissingClassException(null, 'Test Message'); - } catch (Throwable $e) { - $this->assertInstanceOf(MissingClassException::class, $e); - $this->assertSame('Test Message', $e->getMessage()); - } try { throw new MissingClassException(MissingClassException::class, 'Test Message'); } catch (Throwable $e) { diff --git a/tests/Exceptions/MissingPropertyTest.php b/tests/Exceptions/MissingPropertyTest.php new file mode 100644 index 0000000..91317ef --- /dev/null +++ b/tests/Exceptions/MissingPropertyTest.php @@ -0,0 +1,26 @@ +assertInstanceOf(MissingPropertyException::class, $e); + $this->assertSame('Missing property: test', $e->getMessage()); + $this->assertSame('test', $e->property); + } + } +} diff --git a/tests/exceptions/NotSupportedTest.php b/tests/Exceptions/NotSupportedTest.php similarity index 97% rename from tests/exceptions/NotSupportedTest.php rename to tests/Exceptions/NotSupportedTest.php index 7c20507..bfe1b5d 100644 --- a/tests/exceptions/NotSupportedTest.php +++ b/tests/Exceptions/NotSupportedTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Php\Support\Tests; +namespace Php\Support\Tests\Exceptions; use Php\Support\Exceptions\NotSupportedException; use PHPUnit\Framework\TestCase; diff --git a/tests/Exceptions/UnknownMethodTest.php b/tests/Exceptions/UnknownMethodTest.php new file mode 100644 index 0000000..b556bd4 --- /dev/null +++ b/tests/Exceptions/UnknownMethodTest.php @@ -0,0 +1,25 @@ +assertInstanceOf(UnknownMethodException::class, $e); + $this->assertSame('Unknown method: method', $e->getMessage()); + } + } +} diff --git a/tests/Exceptions/UnknownPropertyTest.php b/tests/Exceptions/UnknownPropertyTest.php new file mode 100644 index 0000000..40d4a01 --- /dev/null +++ b/tests/Exceptions/UnknownPropertyTest.php @@ -0,0 +1,26 @@ +assertInstanceOf(UnknownPropertyException::class, $e); + $this->assertSame('Unknown property: test', $e->getMessage()); + $this->assertSame('test', $e->property); + } + } +} diff --git a/tests/exceptions/MissingPropertyTest.php b/tests/exceptions/MissingPropertyTest.php deleted file mode 100644 index bd886b6..0000000 --- a/tests/exceptions/MissingPropertyTest.php +++ /dev/null @@ -1,53 +0,0 @@ -assertInstanceOf(MissingPropertyException::class, $e); - $this->assertSame('Invalid Arg', $e->getMessage()); - } - - try { - throw new MissingPropertyException(null, 'test'); - } catch (Throwable $e) { - $this->assertInstanceOf(MissingPropertyException::class, $e); - $this->assertSame('Missing property', $e->getName()); - $this->assertSame('Missing property: "test"', $e->getMessage()); - $this->assertSame('test', $e->getProperty()); - } - - try { - throw new MissingPropertyException(); - } catch (Throwable $e) { - $this->assertInstanceOf(MissingPropertyException::class, $e); - $this->assertSame('Missing property', $e->getName()); - $this->assertSame('Missing property', $e->getMessage()); - } - - - try { - throw new MissingPropertyException(null, 'test', ['key' => 'val']); - } catch (Throwable $e) { - $this->assertInstanceOf(MissingPropertyException::class, $e); - $this->assertSame('Missing property', $e->getName()); - $this->assertSame('Missing property: "test"', $e->getMessage()); - $this->assertSame('test', $e->getProperty()); - $this->assertSame(['key' => 'val'], $e->getConfig()); - } - } -} diff --git a/tests/exceptions/UnknownMethodTest.php b/tests/exceptions/UnknownMethodTest.php deleted file mode 100644 index ac0f89a..0000000 --- a/tests/exceptions/UnknownMethodTest.php +++ /dev/null @@ -1,42 +0,0 @@ -assertInstanceOf(UnknownMethodException::class, $e); - $this->assertSame('Invalid Arg', $e->getMessage()); - } - - try { - throw new UnknownMethodException(null, 'test'); - } catch (Throwable $e) { - $this->assertInstanceOf(UnknownMethodException::class, $e); - $this->assertSame('Unknown method', $e->getName()); - $this->assertSame('Unknown method: "test"', $e->getMessage()); - $this->assertSame('test', $e->getMethod()); - } - - try { - throw new UnknownMethodException(); - } catch (Throwable $e) { - $this->assertInstanceOf(UnknownMethodException::class, $e); - $this->assertSame('Unknown method', $e->getName()); - $this->assertSame('Unknown method', $e->getMessage()); - } - } -} diff --git a/tests/exceptions/UnknownPropertyTest.php b/tests/exceptions/UnknownPropertyTest.php deleted file mode 100644 index 74e23ca..0000000 --- a/tests/exceptions/UnknownPropertyTest.php +++ /dev/null @@ -1,42 +0,0 @@ -assertInstanceOf(UnknownPropertyException::class, $e); - $this->assertSame('Invalid Arg', $e->getMessage()); - } - - try { - throw new UnknownPropertyException(null, 'test'); - } catch (Throwable $e) { - $this->assertInstanceOf(UnknownPropertyException::class, $e); - $this->assertSame('Unknown property', $e->getName()); - $this->assertSame('Unknown property: "test"', $e->getMessage()); - $this->assertSame('test', $e->getProperty()); - } - - try { - throw new UnknownPropertyException(); - } catch (Throwable $e) { - $this->assertInstanceOf(UnknownPropertyException::class, $e); - $this->assertSame('Unknown property', $e->getName()); - $this->assertSame('Unknown property', $e->getMessage()); - } - } -} From f6066f8e34c6d49c71fa0edbabf02d66b6c33404 Mon Sep 17 00:00:00 2001 From: fureev Date: Wed, 25 Dec 2024 15:14:53 +0200 Subject: [PATCH 92/95] fix --- phpstan.neon | 8 ++++---- src/ConditionalHandler.php | 1 - src/Helpers/URLify.php | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 483dec6..803d483 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,11 +1,11 @@ parameters: - level: '6' + level: '2' paths: - src # - tests - ignoreErrors: - - - identifier: trait.unused +# ignoreErrors: +# - +# identifier: trait.unused excludePaths: - 'src/Types/Point.php' - 'src/Types/GeoPoint.php' diff --git a/src/ConditionalHandler.php b/src/ConditionalHandler.php index 1c6e183..534ca41 100644 --- a/src/ConditionalHandler.php +++ b/src/ConditionalHandler.php @@ -32,7 +32,6 @@ final class ConditionalHandler { /** * @var array - * @phpstan-ignore missingType.iterableValue */ private array $params = []; diff --git a/src/Helpers/URLify.php b/src/Helpers/URLify.php index aba6f6f..4c390bf 100644 --- a/src/Helpers/URLify.php +++ b/src/Helpers/URLify.php @@ -770,7 +770,6 @@ private static function initLanguageMap(string $language = ''): void } // Is a specific map associated with $language? - // @phpstan-ignore booleanAnd.rightAlwaysTrue if (isset(self::$maps[$language]) && is_array(self::$maps[$language])) { // Move this map to end. This means it will have priority over others $map = self::$maps[$language]; From 6ecc9cdb586f64c44c4776f99b4cff9ec9ea740f Mon Sep 17 00:00:00 2001 From: fureev Date: Wed, 15 Jan 2025 13:33:39 +0200 Subject: [PATCH 93/95] feat: add HashCollection --- src/Structures/Collections/HashCollection.php | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/Structures/Collections/HashCollection.php diff --git a/src/Structures/Collections/HashCollection.php b/src/Structures/Collections/HashCollection.php new file mode 100644 index 0000000..ce735ce --- /dev/null +++ b/src/Structures/Collections/HashCollection.php @@ -0,0 +1,198 @@ + $elements + */ + public function __construct(protected array $elements = []) + { + } + + /** + * Gets a native PHP array of the elements. + * + * @return array + */ + public function all(): array + { + return $this->elements; + } + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string $key The key/index to check for. + * + * @return bool TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + public function hasKey(string $key): bool + { + return isset($this->elements[$key]) || array_key_exists($key, $this->elements); + } + + /** + * Gets the element at the specified key/index. + * + * @param string $key The key/index of the element to retrieve. + * + * @return T|null + */ + public function get(string $key): mixed + { + return $this->elements[$key] ?? null; + } + + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string $key The key/index of the element to set. + * @param T $value The element to set. + */ + public function set(string $key, mixed $value): void + { + $this->elements[$key] = $value; + } + + + /** + * Adds an element at the end of the collection. + * + * @param T $element The element to add. + */ + public function add(object $element): bool + { + $this->elements[$element::class] = $element; + + return true; + } + + /** + * Removes the element at the specified index from the collection. + * + * @param string $key The key/index of the element to remove. + * + * @return T|null The removed element or NULL, if the collection did not contain the element. + */ + public function remove(string $key): mixed + { + if (!isset($this->elements[$key]) && !array_key_exists($key, $this->elements)) { + return null; + } + + $removed = $this->elements[$key]; + unset($this->elements[$key]); + + return $removed; + } + + /** + * @return int<0, max> + */ + public function count(): int + { + return count($this->elements); + } + + /** + * @param string $offset + */ + public function offsetExists(mixed $offset): bool + { + return $this->hasKey($offset); + } + + /** + * @param string $offset + * + * @return T|null + */ + public function offsetGet(mixed $offset): mixed + { + return $this->get($offset); + } + + /** + * @param string|null $offset + * @param T $value + */ + public function offsetSet(mixed $offset, mixed $value): void + { + if (!isset($offset)) { + $this->add($value); + + return; + } + + $this->set($offset, $value); + } + + /** + * @param string $offset + */ + public function offsetUnset(mixed $offset): void + { + $this->remove($offset); + } + + /** + * Checks whether the collection is empty (contains no elements). + */ + public function isEmpty(): bool + { + return empty($this->elements); + } + + /** + * Checks whether an element is contained in the collection. + * This is an O(n) operation, where n is the size of the collection. + * + * @param TMaybeContained $element The element to search for. + * + * @return bool TRUE if the collection contains the element, FALSE otherwise. + * @phpstan-return (TMaybeContained is T ? bool : false) + * + * @template TMaybeContained + */ + public function contains(mixed $element): bool + { + return in_array($element, $this->elements, true); + } + + /** + * Clears the collection, removing all elements. + */ + public function clear(): void + { + $this->elements = []; + } + + /** + * Returns the first element of this collection that satisfies the predicate $func. + * + * @param Closure(string, T):bool $func The predicate. + * + * @return null|T The first element respecting the predicate, null if no element respects the predicate. + */ + public function find(Closure $func): mixed + { + return array_find($this->elements, fn($element, $key) => $func($key, $element)); + } +} From f4074a6d2209c0e72b274e19348b6cbfcdd4ed72 Mon Sep 17 00:00:00 2001 From: fureev Date: Wed, 15 Jan 2025 13:37:45 +0200 Subject: [PATCH 94/95] docs: change Readmy, Changelog --- CHANGELOG.md | 10 ++++++++-- readme.md | 15 +++++++++++---- ...rabeStorage.php => UseConfigurableStorage.php} | 2 +- tests/Global/BaseTest.php | 6 +++--- 4 files changed, 23 insertions(+), 10 deletions(-) rename src/Traits/{UseConfigurabeStorage.php => UseConfigurableStorage.php} (94%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2efccab..9ec0ccb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog][keepachangelog] and this project adheres to [Semantic Versioning][semver]. +## v5.1.0 + +### Added + +- Add `HashCollection` Collection. + ## v5.0.0 ### Added - Add `PHP 8.4` support -- Add `UseStorage` trait. Trait `ArrayStorage` is deprecated now -- Add `UseConfigurabeStorage` trait. Trait `ArrayStorageConfigurableTrait` is deprecated now +- Add `UseStorage` trait. +- Add `UseConfigurabeStorage` trait. ### Removed diff --git a/readme.md b/readme.md index 173b90a..3700315 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,13 @@ ## Install -For php >= 8.1 (8.1, 8.2) +For php >= 8.4 + +```bash +composer require efureev/support "^5.1" +``` + +For php >= 8.1 (8.1, 8.2, 8.3) ```bash composer require efureev/support "^4.19" @@ -139,11 +145,12 @@ composer require efureev/support "^2.0" - Structures - Collections (^4.16.0) - - ArrayCollections + - ArrayCollection + - HashCollection (^5.1.0) - Traits - + ArrayStorage - + ArrayStorageConfigurableTrait + + UseStorage + + UseConfigurableStorage + ConfigurableTrait + ConsolePrint + Maker diff --git a/src/Traits/UseConfigurabeStorage.php b/src/Traits/UseConfigurableStorage.php similarity index 94% rename from src/Traits/UseConfigurabeStorage.php rename to src/Traits/UseConfigurableStorage.php index 23fecbf..5a96fe0 100644 --- a/src/Traits/UseConfigurabeStorage.php +++ b/src/Traits/UseConfigurableStorage.php @@ -12,7 +12,7 @@ * @implements ArrayAccess * @mixin ArrayAccess */ -trait UseConfigurabeStorage +trait UseConfigurableStorage { use UseStorage; use ConfigurableTrait { diff --git a/tests/Global/BaseTest.php b/tests/Global/BaseTest.php index b5da4a3..4168df1 100644 --- a/tests/Global/BaseTest.php +++ b/tests/Global/BaseTest.php @@ -303,7 +303,7 @@ public function testTraitUsesRecursive(): void static::assertEquals( [ \Php\Support\Traits\Singleton::class => \Php\Support\Traits\Singleton::class, - \Php\Support\Traits\UseConfigurabeStorage::class => \Php\Support\Traits\UseConfigurabeStorage::class, + \Php\Support\Traits\UseConfigurableStorage::class => \Php\Support\Traits\UseConfigurableStorage::class, \Php\Support\Traits\UseStorage::class => \Php\Support\Traits\UseStorage::class, \Php\Support\Traits\ConfigurableTrait::class => \Php\Support\Traits\ConfigurableTrait::class, ], @@ -318,7 +318,7 @@ public function testClassUsesRecursive(): void static::assertEquals( [ \Php\Support\Traits\Singleton::class => \Php\Support\Traits\Singleton::class, - \Php\Support\Traits\UseConfigurabeStorage::class => \Php\Support\Traits\UseConfigurabeStorage::class, + \Php\Support\Traits\UseConfigurableStorage::class => \Php\Support\Traits\UseConfigurableStorage::class, \Php\Support\Traits\UseStorage::class => \Php\Support\Traits\UseStorage::class, \Php\Support\Traits\ConfigurableTrait::class => \Php\Support\Traits\ConfigurableTrait::class, \Php\Support\Traits\Maker::class => \Php\Support\Traits\Maker::class, @@ -340,7 +340,7 @@ public function testClassBasename(): void class TraitUsesRecursiveClass { use \Php\Support\Traits\Singleton; - use \Php\Support\Traits\UseConfigurabeStorage; + use \Php\Support\Traits\UseConfigurableStorage; protected $username; } From 2a2c3355df16d9cb33a4d0743e586a182c74efb7 Mon Sep 17 00:00:00 2001 From: fureev Date: Wed, 15 Jan 2025 14:19:21 +0200 Subject: [PATCH 95/95] fix: instance --- src/Global/base.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Global/base.php b/src/Global/base.php index bc38836..f7ebf42 100644 --- a/src/Global/base.php +++ b/src/Global/base.php @@ -8,10 +8,10 @@ /** * Return the default value of the given value. */ - function value(mixed $value, mixed ...$args): mixed + function value(mixed $value, mixed ...$params): mixed { return $value instanceof Closure || (is_object($value) && is_callable($value)) - ? $value(...$args) + ? $value(...$params) : $value; } } @@ -151,6 +151,13 @@ function isTrue(mixed $val, bool $return_null = false): ?bool } if (!function_exists('instance')) { + /** + * @phpstan-param T|class-string|null $instance + * @param mixed ...$params + * @phpstan-return T|null + * + * @template T as object + */ function instance(string|object|null $instance, mixed ...$params): ?object { if (is_object($instance)) {