diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..cffc922
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake . --impure
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
new file mode 100644
index 0000000..f129799
--- /dev/null
+++ b/.github/workflows/phpstan.yml
@@ -0,0 +1,31 @@
+name: PHPStan
+
+on:
+ push:
+ branches:
+ - main
+ - github-actions
+ paths:
+ - '**.php'
+ - 'phpstan.neon.dist'
+ - '.github/workflows/phpstan.yml'
+
+jobs:
+ phpstan:
+ name: phpstan
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+ coverage: none
+
+ - name: Install composer dependencies
+ uses: ramsey/composer-install@v3
+
+ - name: Run PHPStan
+ run: ./vendor/bin/phpstan --error-format=github
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
new file mode 100644
index 0000000..f918ca8
--- /dev/null
+++ b/.github/workflows/run-tests.yml
@@ -0,0 +1,69 @@
+name: run-tests
+
+on:
+ push:
+ branches:
+ - main
+ - github-actions
+jobs:
+ test:
+ runs-on: ubuntu-22.04
+ env:
+ BROADCAST_DRIVER: log
+ CACHE_DRIVER: redis
+ QUEUE_CONNECTION: redis
+ SESSION_DRIVER: redis
+ DB_CONNECTION: testing
+ APP_KEY: base64:2fl+Ktvkfl+Fuz3Qp/A76G2RTiGVA/ZjKZaz6fiiM10=
+ APP_ENV: testing
+ BCRYPT_ROUNDS: 10
+ MAIL_MAILER: array
+ TELESCOPE_ENABLED: false
+
+ # Docs: https://docs.github.com/en/actions/using-containerized-services
+ services:
+ elasticsearch:
+ image: elasticsearch:7.17.3
+ env:
+ discovery.type: single-node
+ ES_JAVA_OPTS: '-Xms512m -Xmx512m'
+ ports:
+ - 9200:9200
+ options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=5
+
+ redis:
+ image: redis
+ ports:
+ - 6379/tcp
+ options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
+
+ steps:
+ - name: Checkout 🛎
+ uses: actions/checkout@v4
+
+ - name: Verify Elasticsearch connection 🧩
+ run: |
+ curl -X GET "localhost:${{ job.services.elasticsearch.ports['9200'] }}/_cluster/health?pretty=true"
+
+ - name: Setup PHP 🏗
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.2
+ tools: composer:v2
+ coverage: xdebug
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo, mysql
+
+ - name: Install Project Dependencies 💻
+ run: |
+ composer install --no-interaction --prefer-dist --optimize-autoloader
+
+ - name: List Installed Dependencies 📦
+ run: composer show -D
+
+ - name: Run tests 🧪
+ run: |
+ ./vendor/bin/pest --version
+ ./vendor/bin/pest
+ env:
+ REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
+ ELASTICSEARCH_PORT: ${{ job.services.elasticsearch.ports['9200'] }}
diff --git a/.gitignore b/.gitignore
index 262d48a..1b120b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,30 @@
-/vendor
-/.phpunit.cache
+.env
+.env.*
+
+#composer
+vendor
+
+# Snow-blower
+!secrets/.env*
+.sb*
+secrets.nix
+
+# pre-commit
+.pre-commit-config.yaml
+
+# direnv
+.direnv
+
+.phpunit*
+
+
+.idea
+.phpunit.cache
+build
+composer.lock
+coverage
+docs
+phpunit.xml
+phpstan.neon
+testbench.yaml
+node_modules
\ No newline at end of file
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
new file mode 100644
index 0000000..ff37e37
--- /dev/null
+++ b/.php-cs-fixer.php
@@ -0,0 +1,239 @@
+ true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'binary_operator_spaces' => [
+ 'default' => 'single_space',
+ ],
+ 'blank_line_after_namespace' => true,
+ 'blank_line_after_opening_tag' => true,
+ 'blank_line_before_statement' => [
+ 'statements' => [
+ 'continue',
+ 'return',
+ ],
+ ],
+ 'blank_line_between_import_groups' => true,
+ 'blank_lines_before_namespace' => true,
+ 'braces_position' => [
+ 'control_structures_opening_brace' => 'same_line',
+ 'functions_opening_brace' => 'next_line_unless_newline_at_signature_end',
+ 'anonymous_functions_opening_brace' => 'same_line',
+ 'classes_opening_brace' => 'next_line_unless_newline_at_signature_end',
+ 'anonymous_classes_opening_brace' => 'next_line_unless_newline_at_signature_end',
+ 'allow_single_line_empty_anonymous_classes' => false,
+ 'allow_single_line_anonymous_functions' => false,
+ ],
+ 'cast_spaces' => true,
+ 'class_attributes_separation' => [
+ 'elements' => [
+ 'const' => 'one',
+ 'method' => 'one',
+ 'property' => 'one',
+ 'trait_import' => 'none',
+ ],
+ ],
+ 'class_definition' => [
+ 'multi_line_extends_each_single_line' => true,
+ 'single_item_single_line' => true,
+ 'single_line' => true,
+ ],
+ 'class_reference_name_casing' => true,
+ 'clean_namespace' => true,
+ 'compact_nullable_type_declaration' => true,
+ 'concat_space' => [
+ 'spacing' => 'none',
+ ],
+ 'constant_case' => ['case' => 'lower'],
+ 'control_structure_braces' => true,
+ 'control_structure_continuation_position' => [
+ 'position' => 'same_line',
+ ],
+ 'declare_equal_normalize' => true,
+ 'declare_parentheses' => true,
+ 'elseif' => true,
+ 'encoding' => true,
+ 'full_opening_tag' => true,
+ 'fully_qualified_strict_types' => false,
+ 'function_declaration' => true,
+ 'general_phpdoc_tag_rename' => true,
+ 'heredoc_to_nowdoc' => true,
+ 'include' => true,
+ 'increment_style' => ['style' => 'post'],
+ 'indentation_type' => true,
+ 'integer_literal_case' => true,
+ 'lambda_not_used_import' => true,
+ 'line_ending' => true,
+ 'linebreak_after_opening_tag' => true,
+ 'list_syntax' => true,
+ 'lowercase_cast' => true,
+ 'lowercase_keywords' => true,
+ 'lowercase_static_reference' => true,
+ 'magic_constant_casing' => true,
+ 'magic_method_casing' => true,
+ 'method_argument_space' => [
+ 'on_multiline' => 'ignore',
+ ],
+ 'method_chaining_indentation' => true,
+ 'multiline_whitespace_before_semicolons' => [
+ 'strategy' => 'no_multi_line',
+ ],
+ 'native_function_casing' => true,
+ 'native_type_declaration_casing' => true,
+ 'no_alias_functions' => true,
+ 'no_alias_language_construct_call' => true,
+ 'no_alternative_syntax' => true,
+ 'no_binary_string' => true,
+ 'no_blank_lines_after_class_opening' => true,
+ 'no_blank_lines_after_phpdoc' => true,
+ 'no_closing_tag' => true,
+ 'no_empty_phpdoc' => true,
+ 'no_empty_statement' => true,
+ 'no_extra_blank_lines' => [
+ 'tokens' => [
+ 'extra',
+ 'throw',
+ 'use',
+ ],
+ ],
+ 'no_leading_import_slash' => true,
+ 'no_leading_namespace_whitespace' => true,
+ 'no_mixed_echo_print' => [
+ 'use' => 'echo',
+ ],
+ 'no_multiline_whitespace_around_double_arrow' => true,
+ 'no_multiple_statements_per_line' => true,
+ 'no_short_bool_cast' => true,
+ 'no_singleline_whitespace_before_semicolons' => true,
+ 'no_space_around_double_colon' => true,
+ 'no_spaces_after_function_name' => true,
+ 'no_spaces_around_offset' => [
+ 'positions' => ['inside', 'outside'],
+ ],
+ 'no_superfluous_phpdoc_tags' => [
+ 'allow_mixed' => true,
+ 'allow_unused_params' => true,
+ ],
+ 'no_trailing_comma_in_singleline' => true,
+ 'no_trailing_whitespace' => true,
+ 'no_trailing_whitespace_in_comment' => true,
+ 'no_unneeded_control_parentheses' => [
+ 'statements' => ['break', 'clone', 'continue', 'echo_print', 'return', 'switch_case', 'yield'],
+ ],
+ 'no_unneeded_braces' => true,
+ 'no_unreachable_default_argument_value' => true,
+ 'no_unset_cast' => true,
+ 'no_unused_imports' => true,
+ 'no_useless_return' => true,
+ 'no_whitespace_before_comma_in_array' => true,
+ 'no_whitespace_in_blank_line' => true,
+ 'normalize_index_brace' => true,
+ 'not_operator_with_successor_space' => true,
+ 'nullable_type_declaration' => true,
+ 'nullable_type_declaration_for_default_null_value' => true,
+ 'object_operator_without_whitespace' => true,
+ 'ordered_imports' => ['sort_algorithm' => 'alpha', 'imports_order' => ['const', 'class', 'function']],
+ 'ordered_interfaces' => true,
+ 'ordered_traits' => true,
+ 'phpdoc_align' => [
+ 'align' => 'left',
+ 'spacing' => [
+ 'param' => 2,
+ ],
+ ],
+ 'phpdoc_indent' => true,
+ 'phpdoc_inline_tag_normalizer' => true,
+ 'phpdoc_no_access' => true,
+ 'phpdoc_no_package' => true,
+ 'phpdoc_no_useless_inheritdoc' => true,
+ 'phpdoc_order' => [
+ 'order' => ['param', 'return', 'throws'],
+ ],
+ 'phpdoc_scalar' => true,
+ 'phpdoc_separation' => [
+ 'groups' => [
+ ['deprecated', 'link', 'see', 'since'],
+ ['author', 'copyright', 'license'],
+ ['category', 'package', 'subpackage'],
+ ['property', 'property-read', 'property-write'],
+ ['param', 'return'],
+ ],
+ ],
+ 'phpdoc_single_line_var_spacing' => true,
+ 'phpdoc_summary' => false,
+ 'phpdoc_tag_type' => [
+ 'tags' => [
+ 'inheritdoc' => 'inline',
+ ],
+ ],
+ 'phpdoc_to_comment' => false,
+ 'phpdoc_trim' => true,
+ 'phpdoc_types' => true,
+ 'phpdoc_var_without_name' => true,
+ 'psr_autoloading' => false,
+ 'return_type_declaration' => ['space_before' => 'none'],
+ 'self_accessor' => false,
+ 'self_static_accessor' => true,
+ 'short_scalar_cast' => true,
+ 'simplified_null_return' => false,
+ 'single_blank_line_at_eof' => true,
+ 'single_class_element_per_statement' => [
+ 'elements' => ['const', 'property'],
+ ],
+ 'single_import_per_statement' => true,
+ 'single_line_after_imports' => true,
+ 'single_line_comment_style' => [
+ 'comment_types' => ['hash'],
+ ],
+ 'single_line_empty_body' => true,
+ 'single_quote' => true,
+ 'single_space_around_construct' => true,
+ 'space_after_semicolon' => true,
+ 'spaces_inside_parentheses' => true,
+ 'standardize_not_equals' => true,
+ 'statement_indentation' => true,
+ 'switch_case_semicolon_to_colon' => true,
+ 'switch_case_space' => true,
+ 'ternary_operator_spaces' => true,
+ 'trailing_comma_in_multiline' => ['elements' => ['arrays']],
+ 'trim_array_spaces' => true,
+ 'type_declaration_spaces' => true,
+ 'types_spaces' => true,
+ 'unary_operator_spaces' => true,
+ 'visibility_required' => [
+ 'elements' => ['method', 'property'],
+ ],
+ 'whitespace_after_comma_in_array' => true,
+ 'yoda_style' => [
+ 'always_move_variable' => false,
+ 'equal' => false,
+ 'identical' => false,
+ 'less_and_greater' => false,
+ ],
+ 'declare_strict_types' => true,
+ 'explicit_string_variable' => true,
+ ];
+
+ $finder = Finder::create()
+ ->in([
+ __DIR__.'/src',
+ __DIR__.'/tests',
+ ])
+ ->name('*.php')
+ ->notName('*.blade.php')
+ ->ignoreDotFiles(true)
+ ->ignoreVCS(true);
+
+ $config = new Config();
+
+ return $config->setFinder($finder)
+ ->setRules($rules)
+ ->setRiskyAllowed(true)
+ ->setUsingCache(true);
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..9a4b252
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,37 @@
+# Contribution Guide
+
+## Setting up the project
+
+### Nix Setup (With Flakes)
+
+Pull down this project locally.
+
+when you enter the directory if you have direnv installed run:
+
+```sh
+direnv allow
+```
+
+If you do not run
+
+```sh
+nix develop --impure
+```
+
+this will pull down all dependencies, install hooks and formatters etc. If done correctly you should see a screen like this:
+
+
+
+### Development
+
+During development, you will need to start a local elasticsearch instance. To start elasticsearch run:
+
+```sh
+just up
+```
+
+This will start process-compose and an elastic search instance.
+
+
+
+You can now open a new terminal window and run tests using pest. For conviance purposes the nix shell has the command `p` linked to run `./vendor/bin/pest` and `pf` linked to run `./vendor/bin/pest --filter "$@"`.
diff --git a/composer.json b/composer.json
index 4e186ba..025de5e 100644
--- a/composer.json
+++ b/composer.json
@@ -24,22 +24,42 @@
"illuminate/container": "^10.0|^11.0",
"illuminate/database": "^10.0|^11.0",
"illuminate/events": "^10.0|^11.0",
- "elasticsearch/elasticsearch": "^8.12"
+ "elasticsearch/elasticsearch": "^8.15"
},
"require-dev": {
- "phpunit/phpunit": "^10.3",
- "orchestra/testbench": "^8.0",
+ "orchestra/testbench": "^9.0.0||^8.22.0",
"mockery/mockery": "^1.4.4",
- "doctrine/coding-standard": "12.0.x-dev"
+ "doctrine/coding-standard": "12.0.x-dev",
+ "pestphp/pest": "^2.34",
+ "pestphp/pest-plugin-laravel": "^2.4",
+ "laravel/pint": "^1.14",
+ "nunomaduro/collision": "^8.1.1||^7.10.0",
+ "larastan/larastan": "^2.9",
+ "pestphp/pest-plugin-arch": "^2.7",
+ "phpstan/extension-installer": "^1.3",
+ "phpstan/phpstan-deprecation-rules": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.3"
},
"autoload-dev": {
"psr-4": {
- "PDPhilip\\Elasticsearch\\Tests\\": "tests/"
+ "PDPhilip\\Elasticsearch\\Tests\\": "tests/",
+ "Workbench\\App\\": "workbench/app/",
+ "Workbench\\Database\\Factories\\": "workbench/database/factories/",
+ "Workbench\\Database\\Seeders\\": "workbench/database/seeders/"
}
},
"autoload": {
"psr-4": {
- "PDPhilip\\Elasticsearch\\": "src/"
+ "PDPhilip\\Elasticsearch\\": "src/",
+ "PDPhilip\\Elasticsearch\\Tests\\": "tests"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "pestphp/pest-plugin": true,
+ "dealerdirect/phpcodesniffer-composer-installer": true,
+ "php-http/discovery": true,
+ "phpstan/extension-installer": true
}
},
"extra": {
@@ -48,5 +68,25 @@
"PDPhilip\\Elasticsearch\\ElasticServiceProvider"
]
}
+ },
+ "scripts": {
+ "post-autoload-dump": [
+ "@clear",
+ "@prepare"
+ ],
+ "clear": "@php vendor/bin/testbench package:purge-skeleton --ansi",
+ "prepare": "@php vendor/bin/testbench package:discover --ansi",
+ "build": "@php vendor/bin/testbench workbench:build --ansi",
+ "serve": [
+ "Composer\\Config::disableProcessTimeout",
+ "@build",
+ "@php vendor/bin/testbench serve"
+ ],
+ "lint": [
+ "@php vendor/bin/phpstan analyse"
+ ],
+ "test": [
+ "@php vendor/bin/pest"
+ ]
}
-}
\ No newline at end of file
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..a559927
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,11144 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "0eaadb4bf1ba861097f30c8a88d88bfe",
+ "packages": [
+ {
+ "name": "brick/math",
+ "version": "0.12.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/brick/math.git",
+ "reference": "f510c0a40911935b77b86859eb5223d58d660df1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
+ "reference": "f510c0a40911935b77b86859eb5223d58d660df1",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.2",
+ "phpunit/phpunit": "^10.1",
+ "vimeo/psalm": "5.16.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Brick\\Math\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Arbitrary-precision arithmetic library",
+ "keywords": [
+ "Arbitrary-precision",
+ "BigInteger",
+ "BigRational",
+ "arithmetic",
+ "bigdecimal",
+ "bignum",
+ "bignumber",
+ "brick",
+ "decimal",
+ "integer",
+ "math",
+ "mathematics",
+ "rational"
+ ],
+ "support": {
+ "issues": "https://github.com/brick/math/issues",
+ "source": "https://github.com/brick/math/tree/0.12.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/BenMorel",
+ "type": "github"
+ }
+ ],
+ "time": "2023-11-29T23:19:16+00:00"
+ },
+ {
+ "name": "carbonphp/carbon-doctrine-types",
+ "version": "3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+ "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+ "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "conflict": {
+ "doctrine/dbal": "<4.0.0 || >=5.0.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^4.0.0",
+ "nesbot/carbon": "^2.71.0 || ^3.0.0",
+ "phpunit/phpunit": "^10.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "KyleKatarn",
+ "email": "kylekatarnls@gmail.com"
+ }
+ ],
+ "description": "Types to use Carbon in Doctrine",
+ "keywords": [
+ "carbon",
+ "date",
+ "datetime",
+ "doctrine",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+ "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-09T16:56:22+00:00"
+ },
+ {
+ "name": "dflydev/dot-access-data",
+ "version": "v3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+ "scrutinizer/ocular": "1.6.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dflydev\\DotAccessData\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Dragonfly Development Inc.",
+ "email": "info@dflydev.com",
+ "homepage": "http://dflydev.com"
+ },
+ {
+ "name": "Beau Simensen",
+ "email": "beau@dflydev.com",
+ "homepage": "http://beausimensen.com"
+ },
+ {
+ "name": "Carlos Frutos",
+ "email": "carlos@kiwing.it",
+ "homepage": "https://github.com/cfrutos"
+ },
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com"
+ }
+ ],
+ "description": "Given a deep data structure, access data by dot notation.",
+ "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+ "keywords": [
+ "access",
+ "data",
+ "dot",
+ "notation"
+ ],
+ "support": {
+ "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
+ "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3"
+ },
+ "time": "2024-07-08T12:26:09+00:00"
+ },
+ {
+ "name": "doctrine/inflector",
+ "version": "2.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/inflector.git",
+ "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc",
+ "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^11.0",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.3",
+ "phpunit/phpunit": "^8.5 || ^9.5",
+ "vimeo/psalm": "^4.25 || ^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "keywords": [
+ "inflection",
+ "inflector",
+ "lowercase",
+ "manipulation",
+ "php",
+ "plural",
+ "singular",
+ "strings",
+ "uppercase",
+ "words"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.0.10"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-18T20:23:39+00:00"
+ },
+ {
+ "name": "doctrine/lexer",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^10.5",
+ "psalm/plugin-phpunit": "^0.18.3",
+ "vimeo/psalm": "^5.21"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Lexer\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+ "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "lexer",
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/lexer/issues",
+ "source": "https://github.com/doctrine/lexer/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-05T11:56:58+00:00"
+ },
+ {
+ "name": "dragonmantank/cron-expression",
+ "version": "v3.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dragonmantank/cron-expression.git",
+ "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
+ "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0",
+ "webmozart/assert": "^1.0"
+ },
+ "replace": {
+ "mtdowling/cron-expression": "^1.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-webmozart-assert": "^1.0",
+ "phpunit/phpunit": "^7.0|^8.0|^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Cron\\": "src/Cron/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Tankersley",
+ "email": "chris@ctankersley.com",
+ "homepage": "https://github.com/dragonmantank"
+ }
+ ],
+ "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
+ "keywords": [
+ "cron",
+ "schedule"
+ ],
+ "support": {
+ "issues": "https://github.com/dragonmantank/cron-expression/issues",
+ "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/dragonmantank",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-10T19:36:49+00:00"
+ },
+ {
+ "name": "egulias/email-validator",
+ "version": "4.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/egulias/EmailValidator.git",
+ "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e",
+ "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "^2.0 || ^3.0",
+ "php": ">=8.1",
+ "symfony/polyfill-intl-idn": "^1.26"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.2",
+ "vimeo/psalm": "^5.12"
+ },
+ "suggest": {
+ "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Egulias\\EmailValidator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eduardo Gulias Davis"
+ }
+ ],
+ "description": "A library for validating emails against several RFCs",
+ "homepage": "https://github.com/egulias/EmailValidator",
+ "keywords": [
+ "email",
+ "emailvalidation",
+ "emailvalidator",
+ "validation",
+ "validator"
+ ],
+ "support": {
+ "issues": "https://github.com/egulias/EmailValidator/issues",
+ "source": "https://github.com/egulias/EmailValidator/tree/4.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/egulias",
+ "type": "github"
+ }
+ ],
+ "time": "2023-10-06T06:47:41+00:00"
+ },
+ {
+ "name": "elastic/transport",
+ "version": "v8.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/elastic/elastic-transport-php.git",
+ "reference": "8be37d679637545e50b1cea9f8ee903888783021"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/elastic/elastic-transport-php/zipball/8be37d679637545e50b1cea9f8ee903888783021",
+ "reference": "8be37d679637545e50b1cea9f8ee903888783021",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.0",
+ "open-telemetry/api": "^1.0",
+ "php": "^7.4 || ^8.0",
+ "php-http/discovery": "^1.14",
+ "php-http/httplug": "^2.3",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.0 || ^2.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "nyholm/psr7": "^1.5",
+ "open-telemetry/sdk": "^1.0",
+ "php-http/mock-client": "^1.5",
+ "phpstan/phpstan": "^1.4",
+ "phpunit/phpunit": "^9.5",
+ "symfony/http-client": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Elastic\\Transport\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "HTTP transport PHP library for Elastic products",
+ "keywords": [
+ "PSR_17",
+ "elastic",
+ "http",
+ "psr-18",
+ "psr-7",
+ "transport"
+ ],
+ "support": {
+ "issues": "https://github.com/elastic/elastic-transport-php/issues",
+ "source": "https://github.com/elastic/elastic-transport-php/tree/v8.10.0"
+ },
+ "time": "2024-08-14T08:55:07+00:00"
+ },
+ {
+ "name": "elasticsearch/elasticsearch",
+ "version": "v8.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/elastic/elasticsearch-php.git",
+ "reference": "34c2444fa8d4c3e6c8b009bd8dea90bca007203b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/34c2444fa8d4c3e6c8b009bd8dea90bca007203b",
+ "reference": "34c2444fa8d4c3e6c8b009bd8dea90bca007203b",
+ "shasum": ""
+ },
+ "require": {
+ "elastic/transport": "^8.10",
+ "guzzlehttp/guzzle": "^7.0",
+ "php": "^7.4 || ^8.0",
+ "psr/http-client": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "psr/log": "^1|^2|^3"
+ },
+ "require-dev": {
+ "ext-yaml": "*",
+ "ext-zip": "*",
+ "mockery/mockery": "^1.5",
+ "nyholm/psr7": "^1.5",
+ "php-http/message-factory": "^1.0",
+ "php-http/mock-client": "^1.5",
+ "phpstan/phpstan": "^1.4",
+ "phpunit/phpunit": "^9.5",
+ "psr/http-factory": "^1.0",
+ "symfony/finder": "~4.0",
+ "symfony/http-client": "^5.0|^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Elastic\\Elasticsearch\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHP Client for Elasticsearch",
+ "keywords": [
+ "client",
+ "elastic",
+ "elasticsearch",
+ "search"
+ ],
+ "support": {
+ "issues": "https://github.com/elastic/elasticsearch-php/issues",
+ "source": "https://github.com/elastic/elasticsearch-php/tree/v8.15.0"
+ },
+ "time": "2024-08-14T14:32:50+00:00"
+ },
+ {
+ "name": "fruitcake/php-cors",
+ "version": "v1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fruitcake/php-cors.git",
+ "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b",
+ "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0",
+ "symfony/http-foundation": "^4.4|^5.4|^6|^7"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.4",
+ "phpunit/phpunit": "^9",
+ "squizlabs/php_codesniffer": "^3.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Fruitcake\\Cors\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fruitcake",
+ "homepage": "https://fruitcake.nl"
+ },
+ {
+ "name": "Barryvdh",
+ "email": "barryvdh@gmail.com"
+ }
+ ],
+ "description": "Cross-origin resource sharing library for the Symfony HttpFoundation",
+ "homepage": "https://github.com/fruitcake/php-cors",
+ "keywords": [
+ "cors",
+ "laravel",
+ "symfony"
+ ],
+ "support": {
+ "issues": "https://github.com/fruitcake/php-cors/issues",
+ "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://fruitcake.nl",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/barryvdh",
+ "type": "github"
+ }
+ ],
+ "time": "2023-10-12T05:21:21+00:00"
+ },
+ {
+ "name": "graham-campbell/result-type",
+ "version": "v1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/GrahamCampbell/Result-Type.git",
+ "reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
+ "reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "GrahamCampbell\\ResultType\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "An Implementation Of The Result Type",
+ "keywords": [
+ "Graham Campbell",
+ "GrahamCampbell",
+ "Result Type",
+ "Result-Type",
+ "result"
+ ],
+ "support": {
+ "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-20T21:45:45+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.9.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "d281ed313b989f213357e3be1a179f02196ac99b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
+ "reference": "d281ed313b989f213357e3be1a179f02196ac99b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+ "guzzlehttp/psr7": "^2.7.0",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-curl": "*",
+ "guzzle/client-integration-tests": "3.0.2",
+ "php-http/message-factory": "^1.1",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.9.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-24T11:22:20+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8",
+ "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-18T10:29:17+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "2.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+ "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-18T11:15:46+00:00"
+ },
+ {
+ "name": "guzzlehttp/uri-template",
+ "version": "v1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/uri-template.git",
+ "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c",
+ "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "symfony/polyfill-php80": "^1.24"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.36 || ^9.6.15",
+ "uri-template/tests": "1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\UriTemplate\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ }
+ ],
+ "description": "A polyfill class for uri_template of PHP",
+ "keywords": [
+ "guzzlehttp",
+ "uri-template"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/uri-template/issues",
+ "source": "https://github.com/guzzle/uri-template/tree/v1.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-12-03T19:50:20+00:00"
+ },
+ {
+ "name": "laravel/framework",
+ "version": "v11.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/framework.git",
+ "reference": "868c75beacc47d0f361b919bbc155c0b619bf3d5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/868c75beacc47d0f361b919bbc155c0b619bf3d5",
+ "reference": "868c75beacc47d0f361b919bbc155c0b619bf3d5",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12",
+ "composer-runtime-api": "^2.2",
+ "doctrine/inflector": "^2.0.5",
+ "dragonmantank/cron-expression": "^3.3.2",
+ "egulias/email-validator": "^3.2.1|^4.0",
+ "ext-ctype": "*",
+ "ext-filter": "*",
+ "ext-hash": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-session": "*",
+ "ext-tokenizer": "*",
+ "fruitcake/php-cors": "^1.3",
+ "guzzlehttp/guzzle": "^7.8",
+ "guzzlehttp/uri-template": "^1.0",
+ "laravel/prompts": "^0.1.18",
+ "laravel/serializable-closure": "^1.3",
+ "league/commonmark": "^2.2.1",
+ "league/flysystem": "^3.8.0",
+ "monolog/monolog": "^3.0",
+ "nesbot/carbon": "^2.72.2|^3.0",
+ "nunomaduro/termwind": "^2.0",
+ "php": "^8.2",
+ "psr/container": "^1.1.1|^2.0.1",
+ "psr/log": "^1.0|^2.0|^3.0",
+ "psr/simple-cache": "^1.0|^2.0|^3.0",
+ "ramsey/uuid": "^4.7",
+ "symfony/console": "^7.0",
+ "symfony/error-handler": "^7.0",
+ "symfony/finder": "^7.0",
+ "symfony/http-foundation": "^7.0",
+ "symfony/http-kernel": "^7.0",
+ "symfony/mailer": "^7.0",
+ "symfony/mime": "^7.0",
+ "symfony/polyfill-php83": "^1.28",
+ "symfony/process": "^7.0",
+ "symfony/routing": "^7.0",
+ "symfony/uid": "^7.0",
+ "symfony/var-dumper": "^7.0",
+ "tijsverkoyen/css-to-inline-styles": "^2.2.5",
+ "vlucas/phpdotenv": "^5.4.1",
+ "voku/portable-ascii": "^2.0"
+ },
+ "conflict": {
+ "mockery/mockery": "1.6.8",
+ "tightenco/collect": "<5.5.33"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0",
+ "psr/log-implementation": "1.0|2.0|3.0",
+ "psr/simple-cache-implementation": "1.0|2.0|3.0"
+ },
+ "replace": {
+ "illuminate/auth": "self.version",
+ "illuminate/broadcasting": "self.version",
+ "illuminate/bus": "self.version",
+ "illuminate/cache": "self.version",
+ "illuminate/collections": "self.version",
+ "illuminate/conditionable": "self.version",
+ "illuminate/config": "self.version",
+ "illuminate/console": "self.version",
+ "illuminate/container": "self.version",
+ "illuminate/contracts": "self.version",
+ "illuminate/cookie": "self.version",
+ "illuminate/database": "self.version",
+ "illuminate/encryption": "self.version",
+ "illuminate/events": "self.version",
+ "illuminate/filesystem": "self.version",
+ "illuminate/hashing": "self.version",
+ "illuminate/http": "self.version",
+ "illuminate/log": "self.version",
+ "illuminate/macroable": "self.version",
+ "illuminate/mail": "self.version",
+ "illuminate/notifications": "self.version",
+ "illuminate/pagination": "self.version",
+ "illuminate/pipeline": "self.version",
+ "illuminate/process": "self.version",
+ "illuminate/queue": "self.version",
+ "illuminate/redis": "self.version",
+ "illuminate/routing": "self.version",
+ "illuminate/session": "self.version",
+ "illuminate/support": "self.version",
+ "illuminate/testing": "self.version",
+ "illuminate/translation": "self.version",
+ "illuminate/validation": "self.version",
+ "illuminate/view": "self.version",
+ "spatie/once": "*"
+ },
+ "require-dev": {
+ "ably/ably-php": "^1.0",
+ "aws/aws-sdk-php": "^3.235.5",
+ "ext-gmp": "*",
+ "fakerphp/faker": "^1.23",
+ "league/flysystem-aws-s3-v3": "^3.0",
+ "league/flysystem-ftp": "^3.0",
+ "league/flysystem-path-prefixing": "^3.3",
+ "league/flysystem-read-only": "^3.3",
+ "league/flysystem-sftp-v3": "^3.0",
+ "mockery/mockery": "^1.6",
+ "nyholm/psr7": "^1.2",
+ "orchestra/testbench-core": "^9.1.5",
+ "pda/pheanstalk": "^5.0",
+ "phpstan/phpstan": "^1.11.5",
+ "phpunit/phpunit": "^10.5|^11.0",
+ "predis/predis": "^2.0.2",
+ "resend/resend-php": "^0.10.0",
+ "symfony/cache": "^7.0",
+ "symfony/http-client": "^7.0",
+ "symfony/psr-http-message-bridge": "^7.0"
+ },
+ "suggest": {
+ "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
+ "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).",
+ "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).",
+ "ext-apcu": "Required to use the APC cache driver.",
+ "ext-fileinfo": "Required to use the Filesystem class.",
+ "ext-ftp": "Required to use the Flysystem FTP driver.",
+ "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+ "ext-memcached": "Required to use the memcache cache driver.",
+ "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.",
+ "ext-pdo": "Required to use all database features.",
+ "ext-posix": "Required to use all features of the queue worker.",
+ "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).",
+ "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+ "filp/whoops": "Required for friendly error pages in development (^2.14.3).",
+ "laravel/tinker": "Required to use the tinker console command (^2.0).",
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).",
+ "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).",
+ "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).",
+ "league/flysystem-read-only": "Required to use read-only disks (^3.3)",
+ "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).",
+ "mockery/mockery": "Required to use mocking (^1.6).",
+ "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
+ "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).",
+ "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).",
+ "predis/predis": "Required to use the predis connector (^2.0.2).",
+ "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).",
+ "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).",
+ "symfony/cache": "Required to PSR-6 cache bridge (^7.0).",
+ "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).",
+ "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.0).",
+ "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.0).",
+ "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.0).",
+ "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.0)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "11.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Illuminate/Collections/helpers.php",
+ "src/Illuminate/Events/functions.php",
+ "src/Illuminate/Filesystem/functions.php",
+ "src/Illuminate/Foundation/helpers.php",
+ "src/Illuminate/Support/helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\": "src/Illuminate/",
+ "Illuminate\\Support\\": [
+ "src/Illuminate/Macroable/",
+ "src/Illuminate/Collections/",
+ "src/Illuminate/Conditionable/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Laravel Framework.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "framework",
+ "laravel"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2024-09-03T15:27:15+00:00"
+ },
+ {
+ "name": "laravel/prompts",
+ "version": "v0.1.25",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/prompts.git",
+ "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95",
+ "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "illuminate/collections": "^10.0|^11.0",
+ "php": "^8.1",
+ "symfony/console": "^6.2|^7.0"
+ },
+ "conflict": {
+ "illuminate/console": ">=10.17.0 <10.25.0",
+ "laravel/framework": ">=10.17.0 <10.25.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.5",
+ "pestphp/pest": "^2.3",
+ "phpstan/phpstan": "^1.11",
+ "phpstan/phpstan-mockery": "^1.1"
+ },
+ "suggest": {
+ "ext-pcntl": "Required for the spinner to be animated."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "0.1.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Laravel\\Prompts\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Add beautiful and user-friendly forms to your command-line applications.",
+ "support": {
+ "issues": "https://github.com/laravel/prompts/issues",
+ "source": "https://github.com/laravel/prompts/tree/v0.1.25"
+ },
+ "time": "2024-08-12T22:06:33+00:00"
+ },
+ {
+ "name": "laravel/serializable-closure",
+ "version": "v1.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/serializable-closure.git",
+ "reference": "61b87392d986dc49ad5ef64e75b1ff5fee24ef81"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/61b87392d986dc49ad5ef64e75b1ff5fee24ef81",
+ "reference": "61b87392d986dc49ad5ef64e75b1ff5fee24ef81",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3|^8.0"
+ },
+ "require-dev": {
+ "illuminate/support": "^8.0|^9.0|^10.0|^11.0",
+ "nesbot/carbon": "^2.61|^3.0",
+ "pestphp/pest": "^1.21.3",
+ "phpstan/phpstan": "^1.8.2",
+ "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\SerializableClosure\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "nuno@laravel.com"
+ }
+ ],
+ "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
+ "keywords": [
+ "closure",
+ "laravel",
+ "serializable"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/serializable-closure/issues",
+ "source": "https://github.com/laravel/serializable-closure"
+ },
+ "time": "2024-08-02T07:48:17+00:00"
+ },
+ {
+ "name": "league/commonmark",
+ "version": "2.5.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/commonmark.git",
+ "reference": "b650144166dfa7703e62a22e493b853b58d874b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b650144166dfa7703e62a22e493b853b58d874b0",
+ "reference": "b650144166dfa7703e62a22e493b853b58d874b0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "league/config": "^1.1.1",
+ "php": "^7.4 || ^8.0",
+ "psr/event-dispatcher": "^1.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "cebe/markdown": "^1.0",
+ "commonmark/cmark": "0.31.1",
+ "commonmark/commonmark.js": "0.31.1",
+ "composer/package-versions-deprecated": "^1.8",
+ "embed/embed": "^4.4",
+ "erusev/parsedown": "^1.0",
+ "ext-json": "*",
+ "github/gfm": "0.29.0",
+ "michelf/php-markdown": "^1.4 || ^2.0",
+ "nyholm/psr7": "^1.5",
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0",
+ "scrutinizer/ocular": "^1.8.1",
+ "symfony/finder": "^5.3 | ^6.0 || ^7.0",
+ "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0",
+ "unleashedtech/php-coding-standard": "^3.1.1",
+ "vimeo/psalm": "^4.24.0 || ^5.0.0"
+ },
+ "suggest": {
+ "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\CommonMark\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+ "homepage": "https://commonmark.thephpleague.com",
+ "keywords": [
+ "commonmark",
+ "flavored",
+ "gfm",
+ "github",
+ "github-flavored",
+ "markdown",
+ "md",
+ "parser"
+ ],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "forum": "https://github.com/thephpleague/commonmark/discussions",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-16T11:46:16+00:00"
+ },
+ {
+ "name": "league/config",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/config.git",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "shasum": ""
+ },
+ "require": {
+ "dflydev/dot-access-data": "^3.0.1",
+ "nette/schema": "^1.2",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.5",
+ "scrutinizer/ocular": "^1.8.1",
+ "unleashedtech/php-coding-standard": "^3.1",
+ "vimeo/psalm": "^4.7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Config\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Define configuration arrays with strict schemas and access values with dot notation",
+ "homepage": "https://config.thephpleague.com",
+ "keywords": [
+ "array",
+ "config",
+ "configuration",
+ "dot",
+ "dot-access",
+ "nested",
+ "schema"
+ ],
+ "support": {
+ "docs": "https://config.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/config/issues",
+ "rss": "https://github.com/thephpleague/config/releases.atom",
+ "source": "https://github.com/thephpleague/config"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-11T20:36:23+00:00"
+ },
+ {
+ "name": "league/flysystem",
+ "version": "3.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem.git",
+ "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c",
+ "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c",
+ "shasum": ""
+ },
+ "require": {
+ "league/flysystem-local": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "conflict": {
+ "async-aws/core": "<1.19.0",
+ "async-aws/s3": "<1.14.0",
+ "aws/aws-sdk-php": "3.209.31 || 3.210.0",
+ "guzzlehttp/guzzle": "<7.0",
+ "guzzlehttp/ringphp": "<1.1.1",
+ "phpseclib/phpseclib": "3.0.15",
+ "symfony/http-client": "<5.2"
+ },
+ "require-dev": {
+ "async-aws/s3": "^1.5 || ^2.0",
+ "async-aws/simple-s3": "^1.1 || ^2.0",
+ "aws/aws-sdk-php": "^3.295.10",
+ "composer/semver": "^3.0",
+ "ext-fileinfo": "*",
+ "ext-ftp": "*",
+ "ext-mongodb": "^1.3",
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.5",
+ "google/cloud-storage": "^1.23",
+ "guzzlehttp/psr7": "^2.6",
+ "microsoft/azure-storage-blob": "^1.1",
+ "mongodb/mongodb": "^1.2",
+ "phpseclib/phpseclib": "^3.0.36",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^9.5.11|^10.0",
+ "sabre/dav": "^4.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "File storage abstraction for PHP",
+ "keywords": [
+ "WebDAV",
+ "aws",
+ "cloud",
+ "file",
+ "files",
+ "filesystem",
+ "filesystems",
+ "ftp",
+ "s3",
+ "sftp",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/3.28.0"
+ },
+ "time": "2024-05-22T10:09:12+00:00"
+ },
+ {
+ "name": "league/flysystem-local",
+ "version": "3.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-local.git",
+ "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/13f22ea8be526ea58c2ddff9e158ef7c296e4f40",
+ "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "league/flysystem": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\Local\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Local filesystem adapter for Flysystem.",
+ "keywords": [
+ "Flysystem",
+ "file",
+ "files",
+ "filesystem",
+ "local"
+ ],
+ "support": {
+ "source": "https://github.com/thephpleague/flysystem-local/tree/3.28.0"
+ },
+ "time": "2024-05-06T20:05:52+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301",
+ "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.15.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-01-28T23:22:08+00:00"
+ },
+ {
+ "name": "monolog/monolog",
+ "version": "3.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8",
+ "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/log": "^2.0 || ^3.0"
+ },
+ "provide": {
+ "psr/log-implementation": "3.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "elasticsearch/elasticsearch": "^7 || ^8",
+ "ext-json": "*",
+ "graylog2/gelf-php": "^1.4.2 || ^2.0",
+ "guzzlehttp/guzzle": "^7.4.5",
+ "guzzlehttp/psr7": "^2.2",
+ "mongodb/mongodb": "^1.8",
+ "php-amqplib/php-amqplib": "~2.4 || ^3",
+ "phpstan/phpstan": "^1.9",
+ "phpstan/phpstan-deprecation-rules": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.4",
+ "phpunit/phpunit": "^10.5.17",
+ "predis/predis": "^1.1 || ^2",
+ "ruflin/elastica": "^7",
+ "symfony/mailer": "^5.4 || ^6",
+ "symfony/mime": "^5.4 || ^6"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "ext-openssl": "Required to send log messages using SSL",
+ "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "https://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/3.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-28T09:40:51+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "3.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbd3eef89af8ba66a3aa7952b5439168fbcc529f",
+ "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f",
+ "shasum": ""
+ },
+ "require": {
+ "carbonphp/carbon-doctrine-types": "*",
+ "ext-json": "*",
+ "php": "^8.1",
+ "psr/clock": "^1.0",
+ "symfony/clock": "^6.3 || ^7.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6.3 || ^4.0",
+ "doctrine/orm": "^2.15.2 || ^3.0",
+ "friendsofphp/php-cs-fixer": "^3.57.2",
+ "kylekatarnls/multi-tester": "^2.5.3",
+ "ondrejmirtes/better-reflection": "^6.25.0.4",
+ "phpmd/phpmd": "^2.15.0",
+ "phpstan/extension-installer": "^1.3.1",
+ "phpstan/phpstan": "^1.11.2",
+ "phpunit/phpunit": "^10.5.20",
+ "squizlabs/php_codesniffer": "^3.9.0"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev",
+ "dev-2.x": "2.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "docs": "https://carbon.nesbot.com/docs",
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon#sponsor",
+ "type": "opencollective"
+ },
+ {
+ "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-19T06:22:39+00:00"
+ },
+ {
+ "name": "nette/schema",
+ "version": "v1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/schema.git",
+ "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188",
+ "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188",
+ "shasum": ""
+ },
+ "require": {
+ "nette/utils": "^4.0",
+ "php": "8.1 - 8.3"
+ },
+ "require-dev": {
+ "nette/tester": "^2.4",
+ "phpstan/phpstan-nette": "^1.0",
+ "tracy/tracy": "^2.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "📐 Nette Schema: validating data structures against a given Schema.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "config",
+ "nette"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/schema/issues",
+ "source": "https://github.com/nette/schema/tree/v1.3.0"
+ },
+ "time": "2023-12-11T11:54:22+00:00"
+ },
+ {
+ "name": "nette/utils",
+ "version": "v4.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/utils.git",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "shasum": ""
+ },
+ "require": {
+ "php": "8.0 - 8.4"
+ },
+ "conflict": {
+ "nette/finder": "<3",
+ "nette/schema": "<1.2.2"
+ },
+ "require-dev": {
+ "jetbrains/phpstorm-attributes": "dev-master",
+ "nette/tester": "^2.5",
+ "phpstan/phpstan": "^1.0",
+ "tracy/tracy": "^2.9"
+ },
+ "suggest": {
+ "ext-gd": "to use Image",
+ "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+ "ext-json": "to use Nette\\Utils\\Json",
+ "ext-mbstring": "to use Strings::lower() etc...",
+ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "array",
+ "core",
+ "datetime",
+ "images",
+ "json",
+ "nette",
+ "paginator",
+ "password",
+ "slugify",
+ "string",
+ "unicode",
+ "utf-8",
+ "utility",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/utils/issues",
+ "source": "https://github.com/nette/utils/tree/v4.0.5"
+ },
+ "time": "2024-08-07T15:39:19+00:00"
+ },
+ {
+ "name": "nunomaduro/termwind",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nunomaduro/termwind.git",
+ "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/58c4c58cf23df7f498daeb97092e34f5259feb6a",
+ "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": "^8.2",
+ "symfony/console": "^7.0.4"
+ },
+ "require-dev": {
+ "ergebnis/phpstan-rules": "^2.2.0",
+ "illuminate/console": "^11.0.0",
+ "laravel/pint": "^1.14.0",
+ "mockery/mockery": "^1.6.7",
+ "pestphp/pest": "^2.34.1",
+ "phpstan/phpstan": "^1.10.59",
+ "phpstan/phpstan-strict-rules": "^1.5.2",
+ "symfony/var-dumper": "^7.0.4",
+ "thecodingmachine/phpstan-strict-rules": "^1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Termwind\\Laravel\\TermwindServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php"
+ ],
+ "psr-4": {
+ "Termwind\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Its like Tailwind CSS, but for the console.",
+ "keywords": [
+ "cli",
+ "console",
+ "css",
+ "package",
+ "php",
+ "style"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/termwind/issues",
+ "source": "https://github.com/nunomaduro/termwind/tree/v2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/xiCO2k",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-06T16:17:14+00:00"
+ },
+ {
+ "name": "open-telemetry/api",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opentelemetry-php/api.git",
+ "reference": "87de95d926f46262885d0d390060c095af13e2e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/87de95d926f46262885d0d390060c095af13e2e5",
+ "reference": "87de95d926f46262885d0d390060c095af13e2e5",
+ "shasum": ""
+ },
+ "require": {
+ "open-telemetry/context": "^1.0",
+ "php": "^7.4 || ^8.0",
+ "psr/log": "^1.1|^2.0|^3.0",
+ "symfony/polyfill-php80": "^1.26",
+ "symfony/polyfill-php81": "^1.26",
+ "symfony/polyfill-php82": "^1.26"
+ },
+ "conflict": {
+ "open-telemetry/sdk": "<=1.0.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "Trace/functions.php"
+ ],
+ "psr-4": {
+ "OpenTelemetry\\API\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "opentelemetry-php contributors",
+ "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
+ }
+ ],
+ "description": "API for OpenTelemetry PHP.",
+ "keywords": [
+ "Metrics",
+ "api",
+ "apm",
+ "logging",
+ "opentelemetry",
+ "otel",
+ "tracing"
+ ],
+ "support": {
+ "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
+ "docs": "https://opentelemetry.io/docs/php",
+ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
+ "source": "https://github.com/open-telemetry/opentelemetry-php"
+ },
+ "time": "2024-02-06T01:32:25+00:00"
+ },
+ {
+ "name": "open-telemetry/context",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opentelemetry-php/context.git",
+ "reference": "e9d254a7c89885e63fd2fde54e31e81aaaf52b7c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/e9d254a7c89885e63fd2fde54e31e81aaaf52b7c",
+ "reference": "e9d254a7c89885e63fd2fde54e31e81aaaf52b7c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "symfony/polyfill-php80": "^1.26",
+ "symfony/polyfill-php81": "^1.26",
+ "symfony/polyfill-php82": "^1.26"
+ },
+ "suggest": {
+ "ext-ffi": "To allow context switching in Fibers"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "fiber/initialize_fiber_handler.php"
+ ],
+ "psr-4": {
+ "OpenTelemetry\\Context\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "opentelemetry-php contributors",
+ "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
+ }
+ ],
+ "description": "Context implementation for OpenTelemetry PHP.",
+ "keywords": [
+ "Context",
+ "opentelemetry",
+ "otel"
+ ],
+ "support": {
+ "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
+ "docs": "https://opentelemetry.io/docs/php",
+ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
+ "source": "https://github.com/open-telemetry/opentelemetry-php"
+ },
+ "time": "2024-01-13T05:50:44+00:00"
+ },
+ {
+ "name": "php-http/discovery",
+ "version": "1.19.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/discovery.git",
+ "reference": "0700efda8d7526335132360167315fdab3aeb599"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/0700efda8d7526335132360167315fdab3aeb599",
+ "reference": "0700efda8d7526335132360167315fdab3aeb599",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0|^2.0",
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "nyholm/psr7": "<1.0",
+ "zendframework/zend-diactoros": "*"
+ },
+ "provide": {
+ "php-http/async-client-implementation": "*",
+ "php-http/client-implementation": "*",
+ "psr/http-client-implementation": "*",
+ "psr/http-factory-implementation": "*",
+ "psr/http-message-implementation": "*"
+ },
+ "require-dev": {
+ "composer/composer": "^1.0.2|^2.0",
+ "graham-campbell/phpspec-skip-example-extension": "^5.0",
+ "php-http/httplug": "^1.0 || ^2.0",
+ "php-http/message-factory": "^1.0",
+ "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
+ "sebastian/comparator": "^3.0.5 || ^4.0.8",
+ "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Http\\Discovery\\Composer\\Plugin",
+ "plugin-optional": true
+ },
+ "autoload": {
+ "psr-4": {
+ "Http\\Discovery\\": "src/"
+ },
+ "exclude-from-classmap": [
+ "src/Composer/Plugin.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations",
+ "homepage": "http://php-http.org",
+ "keywords": [
+ "adapter",
+ "client",
+ "discovery",
+ "factory",
+ "http",
+ "message",
+ "psr17",
+ "psr7"
+ ],
+ "support": {
+ "issues": "https://github.com/php-http/discovery/issues",
+ "source": "https://github.com/php-http/discovery/tree/1.19.4"
+ },
+ "time": "2024-03-29T13:00:05+00:00"
+ },
+ {
+ "name": "php-http/httplug",
+ "version": "2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/httplug.git",
+ "reference": "625ad742c360c8ac580fcc647a1541d29e257f67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67",
+ "reference": "625ad742c360c8ac580fcc647a1541d29e257f67",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0",
+ "php-http/promise": "^1.1",
+ "psr/http-client": "^1.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "require-dev": {
+ "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0",
+ "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eric GELOEN",
+ "email": "geloen.eric@gmail.com"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "HTTPlug, the HTTP client abstraction for PHP",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "client",
+ "http"
+ ],
+ "support": {
+ "issues": "https://github.com/php-http/httplug/issues",
+ "source": "https://github.com/php-http/httplug/tree/2.4.0"
+ },
+ "time": "2023-04-14T15:10:03+00:00"
+ },
+ {
+ "name": "php-http/promise",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-http/promise.git",
+ "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
+ "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3",
+ "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Http\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Joel Wurtz",
+ "email": "joel.wurtz@gmail.com"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com"
+ }
+ ],
+ "description": "Promise used for asynchronous HTTP requests",
+ "homepage": "http://httplug.io",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/php-http/promise/issues",
+ "source": "https://github.com/php-http/promise/tree/1.3.1"
+ },
+ "time": "2024-03-15T13:55:21+00:00"
+ },
+ {
+ "name": "phpoption/phpoption",
+ "version": "1.9.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/schmittjoh/php-option.git",
+ "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
+ "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpOption\\": "src/PhpOption/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Johannes M. Schmitt",
+ "email": "schmittjoh@gmail.com",
+ "homepage": "https://github.com/schmittjoh"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "Option Type for PHP",
+ "keywords": [
+ "language",
+ "option",
+ "php",
+ "type"
+ ],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-20T21:41:07+00:00"
+ },
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "79dff0b268932c640297f5208d6298f71855c03e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e",
+ "reference": "79dff0b268932c640297f5208d6298f71855c03e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.1"
+ },
+ "time": "2024-08-21T13:31:24+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "ramsey/collection",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/collection.git",
+ "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
+ "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "captainhook/plugin-composer": "^5.3",
+ "ergebnis/composer-normalize": "^2.28.3",
+ "fakerphp/faker": "^1.21",
+ "hamcrest/hamcrest-php": "^2.0",
+ "jangregor/phpstan-prophecy": "^1.0",
+ "mockery/mockery": "^1.5",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3",
+ "phpcsstandards/phpcsutils": "^1.0.0-rc1",
+ "phpspec/prophecy-phpunit": "^2.0",
+ "phpstan/extension-installer": "^1.2",
+ "phpstan/phpstan": "^1.9",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.3",
+ "phpunit/phpunit": "^9.5",
+ "psalm/plugin-mockery": "^1.1",
+ "psalm/plugin-phpunit": "^0.18.4",
+ "ramsey/coding-standard": "^2.0.3",
+ "ramsey/conventional-commits": "^1.3",
+ "vimeo/psalm": "^5.4"
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ },
+ "ramsey/conventional-commits": {
+ "configFile": "conventional-commits.json"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Ramsey\\Collection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Ramsey",
+ "email": "ben@benramsey.com",
+ "homepage": "https://benramsey.com"
+ }
+ ],
+ "description": "A PHP library for representing and manipulating collections.",
+ "keywords": [
+ "array",
+ "collection",
+ "hash",
+ "map",
+ "queue",
+ "set"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/collection/issues",
+ "source": "https://github.com/ramsey/collection/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ramsey",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-31T21:50:55+00:00"
+ },
+ {
+ "name": "ramsey/uuid",
+ "version": "4.7.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/uuid.git",
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
+ "ext-json": "*",
+ "php": "^8.0",
+ "ramsey/collection": "^1.2 || ^2.0"
+ },
+ "replace": {
+ "rhumsaa/uuid": "self.version"
+ },
+ "require-dev": {
+ "captainhook/captainhook": "^5.10",
+ "captainhook/plugin-composer": "^5.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "doctrine/annotations": "^1.8",
+ "ergebnis/composer-normalize": "^2.15",
+ "mockery/mockery": "^1.3",
+ "paragonie/random-lib": "^2",
+ "php-mock/php-mock": "^2.2",
+ "php-mock/php-mock-mockery": "^1.3",
+ "php-parallel-lint/php-parallel-lint": "^1.1",
+ "phpbench/phpbench": "^1.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9",
+ "ramsey/composer-repl": "^1.4",
+ "slevomat/coding-standard": "^8.4",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.9"
+ },
+ "suggest": {
+ "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+ "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+ "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+ "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Ramsey\\Uuid\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+ "keywords": [
+ "guid",
+ "identifier",
+ "uuid"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "source": "https://github.com/ramsey/uuid/tree/4.7.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ramsey",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-27T21:32:50+00:00"
+ },
+ {
+ "name": "symfony/clock",
+ "version": "v7.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/clock.git",
+ "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7",
+ "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/clock": "^1.0",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/now.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Clock\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Decouples applications from the system clock",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clock",
+ "psr20",
+ "time"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/clock/tree/v7.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T14:57:53+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/1eed7af6961d763e7832e874d7f9b21c3ea9c111",
+ "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^6.4|^7.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/stopwatch": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-15T22:48:53+00:00"
+ },
+ {
+ "name": "symfony/css-selector",
+ "version": "v7.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4",
+ "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Converts CSS selectors to XPath expressions",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v7.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T14:57:53+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-18T09:32:20+00:00"
+ },
+ {
+ "name": "symfony/error-handler",
+ "version": "v7.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/error-handler.git",
+ "reference": "432bb369952795c61ca1def65e078c4a80dad13c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/432bb369952795c61ca1def65e078c4a80dad13c",
+ "reference": "432bb369952795c61ca1def65e078c4a80dad13c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/var-dumper": "^6.4|^7.0"
+ },
+ "conflict": {
+ "symfony/deprecation-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4"
+ },
+ "require-dev": {
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/serializer": "^6.4|^7.0"
+ },
+ "bin": [
+ "Resources/bin/patch-type-declarations"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\ErrorHandler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to manage errors and ease debugging PHP code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/error-handler/tree/v7.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-26T13:02:51+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v7.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7",
+ "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/event-dispatcher-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/service-contracts": "<2.5"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/stopwatch": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T14:57:53+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50",
+ "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-18T09:32:20+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "d95bbf319f7d052082fb7af147e0f835a695e823"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823",
+ "reference": "d95bbf319f7d052082fb7af147e0f835a695e823",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-13T14:28:19+00:00"
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v7.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f602d5c17d1fa02f8019ace2687d9d136b7f4a1a",
+ "reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php83": "^1.27"
+ },
+ "conflict": {
+ "doctrine/dbal": "<3.6",
+ "symfony/cache": "<6.4"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6|^4",
+ "predis/predis": "^1.1|^2.0",
+ "symfony/cache": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/mime": "^6.4|^7.0",
+ "symfony/rate-limiter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Defines an object-oriented layer for the HTTP specification",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v7.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-26T12:41:01+00:00"
+ },
+ {
+ "name": "symfony/http-kernel",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-kernel.git",
+ "reference": "6efcbd1b3f444f631c386504fc83eeca25963747"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6efcbd1b3f444f631c386504fc83eeca25963747",
+ "reference": "6efcbd1b3f444f631c386504fc83eeca25963747",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/browser-kit": "<6.4",
+ "symfony/cache": "<6.4",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/doctrine-bridge": "<6.4",
+ "symfony/form": "<6.4",
+ "symfony/http-client": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/mailer": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/translation": "<6.4",
+ "symfony/translation-contracts": "<2.5",
+ "symfony/twig-bridge": "<6.4",
+ "symfony/validator": "<6.4",
+ "symfony/var-dumper": "<6.4",
+ "twig/twig": "<3.0.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "symfony/browser-kit": "^6.4|^7.0",
+ "symfony/clock": "^6.4|^7.0",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/css-selector": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/dom-crawler": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/finder": "^6.4|^7.0",
+ "symfony/http-client-contracts": "^2.5|^3",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/property-access": "^7.1",
+ "symfony/routing": "^6.4|^7.0",
+ "symfony/serializer": "^7.1",
+ "symfony/stopwatch": "^6.4|^7.0",
+ "symfony/translation": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3",
+ "symfony/uid": "^6.4|^7.0",
+ "symfony/validator": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0",
+ "symfony/var-exporter": "^6.4|^7.0",
+ "twig/twig": "^3.0.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpKernel\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides a structured process for converting a Request into a Response",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-kernel/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-30T17:02:28+00:00"
+ },
+ {
+ "name": "symfony/mailer",
+ "version": "v7.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailer.git",
+ "reference": "8fcff0af9043c8f8a8e229437cea363e282f9aee"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/8fcff0af9043c8f8a8e229437cea363e282f9aee",
+ "reference": "8fcff0af9043c8f8a8e229437cea363e282f9aee",
+ "shasum": ""
+ },
+ "require": {
+ "egulias/email-validator": "^2.1.10|^3|^4",
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "psr/log": "^1|^2|^3",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/mime": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/mime": "<6.4",
+ "symfony/twig-bridge": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/twig-bridge": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps sending emails",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailer/tree/v7.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-28T08:00:31+00:00"
+ },
+ {
+ "name": "symfony/mime",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "ccaa6c2503db867f472a587291e764d6a1e58758"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/ccaa6c2503db867f472a587291e764d6a1e58758",
+ "reference": "ccaa6c2503db867f472a587291e764d6a1e58758",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "symfony/mailer": "<6.4",
+ "symfony/serializer": "<6.4.3|>7.0,<7.0.3"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1|^4",
+ "league/html-to-markdown": "^5.0",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/property-access": "^6.4|^7.0",
+ "symfony/property-info": "^6.4|^7.0",
+ "symfony/serializer": "^6.4.3|^7.0.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mime\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows manipulating MIME messages",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "mime",
+ "mime-type"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-13T14:28:19+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "0424dff1c58f028c451efff2045f5d92410bd540"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
+ "reference": "0424dff1c58f028c451efff2045f5d92410bd540",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T15:07:36+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a",
+ "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T15:07:36+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c",
+ "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "symfony/polyfill-intl-normalizer": "^1.10",
+ "symfony/polyfill-php72": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T15:07:36+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb",
+ "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T15:07:36+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
+ "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-19T12:30:46+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "10112722600777e02d2745716b70c5db4ca70442"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442",
+ "reference": "10112722600777e02d2745716b70c5db4ca70442",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-19T12:30:46+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
+ "reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T15:07:36+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php81",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php81.git",
+ "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af",
+ "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php81\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-19T12:30:46+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php82",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php82.git",
+ "reference": "77ff49780f56906788a88974867ed68bc49fae5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/77ff49780f56906788a88974867ed68bc49fae5b",
+ "reference": "77ff49780f56906788a88974867ed68bc49fae5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php82\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php82/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-19T12:30:46+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php83",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php83.git",
+ "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
+ "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php83\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-19T12:35:24+00:00"
+ },
+ {
+ "name": "symfony/polyfill-uuid",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-uuid.git",
+ "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/2ba1f33797470debcda07fe9dce20a0003df18e9",
+ "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-uuid": "*"
+ },
+ "suggest": {
+ "ext-uuid": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Uuid\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for uuid functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T15:07:36+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v7.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca",
+ "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v7.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-26T12:44:47+00:00"
+ },
+ {
+ "name": "symfony/routing",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/routing.git",
+ "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/1500aee0094a3ce1c92626ed8cf3c2037e86f5a7",
+ "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/config": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/yaml": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Routing\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Maps an HTTP request to a set of configuration variables",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "router",
+ "routing",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/routing/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-29T08:16:25+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
+ "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-18T09:32:20+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/6cd670a6d968eaeb1c77c2e76091c45c56bc367b",
+ "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.1",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-12T09:59:40+00:00"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v7.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/8d5e50c813ba2859a6dfc99a0765c550507934a1",
+ "reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/translation-contracts": "^2.5|^3.0"
+ },
+ "conflict": {
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/service-contracts": "<2.5",
+ "symfony/twig-bundle": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "provide": {
+ "symfony/translation-implementation": "2.3|3.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^4.18|^5.0",
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/finder": "^6.4|^7.0",
+ "symfony/http-client-contracts": "^2.5|^3.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/polyfill-intl-icu": "^1.21",
+ "symfony/routing": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/yaml": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to internationalize your application",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v7.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-26T12:41:01+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v3.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a",
+ "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-18T09:32:20+00:00"
+ },
+ {
+ "name": "symfony/uid",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/uid.git",
+ "reference": "82177535395109075cdb45a70533aa3d7a521cdf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/82177535395109075cdb45a70533aa3d7a521cdf",
+ "reference": "82177535395109075cdb45a70533aa3d7a521cdf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-uuid": "^1.15"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Uid\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to generate and represent UIDs",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "UID",
+ "ulid",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/uid/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-12T09:59:40+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "a5fa7481b199090964d6fd5dab6294d5a870c7aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a5fa7481b199090964d6fd5dab6294d5a870c7aa",
+ "reference": "a5fa7481b199090964d6fd5dab6294d5a870c7aa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/console": "<6.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/uid": "^6.4|^7.0",
+ "twig/twig": "^3.0.4"
+ },
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-30T16:12:47+00:00"
+ },
+ {
+ "name": "tijsverkoyen/css-to-inline-styles",
+ "version": "v2.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
+ "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb",
+ "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "php": "^5.5 || ^7.0 || ^8.0",
+ "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "TijsVerkoyen\\CssToInlineStyles\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Tijs Verkoyen",
+ "email": "css_to_inline_styles@verkoyen.eu",
+ "role": "Developer"
+ }
+ ],
+ "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
+ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
+ "support": {
+ "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+ "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7"
+ },
+ "time": "2023-12-08T13:03:43+00:00"
+ },
+ {
+ "name": "vlucas/phpdotenv",
+ "version": "v5.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2",
+ "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pcre": "*",
+ "graham-campbell/result-type": "^1.1.3",
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.3",
+ "symfony/polyfill-ctype": "^1.24",
+ "symfony/polyfill-mbstring": "^1.24",
+ "symfony/polyfill-php80": "^1.24"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-filter": "*",
+ "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+ },
+ "suggest": {
+ "ext-filter": "Required to use the boolean validator."
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "5.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com",
+ "homepage": "https://github.com/vlucas"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-20T21:52:34+00:00"
+ },
+ {
+ "name": "voku/portable-ascii",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-ascii.git",
+ "reference": "b56450eed252f6801410d810c8e1727224ae0743"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743",
+ "reference": "b56450eed252f6801410d810c8e1727224ae0743",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "suggest": {
+ "ext-intl": "Use Intl for transliterator_transliterate() support"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Lars Moelleken",
+ "homepage": "http://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+ "homepage": "https://github.com/voku/portable-ascii",
+ "keywords": [
+ "ascii",
+ "clean",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-ascii",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-08T17:03:00+00:00"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.13"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.11.0"
+ },
+ "time": "2022-06-03T18:03:27+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "brianium/paratest",
+ "version": "v7.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paratestphp/paratest.git",
+ "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paratestphp/paratest/zipball/64fcfd0e28a6b8078a19dbf9127be2ee645b92ec",
+ "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-simplexml": "*",
+ "fidry/cpu-core-counter": "^1.1.0",
+ "jean85/pretty-package-versions": "^2.0.5",
+ "php": "~8.2.0 || ~8.3.0",
+ "phpunit/php-code-coverage": "^10.1.11 || ^11.0.0",
+ "phpunit/php-file-iterator": "^4.1.0 || ^5.0.0",
+ "phpunit/php-timer": "^6.0.0 || ^7.0.0",
+ "phpunit/phpunit": "^10.5.9 || ^11.0.3",
+ "sebastian/environment": "^6.0.1 || ^7.0.0",
+ "symfony/console": "^6.4.3 || ^7.0.3",
+ "symfony/process": "^6.4.3 || ^7.0.3"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12.0.0",
+ "ext-pcov": "*",
+ "ext-posix": "*",
+ "phpstan/phpstan": "^1.10.58",
+ "phpstan/phpstan-deprecation-rules": "^1.1.4",
+ "phpstan/phpstan-phpunit": "^1.3.15",
+ "phpstan/phpstan-strict-rules": "^1.5.2",
+ "squizlabs/php_codesniffer": "^3.9.0",
+ "symfony/filesystem": "^6.4.3 || ^7.0.3"
+ },
+ "bin": [
+ "bin/paratest",
+ "bin/paratest.bat",
+ "bin/paratest_for_phpstorm"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ParaTest\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Scaturro",
+ "email": "scaturrob@gmail.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Filippo Tessarotto",
+ "email": "zoeslam@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "Parallel testing for PHP",
+ "homepage": "https://github.com/paratestphp/paratest",
+ "keywords": [
+ "concurrent",
+ "parallel",
+ "phpunit",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/paratestphp/paratest/issues",
+ "source": "https://github.com/paratestphp/paratest/tree/v7.4.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/Slamdunk",
+ "type": "github"
+ },
+ {
+ "url": "https://paypal.me/filippotessarotto",
+ "type": "paypal"
+ }
+ ],
+ "time": "2024-02-20T07:24:02+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6",
+ "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.4",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.2"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-12T11:35:52+00:00"
+ },
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "4be43904336affa5c2f70744a348312336afd0da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
+ "reference": "4be43904336affa5c2f70744a348312336afd0da",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0 || ^2.0",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
+ },
+ "require-dev": {
+ "composer/composer": "*",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.3.1",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "franck.nijhof@dealerdirect.com",
+ "homepage": "http://www.frenck.nl",
+ "role": "Developer / IT Manager"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "homepage": "http://www.dealerdirect.com",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcbf",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
+ "source": "https://github.com/PHPCSStandards/composer-installer"
+ },
+ "time": "2023-01-05T11:28:13+00:00"
+ },
+ {
+ "name": "doctrine/coding-standard",
+ "version": "12.0.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/coding-standard.git",
+ "reference": "3e88327e4bb74e5538787642a59a45919376e0a9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/3e88327e4bb74e5538787642a59a45919376e0a9",
+ "reference": "3e88327e4bb74e5538787642a59a45919376e0a9",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0.0",
+ "php": "^7.2 || ^8.0",
+ "slevomat/coding-standard": "^8.11",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "default-branch": true,
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Steve Müller",
+ "email": "st.mueller@dzh-online.de"
+ }
+ ],
+ "description": "The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.",
+ "homepage": "https://www.doctrine-project.org/projects/coding-standard.html",
+ "keywords": [
+ "checks",
+ "code",
+ "coding",
+ "cs",
+ "dev",
+ "doctrine",
+ "rules",
+ "sniffer",
+ "sniffs",
+ "standard",
+ "style"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/coding-standard/issues",
+ "source": "https://github.com/doctrine/coding-standard/tree/12.0.x"
+ },
+ "time": "2023-04-28T07:19:07+00:00"
+ },
+ {
+ "name": "doctrine/deprecations",
+ "version": "1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/deprecations.git",
+ "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
+ "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9",
+ "phpstan/phpstan": "1.4.10 || 1.10.15",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "psalm/plugin-phpunit": "0.18.4",
+ "psr/log": "^1 || ^2 || ^3",
+ "vimeo/psalm": "4.30.0 || 5.12.0"
+ },
+ "suggest": {
+ "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+ "homepage": "https://www.doctrine-project.org/",
+ "support": {
+ "issues": "https://github.com/doctrine/deprecations/issues",
+ "source": "https://github.com/doctrine/deprecations/tree/1.1.3"
+ },
+ "time": "2024-01-30T19:34:25+00:00"
+ },
+ {
+ "name": "fakerphp/faker",
+ "version": "v1.23.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FakerPHP/Faker.git",
+ "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b",
+ "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "psr/container": "^1.0 || ^2.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "conflict": {
+ "fzaninotto/faker": "*"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "doctrine/persistence": "^1.3 || ^2.0",
+ "ext-intl": "*",
+ "phpunit/phpunit": "^9.5.26",
+ "symfony/phpunit-bridge": "^5.4.16"
+ },
+ "suggest": {
+ "doctrine/orm": "Required to use Faker\\ORM\\Doctrine",
+ "ext-curl": "Required by Faker\\Provider\\Image to download images.",
+ "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
+ "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
+ "ext-mbstring": "Required for multibyte Unicode string functionality."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Faker\\": "src/Faker/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "François Zaninotto"
+ }
+ ],
+ "description": "Faker is a PHP library that generates fake data for you.",
+ "keywords": [
+ "data",
+ "faker",
+ "fixtures"
+ ],
+ "support": {
+ "issues": "https://github.com/FakerPHP/Faker/issues",
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1"
+ },
+ "time": "2024-01-02T13:46:09+00:00"
+ },
+ {
+ "name": "fidry/cpu-core-counter",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theofidry/cpu-core-counter.git",
+ "reference": "8520451a140d3f46ac33042715115e290cf5785f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f",
+ "reference": "8520451a140d3f46ac33042715115e290cf5785f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "fidry/makefile": "^0.2.0",
+ "fidry/php-cs-fixer-config": "^1.1.2",
+ "phpstan/extension-installer": "^1.2.0",
+ "phpstan/phpstan": "^1.9.2",
+ "phpstan/phpstan-deprecation-rules": "^1.0.0",
+ "phpstan/phpstan-phpunit": "^1.2.2",
+ "phpstan/phpstan-strict-rules": "^1.4.4",
+ "phpunit/phpunit": "^8.5.31 || ^9.5.26",
+ "webmozarts/strict-phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Fidry\\CpuCoreCounter\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Théo FIDRY",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Tiny utility to get the number of CPU cores.",
+ "keywords": [
+ "CPU",
+ "core"
+ ],
+ "support": {
+ "issues": "https://github.com/theofidry/cpu-core-counter/issues",
+ "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theofidry",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-06T10:04:20+00:00"
+ },
+ {
+ "name": "filp/whoops",
+ "version": "2.15.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/filp/whoops.git",
+ "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546",
+ "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9 || ^7.0 || ^8.0",
+ "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^0.9 || ^1.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
+ "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
+ },
+ "suggest": {
+ "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+ "whoops/soap": "Formats errors as SOAP responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Whoops\\": "src/Whoops/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Filipe Dobreira",
+ "homepage": "https://github.com/filp",
+ "role": "Developer"
+ }
+ ],
+ "description": "php error handling for cool kids",
+ "homepage": "https://filp.github.io/whoops/",
+ "keywords": [
+ "error",
+ "exception",
+ "handling",
+ "library",
+ "throwable",
+ "whoops"
+ ],
+ "support": {
+ "issues": "https://github.com/filp/whoops/issues",
+ "source": "https://github.com/filp/whoops/tree/2.15.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/denis-sokolov",
+ "type": "github"
+ }
+ ],
+ "time": "2023-11-03T12:00:00+00:00"
+ },
+ {
+ "name": "hamcrest/hamcrest-php",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3|^7.0|^8.0"
+ },
+ "replace": {
+ "cordoval/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "kodova/hamcrest-php": "*"
+ },
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "hamcrest"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": [
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+ },
+ "time": "2020-07-09T08:09:16+00:00"
+ },
+ {
+ "name": "jean85/pretty-package-versions",
+ "version": "2.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Jean85/pretty-package-versions.git",
+ "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4",
+ "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.0.0",
+ "php": "^7.1|^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "jean85/composer-provided-replaced-stub-package": "^1.0",
+ "phpstan/phpstan": "^1.4",
+ "phpunit/phpunit": "^7.5|^8.5|^9.4",
+ "vimeo/psalm": "^4.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Jean85\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Alessandro Lai",
+ "email": "alessandro.lai85@gmail.com"
+ }
+ ],
+ "description": "A library to get pretty versions strings of installed dependencies",
+ "keywords": [
+ "composer",
+ "package",
+ "release",
+ "versions"
+ ],
+ "support": {
+ "issues": "https://github.com/Jean85/pretty-package-versions/issues",
+ "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6"
+ },
+ "time": "2024-03-08T09:58:59+00:00"
+ },
+ {
+ "name": "larastan/larastan",
+ "version": "v2.9.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/larastan/larastan.git",
+ "reference": "340badd89b0eb5bddbc503a4829c08cf9a2819d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/larastan/larastan/zipball/340badd89b0eb5bddbc503a4829c08cf9a2819d7",
+ "reference": "340badd89b0eb5bddbc503a4829c08cf9a2819d7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "illuminate/console": "^9.52.16 || ^10.28.0 || ^11.0",
+ "illuminate/container": "^9.52.16 || ^10.28.0 || ^11.0",
+ "illuminate/contracts": "^9.52.16 || ^10.28.0 || ^11.0",
+ "illuminate/database": "^9.52.16 || ^10.28.0 || ^11.0",
+ "illuminate/http": "^9.52.16 || ^10.28.0 || ^11.0",
+ "illuminate/pipeline": "^9.52.16 || ^10.28.0 || ^11.0",
+ "illuminate/support": "^9.52.16 || ^10.28.0 || ^11.0",
+ "php": "^8.0.2",
+ "phpmyadmin/sql-parser": "^5.9.0",
+ "phpstan/phpstan": "^1.11.2"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12.0",
+ "nikic/php-parser": "^4.19.1",
+ "orchestra/canvas": "^7.11.1 || ^8.11.0 || ^9.0.2",
+ "orchestra/testbench": "^7.33.0 || ^8.13.0 || ^9.0.3",
+ "phpunit/phpunit": "^9.6.13 || ^10.5.16"
+ },
+ "suggest": {
+ "orchestra/testbench": "Using Larastan for analysing a package needs Testbench"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Larastan\\Larastan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Can Vural",
+ "email": "can9119@gmail.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel",
+ "keywords": [
+ "PHPStan",
+ "code analyse",
+ "code analysis",
+ "larastan",
+ "laravel",
+ "package",
+ "php",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/larastan/larastan/issues",
+ "source": "https://github.com/larastan/larastan/tree/v2.9.8"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/canvural",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2024-07-06T17:46:02+00:00"
+ },
+ {
+ "name": "laravel/pint",
+ "version": "v1.17.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/pint.git",
+ "reference": "9d77be916e145864f10788bb94531d03e1f7b482"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482",
+ "reference": "9d77be916e145864f10788bb94531d03e1f7b482",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-tokenizer": "*",
+ "ext-xml": "*",
+ "php": "^8.1.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.64.0",
+ "illuminate/view": "^10.48.20",
+ "larastan/larastan": "^2.9.8",
+ "laravel-zero/framework": "^10.4.0",
+ "mockery/mockery": "^1.6.12",
+ "nunomaduro/termwind": "^1.15.1",
+ "pestphp/pest": "^2.35.1"
+ },
+ "bin": [
+ "builds/pint"
+ ],
+ "type": "project",
+ "autoload": {
+ "psr-4": {
+ "App\\": "app/",
+ "Database\\Seeders\\": "database/seeders/",
+ "Database\\Factories\\": "database/factories/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "An opinionated code formatter for PHP.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "format",
+ "formatter",
+ "lint",
+ "linter",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/pint/issues",
+ "source": "https://github.com/laravel/pint"
+ },
+ "time": "2024-09-03T15:00:28+00:00"
+ },
+ {
+ "name": "laravel/tinker",
+ "version": "v2.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/tinker.git",
+ "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/tinker/zipball/502e0fe3f0415d06d5db1f83a472f0f3b754bafe",
+ "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
+ "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
+ "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
+ "php": "^7.2.5|^8.0",
+ "psy/psysh": "^0.11.1|^0.12.0",
+ "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.3|^1.4.2",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^8.5.8|^9.3.3"
+ },
+ "suggest": {
+ "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Laravel\\Tinker\\TinkerServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Tinker\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Powerful REPL for the Laravel framework.",
+ "keywords": [
+ "REPL",
+ "Tinker",
+ "laravel",
+ "psysh"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/tinker/issues",
+ "source": "https://github.com/laravel/tinker/tree/v2.9.0"
+ },
+ "time": "2024-01-04T16:10:04+00:00"
+ },
+ {
+ "name": "mockery/mockery",
+ "version": "1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mockery/mockery.git",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "shasum": ""
+ },
+ "require": {
+ "hamcrest/hamcrest-php": "^2.0.1",
+ "lib-pcre": ">=7.0",
+ "php": ">=7.3"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5 || ^9.6.17",
+ "symplify/easy-coding-standard": "^12.1.14"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/helpers.php",
+ "library/Mockery.php"
+ ],
+ "psr-4": {
+ "Mockery\\": "library/Mockery"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "https://github.com/padraic",
+ "role": "Author"
+ },
+ {
+ "name": "Dave Marshall",
+ "email": "dave.marshall@atstsolutions.co.uk",
+ "homepage": "https://davedevelopment.co.uk",
+ "role": "Developer"
+ },
+ {
+ "name": "Nathanael Esayeas",
+ "email": "nathanael.esayeas@protonmail.com",
+ "homepage": "https://github.com/ghostwriter",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "library",
+ "mock",
+ "mock objects",
+ "mockery",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "docs": "https://docs.mockery.io/",
+ "issues": "https://github.com/mockery/mockery/issues",
+ "rss": "https://github.com/mockery/mockery/releases.atom",
+ "security": "https://github.com/mockery/mockery/security/advisories",
+ "source": "https://github.com/mockery/mockery"
+ },
+ "time": "2024-05-16T03:13:13+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.12.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
+ "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-06-12T14:39:25+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1",
+ "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0"
+ },
+ "time": "2024-07-01T20:03:41+00:00"
+ },
+ {
+ "name": "nunomaduro/collision",
+ "version": "v8.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nunomaduro/collision.git",
+ "reference": "e7d1aa8ed753f63fa816932bbc89678238843b4a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nunomaduro/collision/zipball/e7d1aa8ed753f63fa816932bbc89678238843b4a",
+ "reference": "e7d1aa8ed753f63fa816932bbc89678238843b4a",
+ "shasum": ""
+ },
+ "require": {
+ "filp/whoops": "^2.15.4",
+ "nunomaduro/termwind": "^2.0.1",
+ "php": "^8.2.0",
+ "symfony/console": "^7.1.3"
+ },
+ "conflict": {
+ "laravel/framework": "<11.0.0 || >=12.0.0",
+ "phpunit/phpunit": "<10.5.1 || >=12.0.0"
+ },
+ "require-dev": {
+ "larastan/larastan": "^2.9.8",
+ "laravel/framework": "^11.19.0",
+ "laravel/pint": "^1.17.1",
+ "laravel/sail": "^1.31.0",
+ "laravel/sanctum": "^4.0.2",
+ "laravel/tinker": "^2.9.0",
+ "orchestra/testbench-core": "^9.2.3",
+ "pestphp/pest": "^2.35.0 || ^3.0.0",
+ "sebastian/environment": "^6.1.0 || ^7.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-8.x": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "./src/Adapters/Phpunit/Autoload.php"
+ ],
+ "psr-4": {
+ "NunoMaduro\\Collision\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Cli error handling for console/command-line PHP applications.",
+ "keywords": [
+ "artisan",
+ "cli",
+ "command-line",
+ "console",
+ "error",
+ "handling",
+ "laravel",
+ "laravel-zero",
+ "php",
+ "symfony"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/collision/issues",
+ "source": "https://github.com/nunomaduro/collision"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2024-08-03T15:32:23+00:00"
+ },
+ {
+ "name": "orchestra/canvas",
+ "version": "v9.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/canvas.git",
+ "reference": "c49867fac16b6286bf2b8360088620e697a2ea92"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/canvas/zipball/c49867fac16b6286bf2b8360088620e697a2ea92",
+ "reference": "c49867fac16b6286bf2b8360088620e697a2ea92",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "composer/semver": "^3.0",
+ "illuminate/console": "^11.20",
+ "illuminate/database": "^11.20",
+ "illuminate/filesystem": "^11.20",
+ "illuminate/support": "^11.20",
+ "orchestra/canvas-core": "^9.0",
+ "orchestra/testbench-core": "^9.2",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.28",
+ "symfony/yaml": "^7.0"
+ },
+ "require-dev": {
+ "laravel/framework": "^11.20",
+ "laravel/pint": "^1.17",
+ "mockery/mockery": "^1.6",
+ "phpstan/phpstan": "^1.11",
+ "phpunit/phpunit": "^11.0",
+ "spatie/laravel-ray": "^1.35"
+ },
+ "bin": [
+ "canvas"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.0-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Orchestra\\Canvas\\LaravelServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Orchestra\\Canvas\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com"
+ }
+ ],
+ "description": "Code Generators for Laravel Applications and Packages",
+ "support": {
+ "issues": "https://github.com/orchestral/canvas/issues",
+ "source": "https://github.com/orchestral/canvas/tree/v9.1.1"
+ },
+ "time": "2024-08-06T17:20:26+00:00"
+ },
+ {
+ "name": "orchestra/canvas-core",
+ "version": "v9.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/canvas-core.git",
+ "reference": "3a29eecf324fe02e3e5628e422314b5cd1a80e48"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/canvas-core/zipball/3a29eecf324fe02e3e5628e422314b5cd1a80e48",
+ "reference": "3a29eecf324fe02e3e5628e422314b5cd1a80e48",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "composer/semver": "^3.0",
+ "illuminate/console": "^11.0",
+ "illuminate/filesystem": "^11.0",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "require-dev": {
+ "laravel/framework": "^11.0",
+ "laravel/pint": "^1.6",
+ "mockery/mockery": "^1.5.1",
+ "orchestra/testbench-core": "^9.0",
+ "phpstan/phpstan": "^1.10.6",
+ "phpunit/phpunit": "^10.1",
+ "symfony/yaml": "^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.0-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Orchestra\\Canvas\\Core\\LaravelServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Orchestra\\Canvas\\Core\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com"
+ }
+ ],
+ "description": "Code Generators Builder for Laravel Applications and Packages",
+ "support": {
+ "issues": "https://github.com/orchestral/canvas/issues",
+ "source": "https://github.com/orchestral/canvas-core/tree/v9.0.0"
+ },
+ "time": "2024-03-06T10:00:21+00:00"
+ },
+ {
+ "name": "orchestra/testbench",
+ "version": "v9.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/testbench.git",
+ "reference": "f79c16b4cc9d22e3a71f8a2bc28326de392ff6aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/testbench/zipball/f79c16b4cc9d22e3a71f8a2bc28326de392ff6aa",
+ "reference": "f79c16b4cc9d22e3a71f8a2bc28326de392ff6aa",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "fakerphp/faker": "^1.23",
+ "laravel/framework": "^11.11",
+ "mockery/mockery": "^1.6",
+ "orchestra/testbench-core": "^9.4",
+ "orchestra/workbench": "^9.5",
+ "php": "^8.2",
+ "phpunit/phpunit": "^10.5 || ^11.0.1",
+ "symfony/process": "^7.0",
+ "symfony/yaml": "^7.0",
+ "vlucas/phpdotenv": "^5.4.1"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com",
+ "homepage": "https://github.com/crynobone"
+ }
+ ],
+ "description": "Laravel Testing Helper for Packages Development",
+ "homepage": "https://packages.tools/testbench/",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "dev",
+ "laravel",
+ "laravel-packages",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/orchestral/testbench/issues",
+ "source": "https://github.com/orchestral/testbench/tree/v9.4.0"
+ },
+ "time": "2024-08-26T05:10:07+00:00"
+ },
+ {
+ "name": "orchestra/testbench-core",
+ "version": "v9.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/testbench-core.git",
+ "reference": "422827e195741ca397408eced09ca473ebbb4086"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/422827e195741ca397408eced09ca473ebbb4086",
+ "reference": "422827e195741ca397408eced09ca473ebbb4086",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "php": "^8.2",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "conflict": {
+ "brianium/paratest": "<7.3.0 || >=8.0.0",
+ "laravel/framework": "<11.11.0 || >=12.0.0",
+ "laravel/serializable-closure": "<1.3.0 || >=2.0.0",
+ "nunomaduro/collision": "<8.0.0 || >=9.0.0",
+ "phpunit/phpunit": "<10.5.0 || 11.0.0 || >=11.4.0"
+ },
+ "require-dev": {
+ "fakerphp/faker": "^1.23",
+ "laravel/framework": "^11.11",
+ "laravel/pint": "^1.17",
+ "mockery/mockery": "^1.6",
+ "phpstan/phpstan": "^1.11",
+ "phpunit/phpunit": "^10.5 || ^11.0.1",
+ "spatie/laravel-ray": "^1.35",
+ "symfony/process": "^7.0",
+ "symfony/yaml": "^7.0",
+ "vlucas/phpdotenv": "^5.4.1"
+ },
+ "suggest": {
+ "brianium/paratest": "Allow using parallel tresting (^7.3).",
+ "ext-pcntl": "Required to use all features of the console signal trapping.",
+ "fakerphp/faker": "Allow using Faker for testing (^1.23).",
+ "laravel/framework": "Required for testing (^11.11).",
+ "mockery/mockery": "Allow using Mockery for testing (^1.6).",
+ "nunomaduro/collision": "Allow using Laravel style tests output and parallel testing (^8.0).",
+ "orchestra/testbench-dusk": "Allow using Laravel Dusk for testing (^9.0).",
+ "phpunit/phpunit": "Allow using PHPUnit for testing (^10.5 || ^11.0).",
+ "symfony/process": "Required to use Orchestra\\Testbench\\remote function (^7.0).",
+ "symfony/yaml": "Required for Testbench CLI (^7.0).",
+ "vlucas/phpdotenv": "Required for Testbench CLI (^5.4.1)."
+ },
+ "bin": [
+ "testbench"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Orchestra\\Testbench\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com",
+ "homepage": "https://github.com/crynobone"
+ }
+ ],
+ "description": "Testing Helper for Laravel Development",
+ "homepage": "https://packages.tools/testbench",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "dev",
+ "laravel",
+ "laravel-packages",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/orchestral/testbench/issues",
+ "source": "https://github.com/orchestral/testbench-core"
+ },
+ "time": "2024-08-26T05:01:33+00:00"
+ },
+ {
+ "name": "orchestra/workbench",
+ "version": "v9.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/orchestral/workbench.git",
+ "reference": "4bb12d505f24b450d1693e88faddc44a1c835907"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/orchestral/workbench/zipball/4bb12d505f24b450d1693e88faddc44a1c835907",
+ "reference": "4bb12d505f24b450d1693e88faddc44a1c835907",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "fakerphp/faker": "^1.23",
+ "laravel/framework": "^11.11",
+ "laravel/tinker": "^2.9",
+ "nunomaduro/collision": "^8.0",
+ "orchestra/canvas": "^9.1",
+ "orchestra/testbench-core": "^9.4",
+ "php": "^8.1",
+ "spatie/laravel-ray": "^1.35",
+ "symfony/polyfill-php83": "^1.28",
+ "symfony/yaml": "^7.0"
+ },
+ "require-dev": {
+ "laravel/pint": "^1.17",
+ "mockery/mockery": "^1.6",
+ "phpstan/phpstan": "^1.11",
+ "phpunit/phpunit": "^10.5 || ^11.0",
+ "symfony/process": "^7.0"
+ },
+ "suggest": {
+ "ext-pcntl": "Required to use all features of the console signal trapping."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Orchestra\\Workbench\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mior Muhammad Zaki",
+ "email": "crynobone@gmail.com"
+ }
+ ],
+ "description": "Workbench Companion for Laravel Packages Development",
+ "keywords": [
+ "dev",
+ "laravel",
+ "laravel-packages",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/orchestral/workbench/issues",
+ "source": "https://github.com/orchestral/workbench/tree/v9.6.0"
+ },
+ "time": "2024-08-26T05:38:42+00:00"
+ },
+ {
+ "name": "pestphp/pest",
+ "version": "v2.35.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest.git",
+ "reference": "b13acb630df52c06123588d321823c31fc685545"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest/zipball/b13acb630df52c06123588d321823c31fc685545",
+ "reference": "b13acb630df52c06123588d321823c31fc685545",
+ "shasum": ""
+ },
+ "require": {
+ "brianium/paratest": "^7.3.1",
+ "nunomaduro/collision": "^7.10.0|^8.4.0",
+ "nunomaduro/termwind": "^1.15.1|^2.0.1",
+ "pestphp/pest-plugin": "^2.1.1",
+ "pestphp/pest-plugin-arch": "^2.7.0",
+ "php": "^8.1.0",
+ "phpunit/phpunit": "^10.5.17"
+ },
+ "conflict": {
+ "phpunit/phpunit": ">10.5.17",
+ "sebastian/exporter": "<5.1.0",
+ "webmozart/assert": "<1.11.0"
+ },
+ "require-dev": {
+ "pestphp/pest-dev-tools": "^2.16.0",
+ "pestphp/pest-plugin-type-coverage": "^2.8.5",
+ "symfony/process": "^6.4.0|^7.1.3"
+ },
+ "bin": [
+ "bin/pest"
+ ],
+ "type": "library",
+ "extra": {
+ "pest": {
+ "plugins": [
+ "Pest\\Plugins\\Bail",
+ "Pest\\Plugins\\Cache",
+ "Pest\\Plugins\\Coverage",
+ "Pest\\Plugins\\Init",
+ "Pest\\Plugins\\Environment",
+ "Pest\\Plugins\\Help",
+ "Pest\\Plugins\\Memory",
+ "Pest\\Plugins\\Only",
+ "Pest\\Plugins\\Printer",
+ "Pest\\Plugins\\ProcessIsolation",
+ "Pest\\Plugins\\Profile",
+ "Pest\\Plugins\\Retry",
+ "Pest\\Plugins\\Snapshot",
+ "Pest\\Plugins\\Verbose",
+ "Pest\\Plugins\\Version",
+ "Pest\\Plugins\\Parallel"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php",
+ "src/Pest.php"
+ ],
+ "psr-4": {
+ "Pest\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "The elegant PHP Testing Framework.",
+ "keywords": [
+ "framework",
+ "pest",
+ "php",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "issues": "https://github.com/pestphp/pest/issues",
+ "source": "https://github.com/pestphp/pest/tree/v2.35.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-20T21:41:50+00:00"
+ },
+ {
+ "name": "pestphp/pest-plugin",
+ "version": "v2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin.git",
+ "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b",
+ "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0.0",
+ "composer-runtime-api": "^2.2.2",
+ "php": "^8.1"
+ },
+ "conflict": {
+ "pestphp/pest": "<2.2.3"
+ },
+ "require-dev": {
+ "composer/composer": "^2.5.8",
+ "pestphp/pest": "^2.16.0",
+ "pestphp/pest-dev-tools": "^2.16.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Pest\\Plugin\\Manager"
+ },
+ "autoload": {
+ "psr-4": {
+ "Pest\\Plugin\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The Pest plugin manager",
+ "keywords": [
+ "framework",
+ "manager",
+ "pest",
+ "php",
+ "plugin",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin/tree/v2.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2023-08-22T08:40:06+00:00"
+ },
+ {
+ "name": "pestphp/pest-plugin-arch",
+ "version": "v2.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin-arch.git",
+ "reference": "d23b2d7498475354522c3818c42ef355dca3fcda"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/d23b2d7498475354522c3818c42ef355dca3fcda",
+ "reference": "d23b2d7498475354522c3818c42ef355dca3fcda",
+ "shasum": ""
+ },
+ "require": {
+ "nunomaduro/collision": "^7.10.0|^8.1.0",
+ "pestphp/pest-plugin": "^2.1.1",
+ "php": "^8.1",
+ "ta-tikoma/phpunit-architecture-test": "^0.8.4"
+ },
+ "require-dev": {
+ "pestphp/pest": "^2.33.0",
+ "pestphp/pest-dev-tools": "^2.16.0"
+ },
+ "type": "library",
+ "extra": {
+ "pest": {
+ "plugins": [
+ "Pest\\Arch\\Plugin"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Autoload.php"
+ ],
+ "psr-4": {
+ "Pest\\Arch\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The Arch plugin for Pest PHP.",
+ "keywords": [
+ "arch",
+ "architecture",
+ "framework",
+ "pest",
+ "php",
+ "plugin",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ }
+ ],
+ "time": "2024-01-26T09:46:42+00:00"
+ },
+ {
+ "name": "pestphp/pest-plugin-laravel",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin-laravel.git",
+ "reference": "53df51169a7f9595e06839cce638c73e59ace5e8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/53df51169a7f9595e06839cce638c73e59ace5e8",
+ "reference": "53df51169a7f9595e06839cce638c73e59ace5e8",
+ "shasum": ""
+ },
+ "require": {
+ "laravel/framework": "^10.48.9|^11.5.0",
+ "pestphp/pest": "^2.34.7",
+ "php": "^8.1.0"
+ },
+ "require-dev": {
+ "laravel/dusk": "^7.13.0",
+ "orchestra/testbench": "^8.22.3|^9.0.4",
+ "pestphp/pest-dev-tools": "^2.16.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Pest\\Laravel\\PestServiceProvider"
+ ]
+ },
+ "pest": {
+ "plugins": [
+ "Pest\\Laravel\\Plugin"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Autoload.php"
+ ],
+ "psr-4": {
+ "Pest\\Laravel\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The Pest Laravel Plugin",
+ "keywords": [
+ "framework",
+ "laravel",
+ "pest",
+ "php",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-27T10:41:54+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "php-di/invoker",
+ "version": "2.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHP-DI/Invoker.git",
+ "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/33234b32dafa8eb69202f950a1fc92055ed76a86",
+ "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "psr/container": "^1.0|^2.0"
+ },
+ "require-dev": {
+ "athletic/athletic": "~0.1.8",
+ "mnapoli/hard-mode": "~0.3.0",
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Invoker\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Generic and extensible callable invoker",
+ "homepage": "https://github.com/PHP-DI/Invoker",
+ "keywords": [
+ "callable",
+ "dependency",
+ "dependency-injection",
+ "injection",
+ "invoke",
+ "invoker"
+ ],
+ "support": {
+ "issues": "https://github.com/PHP-DI/Invoker/issues",
+ "source": "https://github.com/PHP-DI/Invoker/tree/2.3.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ }
+ ],
+ "time": "2023-09-08T09:24:21+00:00"
+ },
+ {
+ "name": "php-di/php-di",
+ "version": "7.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHP-DI/PHP-DI.git",
+ "reference": "e87435e3c0e8f22977adc5af0d5cdcc467e15cf1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/e87435e3c0e8f22977adc5af0d5cdcc467e15cf1",
+ "reference": "e87435e3c0e8f22977adc5af0d5cdcc467e15cf1",
+ "shasum": ""
+ },
+ "require": {
+ "laravel/serializable-closure": "^1.0",
+ "php": ">=8.0",
+ "php-di/invoker": "^2.0",
+ "psr/container": "^1.1 || ^2.0"
+ },
+ "provide": {
+ "psr/container-implementation": "^1.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3",
+ "friendsofphp/proxy-manager-lts": "^1",
+ "mnapoli/phpunit-easymock": "^1.3",
+ "phpunit/phpunit": "^9.5",
+ "vimeo/psalm": "^4.6"
+ },
+ "suggest": {
+ "friendsofphp/proxy-manager-lts": "Install it if you want to use lazy injection (version ^1)"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "DI\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The dependency injection container for humans",
+ "homepage": "https://php-di.org/",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interop",
+ "dependency injection",
+ "di",
+ "ioc",
+ "psr11"
+ ],
+ "support": {
+ "issues": "https://github.com/PHP-DI/PHP-DI/issues",
+ "source": "https://github.com/PHP-DI/PHP-DI/tree/7.0.7"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/php-di/php-di",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-21T15:55:45+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-common",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+ "homepage": "http://www.phpdoc.org",
+ "keywords": [
+ "FQSEN",
+ "phpDocumentor",
+ "phpdoc",
+ "reflection",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
+ "time": "2020-06-27T09:03:43+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "5.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
+ "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.1",
+ "ext-filter": "*",
+ "php": "^7.4 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^1.7",
+ "phpstan/phpdoc-parser": "^1.7",
+ "webmozart/assert": "^1.9.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.5",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^9.5",
+ "vimeo/psalm": "^5.13"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ },
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1"
+ },
+ "time": "2024-05-21T05:55:05+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "1.8.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "153ae662783729388a584b4361f2545e4d841e3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c",
+ "reference": "153ae662783729388a584b4361f2545e4d841e3c",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.0",
+ "php": "^7.3 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0",
+ "phpstan/phpdoc-parser": "^1.13"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^9.5",
+ "rector/rector": "^0.13.9",
+ "vimeo/psalm": "^4.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2"
+ },
+ "time": "2024-02-23T11:10:43+00:00"
+ },
+ {
+ "name": "phpmyadmin/sql-parser",
+ "version": "5.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpmyadmin/sql-parser.git",
+ "reference": "91d980ab76c3f152481e367f62b921adc38af451"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/91d980ab76c3f152481e367f62b921adc38af451",
+ "reference": "91d980ab76c3f152481e367f62b921adc38af451",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.3",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "phpmyadmin/motranslator": "<3.0"
+ },
+ "require-dev": {
+ "phpbench/phpbench": "^1.1",
+ "phpmyadmin/coding-standard": "^3.0",
+ "phpmyadmin/motranslator": "^4.0 || ^5.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.9.12",
+ "phpstan/phpstan-phpunit": "^1.3.3",
+ "phpunit/phpunit": "^8.5 || ^9.6",
+ "psalm/plugin-phpunit": "^0.16.1",
+ "vimeo/psalm": "^4.11",
+ "zumba/json-serializer": "~3.0.2"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance",
+ "phpmyadmin/motranslator": "Translate messages to your favorite locale"
+ },
+ "bin": [
+ "bin/highlight-query",
+ "bin/lint-query",
+ "bin/sql-parser",
+ "bin/tokenize-query"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PhpMyAdmin\\SqlParser\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "The phpMyAdmin Team",
+ "email": "developers@phpmyadmin.net",
+ "homepage": "https://www.phpmyadmin.net/team/"
+ }
+ ],
+ "description": "A validating SQL lexer and parser with a focus on MySQL dialect.",
+ "homepage": "https://github.com/phpmyadmin/sql-parser",
+ "keywords": [
+ "analysis",
+ "lexer",
+ "parser",
+ "query linter",
+ "sql",
+ "sql lexer",
+ "sql linter",
+ "sql parser",
+ "sql syntax highlighter",
+ "sql tokenizer"
+ ],
+ "support": {
+ "issues": "https://github.com/phpmyadmin/sql-parser/issues",
+ "source": "https://github.com/phpmyadmin/sql-parser"
+ },
+ "funding": [
+ {
+ "url": "https://www.phpmyadmin.net/donate/",
+ "type": "other"
+ }
+ ],
+ "time": "2024-08-29T20:56:34+00:00"
+ },
+ {
+ "name": "phpstan/extension-installer",
+ "version": "1.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/extension-installer.git",
+ "reference": "46c8219b3fb0deb3fc08301e8f0797d321d17dcd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/46c8219b3fb0deb3fc08301e8f0797d321d17dcd",
+ "reference": "46c8219b3fb0deb3fc08301e8f0797d321d17dcd",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.9.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2.0",
+ "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPStan\\ExtensionInstaller\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\ExtensionInstaller\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Composer plugin for automatic installation of PHPStan extensions",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/extension-installer/issues",
+ "source": "https://github.com/phpstan/extension-installer/tree/1.4.2"
+ },
+ "time": "2024-08-26T07:38:00+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/5ceb0e384997db59f38774bf79c2a6134252c08f",
+ "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^4.15",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^1.5",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.0",
+ "phpunit/phpunit": "^9.5",
+ "symfony/process": "^5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\PhpDocParser\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "support": {
+ "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.0"
+ },
+ "time": "2024-08-29T09:54:52+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "1.12.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "d8ed7fffa66de1db0d2972267d8ed1d8fa0fe5a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d8ed7fffa66de1db0d2972267d8ed1d8fa0fe5a2",
+ "reference": "d8ed7fffa66de1db0d2972267d8ed1d8fa0fe5a2",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2024-09-03T19:55:22+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-deprecation-rules",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
+ "reference": "fa8cce7720fa782899a0aa97b6a41225d1bb7b26"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/fa8cce7720fa782899a0aa97b6a41225d1bb7b26",
+ "reference": "fa8cce7720fa782899a0aa97b6a41225d1bb7b26",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.11"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
+ "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.2.0"
+ },
+ "time": "2024-04-20T06:39:48+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-phpunit",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-phpunit.git",
+ "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11",
+ "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.11"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<7.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^4.13.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-strict-rules": "^1.5.1",
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon",
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPUnit extensions and rules for PHPStan",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0"
+ },
+ "time": "2024-04-20T06:39:00+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "10.1.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.19.1 || ^5.1.0",
+ "php": ">=8.1",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-text-template": "^3.0.1",
+ "sebastian/code-unit-reverse-lookup": "^3.0.0",
+ "sebastian/complexity": "^3.2.0",
+ "sebastian/environment": "^6.1.0",
+ "sebastian/lines-of-code": "^2.0.2",
+ "sebastian/version": "^4.0.1",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.1"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-22T04:31:57+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "4.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T06:24:48+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:56:09+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T14:07:24+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:57:52+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "10.5.17",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "c1f736a473d21957ead7e94fcc029f571895abf5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c1f736a473d21957ead7e94fcc029f571895abf5",
+ "reference": "c1f736a473d21957ead7e94fcc029f571895abf5",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.10.1",
+ "phar-io/manifest": "^2.0.3",
+ "phar-io/version": "^3.0.2",
+ "php": ">=8.1",
+ "phpunit/php-code-coverage": "^10.1.5",
+ "phpunit/php-file-iterator": "^4.0",
+ "phpunit/php-invoker": "^4.0",
+ "phpunit/php-text-template": "^3.0",
+ "phpunit/php-timer": "^6.0",
+ "sebastian/cli-parser": "^2.0",
+ "sebastian/code-unit": "^2.0",
+ "sebastian/comparator": "^5.0",
+ "sebastian/diff": "^5.0",
+ "sebastian/environment": "^6.0",
+ "sebastian/exporter": "^5.1",
+ "sebastian/global-state": "^6.0.1",
+ "sebastian/object-enumerator": "^5.0",
+ "sebastian/recursion-context": "^5.0",
+ "sebastian/type": "^4.0",
+ "sebastian/version": "^4.0"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.17"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-05T04:39:01+00:00"
+ },
+ {
+ "name": "psy/psysh",
+ "version": "v0.12.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bobthecow/psysh.git",
+ "reference": "2fd717afa05341b4f8152547f142cd2f130f6818"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/2fd717afa05341b4f8152547f142cd2f130f6818",
+ "reference": "2fd717afa05341b4f8152547f142cd2f130f6818",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "nikic/php-parser": "^5.0 || ^4.0",
+ "php": "^8.0 || ^7.4",
+ "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
+ "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
+ },
+ "conflict": {
+ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.2"
+ },
+ "suggest": {
+ "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+ "ext-pdo-sqlite": "The doc command requires SQLite to work.",
+ "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well."
+ },
+ "bin": [
+ "bin/psysh"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "0.12.x-dev"
+ },
+ "bamarni-bin": {
+ "bin-links": false,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Psy\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Justin Hileman",
+ "email": "justin@justinhileman.info",
+ "homepage": "http://justinhileman.com"
+ }
+ ],
+ "description": "An interactive shell for modern PHP.",
+ "homepage": "http://psysh.org",
+ "keywords": [
+ "REPL",
+ "console",
+ "interactive",
+ "shell"
+ ],
+ "support": {
+ "issues": "https://github.com/bobthecow/psysh/issues",
+ "source": "https://github.com/bobthecow/psysh/tree/v0.12.4"
+ },
+ "time": "2024-06-10T01:18:23+00:00"
+ },
+ {
+ "name": "rector/rector",
+ "version": "1.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/rectorphp/rector.git",
+ "reference": "42a4aa23b48b4cfc8ebfeac2b570364e27744381"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/42a4aa23b48b4cfc8ebfeac2b570364e27744381",
+ "reference": "42a4aa23b48b4cfc8ebfeac2b570364e27744381",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0",
+ "phpstan/phpstan": "^1.11.11"
+ },
+ "conflict": {
+ "rector/rector-doctrine": "*",
+ "rector/rector-downgrade-php": "*",
+ "rector/rector-phpunit": "*",
+ "rector/rector-symfony": "*"
+ },
+ "suggest": {
+ "ext-dom": "To manipulate phpunit.xml via the custom-rule command"
+ },
+ "bin": [
+ "bin/rector"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Instant Upgrade and Automated Refactoring of any PHP code",
+ "keywords": [
+ "automation",
+ "dev",
+ "migration",
+ "refactoring"
+ ],
+ "support": {
+ "issues": "https://github.com/rectorphp/rector/issues",
+ "source": "https://github.com/rectorphp/rector/tree/1.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-23T09:03:01+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:12:49+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:58:43+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:59:15+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53",
+ "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/diff": "^5.0",
+ "sebastian/exporter": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-12T06:03:08+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-21T08:37:17+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "5.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0",
+ "symfony/process": "^6.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:15:17+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "6.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-23T08:47:14+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "5.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:17:12+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:19:19+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-21T08:38:20+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:08:32+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:06:18+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "05909fb5bc7df4c52992396d0116aed689f93712"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712",
+ "reference": "05909fb5bc7df4c52992396d0116aed689f93712",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:05:40+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:10:45+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-07T11:34:05+00:00"
+ },
+ {
+ "name": "slevomat/coding-standard",
+ "version": "8.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/slevomat/coding-standard.git",
+ "reference": "7d1d957421618a3803b593ec31ace470177d7817"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/7d1d957421618a3803b593ec31ace470177d7817",
+ "reference": "7d1d957421618a3803b593ec31ace470177d7817",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpdoc-parser": "^1.23.1",
+ "squizlabs/php_codesniffer": "^3.9.0"
+ },
+ "require-dev": {
+ "phing/phing": "2.17.4",
+ "php-parallel-lint/php-parallel-lint": "1.3.2",
+ "phpstan/phpstan": "1.10.60",
+ "phpstan/phpstan-deprecation-rules": "1.1.4",
+ "phpstan/phpstan-phpunit": "1.3.16",
+ "phpstan/phpstan-strict-rules": "1.5.2",
+ "phpunit/phpunit": "8.5.21|9.6.8|10.5.11"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+ "keywords": [
+ "dev",
+ "phpcs"
+ ],
+ "support": {
+ "issues": "https://github.com/slevomat/coding-standard/issues",
+ "source": "https://github.com/slevomat/coding-standard/tree/8.15.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kukulich",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-09T15:20:58+00:00"
+ },
+ {
+ "name": "spatie/backtrace",
+ "version": "1.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/backtrace.git",
+ "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/backtrace/zipball/1a9a145b044677ae3424693f7b06479fc8c137a9",
+ "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3|^8.0"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "laravel/serializable-closure": "^1.3",
+ "phpunit/phpunit": "^9.3",
+ "spatie/phpunit-snapshot-assertions": "^4.2",
+ "symfony/var-dumper": "^5.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Backtrace\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van de Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "A better backtrace",
+ "homepage": "https://github.com/spatie/backtrace",
+ "keywords": [
+ "Backtrace",
+ "spatie"
+ ],
+ "support": {
+ "source": "https://github.com/spatie/backtrace/tree/1.6.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/spatie",
+ "type": "github"
+ },
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "other"
+ }
+ ],
+ "time": "2024-07-22T08:21:24+00:00"
+ },
+ {
+ "name": "spatie/laravel-ray",
+ "version": "1.37.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/laravel-ray.git",
+ "reference": "c2bedfd1172648df2c80aaceb2541d70f1d9a5b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/c2bedfd1172648df2c80aaceb2541d70f1d9a5b9",
+ "reference": "c2bedfd1172648df2c80aaceb2541d70f1d9a5b9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "illuminate/contracts": "^7.20|^8.19|^9.0|^10.0|^11.0",
+ "illuminate/database": "^7.20|^8.19|^9.0|^10.0|^11.0",
+ "illuminate/queue": "^7.20|^8.19|^9.0|^10.0|^11.0",
+ "illuminate/support": "^7.20|^8.19|^9.0|^10.0|^11.0",
+ "php": "^7.4|^8.0",
+ "rector/rector": "^0.19.2|^1.0",
+ "spatie/backtrace": "^1.0",
+ "spatie/ray": "^1.41.1",
+ "symfony/stopwatch": "4.2|^5.1|^6.0|^7.0",
+ "zbateson/mail-mime-parser": "^1.3.1|^2.0|^3.0"
+ },
+ "require-dev": {
+ "guzzlehttp/guzzle": "^7.3",
+ "laravel/framework": "^7.20|^8.19|^9.0|^10.0|^11.0",
+ "orchestra/testbench-core": "^5.0|^6.0|^7.0|^8.0|^9.0",
+ "pestphp/pest": "^1.22|^2.0",
+ "phpstan/phpstan": "^1.10.57",
+ "phpunit/phpunit": "^9.3|^10.1",
+ "spatie/pest-plugin-snapshots": "^1.1|^2.0",
+ "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Spatie\\LaravelRay\\RayServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Spatie\\LaravelRay\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "Easily debug Laravel apps",
+ "homepage": "https://github.com/spatie/laravel-ray",
+ "keywords": [
+ "laravel-ray",
+ "spatie"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/laravel-ray/issues",
+ "source": "https://github.com/spatie/laravel-ray/tree/1.37.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/spatie",
+ "type": "github"
+ },
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "other"
+ }
+ ],
+ "time": "2024-07-12T12:35:17+00:00"
+ },
+ {
+ "name": "spatie/macroable",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/macroable.git",
+ "reference": "ec2c320f932e730607aff8052c44183cf3ecb072"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/macroable/zipball/ec2c320f932e730607aff8052c44183cf3ecb072",
+ "reference": "ec2c320f932e730607aff8052c44183cf3ecb072",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.0|^9.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Macroable\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "A trait to dynamically add methods to a class",
+ "homepage": "https://github.com/spatie/macroable",
+ "keywords": [
+ "macroable",
+ "spatie"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/macroable/issues",
+ "source": "https://github.com/spatie/macroable/tree/2.0.0"
+ },
+ "time": "2021-03-26T22:39:02+00:00"
+ },
+ {
+ "name": "spatie/ray",
+ "version": "1.41.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/ray.git",
+ "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/ray/zipball/c44f8cfbf82c69909b505de61d8d3f2d324e93fc",
+ "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "php": "^7.3|^8.0",
+ "ramsey/uuid": "^3.0|^4.1",
+ "spatie/backtrace": "^1.1",
+ "spatie/macroable": "^1.0|^2.0",
+ "symfony/stopwatch": "^4.0|^5.1|^6.0|^7.0",
+ "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3"
+ },
+ "require-dev": {
+ "illuminate/support": "6.x|^8.18|^9.0",
+ "nesbot/carbon": "^2.63",
+ "pestphp/pest": "^1.22",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^9.5",
+ "rector/rector": "^0.19.2",
+ "spatie/phpunit-snapshot-assertions": "^4.2",
+ "spatie/test-time": "^1.2"
+ },
+ "bin": [
+ "bin/remove-ray.sh"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Spatie\\Ray\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "Debug with Ray to fix problems faster",
+ "homepage": "https://github.com/spatie/ray",
+ "keywords": [
+ "ray",
+ "spatie"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/ray/issues",
+ "source": "https://github.com/spatie/ray/tree/1.41.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/spatie",
+ "type": "github"
+ },
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "other"
+ }
+ ],
+ "time": "2024-04-24T14:21:46+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.10.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017",
+ "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-07-21T23:26:44+00:00"
+ },
+ {
+ "name": "symfony/polyfill-iconv",
+ "version": "v1.30.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-iconv.git",
+ "reference": "c027e6a3c6aee334663ec21f5852e89738abc805"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c027e6a3c6aee334663ec21f5852e89738abc805",
+ "reference": "c027e6a3c6aee334663ec21f5852e89738abc805",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-iconv": "*"
+ },
+ "suggest": {
+ "ext-iconv": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Iconv\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Iconv extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "iconv",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-iconv/tree/v1.30.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T15:07:36+00:00"
+ },
+ {
+ "name": "symfony/stopwatch",
+ "version": "v7.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d",
+ "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Stopwatch\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides a way to profile code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/stopwatch/tree/v7.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-31T14:57:53+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v7.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "92e080b851c1c655c786a2da77f188f2dccd0f4b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/92e080b851c1c655c786a2da77f188f2dccd0f4b",
+ "reference": "92e080b851c1c655c786a2da77f188f2dccd0f4b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/console": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0"
+ },
+ "bin": [
+ "Resources/bin/yaml-lint"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Loads and dumps YAML files",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/yaml/tree/v7.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-08-12T09:59:40+00:00"
+ },
+ {
+ "name": "ta-tikoma/phpunit-architecture-test",
+ "version": "0.8.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git",
+ "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/89f0dea1cb0f0d5744d3ec1764a286af5e006636",
+ "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18.0 || ^5.0.0",
+ "php": "^8.1.0",
+ "phpdocumentor/reflection-docblock": "^5.3.0",
+ "phpunit/phpunit": "^10.5.5 || ^11.0.0",
+ "symfony/finder": "^6.4.0 || ^7.0.0"
+ },
+ "require-dev": {
+ "laravel/pint": "^1.13.7",
+ "phpstan/phpstan": "^1.10.52"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPUnit\\Architecture\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ni Shi",
+ "email": "futik0ma011@gmail.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "Methods for testing application architecture",
+ "keywords": [
+ "architecture",
+ "phpunit",
+ "stucture",
+ "test",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues",
+ "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.4"
+ },
+ "time": "2024-01-05T14:10:56+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:36:25+00:00"
+ },
+ {
+ "name": "zbateson/mail-mime-parser",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zbateson/mail-mime-parser.git",
+ "reference": "e0d4423fe27850c9dd301190767dbc421acc2f19"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/e0d4423fe27850c9dd301190767dbc421acc2f19",
+ "reference": "e0d4423fe27850c9dd301190767dbc421acc2f19",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/psr7": "^2.5",
+ "php": ">=8.0",
+ "php-di/php-di": "^6.0|^7.0",
+ "psr/log": "^1|^2|^3",
+ "zbateson/mb-wrapper": "^2.0",
+ "zbateson/stream-decorators": "^2.1"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "*",
+ "monolog/monolog": "^2|^3",
+ "phpstan/phpstan": "*",
+ "phpunit/phpunit": "^9.6"
+ },
+ "suggest": {
+ "ext-iconv": "For best support/performance",
+ "ext-mbstring": "For best support/performance"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZBateson\\MailMimeParser\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Zaahid Bateson"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/zbateson/mail-mime-parser/graphs/contributors"
+ }
+ ],
+ "description": "MIME email message parser",
+ "homepage": "https://mail-mime-parser.org",
+ "keywords": [
+ "MimeMailParser",
+ "email",
+ "mail",
+ "mailparse",
+ "mime",
+ "mimeparse",
+ "parser",
+ "php-imap"
+ ],
+ "support": {
+ "docs": "https://mail-mime-parser.org/#usage-guide",
+ "issues": "https://github.com/zbateson/mail-mime-parser/issues",
+ "source": "https://github.com/zbateson/mail-mime-parser"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/zbateson",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-10T18:44:09+00:00"
+ },
+ {
+ "name": "zbateson/mb-wrapper",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zbateson/mb-wrapper.git",
+ "reference": "9e4373a153585d12b6c621ac4a6bb143264d4619"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zbateson/mb-wrapper/zipball/9e4373a153585d12b6c621ac4a6bb143264d4619",
+ "reference": "9e4373a153585d12b6c621ac4a6bb143264d4619",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0",
+ "symfony/polyfill-iconv": "^1.9",
+ "symfony/polyfill-mbstring": "^1.9"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "*",
+ "phpstan/phpstan": "*",
+ "phpunit/phpunit": "<10.0"
+ },
+ "suggest": {
+ "ext-iconv": "For best support/performance",
+ "ext-mbstring": "For best support/performance"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZBateson\\MbWrapper\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Zaahid Bateson"
+ }
+ ],
+ "description": "Wrapper for mbstring with fallback to iconv for encoding conversion and string manipulation",
+ "keywords": [
+ "charset",
+ "encoding",
+ "http",
+ "iconv",
+ "mail",
+ "mb",
+ "mb_convert_encoding",
+ "mbstring",
+ "mime",
+ "multibyte",
+ "string"
+ ],
+ "support": {
+ "issues": "https://github.com/zbateson/mb-wrapper/issues",
+ "source": "https://github.com/zbateson/mb-wrapper/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/zbateson",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-20T01:38:07+00:00"
+ },
+ {
+ "name": "zbateson/stream-decorators",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zbateson/stream-decorators.git",
+ "reference": "32a2a62fb0f26313395c996ebd658d33c3f9c4e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zbateson/stream-decorators/zipball/32a2a62fb0f26313395c996ebd658d33c3f9c4e5",
+ "reference": "32a2a62fb0f26313395c996ebd658d33c3f9c4e5",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/psr7": "^2.5",
+ "php": ">=8.0",
+ "zbateson/mb-wrapper": "^2.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "*",
+ "phpstan/phpstan": "*",
+ "phpunit/phpunit": "^9.6|^10.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZBateson\\StreamDecorators\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Zaahid Bateson"
+ }
+ ],
+ "description": "PHP psr7 stream decorators for mime message part streams",
+ "keywords": [
+ "base64",
+ "charset",
+ "decorators",
+ "mail",
+ "mime",
+ "psr7",
+ "quoted-printable",
+ "stream",
+ "uuencode"
+ ],
+ "support": {
+ "issues": "https://github.com/zbateson/stream-decorators/issues",
+ "source": "https://github.com/zbateson/stream-decorators/tree/2.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/zbateson",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-29T21:42:39+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {
+ "doctrine/coding-standard": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "^8.2"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.3.0"
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..9bc2de6
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,576 @@
+{
+ "nodes": {
+ "agenix": {
+ "inputs": {
+ "darwin": "darwin",
+ "home-manager": "home-manager",
+ "nixpkgs": "nixpkgs_2",
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1722339003,
+ "narHash": "sha256-ZeS51uJI30ehNkcZ4uKqT4ZDARPyqrHADSKAwv5vVCU=",
+ "owner": "ryantm",
+ "repo": "agenix",
+ "rev": "3f1dae074a12feb7327b4bf43cbac0d124488bb7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "ryantm",
+ "repo": "agenix",
+ "type": "github"
+ }
+ },
+ "darwin": {
+ "inputs": {
+ "nixpkgs": [
+ "snow-blower",
+ "agenix",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1700795494,
+ "narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
+ "owner": "lnl7",
+ "repo": "nix-darwin",
+ "rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "lnl7",
+ "ref": "master",
+ "repo": "nix-darwin",
+ "type": "github"
+ }
+ },
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1696426674,
+ "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1719994518,
+ "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "flake-parts_2": {
+ "inputs": {
+ "nixpkgs-lib": [
+ "snow-blower",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1719994518,
+ "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "flake-root": {
+ "locked": {
+ "lastModified": 1713493429,
+ "narHash": "sha256-ztz8JQkI08tjKnsTpfLqzWoKFQF4JGu2LRz8bkdnYUk=",
+ "owner": "srid",
+ "repo": "flake-root",
+ "rev": "bc748b93b86ee76e2032eecda33440ceb2532fcd",
+ "type": "github"
+ },
+ "original": {
+ "owner": "srid",
+ "repo": "flake-root",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1653893745,
+ "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_2": {
+ "locked": {
+ "lastModified": 1653893745,
+ "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_3": {
+ "locked": {
+ "lastModified": 1653893745,
+ "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_4": {
+ "locked": {
+ "lastModified": 1653893745,
+ "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_5": {
+ "locked": {
+ "lastModified": 1653893745,
+ "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "git-hooks": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "snow-blower",
+ "nixpkgs"
+ ],
+ "nixpkgs-stable": "nixpkgs-stable"
+ },
+ "locked": {
+ "lastModified": 1721042469,
+ "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=",
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "snow-blower",
+ "git-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1709087332,
+ "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
+ "home-manager": {
+ "inputs": {
+ "nixpkgs": [
+ "snow-blower",
+ "agenix",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1703113217,
+ "narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=",
+ "owner": "nix-community",
+ "repo": "home-manager",
+ "rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "home-manager",
+ "type": "github"
+ }
+ },
+ "nixago": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixago-exts": "nixago-exts",
+ "nixpkgs": [
+ "snow-blower",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1714086354,
+ "narHash": "sha256-yKVQMxL9p7zCWUhnGhDzRVT8sDgHoI3V595lBK0C2YA=",
+ "owner": "nix-community",
+ "repo": "nixago",
+ "rev": "5133633e9fe6b144c8e00e3b212cdbd5a173b63d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "nixago",
+ "type": "github"
+ }
+ },
+ "nixago-exts": {
+ "inputs": {
+ "flake-utils": "flake-utils_2",
+ "nixago": "nixago_2",
+ "nixpkgs": [
+ "snow-blower",
+ "nixago",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1676070308,
+ "narHash": "sha256-QaJ65oc2l8iwQIGWUJ0EKjCeSuuCM/LqR8RauxZUUkc=",
+ "owner": "nix-community",
+ "repo": "nixago-extensions",
+ "rev": "e5380cb0456f4ea3c86cf94e3039eb856bf07d0b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "nixago-extensions",
+ "type": "github"
+ }
+ },
+ "nixago-exts_2": {
+ "inputs": {
+ "flake-utils": "flake-utils_4",
+ "nixago": "nixago_3",
+ "nixpkgs": [
+ "snow-blower",
+ "nixago",
+ "nixago-exts",
+ "nixago",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1655508669,
+ "narHash": "sha256-BDDdo5dZQMmwNH/GNacy33nPBnCpSIydWFPZs0kkj/g=",
+ "owner": "nix-community",
+ "repo": "nixago-extensions",
+ "rev": "3022a932ce109258482ecc6568c163e8d0b426aa",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "nixago-extensions",
+ "type": "github"
+ }
+ },
+ "nixago_2": {
+ "inputs": {
+ "flake-utils": "flake-utils_3",
+ "nixago-exts": "nixago-exts_2",
+ "nixpkgs": [
+ "snow-blower",
+ "nixago",
+ "nixago-exts",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1676070010,
+ "narHash": "sha256-iYzJIWptE1EUD8VINAg66AAMUajizg8JUYN3oBmb8no=",
+ "owner": "nix-community",
+ "repo": "nixago",
+ "rev": "d480ba6c0c16e2c5c0bd2122852d6a0c9ad1ed0e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "ref": "rename-config-data",
+ "repo": "nixago",
+ "type": "github"
+ }
+ },
+ "nixago_3": {
+ "inputs": {
+ "flake-utils": "flake-utils_5",
+ "nixpkgs": [
+ "snow-blower",
+ "nixago",
+ "nixago-exts",
+ "nixago",
+ "nixago-exts",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1655405483,
+ "narHash": "sha256-Crd49aZWNrpczlRTOwWGfwBMsTUoG9vlHDKQC7cx264=",
+ "owner": "nix-community",
+ "repo": "nixago",
+ "rev": "e6a9566c18063db5b120e69e048d3627414e327d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "nixago",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1722185531,
+ "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "lastModified": 1719876945,
+ "narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=",
+ "type": "tarball",
+ "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
+ },
+ "original": {
+ "type": "tarball",
+ "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
+ }
+ },
+ "nixpkgs-stable": {
+ "locked": {
+ "lastModified": 1720386169,
+ "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-24.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1703013332,
+ "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_3": {
+ "locked": {
+ "lastModified": 1722185531,
+ "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_4": {
+ "locked": {
+ "lastModified": 1722185531,
+ "narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "process-compose-flake": {
+ "locked": {
+ "lastModified": 1718031437,
+ "narHash": "sha256-+RrlkAVZx0QhyeHAGFJnjST+/7Dc3zsDU3zAKXoDXaI=",
+ "owner": "Platonic-Systems",
+ "repo": "process-compose-flake",
+ "rev": "9344fac44edced4c686721686a6ad904d067c546",
+ "type": "github"
+ },
+ "original": {
+ "owner": "Platonic-Systems",
+ "repo": "process-compose-flake",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-parts": "flake-parts",
+ "nixpkgs": "nixpkgs",
+ "snow-blower": "snow-blower"
+ }
+ },
+ "snow-blower": {
+ "inputs": {
+ "agenix": "agenix",
+ "flake-parts": "flake-parts_2",
+ "flake-root": "flake-root",
+ "git-hooks": "git-hooks",
+ "nixago": "nixago",
+ "nixpkgs": "nixpkgs_3",
+ "process-compose-flake": "process-compose-flake",
+ "systems": "systems_2",
+ "treefmt-nix": "treefmt-nix"
+ },
+ "locked": {
+ "lastModified": 1722456547,
+ "narHash": "sha256-pudMUqtmALLZO/mpSPhxwhDdDIb6LTUqoLqJ1wHT4D4=",
+ "owner": "use-the-fork",
+ "repo": "snow-blower",
+ "rev": "94267dece6a9da3978f19be65bd739f275a649e4",
+ "type": "github"
+ },
+ "original": {
+ "owner": "use-the-fork",
+ "repo": "snow-blower",
+ "type": "github"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "systems_2": {
+ "locked": {
+ "lastModified": 1689347949,
+ "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "type": "github"
+ }
+ },
+ "treefmt-nix": {
+ "inputs": {
+ "nixpkgs": "nixpkgs_4"
+ },
+ "locked": {
+ "lastModified": 1718522839,
+ "narHash": "sha256-ULzoKzEaBOiLRtjeY3YoGFJMwWSKRYOic6VNw2UyTls=",
+ "owner": "numtide",
+ "repo": "treefmt-nix",
+ "rev": "68eb1dc333ce82d0ab0c0357363ea17c31ea1f81",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "treefmt-nix",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..f72cca3
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,148 @@
+{
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+ flake-parts.url = "github:hercules-ci/flake-parts";
+ snow-blower.url = "github:use-the-fork/snow-blower";
+ };
+
+ outputs = inputs:
+ inputs.snow-blower.mkSnowBlower {
+ inherit inputs;
+ perSystem = {
+ config,
+ lib,
+ ...
+ }: let
+ rootDir = config.snow-blower.paths.root;
+ serv = config.snow-blower.services;
+ lang = config.snow-blower.languages;
+
+ # Refrences PHP and Composer later in this config.
+ composer = "${lang.php.packages.composer}/bin/composer";
+ php = "${lang.php.package}/bin/php";
+
+ envKeys = builtins.attrNames config.snow-blower.env;
+ unsetEnv = builtins.concatStringsSep "\n" (
+ map (key: "unset ${key}") envKeys
+ );
+ in {
+ snow-blower = {
+ paths.src = ./.;
+
+ # Conviance scripts
+ scripts = {
+ pf.exec = ''
+ ${unsetEnv}
+ ./vendor/bin/pest --filter "$@"
+ '';
+ p.exec = ''
+ ${unsetEnv}
+ ./vendor/bin/pest
+ '';
+ coverage.exec = ''
+ export XDEBUG_MODE=coverage
+ ./vendor/bin/pest --coverage
+ unset XDEBUG_MODE
+ '';
+
+ # swap a and artisan commands for testbench
+ a.exec = ''
+ ${unsetEnv}
+ ./vendor/bin/testbench "$@"
+ '';
+ artisan.exec = ''
+ ${unsetEnv}
+ ./vendor/bin/testbench "$@"
+ '';
+ };
+
+ languages = {
+ # the required version of PHP for this project.
+ php = {
+ enable = true;
+ version = "8.2";
+ extensions = ["grpc" "redis" "imagick" "memcached" "xdebug"];
+ ini = ''
+ memory_limit = 5G
+ max_execution_time = 90
+ '';
+ };
+ };
+
+ services = {
+ # Elasticsearch service for testing
+ elasticsearch = {
+ enable = true;
+ };
+ };
+
+ integrations = {
+ #Creates Changelogs based on commits
+ git-cliff.enable = true;
+
+ treefmt = {
+ settings.formatter = {
+ # Laravel Pint Formating
+ "laravel-pint" = {
+ command = "${php}";
+ options = [
+ "${rootDir}/vendor/bin/pint"
+ #make it verbose
+ "-v"
+ "--repair"
+ ];
+ includes = ["*.php"];
+ };
+ };
+
+ programs = {
+ #Nix Formater
+ alejandra.enable = true;
+
+ #Format Markdown files.
+ mdformat.enable = true;
+
+ #JS / CSS Formatting.
+ prettier = {
+ enable = true;
+ settings = {
+ trailingComma = "es5";
+ semi = true;
+ singleQuote = true;
+ jsxSingleQuote = true;
+ bracketSpacing = true;
+ printWidth = 80;
+ tabWidth = 2;
+ endOfLine = "lf";
+ };
+ };
+ };
+ };
+
+ # Guess what this does. Go ahead Guess.
+ git-hooks.hooks = {
+ # run formatting on files that are being commited
+ treefmt.enable = true;
+
+ # Code linting
+ phpstan.enable = false;
+
+ #lets make sure there are no keys in the repo
+ detect-private-keys.enable = true;
+
+ #fix line endings.
+ mixed-line-endings.enable = true;
+ };
+ };
+
+ shell.interactive = [
+ ''
+ if [[ ! -d vendor ]]; then
+ ${composer} install
+ fi
+ ''
+ ];
+ };
+ };
+ };
+}
diff --git a/justfile b/justfile
new file mode 100644
index 0000000..4343761
--- /dev/null
+++ b/justfile
@@ -0,0 +1,5 @@
+import 'just-flake.just'
+
+# Display the list of recipes
+default:
+ @just --list
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 0000000..e69de29
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 0000000..7f25c7b
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,10 @@
+includes:
+ - phpstan-baseline.neon
+
+parameters:
+ level: 5
+ paths:
+ - src
+ tmpDir: build/phpstan
+ ignoreErrors:
+ - '#Return type .* of method .* should be compatible with return type .* of method#'
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..0455ff1
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ ./tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ src/
+
+
+
diff --git a/src/Collection/ElasticCollection.php b/src/Collection/ElasticCollection.php
new file mode 100644
index 0000000..ea2f3d7
--- /dev/null
+++ b/src/Collection/ElasticCollection.php
@@ -0,0 +1,25 @@
+
+ */
+class ElasticCollection extends Collection
+{
+ use ElasticCollectionMeta;
+
+ /**
+ * @param Arrayable|iterable|array|null $items
+ */
+ public function __construct($items = [])
+ {
+ parent::__construct($items);
+ }
+}
diff --git a/src/Collection/ElasticCollectionMeta.php b/src/Collection/ElasticCollectionMeta.php
new file mode 100644
index 0000000..e878cd6
--- /dev/null
+++ b/src/Collection/ElasticCollectionMeta.php
@@ -0,0 +1,58 @@
+meta = $meta;
+ }
+
+ public function getQueryMeta(): QueryMetaData
+ {
+ return $this->meta;
+ }
+
+ public function getQueryMetaAsArray(): array
+ {
+ return $this->meta->asArray();
+ }
+
+ public function getDsl(): array
+ {
+ return [
+ 'query' => $this->meta->getQuery(),
+ 'dsl' => $this->meta->getDsl(),
+ ];
+ }
+
+ public function getTook(): int
+ {
+ return $this->meta->getTook();
+ }
+
+ public function getShards(): mixed
+ {
+ return $this->meta->getShards();
+ }
+
+ public function getTotal(): int
+ {
+ return $this->meta->getTotal();
+ }
+
+ public function getMaxScore(): string
+ {
+ return $this->meta->getMaxScore();
+ }
+
+ public function getResults(): array
+ {
+ return $this->meta->getResults();
+ }
+}
diff --git a/src/Collection/ElasticResult.php b/src/Collection/ElasticResult.php
new file mode 100644
index 0000000..0446470
--- /dev/null
+++ b/src/Collection/ElasticResult.php
@@ -0,0 +1,38 @@
+value = $value;
+ }
+
+ public function setValue($value): void
+ {
+ $this->value = $value;
+ }
+
+ public function getValue(): mixed
+ {
+ return $this->value;
+ }
+
+ public function __invoke()
+ {
+ return $this->value;
+ }
+
+ public function __toString()
+ {
+ return (string) $this->value;
+ }
+}
diff --git a/src/Collection/LazyElasticCollection.php b/src/Collection/LazyElasticCollection.php
new file mode 100644
index 0000000..0878d64
--- /dev/null
+++ b/src/Collection/LazyElasticCollection.php
@@ -0,0 +1,16 @@
+
+ */
+class LazyElasticCollection extends LazyCollection
+{
+ use ElasticCollectionMeta;
+}
diff --git a/src/Connection.php b/src/Connection.php
index a716a41..f824599 100644
--- a/src/Connection.php
+++ b/src/Connection.php
@@ -1,34 +1,77 @@
connectionName = $config['name'];
$this->config = $config;
@@ -37,17 +80,16 @@ public function __construct(array $config)
$this->client = $this->buildConnection();
- $this->useDefaultPostProcessor();
+ $this->postProcessor = new Query\Processor;
$this->useDefaultSchemaGrammar();
$this->useDefaultQueryGrammar();
-
}
public function setOptions($config)
{
- if (!empty($config['index_prefix'])) {
+ if (! empty($config['index_prefix'])) {
$this->indexPrefix = $config['index_prefix'];
}
if (isset($config['options']['allow_id_sort'])) {
@@ -56,13 +98,13 @@ public function setOptions($config)
if (isset($config['options']['ssl_verification'])) {
$this->sslVerification = $config['options']['ssl_verification'];
}
- if (!empty($config['options']['retires'])) {
+ if (! empty($config['options']['retires'])) {
$this->retires = $config['options']['retires'];
}
if (isset($config['options']['meta_header'])) {
$this->elasticMetaHeader = $config['options']['meta_header'];
}
- if (!empty($config['error_log_index'])) {
+ if (! empty($config['error_log_index'])) {
if ($this->indexPrefix) {
$this->errorLoggingIndex = $this->indexPrefix.'_'.$config['error_log_index'];
} else {
@@ -71,42 +113,45 @@ public function setOptions($config)
}
}
- public function getIndexPrefix(): string|null
+ protected function buildConnection(): Client
{
- return $this->indexPrefix;
+ $type = config('database.connections.elasticsearch.auth_type') ?? null;
+ $type = strtolower($type);
+ if (! in_array($type, ['http', 'cloud'])) {
+ throw new RuntimeException('Invalid [auth_type] in database config. Must be: http, cloud or api');
+ }
+
+ return $this->{'_'.$type.'Connection'}();
}
- public function setIndexPrefix($newPrefix): void
+ public function getTablePrefix(): ?string
{
- $this->indexPrefix = $newPrefix;
+ return $this->getIndexPrefix();
}
-
- public function getTablePrefix(): string|null
+ public function getIndexPrefix(): ?string
{
- return $this->getIndexPrefix();
+ return $this->indexPrefix;
}
- public function getErrorLoggingIndex(): string|bool
+ public function getPostProcessor(): Query\Processor
{
- return $this->errorLoggingIndex;
+ return $this->postProcessor;
}
- public function setIndex($index): string
+ public function setIndexPrefix($newPrefix): void
{
- $this->index = $index;
- if ($this->indexPrefix) {
- if (!(str_contains($this->index, $this->indexPrefix.'_'))) {
- $this->index = $this->indexPrefix.'_'.$index;
- }
- }
+ $this->indexPrefix = $newPrefix;
+ }
- return $this->getIndex();
+ public function getErrorLoggingIndex(): ?string
+ {
+ return $this->errorLoggingIndex;
}
- public function getSchemaGrammar()
+ public function getSchemaGrammar(): Schema\Grammar
{
- return new Schema\Grammar($this);
+ return new Schema\Grammar;
}
public function getIndex(): string
@@ -114,104 +159,114 @@ public function getIndex(): string
return $this->index;
}
- public function setMaxSize($value)
+ public function setIndex($index): string
{
- $this->maxSize = $value;
- }
+ $this->index = $index;
+ if ($this->indexPrefix) {
+ if (! (str_contains($this->index, $this->indexPrefix.'_'))) {
+ $this->index = $this->indexPrefix.'_'.$index;
+ }
+ }
+ return $this->getIndex();
+ }
public function table($table, $as = null)
{
- $query = new Query\Builder($this, new Query\Processor());
+ $query = new Query\Builder($this, new Query\Processor);
return $query->from($table);
}
/**
- * @inheritdoc
+ * Override the default schema builder.
*/
- public function getSchemaBuilder()
+ public function getSchemaBuilder(): Schema\Builder
{
return new Schema\Builder($this);
}
-
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function disconnect()
+ public function disconnect(): void
{
unset($this->connection);
}
-
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getDriverName(): string
{
return 'elasticsearch';
}
- /**
- * @inheritdoc
- */
- protected function getDefaultPostProcessor()
+ public function rebuildConnection(): void
{
- return new Query\Processor();
+ $this->rebuild = true;
}
- /**
- * @inheritdoc
- */
- protected function getDefaultQueryGrammar()
+ public function getClient(): Client
{
- return new Query\Grammar();
+ return $this->client;
}
- /**
- * @inheritdoc
- */
- protected function getDefaultSchemaGrammar()
+ public function getMaxSize(): int
{
- return new Schema\Grammar();
+ return $this->maxSize;
}
- public function rebuildConnection()
+ public function setMaxSize($value): void
{
- $this->rebuild = true;
+ $this->maxSize = $value;
}
- public function getClient()
+ public function getAllowIdSort(): bool
{
- return $this->client;
+ return $this->allowIdSort;
}
- public function getMaxSize()
+ public function __call($method, $parameters)
{
- return $this->maxSize;
+ if (! $this->index) {
+ $this->index = $this->indexPrefix.'*';
+ }
+ if ($this->rebuild) {
+ $this->client = $this->buildConnection();
+ $this->rebuild = false;
+ }
+ $bridge = new Bridge($this);
+
+ return $bridge->{'process'.Str::studly($method)}(...$parameters);
}
- public function getAllowIdSort()
+ /**
+ * {@inheritdoc}
+ */
+ protected function getDefaultPostProcessor(): Query\Processor
{
- return $this->allowIdSort;
+ return new Query\Processor;
}
-
//----------------------------------------------------------------------
// Connection Builder
//----------------------------------------------------------------------
- protected function buildConnection(): Client
+ /**
+ * {@inheritdoc}
+ */
+ protected function getDefaultQueryGrammar(): Query\Grammar
{
- $type = config('database.connections.elasticsearch.auth_type') ?? null;
- $type = strtolower($type);
- if (!in_array($type, ['http', 'cloud'])) {
- throw new RuntimeException('Invalid [auth_type] in database config. Must be: http, cloud or api');
- }
-
- return $this->{'_'.$type.'Connection'}();
+ return new Query\Grammar;
+ }
+ /**
+ * {@inheritdoc}
+ */
+ protected function getDefaultSchemaGrammar(): Schema\Grammar
+ {
+ return new Schema\Grammar;
}
protected function _httpConnection(): Client
@@ -233,27 +288,6 @@ protected function _httpConnection(): Client
return $cb->build();
}
- protected function _cloudConnection(): Client
- {
- $cloudId = config('database.connections.'.$this->connectionName.'.cloud_id') ?? null;
- $username = config('database.connections.'.$this->connectionName.'.username') ?? null;
- $pass = config('database.connections.'.$this->connectionName.'.password') ?? null;
- $apiId = config('database.connections.'.$this->connectionName.'.api_id') ?? null;
- $apiKey = config('database.connections.'.$this->connectionName.'.api_key') ?? null;
-
- $cb = ClientBuilder::create()->setElasticCloudId($cloudId);
- $cb = $this->_builderOptions($cb);
- if ($username && $pass) {
- $cb->setBasicAuthentication($username, $pass);
- }
- if ($apiKey) {
- $cb->setApiKey($apiKey, $apiId);
- }
-
-
- return $cb->build();
- }
-
protected function _builderOptions($cb)
{
$cb->setSSLVerification($this->sslVerification);
@@ -282,22 +316,27 @@ protected function _builderOptions($cb)
return $cb;
}
-
//----------------------------------------------------------------------
// Dynamic call routing to DSL bridge
//----------------------------------------------------------------------
- public function __call($method, $parameters)
+ protected function _cloudConnection(): Client
{
- if (!$this->index) {
- $this->index = $this->indexPrefix.'*';
+ $cloudId = config('database.connections.'.$this->connectionName.'.cloud_id') ?? null;
+ $username = config('database.connections.'.$this->connectionName.'.username') ?? null;
+ $pass = config('database.connections.'.$this->connectionName.'.password') ?? null;
+ $apiId = config('database.connections.'.$this->connectionName.'.api_id') ?? null;
+ $apiKey = config('database.connections.'.$this->connectionName.'.api_key') ?? null;
+
+ $cb = ClientBuilder::create()->setElasticCloudId($cloudId);
+ $cb = $this->_builderOptions($cb);
+ if ($username && $pass) {
+ $cb->setBasicAuthentication($username, $pass);
}
- if ($this->rebuild) {
- $this->client = $this->buildConnection();
- $this->rebuild = false;
+ if ($apiKey) {
+ $cb->setApiKey($apiKey, $apiId);
}
- $bridge = new Bridge($this);
- return $bridge->{'process'.Str::studly($method)}(...$parameters);
+ return $cb->build();
}
}
diff --git a/src/DSL/Bridge.php b/src/DSL/Bridge.php
index bf926ab..331716b 100644
--- a/src/DSL/Bridge.php
+++ b/src/DSL/Bridge.php
@@ -1,33 +1,35 @@
maxSize = $this->connection->getMaxSize();
$this->indexPrefix = $this->connection->getIndexPrefix();
$this->errorLogger = $this->connection->getErrorLoggingIndex();
-
}
- //----------------------------------------------------------------------
+ //======================================================================
// PIT
- //----------------------------------------------------------------------
+ //======================================================================
/**
* @throws QueryException
@@ -50,34 +51,41 @@ public function __construct(Connection $connection)
public function processOpenPit($keepAlive = '5m'): string
{
$params = [
- 'index' => $this->index,
+ 'index' => $this->index,
'keep_alive' => $keepAlive,
];
+ $res = [];
try {
$process = $this->client->openPointInTime($params);
$res = $process->asArray();
- if (!empty($res['id'])) {
- return $res['id'];
+ if (empty($res['id'])) {
+ throw new Exception('Error on PIT creation. No ID returned.');
}
-
- throw new Exception('Error on PIT creation. No ID returned.');
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $res['id'];
}
/**
* @throws QueryException
+ * @throws ParameterException
*/
- public function processPitFind($wheres, $options, $columns, $pitId, $searchAfter = false, $keepAlive = '5m')
- {
+ public function processPitFind(
+ $wheres,
+ $options,
+ $columns,
+ $pitId,
+ $searchAfter = false,
+ $keepAlive = '5m'
+ ): Results {
$params = $this->buildParams($this->index, $wheres, $options, $columns);
unset($params['index']);
$params['body']['pit'] = [
- 'id' => $pitId,
+ 'id' => $pitId,
'keep_alive' => $keepAlive,
];
if (empty($params['body']['sort'])) {
@@ -89,47 +97,41 @@ public function processPitFind($wheres, $options, $columns, $pitId, $searchAfter
if ($searchAfter) {
$params['body']['search_after'] = $searchAfter;
}
+ $process = [];
try {
$process = $this->client->search($params);
-
- return $this->_sanitizePitSearchResponse($process, $params, $this->_queryTag(__FUNCTION__));
-
-
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
-
+ return $this->_sanitizePitSearchResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
-
/**
* @throws QueryException
*/
public function processClosePit($id): bool
{
-
$params = [
'index' => $this->index,
- 'body' => [
+ 'body' => [
'id' => $id,
],
-
];
+ $res = [];
try {
$process = $this->client->closePointInTime($params);
$res = $process->asArray();
-
- return $res['succeeded'];
-
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $res['succeeded'];
}
- //----------------------------------------------------------------------
- // BYO Query
- //----------------------------------------------------------------------
+ //======================================================================
+ // BYO Query
+ //======================================================================
/**
* @throws Exception
@@ -138,20 +140,20 @@ public function processSearchRaw($bodyParams, $returnRaw): Results
{
$params = [
'index' => $this->index,
- 'body' => $bodyParams,
+ 'body' => $bodyParams,
];
+ $process = [];
try {
$process = $this->client->search($params);
if ($returnRaw) {
return $this->_return($process->asArray(), [], $params, $this->_queryTag(__FUNCTION__));
}
-
- return $this->_sanitizeSearchResponse($process, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_sanitizeSearchResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -161,17 +163,16 @@ public function processAggregationRaw($bodyParams): Results
{
$params = [
'index' => $this->index,
- 'body' => $bodyParams,
-
+ 'body' => $bodyParams,
];
+ $process = [];
try {
$process = $this->client->search($params);
-
- return $this->_sanitizeRawAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_sanitizeRawAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -179,47 +180,45 @@ public function processAggregationRaw($bodyParams): Results
*/
public function processIndicesDsl($method, $params): Results
{
+ $process = [];
try {
$process = $this->client->indices()->{$method}($params);
-
- return $this->_sanitizeSearchResponse($process, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_sanitizeSearchResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
- //----------------------------------------------------------------------
+ //======================================================================
// To DSL
- //----------------------------------------------------------------------
+ //======================================================================
- public function processToDsl($wheres, $options, $columns)
+ /**
+ * @throws QueryException
+ * @throws ParameterException
+ */
+ public function processToDsl($wheres, $options, $columns): array
{
return $this->buildParams($this->index, $wheres, $options, $columns);
}
- public function processToDslForSearch($searchParams, $searchOptions, $wheres, $opts, $fields, $cols)
- {
- return $this->buildSearchParams($this->index, $searchParams, $searchOptions, $wheres, $opts, $fields, $cols);
- }
-
/**
* @throws ParameterException
+ * @throws QueryException
*/
- public function processShowQuery($wheres, $options, $columns)
+ public function processToDslForSearch($searchParams, $searchOptions, $wheres, $opts, $fields, $cols): array
{
- $params = $this->buildParams($this->index, $wheres, $options, $columns);
-
- return $params['body'] ?? null;
+ return $this->buildSearchParams($this->index, $searchParams, $searchOptions, $wheres, $opts, $fields, $cols);
}
-
- //----------------------------------------------------------------------
- // Read Queries
- //----------------------------------------------------------------------
+ //======================================================================
+ // Find/Search Queries
+ //======================================================================
/**
* @throws QueryException
+ * @throws ParameterException
*/
public function processFind($wheres, $options, $columns): Results
{
@@ -230,47 +229,48 @@ public function processFind($wheres, $options, $columns): Results
/**
* @throws QueryException
+ * @throws ParameterException
*/
- public function processSearch($searchParams, $searchOptions, $wheres, $opts, $fields, $cols)
+ public function processSearch($searchParams, $searchOptions, $wheres, $opts, $fields, $cols): Results
{
$params = $this->buildSearchParams($this->index, $searchParams, $searchOptions, $wheres, $opts, $fields, $cols);
return $this->_returnSearch($params, __FUNCTION__);
-
}
/**
* @throws QueryException
*/
- protected function _returnSearch($params, $source)
+ protected function _returnSearch($params, $source): Results
{
if (empty($params['size'])) {
$params['size'] = $this->maxSize;
}
+ $process = [];
try {
-
$process = $this->client->search($params);
-
- return $this->_sanitizeSearchResponse($process, $params, $this->_queryTag($source));
-
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_sanitizeSearchResponse($process, $params, $this->_queryTag($source));
}
+ //----------------------------------------------------------------------
+ // Distinct
+ //----------------------------------------------------------------------
/**
* @throws QueryException
* @throws ParameterException
*/
public function processDistinct($wheres, $options, $columns, $includeDocCount = false): Results
{
- if ($columns && !is_array($columns)) {
+ if ($columns && ! is_array($columns)) {
$columns = [$columns];
}
$sort = $options['sort'] ?? [];
- $skip = $options['skip'] ?? false;
- $limit = $options['limit'] ?? false;
+ $skip = $options['skip'] ?? 0;
+ $limit = $options['limit'] ?? 0;
unset($options['sort']);
unset($options['skip']);
unset($options['limit']);
@@ -281,34 +281,26 @@ public function processDistinct($wheres, $options, $columns, $includeDocCount =
$sort = [$sortField => $sortDir];
}
-
$params = $this->buildParams($this->index, $wheres, $options);
+ $data = [];
+ $response = [];
try {
-
$params['body']['aggs'] = $this->createNestedAggs($columns, $sort);
-
$response = $this->client->search($params);
-
-
- $data = [];
- if (!empty($response['aggregations'])) {
+ if (! empty($response['aggregations'])) {
$data = $this->_sanitizeDistinctResponse($response['aggregations'], $columns, $includeDocCount);
}
-
//process limit and skip from all results
if ($skip || $limit) {
$data = array_slice($data, $skip, $limit);
}
-
- return $this->_return($data, $response, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($data, $response, $params, $this->_queryTag(__FUNCTION__));
}
-
//----------------------------------------------------------------------
// Write Queries
//----------------------------------------------------------------------
@@ -332,26 +324,112 @@ public function processSave($data, $refresh): Results
$params = [
'index' => $this->index,
- 'body' => $data,
+ 'body' => $data,
];
if ($id) {
$params['id'] = $id;
-
}
if ($refresh) {
$params['refresh'] = $refresh;
}
-
+ $response = [];
+ $savedData = [];
try {
$response = $this->client->index($params);
$savedData = ['_id' => $response['_id']] + $data;
-
- return $this->_return($savedData, $response, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ }
+
+ return $this->_return($savedData, $response, $params, $this->_queryTag(__FUNCTION__));
+ }
+
+ /**
+ * Allows us to use the Bulk API.
+ * Such speed!
+ *
+ * More Info:
+ * - https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/indexing_documents.html#_bulk_indexing
+ * - https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
+ *
+ * @throws QueryException
+ */
+ public function processInsertBulk(array $records, bool $returnData = false, string|bool|null $refresh = null): array
+ {
+ $params = ['body' => []];
+
+ // If we don't want to wait for elastic to refresh this needs to be set.
+ if ($refresh) {
+ $params['refresh'] = $refresh;
}
+ // Create action/metadata pairs
+ foreach ($records as $data) {
+ $recordHeader['_index'] = $this->index;
+
+ if (isset($data['_id'])) {
+ $recordHeader['_id'] = $data['_id'];
+ unset($data['_id']);
+ }
+ if (isset($data['_index'])) {
+ unset($data['_index']);
+ }
+ if (isset($data['_meta'])) {
+ unset($data['_meta']);
+ }
+
+ $params['body'][] = [
+ 'index' => $recordHeader,
+ ];
+ $params['body'][] = $data;
+ }
+
+ $finalResponse = [
+ 'hasErrors' => false,
+ 'total' => 0,
+ 'took' => 0,
+ 'success' => 0,
+ 'created' => 0,
+ 'modified' => 0,
+ 'failed' => 0,
+ 'data' => [],
+ 'error_bag' => [],
+ ];
+ try {
+ $response = $this->client->bulk($params);
+ $finalResponse['hasErrors'] = $response['errors'];
+ $finalResponse['took'] = $response['took'];
+ foreach ($response['items'] as $count => $hit) {
+ $finalResponse['total']++;
+ $payload = $params['body'][($count * 2) + 1];
+
+ if (! empty($hit['index']['error'])) {
+ $finalResponse['failed']++;
+ $finalResponse['error_bag'][] = [
+ 'error' => $hit['index']['error'],
+ 'payload' => $payload,
+ ];
+ } else {
+ $finalResponse['success']++;
+ $finalResponse['success']++;
+ if ($hit['index']['result'] === 'created') {
+ $finalResponse['created']++;
+ } else {
+ $finalResponse['modified']++;
+ }
+
+ if ($returnData) {
+ $id = $hit['index']['_id'];
+ $record = ['_id' => $id] + $payload;
+ $finalResponse['data'][] = $record;
+ }
+ }
+ }
+ } catch (Exception $e) {
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ }
+ return $finalResponse;
}
/**
@@ -373,7 +451,7 @@ public function processUpdateMany($wheres, $newValues, $options, $refresh = null
$resultData = [];
$data = $this->processFind($wheres, $options, []);
- if (!empty($data->data)) {
+ if (! empty($data->data)) {
foreach ($data->data as $currentData) {
foreach ($newValues as $field => $value) {
@@ -393,7 +471,6 @@ public function processUpdateMany($wheres, $newValues, $options, $refresh = null
$params['queryOptions'] = $options;
$params['updateValues'] = $newValues;
-
return $this->_return($resultData, $resultMeta, $params, $this->_queryTag(__FUNCTION__));
}
@@ -413,14 +490,14 @@ public function processIncrementMany($wheres, $newValues, $options, $refresh): R
$resultMeta['failed'] = 0;
$resultData = [];
$data = $this->processFind($wheres, $options, []);
- if (!empty($data->data)) {
+ if (! empty($data->data)) {
foreach ($data->data as $currentData) {
$currentValue = $currentData[$incField] ?? 0;
$currentValue += $newValues['inc'][$incField];
- $currentData[$incField] = (int)$currentValue;
+ $currentData[$incField] = (int) $currentValue;
- if (!empty($newValues['set'])) {
+ if (! empty($newValues['set'])) {
foreach ($newValues['set'] as $field => $value) {
$currentData[$field] = $value;
}
@@ -438,11 +515,9 @@ public function processIncrementMany($wheres, $newValues, $options, $refresh): R
$params['queryOptions'] = $options;
$params['updateValues'] = $newValues;
-
return $this->_return($resultData, $resultMeta, $params, $this->_queryTag(__FUNCTION__));
}
-
//----------------------------------------------------------------------
// Delete Queries
//----------------------------------------------------------------------
@@ -456,7 +531,7 @@ public function processDeleteAll($wheres, $options = []): Results
if (isset($wheres['_id'])) {
$params = [
'index' => $this->index,
- 'id' => $wheres['_id'],
+ 'id' => $wheres['_id'],
];
try {
$responseObject = $this->client->delete($params);
@@ -465,41 +540,22 @@ public function processDeleteAll($wheres, $options = []): Results
return $this->_return($response['deleteCount'], $response, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
}
+ $response = [];
$params = $this->buildParams($this->index, $wheres, $options);
try {
$responseObject = $this->client->deleteByQuery($params);
$response = $responseObject->asArray();
$response['deleteCount'] = $response['deleted'] ?? 0;
-
- return $this->_return($response['deleteCount'], $response, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($response['deleteCount'], $response, $params, $this->_queryTag(__FUNCTION__));
}
- public function processScript($id, $script)
- {
-// $params = [
-// 'id' => $id,
-// 'index' => $this->index,
-// ];
-// if ($script) {
-// $params['body']['script']['source'] = $script;
-// }
-//
-// $response = $this->client->update($params);
-//
-// $n = new self($this->index);
-// $find = $n->processFind($id);
-
-// return $this->_return($find->data, $response, $params, $this->_queryTag(__FUNCTION__));
- }
-
-
//----------------------------------------------------------------------
// Index administration
//----------------------------------------------------------------------
@@ -523,67 +579,48 @@ public function processGetIndices($all): array
public function processIndexExists($index): bool
{
$params = ['index' => $index];
-
try {
$test = $this->client->indices()->exists($params);
-
- return $test->getStatusCode() == 200;
} catch (Exception $e) {
return false;
}
+ return $test->getStatusCode() == 200;
}
-
/**
* @throws QueryException
*/
- public function processIndexMappings($index): mixed
- {
- $params = ['index' => $index];
- try {
- $responseObject = $this->client->indices()->getMapping($params);
- $response = $responseObject->asArray();
- $result = $this->_return($response, $response, $params, $this->_queryTag(__FUNCTION__));
-
- return $result->data;
- } catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
- }
- }
-
- /**
- * @throws QueryException
- */
- public function processIndexSettings($index): mixed
+ public function processIndexSettings($index): array
{
$params = ['index' => $index];
+ $response = [];
try {
$response = $this->client->indices()->getSettings($params);
$result = $this->_return($response, $response, $params, $this->_queryTag(__FUNCTION__));
-
- return $result->data->asArray();
+ $response = $result->data->asArray();
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $response;
}
/**
* @throws QueryException
*/
- public function processIndexCreate($settings)
+ public function processIndexCreate($settings): bool
{
$params = $this->buildIndexMap($this->index, $settings);
+ $created = false;
try {
$response = $this->client->indices()->create($params);
-
- $result = $this->_return(true, $response, $params, $this->_queryTag(__FUNCTION__));
-
- return true;
+ $created = $response->asArray();
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return ! empty($created);
}
/**
@@ -593,15 +630,12 @@ public function processIndexDelete(): bool
{
$params = ['index' => $this->index];
try {
- $response = $this->client->indices()->delete($params);
- $this->_return(true, $response, $params, $this->_queryTag(__FUNCTION__));
-
- return true;
+ $this->client->indices()->delete($params);
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
-
+ return true;
}
/**
@@ -619,12 +653,11 @@ public function processIndexModify($settings): bool
try {
$response = $this->client->indices()->putMapping($params);
$result = $this->_return(true, $response, $params, $this->_queryTag(__FUNCTION__));
-
- return true;
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return true;
}
/**
@@ -639,26 +672,27 @@ public function processReIndex($oldIndex, $newIndex): Results
}
$params['body']['source']['index'] = $oldIndex;
$params['body']['dest']['index'] = $newIndex;
+ $resultData = [];
+ $result = [];
try {
$response = $this->client->reindex($params);
$result = $response->asArray();
$resultData = [
- 'took' => $result['took'],
- 'total' => $result['total'],
- 'created' => $result['created'],
- 'updated' => $result['updated'],
- 'deleted' => $result['deleted'],
- 'batches' => $result['batches'],
+ 'took' => $result['took'],
+ 'total' => $result['total'],
+ 'created' => $result['created'],
+ 'updated' => $result['updated'],
+ 'deleted' => $result['deleted'],
+ 'batches' => $result['batches'],
'version_conflicts' => $result['version_conflicts'],
- 'noops' => $result['noops'],
- 'retries' => $result['retries'],
+ 'noops' => $result['noops'],
+ 'retries' => $result['retries'],
];
-
- return $this->_return($resultData, $result, $params, $this->_queryTag(__FUNCTION__));
-
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_return($resultData, $result, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -672,44 +706,36 @@ public function processIndexAnalyzerSettings($settings): bool
$response = $this->client->indices()->putSettings($params);
$result = $this->_return(true, $response, $params, $this->_queryTag(__FUNCTION__));
$this->client->indices()->open(['index' => $this->index]);
-
- return true;
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
- }
+ return true;
+ }
//----------------------------------------------------------------------
// Aggregates
//----------------------------------------------------------------------
-
-
- public function processMultipleAggregate($functions, $wheres, $options, $column)
+ /**
+ * @throws QueryException
+ * @throws ParameterException
+ */
+ public function processMultipleAggregate($functions, $wheres, $options, $column): Results
{
$params = $this->buildParams($this->index, $wheres, $options);
+ $process = [];
try {
$params['body']['aggs'] = ParameterBuilder::multipleAggregations($functions, $column);
$process = $this->client->search($params);
-
- return $this->_return($process['aggregations'] ?? [], $process, $params, $this->_queryTag(__FUNCTION__));
-
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
- }
+ return $this->_return($process['aggregations'] ?? [], $process, $params, $this->_queryTag(__FUNCTION__));
+ }
/**
* Aggregate entry point
- *
- * @param $function
- * @param $wheres
- * @param $options
- * @param $columns
- *
- * @return mixed
*/
public function processAggregate($function, $wheres, $options, $columns): Results
{
@@ -723,15 +749,14 @@ public function processAggregate($function, $wheres, $options, $columns): Result
public function _countAggregate($wheres, $options, $columns): Results
{
$params = $this->buildParams($this->index, $wheres);
+ $process = [];
try {
$process = $this->client->count($params);
-
- return $this->_return($process['count'] ?? 0, $process, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($process['count'] ?? 0, $process, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -744,18 +769,17 @@ private function _maxAggregate($wheres, $options, $columns): Results
if (is_array($columns[0])) {
$columns = $columns[0];
}
+ $process = [];
try {
foreach ($columns as $column) {
$params['body']['aggs']['max_'.$column] = ParameterBuilder::maxAggregation($column);
}
$process = $this->client->search($params);
-
- return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
-
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -768,16 +792,17 @@ private function _minAggregate($wheres, $options, $columns): Results
if (is_array($columns[0])) {
$columns = $columns[0];
}
+ $process = [];
try {
foreach ($columns as $column) {
$params['body']['aggs']['min_'.$column] = ParameterBuilder::minAggregation($column);
}
$process = $this->client->search($params);
-
- return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -790,19 +815,17 @@ private function _sumAggregate($wheres, $options, $columns): Results
if (is_array($columns[0])) {
$columns = $columns[0];
}
+ $process = [];
try {
foreach ($columns as $column) {
$params['body']['aggs']['sum_'.$column] = ParameterBuilder::sumAggregation($column);
}
$process = $this->client->search($params);
-
- return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
-
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -815,17 +838,17 @@ private function _avgAggregate($wheres, $options, $columns): Results
if (is_array($columns[0])) {
$columns = $columns[0];
}
+ $process = [];
try {
foreach ($columns as $column) {
$params['body']['aggs']['avg_'.$column] = ParameterBuilder::avgAggregation($column);
}
$process = $this->client->search($params);
-
- return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
-
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+
+ return $this->_sanitizeAggsResponse($process, $params, $this->_queryTag(__FUNCTION__));
}
/**
@@ -835,42 +858,34 @@ private function _avgAggregate($wheres, $options, $columns): Results
private function _matrixAggregate($wheres, $options, $columns): Results
{
$params = $this->buildParams($this->index, $wheres, $options);
+ $process = [];
try {
$params['body']['aggs']['statistics'] = ParameterBuilder::matrixAggregation($columns);
$process = $this->client->search($params);
-
- return $this->_return($process['aggregations']['statistics'] ?? [], $process, $params, $this->_queryTag(__FUNCTION__));
} catch (Exception $e) {
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($process['aggregations']['statistics'] ?? [], $process, $params, $this->_queryTag(__FUNCTION__));
}
- /**
- * @throws QueryException
- */
- public function parseRequiredKeywordMapping($field)
+ private function _sanitizeAggsResponse($response, $params, $queryTag): Results
{
- $mappings = $this->processIndexMappings($this->index);
- $map = reset($mappings);
- if (!empty($map['mappings']['properties'][$field])) {
- $fieldMap = $map['mappings']['properties'][$field];
- if (!empty($fieldMap['type']) && $fieldMap['type'] === 'keyword') {
- //primary Map is field. Use as is
- return $field;
- }
- if (!empty($fieldMap['fields']['keyword'])) {
- return $field.'.keyword';
- }
- }
+ $meta['timed_out'] = $response['timed_out'];
+ $meta['total'] = $response['hits']['total']['value'] ?? 0;
+ $meta['max_score'] = $response['hits']['max_score'] ?? 0;
+ $meta['sorts'] = [];
- return false;
+ $aggs = $response['aggregations'];
+ $data = (count($aggs) === 1) ? reset($aggs)['value'] ?? 0 : array_map(fn ($value
+ ) => $value['value'] ?? 0, $aggs);
+ return $this->_return($data, $meta, $params, $queryTag);
}
- //----------------------------------------------------------------------
+ //======================================================================
// Distinct Aggregates
- //----------------------------------------------------------------------
+ //======================================================================
public function processDistinctAggregate($function, $wheres, $options, $columns): Results
{
@@ -884,82 +899,78 @@ public function processDistinctAggregate($function, $wheres, $options, $columns)
private function _countDistinctAggregate($wheres, $options, $columns): Results
{
$params = $this->buildParams($this->index, $wheres);
+ $count = 0;
+ $meta = [];
try {
$process = $this->processDistinct($wheres, $options, $columns);
$count = count($process->data);
-
- return $this->_return($count, $process->getMetaData(), $params, $this->_queryTag(__FUNCTION__));
+ $meta = $process->getMetaData();
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($count, $meta, $params, $this->_queryTag(__FUNCTION__));
}
-
/**
* @throws ParameterException
* @throws QueryException
*/
- private function _minDistinctAggregate($wheres, $options, $columns): Results
+ private function _maxDistinctAggregate($wheres, $options, $columns): Results
{
$params = $this->buildParams($this->index, $wheres);
+ $max = 0;
+ $meta = [];
try {
$process = $this->processDistinct($wheres, $options, $columns);
- $min = 0;
- $hasBeenSet = false;
- if (!empty($process->data)) {
+ if (! empty($process->data)) {
foreach ($process->data as $datum) {
- if (!empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
- if (!$hasBeenSet) {
- $min = $datum[$columns[0]];
- $hasBeenSet = true;
- } else {
- $min = min($min, $datum[$columns[0]]);
- }
-
+ if (! empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
+ $max = max($max, $datum[$columns[0]]);
}
}
}
-
- return $this->_return($min, $process->getMetaData(), $params, $this->_queryTag(__FUNCTION__));
+ $meta = $process->getMetaData();
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($max, $meta, $params, $this->_queryTag(__FUNCTION__));
}
/**
* @throws ParameterException
* @throws QueryException
*/
- private function _maxDistinctAggregate($wheres, $options, $columns): Results
+ private function _minDistinctAggregate($wheres, $options, $columns): Results
{
$params = $this->buildParams($this->index, $wheres);
+ $min = 0;
+ $meta = [];
try {
$process = $this->processDistinct($wheres, $options, $columns);
-
- $max = 0;
- if (!empty($process->data)) {
+ $hasBeenSet = false;
+ if (! empty($process->data)) {
foreach ($process->data as $datum) {
- if (!empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
- $max = max($max, $datum[$columns[0]]);
+ if (! empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
+ if (! $hasBeenSet) {
+ $min = $datum[$columns[0]];
+ $hasBeenSet = true;
+ } else {
+ $min = min($min, $datum[$columns[0]]);
+ }
}
}
}
-
-
- return $this->_return($max, $process->getMetaData(), $params, $this->_queryTag(__FUNCTION__));
+ $meta = $process->getMetaData();
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($min, $meta, $params, $this->_queryTag(__FUNCTION__));
}
-
/**
* @throws ParameterException
* @throws QueryException
@@ -967,40 +978,42 @@ private function _maxDistinctAggregate($wheres, $options, $columns): Results
private function _sumDistinctAggregate($wheres, $options, $columns): Results
{
$params = $this->buildParams($this->index, $wheres);
+ $sum = 0;
+ $meta = [];
try {
$process = $this->processDistinct($wheres, $options, $columns);
- $sum = 0;
- if (!empty($process->data)) {
+ if (! empty($process->data)) {
foreach ($process->data as $datum) {
- if (!empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
+ if (! empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
$sum += $datum[$columns[0]];
}
}
}
-
- return $this->_return($sum, $process->getMetaData(), $params, $this->_queryTag(__FUNCTION__));
+ $meta = $process->getMetaData();
} catch (Exception $e) {
-
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
}
+ return $this->_return($sum, $meta, $params, $this->_queryTag(__FUNCTION__));
}
/**
* @throws ParameterException
* @throws QueryException
*/
- private function _avgDistinctAggregate($wheres, $options, $columns)
+ private function _avgDistinctAggregate($wheres, $options, $columns): Results
{
$params = $this->buildParams($this->index, $wheres);
+ $sum = 0;
+ $count = 0;
+ $avg = 0;
+ $meta = [];
try {
$process = $this->processDistinct($wheres, $options, $columns);
- $sum = 0;
- $count = 0;
- $avg = 0;
- if (!empty($process->data)) {
+
+ if (! empty($process->data)) {
foreach ($process->data as $datum) {
- if (!empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
+ if (! empty($datum[$columns[0]]) && is_numeric($datum[$columns[0]])) {
$count++;
$sum += $datum[$columns[0]];
}
@@ -1009,73 +1022,158 @@ private function _avgDistinctAggregate($wheres, $options, $columns)
if ($count > 0) {
$avg = $sum / $count;
}
+ $meta = $process->getMetaData();
+ } catch (Exception $e) {
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ }
+ return $this->_return($avg, $meta, $params, $this->_queryTag(__FUNCTION__));
+ }
- return $this->_return($avg, $process->getMetaData(), $params, $this->_queryTag(__FUNCTION__));
- } catch (Exception $e) {
+ /**
+ * @throws QueryException
+ */
+ private function _matrixDistinctAggregate($wheres, $options, $columns)
+ {
+ $this->_throwError(new Exception('Matrix distinct aggregate not supported', 500), [], $this->_queryTag(__FUNCTION__));
+ }
+
+ //======================================================================
+ // Helpers
+ //======================================================================
- $this->throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ /**
+ * @throws QueryException
+ */
+ public function parseRequiredKeywordMapping($field): ?string
+ {
+ $mappings = $this->processIndexMappings($this->index);
+ $map = reset($mappings);
+ if (! empty($map['mappings']['properties'][$field])) {
+ $fieldMap = $map['mappings']['properties'][$field];
+ if (! empty($fieldMap['type']) && $fieldMap['type'] === 'keyword') {
+ //primary Map is field. Use as is
+ return $field;
+ }
+ if (! empty($fieldMap['fields']['keyword'])) {
+ return $field.'.keyword';
+ }
}
+ return null;
}
/**
* @throws QueryException
*/
- private function _matrixDistinctAggregate($wheres, $options, $columns): Results
+ public function processIndexMappings($index): array
{
- $this->throwError(new Exception('Matrix distinct aggregate not supported', 500), [], $this->_queryTag(__FUNCTION__));
+ $params = ['index' => $index];
+ $result = [];
+ try {
+ $responseObject = $this->client->indices()->getMapping($params);
+ $response = $responseObject->asArray();
+ $result = $this->_return($response, $response, $params, $this->_queryTag(__FUNCTION__));
+ } catch (Exception $e) {
+ $this->_throwError($e, $params, $this->_queryTag(__FUNCTION__));
+ }
+
+ return $result->data;
}
//======================================================================
// Private & Sanitization methods
//======================================================================
+ private function _return($data, $meta, $params, $queryTag): Results
+ {
+ if (is_object($meta)) {
+ $metaAsArray = [];
+ if (method_exists($meta, 'asArray')) {
+ $metaAsArray = $meta->asArray();
+ }
+ $results = new Results($data, $metaAsArray, $params, $queryTag);
+ } else {
+ $results = new Results($data, $meta, $params, $queryTag);
+ }
- private function _queryTag($function)
+ return $results;
+ }
+
+ private function _queryTag($function): string
{
return str_replace('process', '', $function);
}
- private function _sanitizeSearchResponse($response, $params, $queryTag)
+ private function _sanitizePitSearchResponse($response, $params, $queryTag)
{
+ $meta['timed_out'] = $response['timed_out'];
+ $meta['total'] = $response['hits']['total']['value'] ?? 0;
+ $meta['max_score'] = $response['hits']['max_score'] ?? 0;
+ $meta['sort'] = null;
+ $data = [];
+ if (! empty($response['hits']['hits'])) {
+ foreach ($response['hits']['hits'] as $hit) {
+ $datum = [];
+ $datum['_index'] = $hit['_index'];
+ $datum['_id'] = $hit['_id'];
+ if (! empty($hit['_source'])) {
+ foreach ($hit['_source'] as $key => $value) {
+ $datum[$key] = $value;
+ }
+ }
+ if (! empty($hit['sort'][0])) {
+ $meta['sort'] = $hit['sort'];
+ }
+ $data[] = $datum;
+ }
+ }
+
+ return $this->_return($data, $meta, $params, $queryTag);
+ }
+
+ private function _sanitizeSearchResponse($response, $params, $queryTag)
+ {
$meta['took'] = $response['took'] ?? 0;
$meta['timed_out'] = $response['timed_out'];
$meta['total'] = $response['hits']['total']['value'] ?? 0;
$meta['max_score'] = $response['hits']['max_score'] ?? 0;
$meta['shards'] = $response['_shards'] ?? [];
$data = [];
- if (!empty($response['hits']['hits'])) {
+ if (! empty($response['hits']['hits'])) {
foreach ($response['hits']['hits'] as $hit) {
$datum = [];
$datum['_index'] = $hit['_index'];
$datum['_id'] = $hit['_id'];
- if (!empty($hit['_source'])) {
+ if (! empty($hit['_source'])) {
foreach ($hit['_source'] as $key => $value) {
$datum[$key] = $value;
}
-
}
- if (!empty($hit['inner_hits'])) {
+ if (! empty($hit['inner_hits'])) {
foreach ($hit['inner_hits'] as $innerKey => $innerHit) {
$datum[$innerKey] = $this->_filterInnerHits($innerHit);
}
}
//Meta data
- if (!empty($hit['highlight'])) {
+ if (! empty($hit['highlight'])) {
$datum['_meta']['highlights'] = $this->_sanitizeHighlights($hit['highlight']);
}
$datum['_meta']['_index'] = $hit['_index'];
$datum['_meta']['_id'] = $hit['_id'];
- if (!empty($hit['_score'])) {
+ if (! empty($hit['_score'])) {
$datum['_meta']['_score'] = $hit['_score'];
}
$datum['_meta']['_query'] = $meta;
-
+ // If we are sorting we need to store it to be able to pass it on in the search after.
+ if (! empty($hit['sort'])) {
+ $datum['_meta']['sort'] = $hit['sort'];
+ }
+ $datum['_meta'] = $this->_attachStashedMeta($datum['_meta']);
$data[] = $datum;
}
}
@@ -1083,36 +1181,50 @@ private function _sanitizeSearchResponse($response, $params, $queryTag)
return $this->_return($data, $meta, $params, $queryTag);
}
- private function _sanitizeHighlights($highlights)
+ private function _sanitizeDistinctResponse($response, $columns, $includeDocCount): array
{
- //remove keyword results
- foreach ($highlights as $field => $vals) {
- if (str_contains($field, '.keyword')) {
- $cleanField = str_replace('.keyword', '', $field);
- if (isset($highlights[$cleanField])) {
- unset($highlights[$field]);
- } else {
- $highlights[$cleanField] = $vals;
- }
- }
+ $keys = [];
+ foreach ($columns as $column) {
+ $keys[] = 'by_'.$column;
}
- return $highlights;
+ return $this->_processBuckets($columns, $keys, $response, 0, $includeDocCount);
}
- public function _sanitizeAggsResponse($response, $params, $queryTag)
+ private function _processBuckets($columns, $keys, $response, $index, $includeDocCount, $currentData = []): array
{
- $meta['timed_out'] = $response['timed_out'];
- $meta['total'] = $response['hits']['total']['value'] ?? 0;
- $meta['max_score'] = $response['hits']['max_score'] ?? 0;
- $meta['sorts'] = [];
+ $data = [];
+ if (! empty($response[$keys[$index]]['buckets'])) {
+ foreach ($response[$keys[$index]]['buckets'] as $res) {
- $aggs = $response['aggregations'];
- $data = (count($aggs) === 1)
- ? reset($aggs)['value'] ?? 0
- : array_map(fn($value) => $value['value'] ?? 0, $aggs);
+ $datum = $currentData;
- return $this->_return($data, $meta, $params, $queryTag);
+ $col = $columns[$index];
+ if (str_contains($col, '.keyword')) {
+ $col = str_replace('.keyword', '', $col);
+ }
+
+ $datum[$col] = $res['key'];
+
+ if ($includeDocCount) {
+ $datum[$col.'_count'] = $res['doc_count'];
+ }
+
+ if (isset($columns[$index + 1])) {
+ $nestedData = $this->_processBuckets($columns, $keys, $res, $index + 1, $includeDocCount, $datum);
+
+ if (! empty($nestedData)) {
+ $data = array_merge($data, $nestedData);
+ } else {
+ $data[] = $datum;
+ }
+ } else {
+ $data[] = $datum;
+ }
+ }
+ }
+
+ return $data;
}
private function _sanitizeRawAggsResponse($response, $params, $queryTag)
@@ -1122,7 +1234,7 @@ private function _sanitizeRawAggsResponse($response, $params, $queryTag)
$meta['max_score'] = $response['hits']['max_score'] ?? 0;
$meta['sorts'] = [];
$data = [];
- if (!empty($response['aggregations'])) {
+ if (! empty($response['aggregations'])) {
foreach ($response['aggregations'] as $key => $values) {
$data[$key] = $this->_formatAggs($key, $values)[$key];
}
@@ -1131,24 +1243,21 @@ private function _sanitizeRawAggsResponse($response, $params, $queryTag)
return $this->_return($data, $meta, $params, $queryTag);
}
- private function _formatAggs($key, $values)
+ private function _sanitizeHighlights($highlights)
{
- $data[$key] = [];
- $aggTypes = ['buckets', 'values'];
-
- foreach ($values as $subKey => $value) {
- if (in_array($subKey, $aggTypes)) {
- $data[$key] = $this->_formatAggs($subKey, $value)[$subKey];
- } elseif (is_array($value)) {
- $data[$key][$subKey] = $this->_formatAggs($subKey, $value)[$subKey];
- } else {
- $data[$key][$subKey] = $value;
+ //remove keyword results
+ foreach ($highlights as $field => $vals) {
+ if (str_contains($field, '.keyword')) {
+ $cleanField = str_replace('.keyword', '', $field);
+ if (isset($highlights[$cleanField])) {
+ unset($highlights[$field]);
+ } else {
+ $highlights[$cleanField] = $vals;
+ }
}
-
}
- return $data;
-
+ return $highlights;
}
private function _filterInnerHits($innerHit)
@@ -1156,7 +1265,7 @@ private function _filterInnerHits($innerHit)
$hits = [];
foreach ($innerHit['hits']['hits'] as $inner) {
$innerDatum = [];
- if (!empty($inner['_source'])) {
+ if (! empty($inner['_source'])) {
foreach ($inner['_source'] as $innerSourceKey => $innerSourceValue) {
$innerDatum[$innerSourceKey] = $innerSourceValue;
}
@@ -1167,113 +1276,32 @@ private function _filterInnerHits($innerHit)
return $hits;
}
- private function _sanitizePitSearchResponse($response, $params, $queryTag)
- {
-
- $meta['timed_out'] = $response['timed_out'];
- $meta['total'] = $response['hits']['total']['value'] ?? 0;
- $meta['max_score'] = $response['hits']['max_score'] ?? 0;
- $meta['last_sort'] = null;
- $data = [];
- if (!empty($response['hits']['hits'])) {
- foreach ($response['hits']['hits'] as $hit) {
- $datum = [];
- $datum['_index'] = $hit['_index'];
- $datum['_id'] = $hit['_id'];
- if (!empty($hit['_source'])) {
- foreach ($hit['_source'] as $key => $value) {
- $datum[$key] = $value;
- }
- }
- if (!empty($hit['sort'][0])) {
- $meta['last_sort'] = $hit['sort'];
- }
- $data[] = $datum;
-
- }
- }
-
- return $this->_return($data, $meta, $params, $queryTag);
- }
-
-
- private function _parseSort($sort, $sortParams)
- {
- $sortValues = [];
- foreach ($sort as $key => $value) {
- $sortValues[array_key_first($sortParams[$key])] = $value;
- }
-
- return $sortValues;
- }
-
- private function _sanitizeDistinctResponse($response, $columns, $includeDocCount)
- {
- $keys = [];
- foreach ($columns as $column) {
- $keys[] = 'by_'.$column;
- }
-
- return $this->processBuckets($columns, $keys, $response, 0, $includeDocCount);
-
- }
-
- private function processBuckets($columns, $keys, $response, $index, $includeDocCount, $currentData = [])
+ private function _formatAggs($key, $values)
{
- $data = [];
- if (!empty($response[$keys[$index]]['buckets'])) {
- foreach ($response[$keys[$index]]['buckets'] as $res) {
-
- $datum = $currentData;
-
- $col = $columns[$index];
- if (str_contains($col, '.keyword')) {
- $col = str_replace('.keyword', '', $col);
- }
-
- $datum[$col] = $res['key'];
-
- if ($includeDocCount) {
- $datum[$col.'_count'] = $res['doc_count'];
- }
-
- if (isset($columns[$index + 1])) {
- $nestedData = $this->processBuckets($columns, $keys, $res, $index + 1, $includeDocCount, $datum);
+ $data[$key] = [];
+ $aggTypes = ['buckets', 'values'];
- if (!empty($nestedData)) {
- $data = array_merge($data, $nestedData);
- } else {
- $data[] = $datum;
- }
- } else {
- $data[] = $datum;
- }
+ foreach ($values as $subKey => $value) {
+ if (in_array($subKey, $aggTypes)) {
+ $data[$key] = $this->_formatAggs($subKey, $value)[$subKey];
+ } elseif (is_array($value)) {
+ $data[$key][$subKey] = $this->_formatAggs($subKey, $value)[$subKey];
+ } else {
+ $data[$key][$subKey] = $value;
}
}
return $data;
}
- private function _return($data, $meta, $params, $queryTag): Results
- {
- if (is_object($meta)) {
- $metaAsArray = [];
- if (method_exists($meta, 'asArray')) {
- $metaAsArray = $meta->asArray();
- }
- $results = new Results($data, $metaAsArray, $params, $queryTag);
- } else {
- $results = new Results($data, $meta, $params, $queryTag);
- }
-
- return $results;
- }
-
+ //======================================================================
+ // Error and logging
+ //======================================================================
/**
* @throws QueryException
*/
- private function throwError(Exception $exception, $params, $queryTag): QueryException
+ private function _throwError(Exception $exception, $params, $queryTag): QueryException
{
$previous = get_class($exception);
$errorMsg = $exception->getMessage();
@@ -1283,15 +1311,15 @@ private function throwError(Exception $exception, $params, $queryTag): QueryExce
$error = new Results([], [], $params, $queryTag);
$error->setError($errorMsg, $errorCode);
- $meta = $error->getMetaData();
+ $meta = $error->getMetaDataAsArray();
$details = [
- 'error' => $meta['error']['msg'],
- 'details' => $meta['error']['data'],
- 'code' => $errorCode,
+ 'error' => $meta['error']['msg'],
+ 'details' => $meta['error']['data'],
+ 'code' => $errorCode,
'exception' => $previous,
- 'query' => $queryTag,
- 'params' => $params,
- 'original' => $errorMsg,
+ 'query' => $queryTag,
+ 'params' => $params,
+ 'original' => $errorMsg,
];
if ($this->errorLogger) {
$this->_logQuery($error, $details);
@@ -1304,11 +1332,11 @@ private function _logQuery(Results $results, $details)
{
$body = $results->getLogFormattedMetaData();
if ($details) {
- $body['details'] = (array)$details;
+ $body['details'] = (array) $details;
}
$params = [
'index' => $this->errorLogger,
- 'body' => $body,
+ 'body' => $body,
];
try {
$this->client->index($params);
@@ -1317,4 +1345,31 @@ private function _logQuery(Results $results, $details)
}
}
+ //----------------------------------------------------------------------
+ // Meta Stasher
+ //----------------------------------------------------------------------
+
+ private function _stashMeta($meta): void
+ {
+ $this->stashedMeta = $meta;
+ }
+
+ private function _attachStashedMeta($meta): mixed
+ {
+ if (! empty($this->stashedMeta)) {
+ $meta = array_merge($meta, $this->stashedMeta);
+ }
+
+ return $meta;
+ }
+
+ // private function _parseSort($sort, $sortParams): array
+ // {
+ // $sortValues = [];
+ // foreach ($sort as $key => $value) {
+ // $sortValues[array_key_first($sortParams[$key])] = $value;
+ // }
+ //
+ // return $sortValues;
+ // }
}
diff --git a/src/DSL/IndexInterpreter.php b/src/DSL/IndexInterpreter.php
index 3f2effd..e59eeaa 100644
--- a/src/DSL/IndexInterpreter.php
+++ b/src/DSL/IndexInterpreter.php
@@ -1,5 +1,7 @@
$value) {
$params['body']['mappings'][$key] = $value;
}
}
- if (!empty($raw['properties'])) {
+ if (! empty($raw['properties'])) {
$properties = [];
foreach ($raw['properties'] as $prop) {
$field = $prop['field'];
unset($prop['field']);
- if (!empty($properties[$field])) {
+ if (! empty($properties[$field])) {
$type = $prop['type'];
foreach ($prop as $key => $value) {
$properties[$field]['fields'][$type][Str::snake($key)] = $value;
@@ -37,7 +39,7 @@ public function buildIndexMap($index, $raw): array
}
}
}
- if (!empty($properties)) {
+ if (! empty($properties)) {
$params['body']['mappings']['properties'] = $properties;
}
}
@@ -69,14 +71,13 @@ public function buildAnalyzerSettings($index, $raw): array
return $params;
}
-
public function catIndices($data, $all = false): array
{
- if (!$all && $data) {
+ if (! $all && $data) {
$indices = $data;
$data = [];
foreach ($indices as $index) {
- if (!(str_starts_with($index['index'], "."))) {
+ if (! (str_starts_with($index['index'], '.'))) {
$data[] = $index;
}
}
@@ -97,5 +98,4 @@ public function cleanData($data): array
return $data;
}
-
}
diff --git a/src/DSL/ParameterBuilder.php b/src/DSL/ParameterBuilder.php
index 1e99c52..e4fff58 100644
--- a/src/DSL/ParameterBuilder.php
+++ b/src/DSL/ParameterBuilder.php
@@ -1,5 +1,7 @@
[
- 'match_all' => new \stdClass(),
+ 'match_all' => new \stdClass,
],
];
}
- public static function queryStringQuery($string): array
- {
- return [
- 'query' => [
- 'query_string' => [
- 'query' => $string,
- ],
- ],
- ];
- }
+ // public static function queryStringQuery($string): array
+ // {
+ // return [
+ // 'query' => [
+ // 'query_string' => [
+ // 'query' => $string,
+ // ],
+ // ],
+ // ];
+ // }
public static function query($dsl): array
{
@@ -31,24 +33,23 @@ public static function query($dsl): array
];
}
-
public static function fieldSort($field, $payload, $allowId = false): array
{
- if ($field === '_id' && !$allowId) {
+ if ($field === '_id' && ! $allowId) {
return [];
}
- if (!empty($payload['is_geo'])) {
+ if (! empty($payload['is_geo'])) {
return self::fieldSortGeo($field, $payload);
}
- if (!empty($payload['is_nested'])) {
+ if (! empty($payload['is_nested'])) {
return self::filterNested($field, $payload);
}
$sort = [];
$sort['order'] = $payload['order'] ?? 'asc';
- if (!empty($payload['mode'])) {
+ if (! empty($payload['mode'])) {
$sort['mode'] = $payload['mode'];
}
- if (!empty($payload['missing'])) {
+ if (! empty($payload['missing'])) {
$sort['missing'] = $payload['missing'];
}
@@ -64,10 +65,10 @@ public static function fieldSortGeo($field, $payload): array
$sort['order'] = $payload['order'] ?? 'asc';
$sort['unit'] = $payload['unit'] ?? 'km';
- if (!empty($payload['mode'])) {
+ if (! empty($payload['mode'])) {
$sort['mode'] = $payload['mode'];
}
- if (!empty($payload['type'])) {
+ if (! empty($payload['type'])) {
$sort['distance_type'] = $payload['type'];
}
@@ -76,25 +77,58 @@ public static function fieldSortGeo($field, $payload): array
];
}
- public static function filterNested($field, $payload)
+ public static function filterNested($field, $payload): array
{
$sort = [];
$pathParts = explode('.', $field);
$path = $pathParts[0];
$sort['order'] = $payload['order'] ?? 'asc';
- if (!empty($payload['mode'])) {
+ if (! empty($payload['mode'])) {
$sort['mode'] = $payload['mode'];
}
$sort['nested'] = [
'path' => $path,
];
-
return [
$field => $sort,
];
}
+ public static function multipleAggregations($aggregations, $field): array
+ {
+ $aggs = [];
+ foreach ($aggregations as $aggregation) {
+ switch ($aggregation) {
+ case 'max':
+ $aggs['max_'.$field] = self::maxAggregation($field);
+ break;
+ case 'min':
+ $aggs['min_'.$field] = self::minAggregation($field);
+ break;
+ case 'avg':
+ $aggs['avg_'.$field] = self::avgAggregation($field);
+ break;
+ case 'sum':
+ $aggs['sum_'.$field] = self::sumAggregation($field);
+ break;
+ case 'matrix':
+ $aggs['matrix_'.$field] = self::matrixAggregation([$field]);
+ break;
+ case 'count':
+ $aggs['count_'.$field] = [
+ 'value_count' => [
+ 'field' => $field,
+ ],
+ ];
+ break;
+ }
+ }
+
+ return $aggs;
+
+ }
+
public static function maxAggregation($field): array
{
return [
@@ -139,40 +173,4 @@ public static function matrixAggregation(array $fields): array
],
];
}
-
-
- public static function multipleAggregations($aggregations, $field)
- {
- $aggs = [];
- foreach ($aggregations as $aggregation) {
- switch ($aggregation) {
- case 'max':
- $aggs['max_'.$field] = self::maxAggregation($field);
- break;
- case 'min':
- $aggs['min_'.$field] = self::minAggregation($field);
- break;
- case 'avg':
- $aggs['avg_'.$field] = self::avgAggregation($field);
- break;
- case 'sum':
- $aggs['sum_'.$field] = self::sumAggregation($field);
- break;
- case 'matrix':
- $aggs['matrix_'.$field] = self::matrixAggregation([$field]);
- break;
- case 'count':
- $aggs['count_'.$field] = [
- 'value_count' => [
- 'field' => $field,
- ],
- ];
- break;
- }
- }
-
- return $aggs;
-
- }
-
-}
\ No newline at end of file
+}
diff --git a/src/DSL/QueryBuilder.php b/src/DSL/QueryBuilder.php
index a4c6754..f8d4cbc 100644
--- a/src/DSL/QueryBuilder.php
+++ b/src/DSL/QueryBuilder.php
@@ -1,22 +1,24 @@
_clearAndStashMeta($searchOptions);
+ $options = $this->_clearAndStashMeta($options);
$params = [];
if ($index) {
$params['index'] = $index;
@@ -47,7 +51,7 @@ public function buildSearchParams($index, $searchQuery, $searchOptions, $wheres
}
}
- if (!empty($searchOptions['highlight'])) {
+ if (! empty($searchOptions['highlight'])) {
$params['body']['highlight'] = $searchOptions['highlight'];
unset($searchOptions['highlight']);
}
@@ -70,7 +74,7 @@ public function buildSearchParams($index, $searchQuery, $searchOptions, $wheres
if ($opts) {
foreach ($opts as $key => $value) {
if (isset($params[$key])) {
- $params[$key] = array_merge($params[$key], $opts[$key]);
+ $params[$key] = array_merge($params[$key], $value);
} else {
$params[$key] = $value;
}
@@ -91,6 +95,7 @@ public function buildSearchParams($index, $searchQuery, $searchOptions, $wheres
*/
public function buildParams($index, $wheres, $options = [], $columns = [], $_id = null): array
{
+ $options = $this->_clearAndStashMeta($options);
if ($index) {
$params = [
'index' => $index,
@@ -123,20 +128,17 @@ public function buildParams($index, $wheres, $options = [], $columns = [], $_id
return $params;
}
-
- public function createNestedAggs($columns, $sort)
+ public function createNestedAggs($columns, $sort): array
{
$aggs = [];
$terms = [
'terms' => [
'field' => $columns[0],
- 'size' => 10000,
+ 'size' => 10000,
],
];
if (isset($sort['_count'])) {
- if (!isset($terms['terms']['order'])) {
- $terms['terms']['order'] = [];
- }
+ $terms['terms']['order'] = [];
if ($sort['_count'] == 'asc') {
$terms['terms']['order'][] = ['_count' => 'asc'];
} else {
@@ -158,19 +160,18 @@ public function createNestedAggs($columns, $sort)
return $aggs;
}
-
public function addSearchToWheres($wheres, $queryString): array
{
$clause = ['_' => ['search' => $queryString]];
- if (!$wheres) {
+ if (! $wheres) {
return $clause;
}
- if (!empty($wheres['and'])) {
+ if (! empty($wheres['and'])) {
$wheres['and'][] = $clause;
return $wheres;
}
- if (!empty($wheres['or'])) {
+ if (! empty($wheres['or'])) {
$newOrs = [];
foreach ($wheres['or'] as $cond) {
$cond['and'][] = $clause;
@@ -184,38 +185,34 @@ public function addSearchToWheres($wheres, $queryString): array
return ['and' => [$wheres, $clause]];
}
-
//----------------------------------------------------------------------
// Parsers
//----------------------------------------------------------------------
- public function _escape($value): string
- {
- $specialChars = ['"', '\\', '~', '^', '/'];
- foreach ($specialChars as $char) {
- $value = str_replace($char, "\\".$char, $value);
- }
- if (str_starts_with($value, '-')) {
- $value = '\\'.$value;
- }
-
- return $value;
- }
-
/**
* @throws ParameterException
* @throws QueryException
*/
private function _buildQuery($wheres): array
{
- if (!$wheres) {
+ if (! $wheres) {
return ParameterBuilder::matchAll();
}
+
$dsl = $this->_convertWheresToDSL($wheres);
return ParameterBuilder::query($dsl);
}
+ private function _clearAndStashMeta($options): array
+ {
+ if (! empty($options['_meta'])) {
+ $this->_stashMeta($options['_meta']);
+ unset($options['_meta']);
+ }
+
+ return $options;
+ }
/**
* @throws ParameterException
@@ -230,7 +227,7 @@ public function _convertWheresToDSL($wheres, $parentField = false): array
$dsl['bool']['must'] = [];
foreach ($conditions as $condition) {
$parsedCondition = $this->_parseCondition($condition, $parentField);
- if (!empty($parsedCondition)) {
+ if (! empty($parsedCondition)) {
$dsl['bool']['must'][] = $parsedCondition;
}
}
@@ -242,12 +239,12 @@ public function _convertWheresToDSL($wheres, $parentField = false): array
foreach ($conditionGroup as $subConditions) {
foreach ($subConditions as $subCondition) {
$parsedCondition = $this->_parseCondition($subCondition, $parentField);
- if (!empty($parsedCondition)) {
+ if (! empty($parsedCondition)) {
$boolClause['bool']['must'][] = $parsedCondition;
}
}
}
- if (!empty($boolClause['bool']['must'])) {
+ if (! empty($boolClause['bool']['must'])) {
$dsl['bool']['should'][] = $boolClause;
}
}
@@ -268,15 +265,14 @@ private function _parseCondition($condition, $parentField = null): array
{
$field = key($condition);
if ($parentField) {
- if (!str_starts_with($field, $parentField.'.')) {
+ if (! str_starts_with($field, $parentField.'.')) {
$field = $parentField.'.'.$field;
}
}
$value = current($condition);
-
- if (!is_array($value)) {
+ if (! is_array($value)) {
return ['match' => [$field => $value]];
} else {
@@ -328,7 +324,7 @@ private function _parseCondition($condition, $parentField = null): array
break;
case 'in':
$keywordField = $this->parseRequiredKeywordMapping($field);
- if (!$keywordField) {
+ if (! $keywordField) {
$queryPart = ['terms' => [$field => $operand]];
} else {
$queryPart = ['terms' => [$keywordField => $operand]];
@@ -337,7 +333,7 @@ private function _parseCondition($condition, $parentField = null): array
break;
case 'nin':
$keywordField = $this->parseRequiredKeywordMapping($field);
- if (!$keywordField) {
+ if (! $keywordField) {
$queryPart = ['bool' => ['must_not' => ['terms' => [$field => $operand]]]];
} else {
$queryPart = ['bool' => ['must_not' => ['terms' => [$keywordField => $operand]]]];
@@ -358,7 +354,7 @@ private function _parseCondition($condition, $parentField = null): array
break;
case 'exact':
$keywordField = $this->parseRequiredKeywordMapping($field);
- if (!$keywordField) {
+ if (! $keywordField) {
throw new ParameterException('Field ['.$field.'] is not a keyword field which is required for the [exact] operator.');
}
$queryPart = ['term' => [$keywordField => $operand]];
@@ -370,8 +366,8 @@ private function _parseCondition($condition, $parentField = null): array
case 'nested':
$queryPart = [
'nested' => [
- 'path' => $field,
- 'query' => $this->_convertWheresToDSL($operand['wheres'], $field),
+ 'path' => $field,
+ 'query' => $this->_convertWheresToDSL($operand['wheres'], $field),
'score_mode' => $operand['score_mode'],
],
];
@@ -382,8 +378,8 @@ private function _parseCondition($condition, $parentField = null): array
'must_not' => [
[
'nested' => [
- 'path' => $field,
- 'query' => $this->_convertWheresToDSL($operand['wheres']),
+ 'path' => $field,
+ 'query' => $this->_convertWheresToDSL($operand['wheres']),
'score_mode' => $operand['score_mode'],
],
],
@@ -394,24 +390,24 @@ private function _parseCondition($condition, $parentField = null): array
break;
case 'innerNested':
$options = $this->_buildNestedOptions($operand['options'], $field);
- if (!$options) {
+ if (! $options) {
$options['size'] = 100;
}
$query = ParameterBuilder::matchAll()['query'];
- if (!empty($operand['wheres'])) {
+ if (! empty($operand['wheres'])) {
$query = $this->_convertWheresToDSL($operand['wheres'], $field);
}
$queryPart = [
'nested' => [
- 'path' => $field,
- 'query' => $query,
+ 'path' => $field,
+ 'query' => $query,
'inner_hits' => $options,
],
];
break;
default:
- abort('400', 'Invalid operator ['.$operator.'] provided for condition.');
+ abort(400, 'Invalid operator ['.$operator.'] provided for condition.');
}
return $queryPart;
@@ -427,11 +423,17 @@ private function _buildOptions($options): array
if ($options) {
foreach ($options as $key => $value) {
switch ($key) {
+ case 'prev_search_after':
+ $return['_meta']['prev_search_after'] = $value;
+ break;
+ case 'search_after':
+ $return['body']['search_after'] = $value;
+ break;
case 'limit':
$return['size'] = $value;
break;
case 'sort':
- if (!isset($return['body']['sort'])) {
+ if (! isset($return['body']['sort'])) {
$return['body']['sort'] = [];
}
foreach ($value as $field => $sortPayload) {
@@ -470,20 +472,20 @@ private function _buildOptions($options): array
/**
* @throws ParameterException
*/
- private function _buildNestedOptions($options, $field)
+ private function _buildNestedOptions($options, $field): array
{
$options = $this->_buildOptions($options);
- if (!empty($options['body'])) {
+ if (! empty($options['body'])) {
$body = $options['body'];
unset($options['body']);
$options = array_merge($options, $body);
}
- if (!empty($options['sort'])) {
+ if (! empty($options['sort'])) {
//ensure that the sort field is prefixed with the nested field
$sorts = [];
foreach ($options['sort'] as $sort) {
foreach ($sort as $sortField => $sortPayload) {
- if (!str_starts_with($sortField, $field.'.')) {
+ if (! str_starts_with($sortField, $field.'.')) {
$sortField = $field.'.'.$sortField;
}
$sorts[] = [$sortField => $sortPayload];
@@ -501,13 +503,13 @@ public function _parseFilter($filterType, $filterPayload): void
switch ($filterType) {
case 'filterGeoBox':
self::$filter['filter']['geo_bounding_box'][$filterPayload['field']] = [
- 'top_left' => $filterPayload['topLeft'],
+ 'top_left' => $filterPayload['topLeft'],
'bottom_right' => $filterPayload['bottomRight'],
];
break;
case 'filterGeoPoint':
self::$filter['filter']['geo_distance'] = [
- 'distance' => $filterPayload['distance'],
+ 'distance' => $filterPayload['distance'],
$filterPayload['field'] => [
'lat' => $filterPayload['geoPoint'][0],
'lon' => $filterPayload['geoPoint'][1],
@@ -518,8 +520,7 @@ public function _parseFilter($filterType, $filterPayload): void
}
}
-
- public function _parseFilterParameter($params, $filer)
+ public function _parseFilterParameter($params, $filer): array
{
$body = $params['body'];
$currentQuery = $body['query'];
@@ -527,7 +528,7 @@ public function _parseFilterParameter($params, $filer)
$filteredBody = [
'query' => [
'bool' => [
- 'must' => [
+ 'must' => [
$currentQuery,
],
'filter' => $filer['filter'],
diff --git a/src/DSL/Results.php b/src/DSL/Results.php
index 63c80fd..1afb8c5 100644
--- a/src/DSL/Results.php
+++ b/src/DSL/Results.php
@@ -1,112 +1,83 @@
data = $data;
- $this->_meta = ['query' => $queryTag] + $meta;
- $this->_meta['params'] = $params;
- $this->_meta['_id'] = $data['_id'] ?? null;
- $this->_meta['success'] = true;
-
+ $this->_meta = new QueryMetaData($meta);
+ $this->_meta->setQuery($queryTag);
+ $this->_meta->setSuccess();
+ $this->_meta->setDsl($params);
+ if (! empty($data['_id'])) {
+ $this->_meta->setId($data['_id']);
+ }
+ if (! empty($meta['deleteCount'])) {
+ $this->_meta->setDeleted($meta['deleteCount']);
+ }
+ if (! empty($meta['modified'])) {
+ $this->_meta->setModified($meta['modified']);
+ }
+ if (! empty($meta['failed'])) {
+ $this->_meta->setFailed($meta['failed']);
+ }
}
public function setError($error, $errorCode): void
{
- $details = $this->_decodeError($error);
- $this->_meta['error']['msg'] = $details['msg'];
- $this->_meta['error']['data'] = $details['data'];
- $this->_meta['error']['code'] = $errorCode;
- $this->_meta['success'] = false;
- $this->errorMessage = $error;
-
+ $this->_meta->parseAndSetError($error, $errorCode);
}
- private function _decodeError($error)
+ public function isSuccessful(): bool
{
- $return['msg'] = $error;
- $return['data'] = [];
- $jsonStartPos = strpos($error, ': ') + 2;
- $response = ($error);
- $title = substr($response, 0, $jsonStartPos);
- $jsonString = substr($response, $jsonStartPos);
- $errorArray = json_decode($jsonString, true);
-
- if (json_last_error() === JSON_ERROR_NONE) {
- $errorReason = $errorArray['error']['reason'] ?? null;
- if (!$errorReason) {
- return $return;
- }
- $return['msg'] = $title.$errorReason;
- $cause = $errorArray['error']['root_cause'][0]['reason'] ?? null;
- if ($cause) {
- $return['msg'] .= ' - '.$cause;
- }
-
- $return['data'] = $errorArray;
-
- }
-
- return $return;
+ return $this->_meta->isSuccessful();
}
- public function isSuccessful(): bool
+ public function getMetaData(): QueryMetaData
{
- return $this->_meta['success'] ?? false;
+ return $this->_meta;
}
- public function getMetaData(): array
+ public function getMetaDataAsArray(): array
{
- return $this->_meta;
+ return $this->_meta->asArray();
}
public function getLogFormattedMetaData(): array
{
$return = [];
- foreach ($this->_meta as $key => $value) {
+ $meta = $this->getMetaDataAsArray();
+ foreach ($meta as $key => $value) {
$return['logged_'.$key] = $value;
}
return $return;
}
- public function getInsertedId(): string|null
+ public function getInsertedId(): mixed
{
- return $this->_meta['_id'] ?? null;
+ return $this->_meta->getId();
}
-
public function getModifiedCount(): int
{
- return $this->_meta['modified'] ?? 0;
+ return $this->_meta->getModified();
}
public function getDeletedCount(): int
{
- return $this->_meta['deleted'] ?? 0;
- }
-
- private function _isJson($string): bool
- {
- json_decode($string);
-
- return (json_last_error() == JSON_ERROR_NONE);
+ return $this->_meta->getDeleted();
}
-
}
diff --git a/src/DSL/exceptions/ParameterException.php b/src/DSL/exceptions/ParameterException.php
index 71fc429..d87679a 100644
--- a/src/DSL/exceptions/ParameterException.php
+++ b/src/DSL/exceptions/ParameterException.php
@@ -1,22 +1,24 @@
_details = $details;
}
- public function getDetails()
+ public function getDetails(): array
{
return $this->_details;
}
-}
\ No newline at end of file
+}
diff --git a/src/DSL/exceptions/QueryException.php b/src/DSL/exceptions/QueryException.php
index 8809b52..0f4d60d 100644
--- a/src/DSL/exceptions/QueryException.php
+++ b/src/DSL/exceptions/QueryException.php
@@ -1,22 +1,24 @@
_details = $details;
}
- public function getDetails()
+ public function getDetails(): array
{
return $this->_details;
}
-}
\ No newline at end of file
+}
diff --git a/src/ElasticServiceProvider.php b/src/ElasticServiceProvider.php
index b1ba245..0c08196 100644
--- a/src/ElasticServiceProvider.php
+++ b/src/ElasticServiceProvider.php
@@ -1,5 +1,7 @@
app['db']);
Model::setEventDispatcher($this->app['events']);
@@ -19,7 +21,7 @@ public function boot()
/**
* Register the service provider.
*/
- public function register()
+ public function register(): void
{
// Add database driver.
$this->app->resolving('db', function ($db) {
diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php
index b5bcc18..123d9c1 100644
--- a/src/Eloquent/Builder.php
+++ b/src/Eloquent/Builder.php
@@ -1,14 +1,31 @@
*/
protected $passthru = [
'aggregate',
@@ -57,117 +74,152 @@ class Builder extends BaseEloquentBuilder
'search',
'todsl',
'agg',
+ 'insertwithoutrefresh',
];
-
/**
- * @inheritDoc
+ * @inerhitDoc
*/
- public function getConnection()
+ public function getConnection(): ConnectionInterface
{
return $this->query->getConnection();
}
/**
- * @inerhitDoc
+ * Override the default getModels
+ *
+ * @return array
*/
- public function getModels($columns = ['*'])
+ public function getModels($columns = ['*']): array
{
-
$data = $this->query->get($columns);
+ $meta = $data->getQueryMeta();
$results = $this->model->hydrate($data->all())->all();
- return ['results' => $results];
-
+ return [
+ 'results' => $results,
+ 'meta' => $meta,
+ ];
}
- /**
- * @see getModels($columns = ['*'])
- */
- public function searchModels($columns = ['*'])
+ public function getModel(): Model
{
-
- $data = $this->query->search($columns);
- $results = $this->model->hydrate($data->all())->all();
-
- return ['results' => $results];
-
+ return $this->model;
}
/**
- * @inerhitDoc
+ * @param string[] $columns
+ * @return TCollection
*/
- public function get($columns = ['*'])
+ public function get($columns = ['*']): ElasticCollection
{
$builder = $this->applyScopes();
$fetch = $builder->getModels($columns);
+ $meta = $fetch['meta'];
if (count($models = $fetch['results']) > 0) {
$models = $builder->eagerLoadRelations($models);
}
+ $elasticCollection = $builder->getModel()->newCollection($models);
- return $builder->getModel()->newCollection($models);
+ $elasticCollection->setQueryMeta($meta);
+ return $elasticCollection;
}
/**
- * @see get($columns = ['*'])
+ * Hydrate the models from the given array.
*/
- public function search($columns = ['*'])
+ public function hydrate(array $items): ElasticCollection
{
- $builder = $this->applyScopes();
- $fetch = $builder->searchModels($columns);
- if (count($models = $fetch['results']) > 0) {
- $models = $builder->eagerLoadRelations($models);
- }
+ $instance = $this->newModelInstance();
- return $builder->getModel()->newCollection($models);
+ return $instance->newCollection(array_map(function ($item) use ($items, $instance) {
+ $recordIndex = null;
+ if (is_array($item)) {
+ $recordIndex = ! empty($item['_index']) ? $item['_index'] : null;
+ if ($recordIndex) {
+ unset($item['_index']);
+ }
+ }
+ $meta = [];
+ if (isset($item['_meta'])) {
+ $meta = $item['_meta'];
+ unset($item['_meta']);
+ }
+ $instance->setMeta($meta);
+ $model = $instance->newFromBuilder($item);
+ if ($recordIndex) {
+ $model->setRecordIndex($recordIndex);
+ $model->setIndex($recordIndex);
+ }
+ if ($meta) {
+ $model->setMeta($meta);
+ }
+ if (count($items) > 1) {
+ $model->preventsLazyLoading = Model::preventsLazyLoading();
+ }
+
+ return $model;
+ }, $items));
}
/**
- * @param array $values
- *
- * @return array
+ * @see getModels($columns = ['*'])
*/
- protected function addUpdatedAtColumn(array $values)
+ public function searchModels($columns = ['*']): array
{
- if (!$this->model->usesTimestamps() || $this->model->getUpdatedAtColumn() === null) {
- return $values;
- }
- $column = $this->model->getUpdatedAtColumn();
- $values = array_merge([$column => $this->model->freshTimestampString()], $values);
+ $data = $this->query->search($columns);
+ $results = $this->model->hydrate($data->all())->all();
- return $values;
+ return ['results' => $results];
}
+ /**
+ * @see get($columns = ['*'])
+ */
+ public function search($columns = ['*']): Collection
+ {
+ $builder = $this->applyScopes();
+ $fetch = $builder->searchModels($columns);
+ if (count($models = $fetch['results']) > 0) {
+ $models = $builder->eagerLoadRelations($models);
+ }
+
+ return $builder->getModel()->newCollection($models);
+ }
- public function firstOrCreate(array $attributes = [], array $values = [])
+ public function firstOrCreate(array $attributes = [], array $values = []): Model
{
$instance = $this->_instanceBuilder($attributes);
- if (!is_null($instance)) {
+ if (! is_null($instance)) {
return $instance;
}
return $this->create(array_merge($attributes, $values));
}
-
- /**
- *
- * Fast create method for 'write and forget'
- *
- * @param array $attributes
- *
- * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Support\HigherOrderTapProxy|mixed|Builder
- */
- public function createWithoutRefresh(array $attributes = [])
+ private function _instanceBuilder(array $attributes = [])
{
- return tap($this->newModelInstance($attributes), function ($instance) {
- $instance->saveWithoutRefresh();
- });
+ $instance = clone $this;
+
+ foreach ($attributes as $field => $value) {
+ $method = is_string($value) ? 'whereExact' : 'where';
+
+ if (is_array($value)) {
+ foreach ($value as $v) {
+ $specificMethod = is_string($v) ? 'whereExact' : 'where';
+ $instance = $instance->$specificMethod($field, $v);
+ }
+ } else {
+ $instance = $instance->$method($field, $value);
+ }
+ }
+
+ return $instance->first();
}
- public function updateWithoutRefresh(array $attributes = [])
+ public function updateWithoutRefresh(array $attributes = []): int
{
$query = $this->toBase();
$query->setRefresh(false);
@@ -175,11 +227,21 @@ public function updateWithoutRefresh(array $attributes = [])
return $query->update($this->addUpdatedAtColumn($attributes));
}
+ protected function addUpdatedAtColumn(array $values): array
+ {
+ if (! $this->model->usesTimestamps() || $this->model->getUpdatedAtColumn() === null) {
+ return $values;
+ }
+
+ $column = $this->model->getUpdatedAtColumn();
+
+ return array_merge([$column => $this->model->freshTimestampString()], $values);
+ }
public function firstOrCreateWithoutRefresh(array $attributes = [], array $values = [])
{
$instance = $this->_instanceBuilder($attributes);
- if (!is_null($instance)) {
+ if (! is_null($instance)) {
return $instance;
}
@@ -187,10 +249,29 @@ public function firstOrCreateWithoutRefresh(array $attributes = [], array $value
}
/**
- * @inheritdoc
+ * Fast create method for 'write and forget'
*/
- public function chunkById($count, callable $callback, $column = '_id', $alias = null, $keepAlive = '5m')
- {
+ public function createWithoutRefresh(array $attributes = []
+ ): \Illuminate\Database\Eloquent\Model|\Illuminate\Support\HigherOrderTapProxy|null|Builder {
+ return tap($this->newModelInstance($attributes), function ($instance) {
+ $instance->saveWithoutRefresh();
+ });
+ }
+
+ //----------------------------------------------------------------------
+ // ES Filters
+ //----------------------------------------------------------------------
+
+ /**
+ * {@inheritdoc}
+ */
+ public function chunkById(
+ mixed $count,
+ callable $callback,
+ mixed $column = '_id',
+ mixed $alias = null,
+ string $keepAlive = '5m'
+ ): bool {
$column ??= $this->defaultKeyName();
$alias ??= $column;
//remove sort
@@ -198,8 +279,6 @@ public function chunkById($count, callable $callback, $column = '_id', $alias =
if ($column === '_id') {
//Use PIT
-
-
return $this->_chunkByPit($count, $callback, $keepAlive);
} else {
$lastId = null;
@@ -212,10 +291,10 @@ public function chunkById($count, callable $callback, $column = '_id', $alias =
break;
}
if ($callback($results, $page) === false) {
- return false;
+ return true;
}
$aliasClean = $alias;
- if (substr($aliasClean, -8) == '.keyword') {
+ if (str_ends_with($aliasClean, '.keyword')) {
$aliasClean = substr($aliasClean, 0, -8);
}
$lastId = data_get($results->last(), $aliasClean);
@@ -228,335 +307,290 @@ public function chunkById($count, callable $callback, $column = '_id', $alias =
$page++;
} while ($countResults == $count);
-
- return true;
}
-
+ return true;
}
-
- public function chunk($count, callable $callback, $keepAlive = '5m')
+ private function _chunkByPit(mixed $count, callable $callback, string $keepAlive = '5m'): bool
{
- //default to using PIT
- return $this->_chunkByPit($count, $callback, $keepAlive);
- }
+ $pitId = $this->query->openPit($keepAlive);
+ $searchAfter = null;
+ $page = 1;
+ do {
+ $clone = clone $this;
+ $search = $clone->query->pitFind($count, $pitId, $searchAfter, $keepAlive);
+ $meta = $search->getMetaData();
+ $searchAfter = $meta->getSort();
+ $results = $this->hydrate($search->data);
+ $countResults = $results->count();
+
+ if ($countResults == 0) {
+ break;
+ }
+ if ($callback($results, $page) === false) {
+ return true;
+ }
+ unset($results);
+
+ $page++;
+ } while ($countResults == $count);
+
+ $this->query->closePit($pitId);
+
+ return true;
+ }
//----------------------------------------------------------------------
- // ES Filters
+ // ES Search query builders
//----------------------------------------------------------------------
- /**
- * @param string $field
- * @param array $topLeft
- * @param array $bottomRight
- *
- * @return $this
- */
- public function filterGeoBox(string $field, array $topLeft, array $bottomRight)
+ public function chunk(mixed $count, callable $callback, string $keepAlive = '5m'): bool
+ {
+ //default to using PIT
+ return $this->_chunkByPit($count, $callback, $keepAlive);
+ }
+
+ public function filterGeoBox(string $field, array $topLeft, array $bottomRight): self
{
$this->query->filterGeoBox($field, $topLeft, $bottomRight);
return $this;
}
- /**
- * @param string $field
- * @param string $distance
- * @param array $geoPoint
- *
- * @return $this
- */
- public function filterGeoPoint(string $field, string $distance, array $geoPoint)
+ public function filterGeoPoint(string $field, string $distance, array $geoPoint): self
{
$this->query->filterGeoPoint($field, $distance, $geoPoint);
return $this;
}
- //----------------------------------------------------------------------
- // ES Search query builders
- //----------------------------------------------------------------------
-
- /**
- * @param string $term
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function term(string $term, int $boostFactor = null)
+ public function term(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor);
return $this;
}
- /**
- * @param string $term
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function andTerm(string $term, int $boostFactor = null)
+ public function andTerm(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, 'AND');
return $this;
}
- /**
- * @param string $term
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function orTerm(string $term, int $boostFactor = null)
+ public function orTerm(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, 'OR');
return $this;
}
- /**
- * @param string $term
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function fuzzyTerm(string $term, int $boostFactor = null)
+ public function fuzzyTerm(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, null, 'fuzzy');
return $this;
}
- /**
- * @param string $term
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function andFuzzyTerm(string $term, int $boostFactor = null)
+ public function andFuzzyTerm(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, 'AND', 'fuzzy');
return $this;
}
- /**
- * @param string $term
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function orFuzzyTerm(string $term, int $boostFactor = null)
+ public function orFuzzyTerm(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, 'OR', 'fuzzy');
return $this;
}
- /**
- * @param string $regEx
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function regEx(string $regEx, int $boostFactor = null)
+ public function regEx(string $regEx, ?int $boostFactor = null): self
{
$this->query->searchQuery($regEx, $boostFactor, null, 'regex');
return $this;
}
- /**
- * @param string $regEx
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function andRegEx(string $regEx, int $boostFactor = null)
+ public function andRegEx(string $regEx, ?int $boostFactor = null): self
{
$this->query->searchQuery($regEx, $boostFactor, 'AND', 'regex');
return $this;
}
- /**
- * @param string $regEx
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function orRegEx(string $regEx, int $boostFactor = null)
+ public function orRegEx(string $regEx, ?int $boostFactor = null): self
{
$this->query->searchQuery($regEx, $boostFactor, 'OR', 'regex');
return $this;
}
-
- public function phrase(string $term, int $boostFactor = null)
+ public function phrase(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, null, 'phrase');
return $this;
}
- public function andPhrase(string $term, int $boostFactor = null)
+ public function andPhrase(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, 'AND', 'phrase');
return $this;
}
- public function orPhrase(string $term, int $boostFactor = null)
+ public function orPhrase(string $term, ?int $boostFactor = null): self
{
$this->query->searchQuery($term, $boostFactor, 'OR', 'phrase');
return $this;
}
- /**
- * @param $value
- *
- * @return $this
- */
- public function minShouldMatch($value)
+ public function minShouldMatch($value): self
{
$this->query->minShouldMatch($value);
return $this;
}
- /**
- * @param float $value
- *
- * @return $this
- */
- public function minScore(float $value)
+ public function minScore(float $value): self
{
$this->query->minScore($value);
return $this;
}
- /**
- * @param string $field
- * @param int|null $boostFactor
- *
- * @return $this
- */
- public function field(string $field, int $boostFactor = null)
+ // Elastic type paginator that uses the search_after instead of limiting to Max results.
+
+ public function field(string $field, ?int $boostFactor = null): self
{
$this->query->searchField($field, $boostFactor);
return $this;
}
- /**
- * @param array $fields
- *
- * @return $this
- */
- public function fields(array $fields)
+ public function fields(array $fields): self
{
$this->query->searchFields($fields);
return $this;
}
- public function hydrate(array $items)
+ //----------------------------------------------------------------------
+ // Inherited as is but typed
+ //----------------------------------------------------------------------
+ /**
+ * Create a new instance of the model being queried.
+ *
+ * @param array $attributes
+ */
+ public function newModelInstance($attributes = []): Model
{
- $instance = $this->newModelInstance();
-
- return $instance->newCollection(array_map(function ($item) use ($items, $instance) {
- $recordIndex = null;
- if (is_array($item)) {
- $recordIndex = !empty($item['_index']) ? $item['_index'] : null;
- if ($recordIndex) {
- unset($item['_index']);
- }
- }
- $meta = [];
- if (isset($item['_meta'])) {
- $meta = $item['_meta'];
- unset($item['_meta']);
- }
- $model = $instance->newFromBuilder($item);
- if ($recordIndex) {
- $model->setRecordIndex($recordIndex);
- $model->setIndex($recordIndex);
-
- }
- if ($meta) {
- $model->setMeta($meta);
- }
- if (count($items) > 1) {
- $model->preventsLazyLoading = Model::preventsLazyLoading();
- }
+ return $this->model->newInstance($attributes)->setConnection($this->query->getConnection()->getName());
+ }
- return $model;
- }, $items));
+ /**
+ * Override the default schema builder.
+ */
+ public function toBase(): QueryBuilder
+ {
+ return $this->applyScopes()->getQuery();
}
+ public function create(array $attributes = []): Model
+ {
+ return tap($this->newModelInstance($attributes), function ($instance) {
+ $instance->save();
+ });
+ }
+ public function getQuery(): QueryBuilder
+ {
+ return $this->query;
+ }
//----------------------------------------------------------------------
// Private methods
//----------------------------------------------------------------------
- private function _instanceBuilder(array $attributes = [])
+ /**
+ * Using Laravel base method name rather
+ *
+ * @throws MissingOrderException|BindingResolutionException
+ */
+ public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null): SearchAfterPaginator
{
- $instance = clone $this;
+ if (empty($this->query->orders)) {
+ //try set created_at & updated_at
+ if (! $this->inferSort()) {
+ throw new MissingOrderException;
+ }
+ } elseif (count($this->query->orders) === 1) {
+ //try set a tie-breaker with created_at & updated_at
+ $this->inferSort();
+ }
- foreach ($attributes as $field => $value) {
- $method = is_string($value) ? 'whereExact' : 'where';
+ if (! $cursor instanceof Cursor) {
+ $cursor = is_string($cursor) ? Cursor::fromEncoded($cursor) : CursorPaginator::resolveCurrentCursor('cursor', $cursor);
+ }
- if (is_array($value)) {
- foreach ($value as $v) {
- $specificMethod = is_string($v) ? 'whereExact' : 'where';
- $instance = $instance->$specificMethod($field, $v);
- }
- } else {
- $instance = $instance->$method($field, $value);
- }
+ $this->query->limit($perPage);
+ $cursorPayload = $this->query->initCursor($cursor);
+ $age = time() - $cursorPayload['ts'];
+ $ttl = 300; //5 minutes
+ if ($age > $ttl) {
+ // cursor is older than 5m, let's refresh it
+ $clone = $this->clone();
+ $cursorPayload['records'] = $clone->count();
+ $cursorPayload['pages'] = (int) ceil($cursorPayload['records'] / $perPage);
+ $cursorPayload['ts'] = time();
}
+ if ($cursorPayload['next_sort'] && ! in_array($cursorPayload['next_sort'], $cursorPayload['sort_history'])) {
+ $cursorPayload['sort_history'][] = $cursorPayload['next_sort'];
+ }
+ $this->query->cursor = $cursorPayload;
+ $search = $this->get($columns);
- return $instance->first();
+ return $this->searchAfterPaginator($search, $perPage, $cursor, [
+ 'path' => Paginator::resolveCurrentPath(),
+ 'cursorName' => 'cursor',
+ 'records' => $cursorPayload['records'],
+ 'totalPages' => $cursorPayload['pages'],
+ 'currentPage' => $cursorPayload['page'],
+ ]);
}
-
- private function _chunkByPit($count, callable $callback, $keepAlive = '5m')
+ protected function inferSort(): bool
{
- $pitId = $this->query->openPit($keepAlive);
+ $found = false;
+ $indexMappings = $this->query->getIndexMappings();
+ $mappings = reset($indexMappings);
+ $fields = $mappings['mappings']['properties'];
+ if (! empty($fields['created_at'])) {
+ $this->query->orderBy('created_at');
- $searchAfter = null;
- $page = 1;
- do {
- $clone = clone $this;
- $search = $clone->query->pitFind($count, $pitId, $searchAfter, $keepAlive);
- $meta = $search->getMetaData();
- $searchAfter = $meta['last_sort'];
- $results = $this->hydrate($search->data);
- $countResults = $results->count();
-
- if ($countResults == 0) {
- break;
- }
-
- if ($callback($results, $page) === false) {
- return false;
- }
+ $found = true;
+ }
+ if (! empty($fields['updated_at'])) {
+ $this->query->orderBy('updated_at');
- unset($results);
+ $found = true;
+ }
- $page++;
- } while ($countResults == $count);
+ return $found;
+ }
- $this->query->closePit($pitId);
+ /**
+ * @throws BindingResolutionException
+ */
+ protected function searchAfterPaginator($items, $perPage, $cursor, $options)
+ {
+ return Container::getInstance()->makeWith(SearchAfterPaginator::class, compact('items', 'perPage', 'cursor', 'options'));
}
}
diff --git a/src/Eloquent/Docs/ModelDocs.php b/src/Eloquent/Docs/ModelDocs.php
index b996b5e..4b75774 100644
--- a/src/Eloquent/Docs/ModelDocs.php
+++ b/src/Eloquent/Docs/ModelDocs.php
@@ -1,66 +1,89 @@
', string|array $postTag = '', $globalOptions = [])
- *
- * @method $this deleteIndexIfExists()
+ * @method static $this andPhrase(string $term, $boostFactor = null)
+ * @method static $this orPhrase(string $term, $boostFactor = null)
+ * @method static $this minShouldMatch(int $value)
+ * @method static $this minScore(float $value)
+ * @method static $this field(string $field, int $boostFactor = null)
+ * @method static $this fields(array $fields)
+ * @method static int|array sum(array|string $columns)
+ * @method static int|array min(array|string $columns)
+ * @method static int|array max(array|string $columns)
+ * @method static int|array avg(array|string $columns)
+ * @method static array getModels(array $columns = ['*'])
+ * @method static array searchModels(array $columns = ['*'])
+ * @method static ElasticCollection get(array $columns = ['*'])
+ * @method static Model|null first(array $columns = ['*'])
+ * @method static ElasticCollection search(array $columns = ['*'])
+ * @method static array toDsl(array $columns = ['*'])
+ * @method static mixed agg(array $functions, $column)
+ * @method static $this where(array|Closure|Expression|string $column, $operator = null, $value = null, $boolean = 'and')
+ * @method static $this whereDate($column, $operator = null, $value = null, $boolean = 'and')
+ * @method static $this whereTimestamp($column, $operator = null, $value = null, $boolean = 'and')
+ * @method static $this whereIn(string $column, array $values)
+ * @method static $this whereExact(string $column, string $value)
+ * @method static $this wherePhrase(string $column, string $value)
+ * @method static $this wherePhrasePrefix(string $column, string $value)
+ * @method static $this filterGeoBox(string $column, array $topLeftCoords, array $bottomRightCoords)
+ * @method static $this filterGeoPoint(string $column, string $distance, array $point)
+ * @method static $this whereRegex(string $column, string $regex)
+ * @method static $this whereNestedObject(string $column, Callable $callback, string $scoreType = 'avg')
+ * @method static $this whereNotNestedObject(string $column, Callable $callback, string $scoreType = 'avg')
+ * @method static $this firstOrCreate(array $attributes, array $values = [])
+ * @method static $this firstOrCreateWithoutRefresh(array $attributes, array $values = [])
+ * @method static $this orderBy(string $column, string $direction = 'asc', string $mode = null, array $missing = '_last')
+ * @method static $this orderByDesc(string $column, string $mode = null, array $missing = '_last')
+ * @method static $this orderByGeo(string $column, array $pin, $direction = 'asc', $unit = 'km', $mode = null, $type = 'arc')
+ * @method static $this orderByGeoDesc(string $column, array $pin, $unit = 'km', $mode = null, $type = 'arc')
+ * @method static $this orderByNested(string $column, string $direction = 'asc', string $mode = null)
+ * @method static bool chunk(mixed $count, callable $callback, string $keepAlive = '5m')
+ * @method static bool chunkById(mixed $count, callable $callback, $column = '_id', $alias = null, $keepAlive = '5m')
+ * @method static $this queryNested(string $column, Callable $callback)
+ * @method static array rawSearch(array $bodyParams, bool $returnRaw = false)
+ * @method static array rawAggregation(array $bodyParams)
+ * @method static $this highlight(array $fields = [], string|array $preTag = '', string|array $postTag = '', $globalOptions = [])
+ * @method static bool deleteIndexIfExists()
+ * @method static bool deleteIndex()
+ * @method static bool createIndex(array $settings = [])
+ * @method static array getIndexMappings()
+ * @method static array getIndexSettings()
+ * @method static bool indexExists()
+ * @method static LengthAwarePaginator paginate(int $perPage = 15, array $columns = ['*'], string $pageName = 'page', ?int $page = null, ?int $total = null)
+ * @method static SearchAfterPaginator cursorPaginate(int|null $perPage = null, array $columns = [], string $cursorName = 'cursor', ?Cursor $cursor = null)
+ * @method static string getQualifiedKeyName()
+ * @method static string getConnection()
+ * @method static void truncate()
+ * @method static ElasticCollection insert($values, $returnData = null):
+ * @method static ElasticCollection insertWithoutRefresh($values, $returnData = null)
*
+ * @property object $search_highlights
+ * @property object $with_highlights
+ * @property array $search_highlights_as_array
*
- * @mixin \Illuminate\Database\Query\Builder
+ * @mixin Builder
*/
-trait ModelDocs
-{
-}
+trait ModelDocs {}
diff --git a/src/Eloquent/HasCollection.php b/src/Eloquent/HasCollection.php
new file mode 100644
index 0000000..44c3e58
--- /dev/null
+++ b/src/Eloquent/HasCollection.php
@@ -0,0 +1,19 @@
+ $models
+ * @return \PDPhilip\Elasticsearch\Collection\ElasticCollection;
+ */
+ public function newCollection(array $models = []): ElasticCollection
+ {
+ return new ElasticCollection($models);
+ }
+}
diff --git a/src/Eloquent/HybridRelations.php b/src/Eloquent/HybridRelations.php
index 167b452..f9d572d 100644
--- a/src/Eloquent/HybridRelations.php
+++ b/src/Eloquent/HybridRelations.php
@@ -1,70 +1,67 @@
getForeignKey();
+ $foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
- $localKey = $localKey ? : $this->getKeyName();
+ $localKey = $localKey ?: $this->getKeyName();
return new HasOne($instance->newQuery(), $this, $foreignKey, $localKey);
}
/**
- * @inheritDoc
+ * {@inheritDoc}
*/
- public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
+ public function morphOne($related, $name, $type = null, $id = null, $localKey = null): MorphOne
{
$instance = new $related;
[$type, $id] = $this->getMorphs($name, $type, $id);
- $localKey = $localKey ? : $this->getKeyName();
+ $localKey = $localKey ?: $this->getKeyName();
return new MorphOne($instance->newQuery(), $this, $type, $id, $localKey);
-
}
/**
- * @inheritDoc
+ * {@inheritDoc}
*/
- public function hasMany($related, $foreignKey = null, $localKey = null)
+ public function hasMany($related, $foreignKey = null, $localKey = null): HasMany
{
- $foreignKey = $foreignKey ? : $this->getForeignKey();
+ $foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
- $localKey = $localKey ? : $this->getKeyName();
+ $localKey = $localKey ?: $this->getKeyName();
return new HasMany($instance->newQuery(), $this, $foreignKey, $localKey);
}
/**
- * @inheritDoc
+ * {@inheritDoc}
*/
- public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
+ public function morphMany($related, $name, $type = null, $id = null, $localKey = null): MorphMany
{
$instance = new $related;
@@ -73,24 +70,23 @@ public function morphMany($related, $name, $type = null, $id = null, $localKey =
$table = $instance->getTable();
- $localKey = $localKey ? : $this->getKeyName();
+ $localKey = $localKey ?: $this->getKeyName();
return new MorphMany($instance->newQuery(), $this, $type, $id, $localKey);
}
/**
- * @inheritDoc
+ * {@inheritDoc}
*/
- public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
+ public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null): BelongsTo
{
if ($relation === null) {
- [$current, $caller] = debug_backtrace(false, 2);
+ [$current, $caller] = debug_backtrace(0, 2);
$relation = $caller['function'];
}
-
if ($foreignKey === null) {
$foreignKey = Str::snake($relation).'_id';
}
@@ -99,15 +95,15 @@ public function belongsTo($related, $foreignKey = null, $otherKey = null, $relat
$query = $instance->newQuery();
- $otherKey = $otherKey ? : $instance->getKeyName();
+ $otherKey = $otherKey ?: $instance->getKeyName();
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
}
/**
- * @inheritDoc
+ * {@inheritDoc}
*/
- public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
+ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null): MorphTo
{
if ($name === null) {
[$current, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
@@ -118,9 +114,7 @@ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null
[$type, $id] = $this->getMorphs($name, $type, $id);
if (($class = $this->$type) === null) {
- return new MorphTo(
- $this->newQuery(), $this, $id, $ownerKey, $type, $name
- );
+ return new MorphTo($this->newQuery(), $this, $id, $ownerKey, $type, $name);
}
$class = $this->getActualClassNameForMorph($class);
@@ -129,45 +123,25 @@ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null
$ownerKey = $ownerKey ?? $instance->getKeyName();
- return new MorphTo(
- $instance->newQuery(), $this, $id, $ownerKey, $type, $name
- );
+ return new MorphTo($instance->newQuery(), $this, $id, $ownerKey, $type, $name);
}
/**
- * @inheritDoc
+ * {@inheritdoc}
*/
- public function belongsToMany($related, $collection = null, $foreignKey = null, $otherKey = null, $parentKey = null, $relatedKey = null, $relation = null)
+ public function newEloquentBuilder($query): EloquentBuilder|Builder
{
-
- if ($relation === null) {
- $relation = $this->guessBelongsToManyRelation();
- }
-
-
- if (!is_subclass_of($related, ParentModel::class)) {
- return parent::belongsToMany($related, $collection, $foreignKey, $otherKey, $parentKey, $relatedKey, $relation);
- }
-
- $foreignKey = $foreignKey ? : $this->getForeignKey().'s';
- $instance = new $related;
- $otherKey = $otherKey ? : $instance->getForeignKey().'s';
-
- if ($collection === null) {
- $collection = $instance->getTable();
+ if (is_subclass_of($this, ParentModel::class)) {
+ return new Builder($query);
}
- $query = $instance->newQuery();
-
- return new BelongsToMany($query, $this, $collection, $foreignKey, $otherKey, $parentKey ? : $this->getKeyName(), $relatedKey ? : $instance->getKeyName(), $relation
- );
+ return new EloquentBuilder($query);
}
/**
- * @inheritDoc
+ * {@inheritDoc}
*/
-
- protected function guessBelongsToManyRelation()
+ protected function guessBelongsToManyRelation(): string
{
if (method_exists($this, 'getBelongsToManyCaller')) {
return $this->getBelongsToManyCaller();
@@ -175,16 +149,4 @@ protected function guessBelongsToManyRelation()
return parent::guessBelongsToManyRelation();
}
-
- /**
- * @inheritdoc
- */
- public function newEloquentBuilder($query)
- {
- if (is_subclass_of($this, ParentModel::class)) {
- return new Builder($query);
- }
-
- return new EloquentBuilder($query);
- }
}
diff --git a/src/Eloquent/Model.php b/src/Eloquent/Model.php
index 50d940f..cabbfc2 100644
--- a/src/Eloquent/Model.php
+++ b/src/Eloquent/Model.php
@@ -1,5 +1,7 @@
forcePrimaryKey();
}
+ public function forcePrimaryKey(): void
+ {
+ $this->primaryKey = '_id';
+ }
- public function setIndex($index = null)
+ public function getRecordIndex(): ?string
{
- if ($index) {
- return $this->index = $index;
- }
- $this->index = $this->index ?? $this->getTable();
- unset($this->table);
+ return $this->recordIndex;
}
public function setRecordIndex($recordIndex = null)
@@ -62,11 +70,9 @@ public function setRecordIndex($recordIndex = null)
return $this->recordIndex = $this->index;
}
- public function getRecordIndex()
- {
- return $this->recordIndex;
- }
-
+ /**
+ * {@inheritdoc}
+ */
public function setTable($index)
{
$this->index = $index;
@@ -75,22 +81,10 @@ public function setTable($index)
return $this;
}
-
- public function forcePrimaryKey()
- {
- $this->primaryKey = '_id';
- }
-
-
- public function getMaxSize()
- {
- return static::MAX_SIZE;
- }
-
public function getIdAttribute($value = null)
{
// If no value for id, then set ES's _id
- if (!$value && array_key_exists('_id', $this->attributes)) {
+ if (! $value && array_key_exists('_id', $this->attributes)) {
$value = $this->attributes['_id'];
}
@@ -98,116 +92,104 @@ public function getIdAttribute($value = null)
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function getQualifiedKeyName()
+ public function getQualifiedKeyName(): string
{
return $this->getKeyName();
}
- /**
- * @inheritdoc
- */
- public function fromDateTime($value)
+ public function getMeta(): ModelMetaData
{
- return parent::asDateTime($value);
+ return $this->_meta;
}
- /**
- * @inheritdoc
- */
- protected function asDateTime($value)
+ public function getMetaAsArray(): array
{
-
- return parent::asDateTime($value);
+ return $this->_meta->asArray();
}
- /**
- * @inheritdoc
- */
- public function getDateFormat()
+ public function setMeta($meta): static
{
- return $this->dateFormat ? : 'Y-m-d H:i:s';
- }
-
- public function setMeta($meta)
- {
- $this->_meta = $meta;
+ $this->_meta = new ModelMetaData($meta);
return $this;
}
- public function getMeta()
- {
- return (object)$this->_meta;
- }
-
- public function getSearchHighlightsAttribute()
+ public function getSearchHighlightsAttribute(): ?object
{
- if (!empty($this->_meta['highlights'])) {
- $data = [];
- $this->_mergeFlatKeysIntoNestedArray($data, $this->_meta['highlights']);
-
- return (object)$data;
- }
-
- return null;
+ return $this->_meta->parseHighlights();
}
- public function getSearchHighlightsAsArrayAttribute()
+ public function getSearchHighlightsAsArrayAttribute(): array
{
- if (!empty($this->_meta['highlights'])) {
- return $this->_meta['highlights'];
- }
-
- return [];
+ return $this->_meta->getHighlights();
}
- public function getWithHighlightsAttribute()
+ public function getWithHighlightsAttribute(): object
{
$data = $this->attributes;
- $mutators = array_values(array_diff($this->getMutatedAttributes(), ['id', 'search_highlights', 'search_highlights_as_array', 'with_highlights']));
+ $mutators = array_values(array_diff($this->getMutatedAttributes(), [
+ 'id',
+ 'search_highlights',
+ 'search_highlights_as_array',
+ 'with_highlights',
+ ]));
if ($mutators) {
foreach ($mutators as $mutator) {
$data[$mutator] = $this->{$mutator};
}
}
- if (!empty($this->_meta['highlights'])) {
- $this->_mergeFlatKeysIntoNestedArray($data, $this->_meta['highlights']);
- }
- return (object)$data;
+ return (object) $this->_meta->parseHighlights($data);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function freshTimestamp()
+ public function freshTimestamp(): string
{
-// return Carbon::now()->toIso8601String();
+ // return Carbon::now()->toIso8601String();
return Carbon::now()->format($this->getDateFormat());
}
- public function getIndex()
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateFormat(): string
{
- return $this->index ? : parent::getTable();
+ return $this->dateFormat ?: 'Y-m-d H:i:s';
+ }
+
+ public function getIndex(): string
+ {
+ return $this->index ?: parent::getTable();
+ }
+
+ public function setIndex($index = null)
+ {
+ if ($index) {
+ return $this->index = $index;
+ }
+ $this->index = $this->index ?? $this->getTable();
+ unset($this->table);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function getTable()
+ public function getTable(): string
{
return $this->getIndex();
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function getAttribute($key)
+ public function getAttribute($key): mixed
{
- if (!$key) {
- return;
+ if (! $key) {
+ return null;
}
// Dot notation support.
@@ -219,22 +201,9 @@ public function getAttribute($key)
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- protected function getAttributeFromArray($key)
- {
- // Support keys in dot notation.
- if (Str::contains($key, '.')) {
- return Arr::get($this->attributes, $key);
- }
-
- return parent::getAttributeFromArray($key);
- }
-
- /**
- * @inheritdoc
- */
- public function setAttribute($key, $value)
+ public function setAttribute($key, $value): mixed
{
if (Str::contains($key, '.')) {
@@ -244,26 +213,40 @@ public function setAttribute($key, $value)
Arr::set($this->attributes, $key, $value);
- return;
+ return null;
}
return parent::setAttribute($key, $value);
}
+ public function fromDateTime(mixed $value): Carbon
+ {
+ return parent::asDateTime($value);
+ }
+
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function getCasts()
+ protected function asDateTime($value): Carbon
+ {
+
+ return parent::asDateTime($value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCasts(): array
{
return $this->casts;
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function originalIsEquivalent($key)
+ public function originalIsEquivalent($key): bool
{
- if (!array_key_exists($key, $this->original)) {
+ if (! array_key_exists($key, $this->original)) {
return false;
}
@@ -274,33 +257,68 @@ public function originalIsEquivalent($key)
return true;
}
- if (null === $attribute) {
+ if ($attribute === null) {
return false;
}
if ($this->hasCast($key, static::$primitiveCastTypes)) {
- return $this->castAttribute($key, $attribute) ===
- $this->castAttribute($key, $original);
+ return $this->castAttribute($key, $attribute) === $this->castAttribute($key, $original);
}
- return is_numeric($attribute) && is_numeric($original) && strcmp((string)$attribute, (string)$original) === 0;
+ return is_numeric($attribute) && is_numeric($original) && strcmp((string) $attribute, (string) $original) === 0;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getForeignKey(): string
+ {
+ return Str::snake(class_basename($this)).'_'.ltrim($this->primaryKey, '_');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function newEloquentBuilder($query): Builder
+ {
+ $builder = new Builder($query);
+
+ return $builder;
+ }
+
+ public function saveWithoutRefresh(array $options = []): bool
+ {
+ $this->mergeAttributesFromCachedCasts();
+
+ $query = $this->newModelQuery();
+ //@phpstan-ignore-next-line
+ $query->setRefresh(false);
+
+ if ($this->exists) {
+ //@phpstan-ignore-next-line
+ $saved = ! $this->isDirty() || $this->performUpdate($query);
+ } else {
+ //@phpstan-ignore-next-line
+ $saved = $this->performInsert($query);
+ }
+
+ if ($saved) {
+ $this->finishSave($options);
+ }
+
+ return $saved;
+ }
/**
* Append one or more values to the underlying attribute value and sync with original.
- *
- * @param string $column
- * @param array $values
- * @param bool $unique
*/
- protected function pushAttributeValues($column, array $values, $unique = false)
+ protected function pushAttributeValues(string $column, array $values, bool $unique = false): void
{
- $current = $this->getAttributeFromArray($column) ? : [];
+ $current = $this->getAttributeFromArray($column) ?: [];
foreach ($values as $value) {
// Don't add duplicate values when we only want unique values.
- if ($unique && (!is_array($current) || in_array($value, $current))) {
+ if ($unique && (! is_array($current) || in_array($value, $current))) {
continue;
}
@@ -312,15 +330,25 @@ protected function pushAttributeValues($column, array $values, $unique = false)
$this->syncOriginalAttribute($column);
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function getAttributeFromArray($key): mixed
+ {
+ // Support keys in dot notation.
+ if (Str::contains($key, '.')) {
+ return Arr::get($this->attributes, $key);
+ }
+
+ return parent::getAttributeFromArray($key);
+ }
+
/**
* Remove one or more values to the underlying attribute value and sync with original.
- *
- * @param string $column
- * @param array $values
*/
- protected function pullAttributeValues($column, array $values)
+ protected function pullAttributeValues(string $column, array $values): void
{
- $current = $this->getAttributeFromArray($column) ? : [];
+ $current = $this->getAttributeFromArray($column) ?: [];
if (is_array($current)) {
foreach ($values as $value) {
@@ -337,144 +365,88 @@ protected function pullAttributeValues($column, array $values)
$this->syncOriginalAttribute($column);
}
- /**
- * @inheritdoc
- */
- public function getForeignKey()
+ protected function newBaseQueryBuilder(): QueryBuilder
{
- return Str::snake(class_basename($this)).'_'.ltrim($this->primaryKey, '_');
- }
+ /** @phpstan-var Connection $connection */
+ $connection = $this->getConnection();
+ $connection->setIndex($this->getTable());
+ $connection->setMaxSize($this->getMaxSize());
- /**
- * Set the parent relation.
- *
- * @param \Illuminate\Database\Eloquent\Relations\Relation $relation
- */
- public function setParentRelation(Relation $relation)
- {
- $this->parentRelation = $relation;
+ return new QueryBuilder($connection, $connection->getPostProcessor());
}
/**
- * Get the parent relation.
+ * Get the database connection instance.
*
- * @return \Illuminate\Database\Eloquent\Relations\Relation
- */
- public function getParentRelation()
- {
- return $this->parentRelation;
- }
-
- /**
- * @inheritdoc
- */
- public function newEloquentBuilder($query)
- {
- $builder = new Builder($query);
-
- return $builder;
- }
-
- /**
- * @inheritdoc
+ *
+ * @throws \RuntimeException
*/
- protected function newBaseQueryBuilder()
+ public function getConnection(): Connection
{
- $connection = $this->getConnection();
- if (!($connection instanceof Connection)) {
+ $connection = clone static::resolveConnection($this->getConnectionName());
+ if (! ($connection instanceof Connection)) {
$config = $connection->getConfig() ?? null;
- if (!empty($config['driver'])) {
+ if (! empty($config['driver'])) {
throw new RuntimeException('Invalid connection settings; expected "elasticsearch", got "'.$config['driver'].'"');
} else {
throw new RuntimeException('Invalid connection settings; expected "elasticsearch"');
}
}
- $connection->setIndex($this->getTable());
- $connection->setMaxSize($this->getMaxSize());
-
+ return $connection;
+ }
- return new QueryBuilder($connection, $connection->getPostProcessor());
+ public function getMaxSize(): int
+ {
+ return static::MAX_SIZE;
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- protected function removeTableFromKey($key)
+ protected function removeTableFromKey($key): string
{
return $key;
}
-
/**
* Get loaded relations for the instance without parent.
- *
- * @return array
*/
- protected function getRelationsWithoutParent()
+ protected function getRelationsWithoutParent(): array
{
$relations = $this->getRelations();
- if ($parentRelation = $this->getParentRelation()) {
+ $parentRelation = $this->getParentRelation();
+ if ($parentRelation instanceof Relation) {
+ //@phpstan-ignore-next-line
unset($relations[$parentRelation->getQualifiedForeignKeyName()]);
}
return $relations;
}
-
- protected function isGuardableColumn($key)
+ /**
+ * Get the parent relation.
+ */
+ public function getParentRelation(): ?Relation
{
- return true;
+ return $this->parentRelation ?? null;
}
-
- public function saveWithoutRefresh(array $options = [])
+ /**
+ * Set the parent relation.
+ */
+ public function setParentRelation(Relation $relation): void
{
- $this->mergeAttributesFromCachedCasts();
-
- $query = $this->newModelQuery();
- $query->setRefresh(false);
-
- if ($this->exists) {
- $saved = $this->isDirty() ? $this->performUpdate($query) : true;
- } else {
- $saved = $this->performInsert($query);
- }
-
- if ($saved) {
- $this->finishSave($options);
- }
-
- return $saved;
+ $this->parentRelation = $relation;
}
-
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
- protected function _mergeFlatKeysIntoNestedArray(&$data, $attrs)
- {
- foreach ($attrs as $key => $value) {
- if ($value) {
- $value = implode('......', $value);
- $parts = explode('.', $key);
- $current = &$data;
-
- foreach ($parts as $partIndex => $part) {
- if ($partIndex === count($parts) - 1) {
- $current[$part] = $value;
- } else {
- if (!isset($current[$part]) || !is_array($current[$part])) {
- $current[$part] = [];
- }
- $current = &$current[$part];
- }
- }
- }
-
- }
+ protected function isGuardableColumn($key): bool
+ {
+ return true;
}
-
}
diff --git a/src/Eloquent/SoftDeletes.php b/src/Eloquent/SoftDeletes.php
index 4d8df3d..05ddece 100644
--- a/src/Eloquent/SoftDeletes.php
+++ b/src/Eloquent/SoftDeletes.php
@@ -1,5 +1,7 @@
getDeletedAtColumn();
}
@@ -19,20 +21,19 @@ public function getQualifiedDeletedAtColumn()
* Requires more testing
*/
-
-// protected function runSoftDelete()
-// {
-// $query = $this->setKeysForSaveQuery($this->newModelQuery());
-// $time = $this->freshTimestamp();
-// $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];
-// $this->{$this->getDeletedAtColumn()} = $time;
-// if ($this->usesTimestamps() && !is_null($this->getUpdatedAtColumn())) {
-// $this->{$this->getUpdatedAtColumn()} = $time;
-// $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
-// }
-// $query->updateWithoutRefresh($columns);
-// $this->syncOriginalAttributes(array_keys($columns));
-// $this->fireModelEvent('trashed', false);
-// }
+ // protected function runSoftDelete()
+ // {
+ // $query = $this->setKeysForSaveQuery($this->newModelQuery());
+ // $time = $this->freshTimestamp();
+ // $columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];
+ // $this->{$this->getDeletedAtColumn()} = $time;
+ // if ($this->usesTimestamps() && !is_null($this->getUpdatedAtColumn())) {
+ // $this->{$this->getUpdatedAtColumn()} = $time;
+ // $columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
+ // }
+ // $query->updateWithoutRefresh($columns);
+ // $this->syncOriginalAttributes(array_keys($columns));
+ // $this->fireModelEvent('trashed', false);
+ // }
}
diff --git a/src/Exceptions/MissingOrderException.php b/src/Exceptions/MissingOrderException.php
new file mode 100644
index 0000000..52e14e0
--- /dev/null
+++ b/src/Exceptions/MissingOrderException.php
@@ -0,0 +1,24 @@
+json([
+ 'error' => $this->getMessage(),
+ ], 400);
+ }
+}
diff --git a/src/Helpers/QueriesRelationships.php b/src/Helpers/QueriesRelationships.php
index 891c0a7..fc3ba38 100644
--- a/src/Helpers/QueriesRelationships.php
+++ b/src/Helpers/QueriesRelationships.php
@@ -1,7 +1,6 @@
=', $count = 1, $boolean = 'and', Closure $callback = null)
- {
+ public function has(
+ $relation,
+ $operator = '>=',
+ $count = 1,
+ $boolean = 'and',
+ ?Closure $callback = null
+ ): Builder|static {
if (is_string($relation)) {
- if (strpos($relation, '.') !== false) {
+ if (str_contains($relation, '.')) {
return $this->hasNested($relation, $operator, $count, $boolean, $callback);
}
@@ -39,6 +42,7 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C
// If this is a hybrid relation then we can not use a normal whereExists() query that relies on a subquery
// We need to use a `whereIn` query
+ //@phpstan-ignore-next-line
if ($this->getModel() instanceof Model || $this->isAcrossConnections($relation)) {
return $this->addHybridHas($relation, $operator, $count, $boolean, $callback);
}
@@ -46,13 +50,10 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C
// If we only need to check for the existence of the relation, then we can optimize
// the subquery to only run a "where exists" clause instead of this full "count"
// clause. This will make these queries run much faster compared with a count.
- $method = $this->canUseExistsForExistenceCheck($operator, $count)
- ? 'getRelationExistenceQuery'
- : 'getRelationExistenceCountQuery';
+ //@phpstan-ignore-next-line
+ $method = $this->canUseExistsForExistenceCheck($operator, $count) ? 'getRelationExistenceQuery' : 'getRelationExistenceCountQuery';
- $hasQuery = $relation->{$method}(
- $relation->getRelated()->newQuery(), $this
- );
+ $hasQuery = $relation->{$method}($relation->getRelated()->newQuery(), $this);
// Next we will call any given callback as an "anonymous" scope so they can get the
// proper logical grouping of the where clauses if needed by this Eloquent query
@@ -61,17 +62,10 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C
$hasQuery->callScope($callback);
}
- return $this->addHasWhere(
- $hasQuery, $relation, $operator, $count, $boolean
- );
+ return $this->addHasWhere($hasQuery, $relation, $operator, $count, $boolean);
}
- /**
- * @param Relation $relation
- *
- * @return bool
- */
- protected function isAcrossConnections(Relation $relation)
+ protected function isAcrossConnections(Relation $relation): bool
{
return $relation->getParent()->getConnectionName() !== $relation->getRelated()->getConnectionName();
}
@@ -79,17 +73,16 @@ protected function isAcrossConnections(Relation $relation)
/**
* Compare across databases.
*
- * @param Relation $relation
- * @param string $operator
- * @param int $count
- * @param string $boolean
- * @param Closure|null $callback
*
- * @return mixed
* @throws Exception
*/
- public function addHybridHas(Relation $relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
- {
+ public function addHybridHas(
+ Relation $relation,
+ string $operator = '>=',
+ int $count = 1,
+ string $boolean = 'and',
+ ?Closure $callback = null
+ ): mixed {
$hasQuery = $relation->getQuery();
if ($callback) {
$hasQuery->callScope($callback);
@@ -99,7 +92,7 @@ public function addHybridHas(Relation $relation, $operator = '>=', $count = 1, $
$not = in_array($operator, ['<', '<=', '!=']);
// If we are comparing to 0, we need an additional $not flip.
if ($count == 0) {
- $not = !$not;
+ $not = ! $not;
}
$relations = $hasQuery->pluck($this->getHasCompareKey($relation));
@@ -109,31 +102,20 @@ public function addHybridHas(Relation $relation, $operator = '>=', $count = 1, $
return $this->whereIn($this->getRelatedConstraintKey($relation), $relatedIds, $boolean, $not);
}
- /**
- * @param Relation $relation
- *
- * @return string
- */
- protected function getHasCompareKey(Relation $relation)
+ protected function getHasCompareKey(Relation $relation): string
{
if (method_exists($relation, 'getHasCompareKey')) {
return $relation->getHasCompareKey();
}
+ //@phpstan-ignore-next-line
return $relation instanceof HasOneOrMany ? $relation->getForeignKeyName() : $relation->getOwnerKeyName();
}
- /**
- * @param $relations
- * @param $operator
- * @param $count
- *
- * @return array
- */
- protected function getConstrainedRelatedIds($relations, $operator, $count)
+ protected function getConstrainedRelatedIds($relations, $operator, $count): array
{
$relationCount = array_count_values(array_map(function ($id) {
- return (string)$id; // Convert Back ObjectIds to Strings
+ return (string) $id; // Convert Back ObjectIds to Strings
}, is_array($relations) ? $relations : $relations->flatten()->toArray()));
// Remove unwanted related objects based on the operator and count.
$relationCount = array_filter($relationCount, function ($counted) use ($count, $operator) {
@@ -161,12 +143,11 @@ protected function getConstrainedRelatedIds($relations, $operator, $count)
/**
* Returns key we are constraining this parent model's query with.
*
- * @param Relation $relation
*
- * @return string
+ *
* @throws Exception
*/
- protected function getRelatedConstraintKey(Relation $relation)
+ protected function getRelatedConstraintKey(Relation $relation): string
{
if ($relation instanceof HasOneOrMany) {
return $relation->getLocalKeyName();
@@ -176,7 +157,7 @@ protected function getRelatedConstraintKey(Relation $relation)
return $relation->getForeignKeyName();
}
- if ($relation instanceof BelongsToMany && !$this->isAcrossConnections($relation)) {
+ if ($relation instanceof BelongsToMany && ! $this->isAcrossConnections($relation)) {
return $this->model->getKeyName();
}
diff --git a/src/Helpers/Utilities.php b/src/Helpers/Utilities.php
new file mode 100644
index 0000000..44bfe8d
--- /dev/null
+++ b/src/Helpers/Utilities.php
@@ -0,0 +1,21 @@
+sort = $meta['score'];
+ }
+ if (isset($meta['index'])) {
+ $this->sort = $meta['index'];
+ }
+ if (isset($meta['sort'])) {
+ $this->sort = $meta['sort'];
+ }
+ if (isset($meta['cursor'])) {
+ $this->cursor = $meta['cursor'];
+ }
+ if (isset($meta['_id'])) {
+ $this->_id = $meta['_id'];
+ }
+ if (isset($meta['_query'])) {
+ $this->_query = $meta['_query'];
+ }
+ if (isset($meta['highlights'])) {
+ $this->highlights = $meta['highlights'];
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Getters
+ //----------------------------------------------------------------------
+
+ public function getIndex()
+ {
+ return $this->index;
+ }
+
+ public function getScore()
+ {
+ return $this->score;
+ }
+
+ public function getId(): mixed
+ {
+ return $this->_id ?? null;
+ }
+
+ public function getSort(): ?array
+ {
+ return $this->sort;
+ }
+
+ public function getCursor(): ?array
+ {
+ return $this->cursor;
+ }
+
+ public function getQuery()
+ {
+ return $this->_query;
+ }
+
+ public function getHighlights(): array
+ {
+ return $this->highlights ?? [];
+ }
+
+ public function parseHighlights($data = []): ?object
+ {
+ if ($this->highlights) {
+ $this->_mergeFlatKeysIntoNestedArray($data, $this->highlights);
+
+ return (object) $data;
+ }
+
+ return null;
+ }
+
+ public function asArray()
+ {
+ return [
+ 'score' => $this->score,
+ 'index' => $this->index,
+ '_id' => $this->_id,
+ 'sort' => $this->sort,
+ 'cursor' => $this->cursor,
+ '_query' => $this->_query,
+ 'highlights' => $this->highlights,
+ ];
+ }
+
+ //----------------------------------------------------------------------
+ // Setters
+ //----------------------------------------------------------------------
+ public function setId($id): void
+ {
+ $this->_id = $id;
+ }
+
+ public function setSort(array $sort): void
+ {
+ $this->sort = $sort;
+ }
+
+ public function setCursor(array $cursor): void
+ {
+ $this->cursor = $cursor;
+ }
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+
+ protected function _mergeFlatKeysIntoNestedArray(&$data, $attrs): void
+ {
+ foreach ($attrs as $key => $value) {
+ if ($value) {
+ $value = implode('......', $value);
+ $parts = explode('.', $key);
+ $current = &$data;
+
+ foreach ($parts as $partIndex => $part) {
+ if ($partIndex === count($parts) - 1) {
+ $current[$part] = $value;
+ } else {
+ if (! isset($current[$part]) || ! is_array($current[$part])) {
+ $current[$part] = [];
+ }
+ $current = &$current[$part];
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Meta/QueryMetaData.php b/src/Meta/QueryMetaData.php
new file mode 100644
index 0000000..a55a22a
--- /dev/null
+++ b/src/Meta/QueryMetaData.php
@@ -0,0 +1,333 @@
+timed_out = $meta['timed_out'] ?? false;
+ unset($meta['timed_out']);
+ if (isset($meta['took'])) {
+ $this->took = $meta['took'];
+ unset($meta['took']);
+ }
+ if (isset($meta['total'])) {
+ $this->total = $meta['total'];
+ unset($meta['total']);
+ }
+ if (isset($meta['max_score'])) {
+ $this->max_score = $meta['max_score'];
+ unset($meta['max_score']);
+ }
+ if (isset($meta['total'])) {
+ $this->total = $meta['total'];
+ unset($meta['total']);
+ }
+ if (isset($meta['shards'])) {
+ $this->shards = $meta['shards'];
+ unset($meta['shards']);
+ }
+ if (isset($meta['sort'])) {
+ $this->sort = $meta['sort'];
+ unset($meta['sort']);
+ }
+ if (isset($meta['cursor'])) {
+ $this->cursor = $meta['cursor'];
+ unset($meta['cursor']);
+ }
+ if (isset($meta['_id'])) {
+ $this->_id = $meta['_id'];
+ unset($meta['_id']);
+ }
+ if ($meta) {
+ $this->_meta = $meta;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Getters
+ //----------------------------------------------------------------------
+
+ public function getId(): mixed
+ {
+ return $this->_id ?? null;
+ }
+
+ public function getModified(): int
+ {
+ return $this->getResults('modified') ?? 0;
+ }
+
+ public function getDeleted(): int
+ {
+ return $this->getResults('deleted') ?? 0;
+ }
+
+ public function getCreated(): int
+ {
+ return $this->getResults('created') ?? 0;
+ }
+
+ public function isSuccessful(): bool
+ {
+ return $this->success;
+ }
+
+ public function getSort(): ?array
+ {
+ return $this->sort;
+ }
+
+ public function getCursor(): ?array
+ {
+ return $this->cursor;
+ }
+
+ public function getQuery(): string
+ {
+ return $this->query;
+ }
+
+ public function getDsl(): array
+ {
+ return $this->dsl;
+ }
+
+ public function getTook(): int
+ {
+ return $this->took;
+ }
+
+ public function getTotal(): int
+ {
+ return $this->total;
+ }
+
+ public function getMaxScore(): string
+ {
+ return $this->max_score;
+ }
+
+ public function getShards(): mixed
+ {
+ return $this->shards;
+ }
+
+ public function getErrorMessage(): string
+ {
+ return $this->errorMessage;
+ }
+
+ public function getError(): array
+ {
+ return $this->error;
+ }
+
+ public function asArray(): array
+ {
+ $return = [
+ 'query' => $this->query,
+ 'success' => $this->success,
+ 'timed_out' => $this->timed_out,
+ 'took' => $this->took,
+ 'total' => $this->total,
+ ];
+ if ($this->max_score) {
+ $return['max_score'] = $this->max_score;
+ }
+ if ($this->shards) {
+ $return['shards'] = $this->shards;
+ }
+ if ($this->dsl) {
+ $return['dsl'] = $this->dsl;
+ }
+ if ($this->_id) {
+ $return['_id'] = $this->_id;
+ }
+ if ($this->results) {
+ foreach ($this->results as $key => $value) {
+ $return[$key] = $value;
+ }
+ }
+ if ($this->error) {
+ $return['error'] = $this->error;
+ $return['errorMessage'] = $this->errorMessage;
+ }
+ if ($this->sort) {
+ $return['sort'] = $this->sort;
+ }
+ if ($this->cursor) {
+ $return['cursor'] = $this->cursor;
+ }
+ if ($this->_meta) {
+ $return['_meta'] = $this->_meta;
+ }
+
+ return $return;
+ }
+
+ public function getResults($key = null)
+ {
+ if ($key) {
+ return $this->results[$key] ?? null;
+ }
+
+ return $this->results;
+ }
+
+ //----------------------------------------------------------------------
+ // Setters
+ //----------------------------------------------------------------------
+ public function setId($id): void
+ {
+ $this->_id = $id;
+ }
+
+ public function setTook(int $took): void
+ {
+ $this->took = $took;
+ }
+
+ public function setTotal(int $total): void
+ {
+ $this->total = $total;
+ }
+
+ public function setQuery($query): void
+ {
+ $this->query = $query;
+ }
+
+ public function setSuccess(): void
+ {
+ $this->success = true;
+ }
+
+ public function setResult($key, $value): void
+ {
+ $this->results[$key] = $value;
+ }
+
+ public function setModified(int $count): void
+ {
+ $this->setResult('modified', $count);
+ }
+
+ public function setCreated(int $count): void
+ {
+ $this->setResult('created', $count);
+ }
+
+ public function setDeleted(int $count): void
+ {
+ $this->setResult('deleted', $count);
+ }
+
+ public function setFailed(int $count): void
+ {
+ $this->setResult('failed', $count);
+ }
+
+ public function setSort(array $sort): void
+ {
+ $this->sort = $sort;
+ }
+
+ public function setCursor(array $cursor): void
+ {
+ $this->cursor = $cursor;
+ }
+
+ public function setDsl($params)
+ {
+ $this->dsl = $params;
+ }
+
+ public function setError(array $error, string $errorMessage = ''): void
+ {
+ $this->success = false;
+ $this->error = $error;
+ $this->errorMessage = $errorMessage;
+ }
+
+ public function parseAndSetError($error, $errorCode)
+ {
+ $errorMessage = $error;
+ $this->success = false;
+ $details = $this->_decodeError($errorMessage);
+ $error = [
+ 'msg' => $details['msg'],
+ 'data' => $details['data'],
+ 'code' => $errorCode,
+ ];
+ $this->error = $error;
+ $this->errorMessage = $errorMessage;
+ }
+
+ private function _decodeError($error): array
+ {
+ $return['msg'] = $error;
+ $return['data'] = [];
+ $jsonStartPos = strpos($error, ': ') + 2;
+ $response = ($error);
+ $title = substr($response, 0, $jsonStartPos);
+ $jsonString = substr($response, $jsonStartPos);
+ if ($this->_isJson($jsonString)) {
+ $errorArray = json_decode($jsonString, true);
+ } else {
+ $errorArray = [$jsonString];
+ }
+
+ if (json_last_error() === JSON_ERROR_NONE) {
+ $errorReason = $errorArray['error']['reason'] ?? null;
+ if (! $errorReason) {
+ return $return;
+ }
+ $return['msg'] = $title.$errorReason;
+ $cause = $errorArray['error']['root_cause'][0]['reason'] ?? null;
+ if ($cause) {
+ $return['msg'] .= ' - '.$cause;
+ }
+
+ $return['data'] = $errorArray;
+ }
+
+ return $return;
+ }
+
+ private function _isJson($string): bool
+ {
+ return json_validate($string);
+ }
+}
diff --git a/src/Pagination/SearchAfterPaginator.php b/src/Pagination/SearchAfterPaginator.php
new file mode 100644
index 0000000..248ef7c
--- /dev/null
+++ b/src/Pagination/SearchAfterPaginator.php
@@ -0,0 +1,102 @@
+getMeta()->getCursor();
+ $search_after = $item->getMeta()->getSort();
+ $cursor['page']++;
+ $cursor['next_sort'] = $search_after;
+
+ return $cursor;
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'data' => $this->items->toArray(),
+ 'path' => $this->path(),
+ 'per_page' => $this->perPage(),
+ 'next_cursor' => $this->nextCursor()?->encode(),
+ 'next_page_url' => $this->nextPageUrl(),
+ 'prev_cursor' => $this->previousCursor()?->encode(),
+ 'prev_page_url' => $this->previousPageUrl(),
+ 'current_page' => $this->currentPageNumber(),
+ 'total' => $this->totalRecords(),
+ 'from' => $this->showingFrom(),
+ 'to' => $this->showingTo(),
+ 'last_page' => $this->lastPage(),
+
+ ];
+ }
+
+ public function currentPageNumber(): int
+ {
+ return $this->options['currentPage'];
+ }
+
+ public function totalRecords(): int
+ {
+ return $this->options['records'];
+ }
+
+ public function showingFrom(): int
+ {
+ $perPage = $this->perPage();
+ $currentPage = $this->currentPageNumber();
+
+ return ($currentPage - 1) * $perPage + 1;
+
+ }
+
+ public function showingTo(): int
+ {
+ $records = count($this->items);
+ $currentPage = $this->currentPageNumber();
+ $perPage = $this->perPage();
+
+ return (($currentPage - 1) * $perPage) + $records;
+ }
+
+ public function lastPage(): int
+ {
+ return $this->options['totalPages'];
+ }
+
+ // Builds the cursor for the previous page
+ public function previousCursor(): ?Cursor
+ {
+ if (! $this->cursor) {
+ return null;
+ }
+ $current = $this->cursor->toArray();
+ if ($current['page'] < 2) {
+ return null;
+ }
+ $previousCursor = $current;
+ unset($previousCursor['_pointsToNextItems']);
+ $previousCursor['page']--;
+ $previousCursor['next_sort'] = array_pop($previousCursor['sort_history']);
+
+ return new Cursor($previousCursor, false);
+ }
+
+ protected function setItems($items): void
+ {
+ $this->items = $items instanceof Collection ? $items : Collection::make($items);
+ $this->hasMore = $this->options['currentPage'] < $this->options['totalPages'];
+ }
+}
diff --git a/src/Query/Builder.php b/src/Query/Builder.php
index 1859c9e..e68ab82 100644
--- a/src/Query/Builder.php
+++ b/src/Query/Builder.php
@@ -1,40 +1,58 @@
', '<=', '>=', '<>', '!=', '<=>',
- 'like', 'like binary', 'not like', 'ilike',
- '&', '|', '^', '<<', '>>', '&~',
- 'rlike', 'not rlike', 'regexp', 'not regexp',
- '~', '~*', '!~', '!~*', 'similar to',
- 'not similar to', 'not ilike', '~~*', '!~~*',
+ '=',
+ '<',
+ '>',
+ '<=',
+ '>=',
+ '<>',
+ '!=',
+ '<=>',
+ 'like',
+ 'like binary',
+ 'not like',
+ 'ilike',
+ '&',
+ '|',
+ '^',
+ '<<',
+ '>>',
+ '&~',
+ 'rlike',
+ 'not rlike',
+ 'regexp',
+ 'not regexp',
+ '~',
+ '~*',
+ '!~',
+ '!~*',
+ 'similar to',
+ 'not similar to',
+ 'not ilike',
+ '~~*',
+ '!~~*',
// @Elastic Search
- 'exist', 'regex',
+ 'exist',
+ 'regex',
];
+ protected string $index = '';
+
+ protected string|bool $refresh = 'wait_for';
+
/**
* Operator conversion.
- *
- * @var array
*/
- protected $conversion = [
- '=' => '=',
+ protected array $conversion = [
+ '=' => '=',
'!=' => 'ne',
'<>' => 'ne',
- '<' => 'lt',
+ '<' => 'lt',
'<=' => 'lte',
- '>' => 'gt',
+ '>' => 'gt',
'>=' => 'gte',
];
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function __construct(Connection $connection, Processor $processor)
{
$this->grammar = new Grammar;
$this->connection = $connection;
$this->processor = $processor;
+ }
+ public function getProcessor(): Processor
+ {
+ return $this->processor;
}
+ public function getConnection(): Connection
+ {
+ return $this->connection;
+ }
- public function setRefresh($value)
+ public function setRefresh($value): void
{
$this->refresh = $value;
}
+ public function initCursor($cursor): array
+ {
+
+ $this->cursor = [
+ 'page' => 1,
+ 'pages' => 0,
+ 'records' => 0,
+ 'sort_history' => [],
+ 'next_sort' => null,
+ 'ts' => 0,
+ ];
+
+ if (! empty($cursor)) {
+ $this->cursor = [
+ 'page' => $cursor->parameter('page'),
+ 'pages' => $cursor->parameter('pages'),
+ 'records' => $cursor->parameter('records'),
+ 'sort_history' => $cursor->parameter('sort_history'),
+ 'next_sort' => $cursor->parameter('next_sort'),
+ 'ts' => $cursor->parameter('ts'),
+ ];
+ }
+
+ return $this->cursor;
+ }
//----------------------------------------------------------------------
// Querying Executors
//----------------------------------------------------------------------
/**
- * @inheritdoc
- */
- public function find($id, $columns = [])
- {
- return $this->where('_id', $id)->first($columns);
- }
-
- /**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function value($column)
{
- $result = (array)$this->first([$column]);
+ $result = (array) $this->first([$column]);
return Arr::get($result, $column);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function all($columns = [])
+ public function get($columns = []): ElasticCollection|LazyCollection
{
return $this->_processGet($columns);
}
/**
- * @inheritdoc
+ * @return ElasticCollection|LazyElasticCollection|void
*/
- public function get($columns = [])
+ protected function _processGet(array|string $columns = [], bool $returnLazy = false)
{
- return $this->_processGet($columns);
- }
- /**
- * @inheritdoc
- */
- public function cursor($columns = [])
- {
- $result = $this->_processGet($columns, true);
- if ($result instanceof LazyCollection) {
- return $result;
+ $wheres = $this->compileWheres();
+ $options = $this->compileOptions();
+ $columns = $this->prepareColumns($columns);
+
+ if ($this->groups) {
+ throw new RuntimeException('Groups are not used');
}
- throw new RuntimeException('Query not compatible with cursor');
- }
- /**
- * @inheritdoc
- */
- public function exists()
- {
- return $this->first() !== null;
- }
+ if ($this->aggregate) {
+ $function = $this->aggregate['function'];
+ $aggColumns = $this->aggregate['columns'];
+ if (in_array('*', $aggColumns)) {
+ $aggColumns = null;
+ }
+ if ($aggColumns) {
+ $columns = $aggColumns;
+ }
- /**
- * @inheritdoc
- */
- public function insert(array $values)
- {
- if (empty($values)) {
- return true;
- }
+ if ($this->distinctType) {
+ $totalResults = $this->connection->distinctAggregate($function, $wheres, $options, $columns);
+ } else {
+ $totalResults = $this->connection->aggregate($function, $wheres, $options, $columns);
+ }
- if (!is_array(reset($values))) {
- $values = [$values];
+ if (! $totalResults->isSuccessful()) {
+ throw new RuntimeException($totalResults->errorMessage);
+ }
+ $results = [
+ [
+ '_id' => null,
+ 'aggregate' => $totalResults->data,
+ ],
+ ];
+ $result = new ElasticCollection($results);
+ $result->setQueryMeta($totalResults->getMetaData());
+
+ // Return results
+ return $result;
}
- $allSuccess = true;
- foreach ($values as $value) {
- $result = $this->_processInsert($value, true);
- if (!$result) {
- $allSuccess = false;
+ if ($this->distinctType) {
+ if (empty($columns[0]) || $columns[0] == '*') {
+ throw new RuntimeException('Columns are required for term aggregation when using distinct()');
+ } else {
+
+ if ($this->distinctType == 2) {
+ $find = $this->connection->distinct($wheres, $options, $columns, true);
+ } else {
+ $find = $this->connection->distinct($wheres, $options, $columns);
+ }
}
+ } else {
+ $find = $this->connection->find($wheres, $options, $columns);
}
- return $allSuccess;
- }
-
- /**
- * @inheritdoc
- */
- public function insertGetId(array $values, $sequence = null)
- {
- //Also Model->save()
- return $this->_processInsert($values, true);
- }
+ //Else Normal find query
+ if ($find->isSuccessful()) {
+ $data = $find->data;
+ if ($returnLazy) {
+ if ($data) {
+ $lazy = LazyElasticCollection::make(function () use ($data) {
+ foreach ($data as $item) {
+ yield $item;
+ }
+ });
+ $lazy->setQueryMeta($find->getMetaData());
- /**
- * @inheritdoc
- */
- public function update(array $values, array $options = [])
- {
- $this->_checkValues($values);
+ return $lazy;
+ }
+ }
+ $collection = new ElasticCollection($data);
+ $collection->setQueryMeta($find->getMetaData());
- return $this->_processUpdate($values, $options);
+ return $collection;
+ } else {
+ throw new RuntimeException('Error: '.$find->errorMessage);
+ }
}
- /**
- * @inheritdoc
- */
- public function increment($column, $amount = 1, $extra = [], $options = [])
+ protected function compileWheres(): array
{
- $values = ['inc' => [$column => $amount]];
-
- if (!empty($extra)) {
- $values['set'] = $extra;
- }
-
- $this->where(function ($query) use ($column) {
- $query->where($column, 'exists', false);
+ $wheres = $this->wheres ?: [];
+ $compiledWheres = [];
+ if ($wheres) {
+ if ($wheres[0]['boolean'] == 'or') {
+ throw new RuntimeException('Cannot start a query with an OR statement');
+ }
+ if (count($wheres) == 1) {
+ return $this->{'_parseWhere'.$wheres[0]['type']}($wheres[0]);
+ }
+ $and = [];
+ $or = [];
+ foreach ($wheres as $where) {
+ if ($where['boolean'] == 'or') {
+ $or[] = $and;
+ //clear AND for the next bucket
+ $and = [];
+ }
- $query->orWhereNotNull($column);
- });
+ $result = $this->{'_parseWhere'.$where['type']}($where);
+ $and[] = $result;
+ }
+ if ($or) {
+ //Add the last AND bucket
+ $or[] = $and;
+ foreach ($or as $and) {
+ $compiledWheres['or'][] = $this->_prepAndBucket($and);
+ }
+ } else {
+ $compiledWheres = $this->_prepAndBucket($and);
+ }
+ }
- return $this->_processUpdate($values, $options, 'incrementMany');
+ return $compiledWheres;
}
- /**
- * @inheritdoc
- */
- public function decrement($column, $amount = 1, $extra = [], $options = [])
+ private function _prepAndBucket($andData): array
{
- return $this->increment($column, -1 * $amount, $extra, $options);
- }
+ $data = [];
+ foreach ($andData as $key => $ops) {
+ $data['and'][$key] = $ops;
+ }
+ return $data;
+ }
- public function agg(array $functions, $column)
+ protected function compileOptions(): array
{
- if (is_array($column)) {
- throw new RuntimeException('Column must be a string');
+ $options = [];
+ if ($this->orders) {
+ $options['sort'] = $this->orders;
}
- $aggregateTypes = ['sum', 'avg', 'min', 'max', 'matrix', 'count'];
- foreach ($functions as $function) {
- if (!in_array($function, $aggregateTypes)) {
- throw new RuntimeException('Invalid aggregate type: '.$function);
+ if ($this->offset) {
+ $options['skip'] = $this->offset;
+ }
+ if ($this->limit) {
+ $options['limit'] = $this->limit;
+ //Check if it's first() with no ordering,
+ //Set order to created_at -> asc for consistency
+ //TODO
+ }
+ if ($this->cursor) {
+ $options['_meta']['cursor'] = $this->cursor;
+ if (! empty($this->cursor['next_sort'])) {
+ $options['search_after'] = $this->cursor['next_sort'];
}
}
- $wheres = $this->compileWheres();
- $options = $this->compileOptions();
- $results = $this->connection->multipleAggregate($functions, $wheres, $options, $column);
+ if ($this->previousSearchAfter) {
+ $options['prev_search_after'] = $this->previousSearchAfter;
+ }
+ if ($this->minScore) {
+ $options['minScore'] = $this->minScore;
+ }
+ if ($this->searchOptions) {
+ $options['searchOptions'] = $this->searchOptions;
+ }
+ if ($this->filters) {
+ $options['filters'] = $this->filters;
+ }
- return $results->data ?? [];
+ return $options;
}
-//
-
- /**
- * @inheritdoc
- */
- public function forPageAfterId($perPage = 15, $lastId = 0, $column = '_id')
+ protected function prepareColumns($columns): array
{
- return parent::forPageAfterId($perPage, $lastId, $column);
- }
+ $final = [];
+ if ($this->columns) {
+ foreach ($this->columns as $col) {
+ $final[] = $col;
+ }
+ }
- /**
- * @inheritdoc
- */
- public function delete($id = null)
- {
+ if ($columns) {
+ if (! is_array($columns)) {
+ $columns = [$columns];
+ }
- if ($id !== null) {
- $this->where('_id', '=', $id);
+ foreach ($columns as $col) {
+ $final[] = $col;
+ }
+ }
+ if (! $final) {
+ return ['*'];
}
- return $this->_processDelete();
+ $final = array_values(array_unique($final));
+ if (($key = array_search('*', $final)) !== false) {
+ unset($final[$key]);
+ }
+ return $final;
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function aggregate($function, $columns = [])
+ public function aggregate($function, $columns = []): mixed
{
$this->aggregate = compact('function', 'columns');
@@ -275,275 +403,208 @@ public function aggregate($function, $columns = [])
$this->bindings['select'] = [];
$results = $this->get($columns);
-
// Restore bindings after aggregate search
- $this->aggregate = null;
+ $this->aggregate = [];
$this->columns = $previousColumns;
$this->bindings['select'] = $previousSelectBindings;
if (isset($results[0])) {
- $result = (array)$results[0];
+ $result = (array) $results[0];
+ $esResult = new ElasticResult();
+ $esResult->setQueryMeta($results->getQueryMeta());
+ $esResult->setValue($result['aggregate']);
- return $result['aggregate'];
+ // For now we'll return the result as is,
+ // Later we'll return ElasticResult to get access to the meta
+ return $esResult->getValue();
}
return null;
}
/**
- * @param $column
- * @param $callBack
- * @param $scoreMode
- *
- * @return $this
+ * {@inheritdoc}
*/
- public function whereNestedObject($column, $callBack, $scoreMode = 'avg')
+ public function distinct($includeCount = false): static
{
- $boolean = 'and';
- $query = $this->newQuery();
- $callBack($query);
- $wheres = $query->compileWheres();
- $this->wheres[] = [
- 'column' => $column,
- 'type' => 'NestedObject',
- 'wheres' => $wheres,
- 'score_mode' => $scoreMode,
- 'boolean' => $boolean,
- ];
+ $this->distinctType = 1;
+ if ($includeCount) {
+ $this->distinctType = 2;
+ }
return $this;
}
/**
- * @param $column
- * @param $callBack
- * @param $scoreMode
- *
- * @return $this
+ * {@inheritdoc}
*/
- public function whereNotNestedObject($column, $callBack, $scoreMode = 'avg')
+ public function find($id, $columns = [])
{
- $boolean = 'and';
- $query = $this->newQuery();
- $callBack($query);
- $wheres = $query->compileWheres();
- $this->wheres[] = [
- 'column' => $column,
- 'type' => 'NotNestedObject',
- 'wheres' => $wheres,
- 'score_mode' => $scoreMode,
- 'boolean' => $boolean,
- ];
-
- return $this;
+ return $this->where('_id', $id)->first($columns);
}
/**
- * @param $column
- * @param $value
- *
- * @return $this
+ * {@inheritdoc}
*/
- public function wherePhrase($column, $value)
+ public function cursor($columns = []): LazyCollection
{
- $boolean = 'and';
- $this->wheres[] = [
- 'column' => $column,
- 'type' => 'Basic',
- 'value' => $value,
- 'operator' => 'phrase',
- 'boolean' => $boolean,
- ];
-
- return $this;
+ $result = $this->_processGet($columns, true);
+ if ($result instanceof LazyCollection) {
+ return $result;
+ }
+ throw new RuntimeException('Query not compatible with cursor');
}
/**
- * @param $column
- * @param $value
- *
- * @return $this
+ * {@inheritdoc}
*/
- public function wherePhrasePrefix($column, $value)
+ public function exists(): bool
{
- $boolean = 'and';
- $this->wheres[] = [
- 'column' => $column,
- 'type' => 'Basic',
- 'value' => $value,
- 'operator' => 'phrase_prefix',
- 'boolean' => $boolean,
- ];
-
- return $this;
+ return $this->first() !== null;
}
/**
- * @param $column
- * @param $value
- *
- * @return $this
+ * {@inheritdoc}
*/
- public function whereExact($column, $value)
+ public function insert(array $values, $returnData = false): ElasticCollection
{
- $boolean = 'and';
- $this->wheres[] = [
- 'column' => $column,
- 'type' => 'Basic',
- 'value' => $value,
- 'operator' => 'exact',
- 'boolean' => $boolean,
- ];
-
- return $this;
+ return $this->_processInsert($values, $returnData, false);
}
-
- /**
- * @param $column
- * @param $callBack
- *
- * @return $this
+ /*
+ * @see insert(array $values, $returnData = false)
*/
- public function queryNested($column, $callBack)
- {
- $boolean = 'and';
- $query = $this->newQuery();
- $callBack($query);
- $wheres = $query->compileWheres();
- $options = $query->compileOptions();
- $this->wheres[] = [
- 'column' => $column,
- 'type' => 'QueryNested',
- 'wheres' => $wheres,
- 'options' => $options,
- 'boolean' => $boolean,
- ];
- return $this;
+ public function insertWithoutRefresh(array $values, $returnData = false): ElasticCollection
+ {
+ return $this->_processInsert($values, $returnData, true);
}
- public function whereTimestamp($column, $operator = null, $value = null, $boolean = 'and')
+ protected function _processInsert(array $values, bool $returnData, bool $saveWithoutRefresh): ElasticCollection
{
- [$value, $operator] = $this->prepareValueAndOperator(
- $value, $operator, func_num_args() === 2
- );
- if ($this->invalidOperator($operator)) {
- [$value, $operator] = [$operator, '='];
- }
- $this->wheres[] = [
- 'column' => $column,
- 'type' => 'Timestamp',
- 'value' => $value,
- 'operator' => $operator,
- 'boolean' => $boolean,
+ $response = [
+ 'hasErrors' => false,
+ 'took' => 0,
+ 'total' => 0,
+ 'success' => 0,
+ 'created' => 0,
+ 'modified' => 0,
+ 'failed' => 0,
+ 'data' => [],
+ 'error_bag' => [],
];
+ if (empty($values)) {
+ return $this->_parseBulkInsertResult($response, $returnData);
+ }
- return $this;
- }
-
+ if ($saveWithoutRefresh) {
+ $this->refresh = false;
+ }
- //----------------------------------------------------------------------
- // Query Processing (Connection API)
- //----------------------------------------------------------------------
+ if (! is_array(reset($values))) {
+ $values = [$values];
+ }
+ $this->applyBeforeQueryCallbacks();
- /**
- * @param array $columns
- * @param false $returnLazy
- *
- * @return Collection|LazyCollection|void
- */
- protected function _processGet($columns = [], $returnLazy = false)
- {
+ collect($values)->chunk(1000)->each(callback: function ($chunk) use (&$response, $returnData) {
+ $result = $this->connection->insertBulk($chunk->toArray(), $returnData, $this->refresh);
+ if ((bool) $result['hasErrors']) {
+ $response['hasErrors'] = true;
+ }
+ $response['total'] += $result['total'];
+ $response['took'] += $result['took'];
+ $response['success'] += $result['success'];
+ $response['failed'] += $result['failed'];
+ $response['created'] += $result['created'];
+ $response['modified'] += $result['modified'];
+ $response['data'] = array_merge($response['data'], $result['data']);
+
+ $response['error_bag'] = array_merge($response['error_bag'], $result['error_bag']);
+ });
- $wheres = $this->compileWheres();
- $options = $this->compileOptions();
- $columns = $this->prepareColumns($columns);
+ return $this->_parseBulkInsertResult($response, $returnData);
- if ($this->groups) {
- throw new RuntimeException('Groups are not used');
- }
+ }
- if ($this->aggregate) {
- $function = $this->aggregate['function'];
- $aggColumns = $this->aggregate['columns'];
- if (in_array('*', $aggColumns)) {
- $aggColumns = null;
+ protected function _parseBulkInsertResult($response, $returnData): ElasticCollection
+ {
+ $result = new ElasticCollection($response['data']);
+ $result->setQueryMeta(new QueryMetaData([]));
+ $result->getQueryMeta()->setSuccess();
+ $result->getQueryMeta()->setCreated($response['created']);
+ $result->getQueryMeta()->setModified($response['modified']);
+ $result->getQueryMeta()->setFailed($response['failed']);
+ $result->getQueryMeta()->setQuery('InsertBulk');
+ $result->getQueryMeta()->setTook($response['took']);
+ $result->getQueryMeta()->setTotal($response['total']);
+ if ($response['hasErrors']) {
+ $errorMessage = 'Bulk insert failed for all values';
+ if ($response['success'] > 0) {
+ $errorMessage = 'Bulk insert failed for some values';
}
- if ($aggColumns) {
- $columns = $aggColumns;
- }
+ $result->getQueryMeta()->setError($response['error_bag'], $errorMessage);
+ }
+ if (! $returnData) {
+ $data = $result->getQueryMetaAsArray();
+ unset($data['query']);
+ $response['data'] = $data;
- if ($this->distinct) {
- $totalResults = $this->connection->distinctAggregate($function, $wheres, $options, $columns);
- } else {
- $totalResults = $this->connection->aggregate($function, $wheres, $options, $columns);
- }
+ return $this->_parseBulkInsertResult($response, true);
+ }
- if (!$totalResults->isSuccessful()) {
- throw new RuntimeException($totalResults->errorMessage);
- }
- $results = [
- [
- '_id' => null,
- 'aggregate' => $totalResults->data,
- ],
- ];
+ return $result;
+ }
- // Return results
- return new Collection($results);
+ /**
+ * {@inheritdoc}
+ */
+ public function insertGetId(array $values, $sequence = null): int|array|string|null
+ {
+ $result = $this->connection->save($values, $this->refresh);
+ if ($result->isSuccessful()) {
+ // Return id
+ return $sequence ? $result->getInsertedId() : $result->data;
}
- if ($this->distinct) {
- if (empty($columns[0]) || $columns[0] == '*') {
- throw new RuntimeException('Columns are required for term aggregation when using distinct()');
- } else {
- if ($this->distinct == 2) {
- $find = $this->connection->distinct($wheres, $options, $columns, true);
- } else {
- $find = $this->connection->distinct($wheres, $options, $columns);
- }
+ return null;
+ }
- }
+ /**
+ * {@inheritdoc}
+ */
+ public function update(array $values, array $options = [])
+ {
+ $this->_checkValues($values);
- } else {
- $find = $this->connection->find($wheres, $options, $columns);
- }
+ return $this->_processUpdate($values, $options);
+ }
- //Else Normal find query
- if ($find->isSuccessful()) {
- $data = $find->data;
- if ($returnLazy) {
- if ($data) {
- return LazyCollection::make(function () use ($data) {
- foreach ($data as $item) {
- yield $item;
- }
- });
- }
+ private function _checkValues($values): true
+ {
+ unset($values['updated_at']);
+ unset($values['created_at']);
+ if (! $this->_isAssociative($values)) {
+ throw new RuntimeException('Invalid value format. Expected associative array, got sequential array');
+ }
- }
+ return true;
+ }
- return new Collection($data);
- } else {
- throw new RuntimeException('Error: '.$find->errorMessage);
+ private function _isAssociative(array $arr): bool
+ {
+ if ($arr === []) {
+ return true;
}
+ return array_keys($arr) !== range(0, count($arr) - 1);
}
- /**
- * @param $query
- * @param array $options
- * @param string $method
- *
- * @return int
- */
- protected function _processUpdate($values, array $options = [], $method = 'updateMany')
+ protected function _processUpdate($values, array $options = [], $method = 'updateMany'): int
{
// Update multiple items by default.
- if (!array_key_exists('multiple', $options)) {
+ if (! array_key_exists('multiple', $options)) {
$options['multiple'] = true;
}
$wheres = $this->compileWheres();
@@ -555,688 +616,402 @@ protected function _processUpdate($values, array $options = [], $method = 'updat
return 0;
}
-
/**
- * @param array $values
- * @param false $returnIdOnly
- *
- * @return null|string|array
+ * {@inheritdoc}
*/
- protected function _processInsert(array $values, $returnIdOnly = false)
+ public function decrement($column, $amount = 1, $extra = [], $options = [])
{
- $result = $this->connection->save($values, $this->refresh);
-
- if ($result->isSuccessful()) {
-
- // Return id
- return $returnIdOnly ? $result->getInsertedId() : $result->data;
- }
-
- return null;
+ return $this->increment($column, -1 * $amount, $extra, $options);
}
/**
- * @return int
+ * {@inheritdoc}
*/
- protected function _processDelete()
+ public function increment($column, $amount = 1, $extra = [], $options = [])
{
- $wheres = $this->compileWheres();
- $options = $this->compileOptions();
- $result = $this->connection->deleteAll($wheres, $options);
- if ($result->isSuccessful()) {
- return $result->getDeletedCount();
+ $values = ['inc' => [$column => $amount]];
+
+ if (! empty($extra)) {
+ $values['set'] = $extra;
}
- return 0;
- }
+ $this->where(function ($query) use ($column) {
+ $query->where($column, 'exists', false);
+ $query->orWhereNotNull($column);
+ });
- //----------------------------------------------------------------------
- // Clause Operators
- //----------------------------------------------------------------------
+ return $this->_processUpdate($values, $options, 'incrementMany');
+ }
- /**
- * @inheritdoc
- */
- public function orderBy($column, $direction = 'asc', $mode = null, $missing = null)
+ public function agg(array $functions, $column)
{
- if (is_string($direction)) {
- $direction = (strtolower($direction) == 'asc' ? 'asc' : 'desc');
+ if (is_array($column)) {
+ throw new RuntimeException('Column must be a string');
}
+ $aggregateTypes = ['sum', 'avg', 'min', 'max', 'matrix', 'count'];
+ foreach ($functions as $function) {
+ if (! in_array($function, $aggregateTypes)) {
+ throw new RuntimeException('Invalid aggregate type: '.$function);
+ }
+ }
+ $wheres = $this->compileWheres();
+ $options = $this->compileOptions();
- $this->orders[$column] = [
- 'order' => $direction,
- 'mode' => $mode,
- 'missing' => $missing,
- ];
-
-// dd($this->orders);
+ $results = $this->connection->multipleAggregate($functions, $wheres, $options, $column);
- return $this;
+ return $results->data ?? [];
}
+ //----------------------------------------------------------------------
+ // Query Processing (Connection API)
+ //----------------------------------------------------------------------
+
/**
- * @inheritDoc
+ * {@inheritdoc}
*/
- public function orderByDesc($column, $mode = null, $missing = null)
+ public function forPageAfterId($perPage = 15, $lastId = 0, $column = '_id')
{
- return $this->orderBy($column, 'desc', $mode, $missing);
+ return parent::forPageAfterId($perPage, $lastId, $column);
}
/**
- * @param $column
- * @param $pin
- * @param $direction @values: 'asc', 'desc'
- * @param $unit @values: 'km', 'mi', 'm', 'ft'
- * @param $mode @values: 'min', 'max', 'avg', 'sum'
- * @param $type @values: 'arc', 'plane'
- *
* @return $this
*/
- public function orderByGeo($column, $pin, $direction = 'asc', $unit = 'km', $mode = null, $type = null)
+ public function whereNestedObject($column, $callBack, $scoreMode = 'avg'): static
{
- $this->orders[$column] = [
- 'is_geo' => true,
- 'order' => $direction,
- 'pin' => $pin,
- 'unit' => $unit,
- 'mode' => $mode,
- 'type' => $type,
+ $boolean = 'and';
+ $query = $this->newQuery();
+ $callBack($query);
+ $wheres = $query->compileWheres();
+ $this->wheres[] = [
+ 'column' => $column,
+ 'type' => 'NestedObject',
+ 'wheres' => $wheres,
+ 'score_mode' => $scoreMode,
+ 'boolean' => $boolean,
];
return $this;
}
/**
- * @param $column
- * @param $pin
- * @param $unit @values: 'km', 'mi', 'm', 'ft'
- * @param $mode @values: 'min', 'max', 'avg', 'sum'
- * @param $type @values: 'arc', 'plane'
- *
- * @return $this
+ * {@inheritdoc}
*/
- public function orderByGeoDesc($column, $pin, $unit = 'km', $mode = null, $type = null)
+ public function newQuery(): Builder
{
- return $this->orderByGeo($column, $pin, 'desc', $unit, $mode, $type);
+ return new self($this->connection, $this->processor);
}
-
/**
- * @param $column
- * @param $direction
- * @param $mode
- *
* @return $this
*/
- public function orderByNested($column, $direction = 'asc', $mode = null)
+ public function whereNotNestedObject($column, $callBack, $scoreMode = 'avg'): static
{
- $this->orders[$column] = [
- 'is_nested' => true,
- 'order' => $direction,
- 'mode' => $mode,
-
+ $boolean = 'and';
+ $query = $this->newQuery();
+ $callBack($query);
+ $wheres = $query->compileWheres();
+ $this->wheres[] = [
+ 'column' => $column,
+ 'type' => 'NotNestedObject',
+ 'wheres' => $wheres,
+ 'score_mode' => $scoreMode,
+ 'boolean' => $boolean,
];
return $this;
}
+ //----------------------------------------------------------------------
+ // Clause Operators
+ //----------------------------------------------------------------------
- /**
- * @inheritdoc
- */
- public function whereBetween($column, iterable $values, $boolean = 'and', $not = false)
+ public function wherePhrase($column, $value): static
{
- $type = 'between';
-
- $this->wheres[] = compact('column', 'type', 'boolean', 'values', 'not');
+ $boolean = 'and';
+ $this->wheres[] = [
+ 'column' => $column,
+ 'type' => 'Basic',
+ 'value' => $value,
+ 'operator' => 'phrase',
+ 'boolean' => $boolean,
+ ];
return $this;
}
- /**
- * @inheritdoc
- */
- public function select($columns = ['*'])
+ public function wherePhrasePrefix($column, $value): static
{
- $columns = is_array($columns) ? $columns : [$columns];
- $this->columns = $columns;
-
- return $this;
- }
-
- public function addSelect($column)
- {
- if (!is_array($column)) {
- $column = [$column];
- }
-
- $currentColumns = $this->columns;
- if ($currentColumns) {
- return $this->select(array_merge($currentColumns, $column));
- }
-
- return $this->select($column);
-
- }
-
- /**
- * @inheritdoc
- */
-
- public function distinct($includeCount = false)
- {
- $this->distinct = 1;
- if ($includeCount) {
- $this->distinct = 2;
- }
-
- return $this;
- }
-
- /**
- * @param ...$groups
- *
- * GroupBy will be passed on to distinct
- *
- * @return $this|Builder
- */
- public function groupBy(...$groups)
- {
- if (is_array($groups[0])) {
- $groups = $groups[0];
- }
-
- $this->addSelect($groups);
- $this->distinct = 1;
-
- return $this;
- }
-
- //Filters
-
- public function filterGeoBox($field, $topLeft, $bottomRight)
- {
- $this->filters['filterGeoBox'] = [
- 'field' => $field,
- 'topLeft' => $topLeft,
- 'bottomRight' => $bottomRight,
- ];
- }
-
- public function filterGeoPoint($field, $distance, $geoPoint)
- {
- $this->filters['filterGeoPoint'] = [
- 'field' => $field,
- 'distance' => $distance,
- 'geoPoint' => $geoPoint,
- ];
- }
-
- //Regexs
-
- public function whereRegex($column, $expression)
- {
- $type = 'regex';
$boolean = 'and';
- $this->wheres[] = compact('column', 'type', 'expression', 'boolean');
-
- return $this;
- }
-
- public function orWhereRegex($column, $expression)
- {
- $type = 'regex';
- $boolean = 'or';
- $this->wheres[] = compact('column', 'type', 'expression', 'boolean');
-
- return $this;
- }
-
- /**
- * @inheritdoc
- */
- public function newQuery()
- {
- return new self($this->connection, $this->processor);
- }
-
- protected function prepareColumns($columns)
- {
- $final = [];
- if ($this->columns) {
- foreach ($this->columns as $col) {
- $final[] = $col;
- }
-
- }
-
- if ($columns) {
- if (!is_array($columns)) {
- $columns = [$columns];
- }
-
- foreach ($columns as $col) {
- $final[] = $col;
- }
- }
- if (!$final) {
- return ['*'];
- }
-
- $final = array_values(array_unique($final));
- if (($key = array_search('*', $final)) !== false) {
- unset($final[$key]);
- }
-
- return $final;
-
-
- }
-
- protected function compileOptions()
- {
- $options = [];
- if ($this->orders) {
- $options['sort'] = $this->orders;
- }
- if ($this->offset) {
- $options['skip'] = $this->offset;
- }
- if ($this->limit) {
- $options['limit'] = $this->limit;
- //Check if it's first() with no ordering,
- //Set order to created_at -> asc for consistency
- //TODO
- }
- if ($this->minScore) {
- $options['minScore'] = $this->minScore;
- }
- if ($this->searchOptions) {
- $options['searchOptions'] = $this->searchOptions;
- }
- if ($this->filters) {
- $options['filters'] = $this->filters;
- }
-
- return $options;
- }
-
- /**
- * @return array
- */
- protected function compileWheres()
- {
- $wheres = $this->wheres ? : [];
- $compiledWheres = [];
- if ($wheres) {
- if ($wheres[0]['boolean'] == 'or') {
- throw new RuntimeException('Cannot start a query with an OR statement');
- }
- if (count($wheres) == 1) {
- return $this->{'_parseWhere'.$wheres[0]['type']}($wheres[0]);
- }
- $and = [];
- $or = [];
- foreach ($wheres as $where) {
- if ($where['boolean'] == 'or') {
- $or[] = $and;
- //clear AND for the next bucket
- $and = [];
- }
-
- $result = $this->{'_parseWhere'.$where['type']}($where);
- $and[] = $result;
-
- }
- if ($or) {
- //Add the last AND bucket
- $or[] = $and;
- foreach ($or as $and) {
- $compiledWheres['or'][] = $this->_prepAndBucket($and);
- }
- } else {
-
- $compiledWheres = $this->_prepAndBucket($and);
- }
- }
-
- return $compiledWheres;
- }
-
- private function _prepAndBucket($andData)
- {
- $data = [];
- foreach ($andData as $key => $ops) {
- $data['and'][$key] = $ops;
- }
-
- return $data;
- }
-
- /**
- * @param array $where
- *
- * @return array
- */
- protected function _parseWhereBasic(array $where)
- {
- $operator = $where['operator'];
- $column = $where['column'];
- $value = $where['value'];
- $boolean = $where['boolean'] ?? null;
- if ($boolean === 'and not') {
- $operator = '!=';
- }
- if ($boolean === 'or not') {
- $operator = '!=';
- }
- if ($operator === 'not like') {
- $operator = 'not_like';
- }
-
- if (!isset($operator) || $operator == '=') {
- $query = [$column => $value];
- } elseif (array_key_exists($operator, $this->conversion)) {
- $query = [$column => [$this->conversion[$operator] => $value]];
- } else {
- if (is_callable($column)) {
- throw new RuntimeException('Invalid closure for where clause');
- }
- $query = [$column => [$operator => $value]];
- }
-
- return $query;
- }
-
- /**
- * @param array $where
- *
- * @return mixed
- */
- protected function _parseWhereNested(array $where)
- {
-
- $boolean = $where['boolean'];
-// if ($boolean !== 'and') {
-// throw new RuntimeException('Nested where clause with boolean other than "and" is not supported');
-// }
- if ($boolean === 'and not') {
- $boolean = 'not';
- }
- $must = match ($boolean) {
- 'and' => 'must',
- 'not', 'or not' => 'must_not',
- 'or' => 'should',
- default => throw new RuntimeException($boolean.' is not supported for parameter grouping'),
- };
-
- $query = $where['query'];
- $wheres = $query->compileWheres();
-
- return [
- $must => ['group' => ['wheres' => $wheres]],
- ];
-
-
- }
-
- protected function _parseWhereQueryNested(array $where)
- {
- return [
- $where['column'] => [
- 'innerNested' => [
- 'wheres' => $where['wheres'],
- 'options' => $where['options'],
- ],
- ],
+ $this->wheres[] = [
+ 'column' => $column,
+ 'type' => 'Basic',
+ 'value' => $value,
+ 'operator' => 'phrase_prefix',
+ 'boolean' => $boolean,
];
- }
-
- /**
- * @param array $where
- *
- * @return array
- */
- protected function _parseWhereIn(array $where)
- {
- $column = $where['column'];
- $values = $where['values'];
-
- return [$column => ['in' => array_values($values)]];
- }
-
-
- /**
- * @param array $where
- *
- * @return array
- */
- protected function _parseWhereNotIn(array $where)
- {
- $column = $where['column'];
- $values = $where['values'];
-
- return [$column => ['nin' => array_values($values)]];
- }
-
- /**
- * @param array $where
- *
- * @return array
- */
- protected function _parseWhereNull(array $where)
- {
- $where['operator'] = 'not_exists';
- $where['value'] = null;
-
- return $this->_parseWhereBasic($where);
- }
-
- /**
- * @param array $where
- *
- * @return array
- */
- protected function _parseWhereNotNull(array $where)
- {
- $where['operator'] = 'exists';
- $where['value'] = null;
- return $this->_parseWhereBasic($where);
+ return $this;
}
/**
- * @param array $where
- *
- * @return array
+ * @return $this
*/
- protected function _parseWhereBetween(array $where)
+ public function whereExact($column, $value): static
{
- $not = $where['not'] ?? false;
- $values = $where['values'];
- $column = $where['column'];
-
- if ($not) {
- return [
- $column => [
- 'not_between' => [$values[0], $values[1]],
- ],
- ];
- }
-
- return [
- $column => [
- 'between' => [$values[0], $values[1]],
- ],
+ $boolean = 'and';
+ $this->wheres[] = [
+ 'column' => $column,
+ 'type' => 'Basic',
+ 'value' => $value,
+ 'operator' => 'exact',
+ 'boolean' => $boolean,
];
+
+ return $this;
}
/**
- * @param array $where
- *
- * @return array
+ * @return $this
*/
- protected function _parseWhereDate(array $where)
+ public function queryNested($column, $callBack): static
{
- //return a normal where clause
- return $this->_parseWhereBasic($where);
+ $boolean = 'and';
+ $query = $this->newQuery();
+ $callBack($query);
+ $wheres = $query->compileWheres();
+ $options = $query->compileOptions();
+ $this->wheres[] = [
+ 'column' => $column,
+ 'type' => 'QueryNested',
+ 'wheres' => $wheres,
+ 'options' => $options,
+ 'boolean' => $boolean,
+ ];
+
+ return $this;
}
- protected function _parseWhereTimestamp(array $where)
+ public function whereTimestamp($column, $operator = null, $value = null, $boolean = 'and'): static
{
- $where['value'] = $this->_formatTimestamp($where['value']);
-
- return $this->_parseWhereBasic($where);
+ [$value, $operator] = $this->prepareValueAndOperator($value, $operator, func_num_args() === 2);
+ if ($this->invalidOperator($operator)) {
+ [$value, $operator] = [$operator, '='];
+ }
+ $this->wheres[] = [
+ 'column' => $column,
+ 'type' => 'Timestamp',
+ 'value' => $value,
+ 'operator' => $operator,
+ 'boolean' => $boolean,
+ ];
+ return $this;
}
/**
- * @param array $where
- *
- * @return array
+ * {@inheritDoc}
*/
- protected function _parseWhereMonth(array $where)
+ public function orderByDesc($column, $mode = null, $missing = null): static
{
- throw new LogicException('whereMonth clause is not available yet');
-
+ return $this->orderBy($column, 'desc', $mode, $missing);
}
/**
- * @param array $where
- *
- * @return array
+ * {@inheritdoc}
*/
- protected function _parseWhereDay(array $where)
+ public function orderBy($column, $direction = 'asc', $mode = null, $missing = null): static
{
- throw new LogicException('whereDay clause is not available yet');
+ if (is_string($direction)) {
+ $direction = (strtolower($direction) == 'asc' ? 'asc' : 'desc');
+ }
+ $this->orders[$column] = [
+ 'order' => $direction,
+ 'mode' => $mode,
+ 'missing' => $missing,
+ ];
+
+ return $this;
}
/**
- * @param array $where
- *
- * @return array
+ * @param $unit @values: 'km', 'mi', 'm', 'ft'
+ * @param $mode @values: 'min', 'max', 'avg', 'sum'
+ * @param $type @values: 'arc', 'plane'
+ * @return $this
*/
- protected function _parseWhereYear(array $where)
+ public function orderByGeoDesc($column, $pin, $unit = 'km', $mode = null, $type = null): static
{
- throw new LogicException('whereYear clause is not available yet');
-
+ return $this->orderByGeo($column, $pin, 'desc', $unit, $mode, $type);
}
/**
- * @param array $where
- *
- * @return array
+ * @param string $direction @values: 'asc', 'desc'
+ * @param string $unit @values: 'km', 'mi', 'm', 'ft'
+ * @param $mode @values: 'min', 'max', 'avg', 'sum'
+ * @param $type @values: 'arc', 'plane'
+ * @return $this
*/
- protected function _parseWhereTime(array $where)
- {
- throw new LogicException('whereTime clause is not available yet');
+ public function orderByGeo(
+ $column,
+ $pin,
+ string $direction = 'asc',
+ string $unit = 'km',
+ ?string $mode = null,
+ ?string $type = null
+ ): static {
+ $this->orders[$column] = [
+ 'is_geo' => true,
+ 'order' => $direction,
+ 'pin' => $pin,
+ 'unit' => $unit,
+ 'mode' => $mode,
+ 'type' => $type,
+ ];
+ return $this;
}
/**
- * @param array $where
- *
- * @return mixed
+ * @return $this
*/
- protected function _parseWhereRaw(array $where)
+ public function orderByNested($column, $direction = 'asc', $mode = null): static
{
- throw new LogicException('whereRaw clause is not available yet');
+ $this->orders[$column] = [
+ 'is_nested' => true,
+ 'order' => $direction,
+ 'mode' => $mode,
+
+ ];
+ return $this;
}
- public function _parseWhereExists(array $where)
+ //Filters
+
+ /**
+ * {@inheritdoc}
+ */
+ public function whereBetween($column, iterable $values, $boolean = 'and', $not = false): static
{
- throw new LogicException('SQL type "where exists" query is not valid for Elasticsearch. Use whereNotNull() or whereNull() to query the existence of a field');
+ $type = 'between';
+
+ $this->wheres[] = compact('column', 'type', 'boolean', 'values', 'not');
+
+ return $this;
}
- public function _parseWhereNotExists(array $where)
+ public function groupBy(...$groups): Builder
{
- throw new LogicException('SQL type "where exists" query is not valid for Elasticsearch. Use whereNotNull() or whereNull() to query the existence of a field');
+ if (is_array($groups[0])) {
+ $groups = $groups[0];
+ }
+
+ $this->addSelect($groups);
+ $this->distinctType = 1;
+
+ return $this;
}
+ //Regexs
- /**
- * @param array $where
- *
- * @return mixed
- */
- protected function _parseWhereRegex(array $where)
+ public function addSelect($column): static
{
- $value = $where['expression'];
- $column = $where['column'];
+ if (! is_array($column)) {
+ $column = [$column];
+ }
- return [$column => ['regex' => $value]];
+ $currentColumns = $this->columns;
+ if ($currentColumns) {
+ return $this->select(array_merge($currentColumns, $column));
+ }
+ return $this->select($column);
}
/**
- * @param array $where
- *
- * @return array[]
+ * {@inheritdoc}
*/
- protected function _parseWhereNestedObject(array $where)
+ public function select($columns = ['*']): static
{
- $wheres = $where['wheres'];
- $column = $where['column'];
- $scoreMode = $where['score_mode'];
+ $columns = is_array($columns) ? $columns : [$columns];
+ $this->columns = $columns;
+ return $this;
+ }
- return [
- $column => ['nested' => ['wheres' => $wheres, 'score_mode' => $scoreMode]],
+ public function filterGeoBox($field, $topLeft, $bottomRight): void
+ {
+ $this->filters['filterGeoBox'] = [
+ 'field' => $field,
+ 'topLeft' => $topLeft,
+ 'bottomRight' => $bottomRight,
];
}
+ public function filterGeoPoint($field, $distance, $geoPoint): void
+ {
+ $this->filters['filterGeoPoint'] = [
+ 'field' => $field,
+ 'distance' => $distance,
+ 'geoPoint' => $geoPoint,
+ ];
+ }
- /**
- * @param array $where
- *
- * @return array[]
- */
- protected function _parseWhereNotNestedObject(array $where)
+ public function whereRegex($column, $expression): static
{
- $wheres = $where['wheres'];
- $column = $where['column'];
- $scoreMode = $where['score_mode'];
+ $type = 'regex';
+ $boolean = 'and';
+ $this->wheres[] = compact('column', 'type', 'expression', 'boolean');
+ return $this;
+ }
- return [
- $column => ['not_nested' => ['wheres' => $wheres, 'score_mode' => $scoreMode]],
- ];
+ public function orWhereRegex($column, $expression): static
+ {
+ $type = 'regex';
+ $boolean = 'or';
+ $this->wheres[] = compact('column', 'type', 'expression', 'boolean');
+
+ return $this;
+ }
+
+ public function _parseWhereExists(array $where)
+ {
+ throw new LogicException('SQL type "where exists" query is not valid for Elasticsearch. Use whereNotNull() or whereNull() to query the existence of a field');
}
+ public function _parseWhereNotExists(array $where)
+ {
+ throw new LogicException('SQL type "where exists" query is not valid for Elasticsearch. Use whereNotNull() or whereNull() to query the existence of a field');
+ }
/**
* Set custom options for the query.
*
- * @param array $options
*
* @return $this
*/
- public function options(array $options)
+ public function options(array $options): static
{
$this->options = $options;
return $this;
}
-
- //----------------------------------------------------------------------
- // Collection bindings
- //----------------------------------------------------------------------
-
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function pluck($column, $key = null)
+ public function pluck($column, $key = null): Collection
{
$results = $this->get($key === null ? [$column] : [$column, $key]);
// Convert ObjectID's to strings
if ($key == '_id') {
$results = $results->map(function ($item) {
- $item['_id'] = (string)$item['_id'];
+ $item['_id'] = (string) $item['_id'];
return $item;
});
@@ -1247,14 +1022,10 @@ public function pluck($column, $key = null)
return new Collection($p);
}
- //----------------------------------------------------------------------
- // Index/Schema
- //----------------------------------------------------------------------
-
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function from($index, $as = null)
+ public function from($index, $as = null): static
{
if ($index) {
@@ -1266,9 +1037,9 @@ public function from($index, $as = null)
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function truncate()
+ public function truncate(): int
{
$result = $this->connection->deleteAll([]);
@@ -1279,37 +1050,55 @@ public function truncate()
return 0;
}
- public function deleteIndex()
+ public function deleteIndex(): bool
{
return Schema::connection($this->connection->getName())->delete($this->index);
+ }
+ /**
+ * {@inheritdoc}
+ */
+ public function delete($id = null): int
+ {
+
+ if ($id !== null) {
+ $this->where('_id', '=', $id);
+ }
+
+ return $this->_processDelete();
}
- public function deleteIndexIfExists()
+ protected function _processDelete(): int
{
- return Schema::connection($this->connection->getName())->deleteIfExists($this->index);
+ $wheres = $this->compileWheres();
+ $options = $this->compileOptions();
+ $result = $this->connection->deleteAll($wheres, $options);
+ if ($result->isSuccessful()) {
+ return $result->getDeletedCount();
+ }
+ return 0;
}
- public function getIndexMappings()
+ public function deleteIndexIfExists(): bool
{
- return Schema::connection($this->connection->getName())->getMappings($this->index);
+ return Schema::connection($this->connection->getName())->deleteIfExists($this->index);
}
- public function getIndexSettings()
+ public function getIndexMappings(): array
{
- return Schema::connection($this->connection->getName())->getSettings($this->index);
+ return Schema::connection($this->connection->getName())->getMappings($this->index);
}
- public function indexExists()
+ public function getIndexSettings(): array
{
- return Schema::connection($this->connection->getName())->hasIndex($this->index);
+ return Schema::connection($this->connection->getName())->getSettings($this->index);
}
- public function createIndex()
+ public function createIndex(array $settings = []): bool
{
- if (!$this->indexExists()) {
- $this->connection->indexCreate($this->index);
+ if (! $this->indexExists()) {
+ $this->connection->indexCreate($settings);
return true;
}
@@ -1317,148 +1106,81 @@ public function createIndex()
return false;
}
- public function rawSearch(array $bodyParams, $returnRaw = false)
+ public function indexExists(): bool
+ {
+ return Schema::connection($this->connection->getName())->hasIndex($this->index);
+ }
+
+ public function rawSearch(array $bodyParams, $returnRaw = false): Collection
{
$find = $this->connection->searchRaw($bodyParams, $returnRaw);
$data = $find->data;
return new Collection($data);
-
}
- public function rawAggregation(array $bodyParams)
+ public function rawAggregation(array $bodyParams): Collection
{
$find = $this->connection->aggregationRaw($bodyParams);
$data = $find->data;
return new Collection($data);
-
- }
- //----------------------------------------------------------------------
- // Pagination overrides
- //----------------------------------------------------------------------
-
-
- protected function runPaginationCountQuery($columns = ['*'])
- {
- if ($this->distinct) {
- $clone = $this->cloneForPaginationCount();
- $currentCloneCols = $clone->columns;
- if ($columns && $columns !== ['*']) {
- $currentCloneCols = array_merge($currentCloneCols, $columns);
- }
-
- return $clone->setAggregate('count', $currentCloneCols)->get()->all();
- }
-
- $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
-
- return $this->cloneWithout($without)
- ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
- ->setAggregate('count', $this->withoutSelectAliases($columns))
- ->get()->all();
- }
-
- public function toSql()
- {
- return $this->toDsl();
- }
-
- public function toDsl()
- {
- $wheres = $this->compileWheres();
- $options = $this->compileOptions();
- $columns = $this->prepareColumns([]);
- if ($this->searchQuery) {
- $searchParams = $this->searchQuery;
- $searchOptions = $this->searchOptions;
- $fields = $this->fields;
-
- return $this->connection->toDslForSearch($searchParams, $searchOptions, $wheres, $options, $fields, $columns);
- }
-
- return $this->connection->toDsl($wheres, $options, $columns);
-
-
- }
-
- //----------------------------------------------------------------------
- // Disabled features (for now)
- //----------------------------------------------------------------------
-
- /**
- * @inheritdoc
- */
- public function upsert(array $values, $uniqueBy, $update = null)
- {
- throw new LogicException('The upsert feature for Elasticsearch is currently not supported. Please use updateAll()');
- }
-
-
- /**
- * @inheritdoc
- */
- public function groupByRaw($sql, array $bindings = [])
- {
- throw new LogicException('groupByRaw() is currently not supported');
}
-
- //----------------------------------------------------------------------
- // Helpers
- //----------------------------------------------------------------------
-
- private function _checkValues($values)
- {
- unset($values['updated_at']);
- unset($values['created_at']);
- if (!$this->_isAssociative($values)) {
- throw new RuntimeException('Invalid value format. Expected associative array, got sequential array');
- }
-
- return true;
+ public function toSql(): array
+ {
+ return $this->toDsl();
}
- private function _isAssociative(array $arr)
+ public function toDsl(): array
{
- if ([] === $arr) {
- return true;
+ $wheres = $this->compileWheres();
+ $options = $this->compileOptions();
+ $columns = $this->prepareColumns([]);
+ if ($this->searchQuery) {
+ $searchParams = $this->searchQuery;
+ $searchOptions = $this->searchOptions;
+ $fields = $this->fields;
+
+ return $this->connection->toDslForSearch($searchParams, $searchOptions, $wheres, $options, $fields, $columns);
}
- return array_keys($arr) !== range(0, count($arr) - 1);
+ return $this->connection->toDsl($wheres, $options, $columns);
}
-
- //----------------------------------------------------------------------
- // ES query executors
- //----------------------------------------------------------------------
-
- public function query($columns = [])
+ /**
+ * {@inheritdoc}
+ */
+ public function upsert(array $values, $uniqueBy, $update = null): int
{
- $wheres = $this->compileWheres();
- $options = $this->compileOptions();
+ throw new LogicException('The upsert feature for Elasticsearch is currently not supported. Please use updateAll()');
+ }
- return $this->connection->showQuery($wheres, $options, $columns);
+ /**
+ * {@inheritdoc}
+ */
+ public function groupByRaw($sql, array $bindings = [])
+ {
+ throw new LogicException('groupByRaw() is currently not supported');
}
public function matrix($column)
{
- if (!is_array($column)) {
+ if (! is_array($column)) {
$column = [$column];
}
$result = $this->aggregate(__FUNCTION__, $column);
- return $result ? : 0;
+ return $result ?: 0;
}
//----------------------------------------------------------------------
- // ES Search query methods
+ // Collection bindings
//----------------------------------------------------------------------
-
- public function searchQuery($term, $boostFactor = null, $clause = null, $type = 'term')
+ public function searchQuery($term, $boostFactor = null, $clause = null, $type = 'term'): void
{
- if (!$clause && !empty($this->searchQuery)) {
+ if (! $clause && ! empty($this->searchQuery)) {
switch ($type) {
case 'fuzzy':
throw new RuntimeException('Incorrect query sequencing, fuzzyTerm() should only start the ORM chain');
@@ -1469,7 +1191,6 @@ public function searchQuery($term, $boostFactor = null, $clause = null, $type =
default:
throw new RuntimeException('Incorrect query sequencing, term() should only start the ORM chain');
}
-
}
if ($clause && empty($this->searchQuery)) {
switch ($type) {
@@ -1482,7 +1203,6 @@ public function searchQuery($term, $boostFactor = null, $clause = null, $type =
default:
throw new RuntimeException('Incorrect query sequencing, andTerm()/orTerm() cannot start the ORM chain');
}
-
}
switch ($type) {
case 'fuzzy':
@@ -1509,22 +1229,26 @@ public function searchQuery($term, $boostFactor = null, $clause = null, $type =
}
}
- public function minShouldMatch($value)
+ //----------------------------------------------------------------------
+ // Index/Schema
+ //----------------------------------------------------------------------
+
+ public function minShouldMatch($value): void
{
$this->searchOptions['minimum_should_match'] = $value;
}
- public function minScore($value)
+ public function minScore($value): void
{
$this->minScore = $value;
}
- public function boostField($field, $factor)
+ public function boostField($field, $factor): void
{
$this->fields[$field] = $factor ?? 1;
}
- public function searchFields(array $fields)
+ public function searchFields(array $fields): void
{
foreach ($fields as $field) {
if (empty($this->fields[$field])) {
@@ -1533,31 +1257,34 @@ public function searchFields(array $fields)
}
}
- public function searchField($field, $boostFactor = null)
+ public function searchField($field, $boostFactor = null): void
{
$this->fields[$field] = $boostFactor ?? 1;
}
- public function highlight(array $fields = [], string|array $preTag = '', string|array $postTag = '', array $globalOptions = [])
- {
+ public function highlight(
+ array $fields = [],
+ string|array $preTag = '',
+ string|array $postTag = '',
+ array $globalOptions = []
+ ): void {
$highlightFields = [
- '*' => (object)[],
+ '*' => (object) [],
];
- if (!empty($fields)) {
+ if (! empty($fields)) {
$highlightFields = [];
foreach ($fields as $field => $payload) {
if (is_int($field)) {
- $highlightFields[$payload] = (object)[];
+ $highlightFields[$payload] = (object) [];
} else {
$highlightFields[$field] = $payload;
}
-
}
}
- if (!is_array($preTag)) {
+ if (! is_array($preTag)) {
$preTag = [$preTag];
}
- if (!is_array($postTag)) {
+ if (! is_array($postTag)) {
$postTag = [$postTag];
}
@@ -1569,16 +1296,14 @@ public function highlight(array $fields = [], string|array $preTag = '', str
$highlight['post_tags'] = $postTag;
$highlight['fields'] = $highlightFields;
-
$this->searchOptions['highlight'] = $highlight;
}
-
- public function search($columns = '*')
+ public function search($columns = '*'): Collection
{
$searchParams = $this->searchQuery;
- if (!$searchParams) {
+ if (! $searchParams) {
throw new RuntimeException('No search parameters. Add terms to search for.');
}
$searchOptions = $this->searchOptions;
@@ -1590,27 +1315,18 @@ public function search($columns = '*')
if ($search->isSuccessful()) {
$data = $search->data;
-
return new Collection($data);
-
-
} else {
throw new RuntimeException('Error: '.$search->errorMessage);
}
-
-
}
- //----------------------------------------------------------------------
- // PIT API
- //----------------------------------------------------------------------
-
- public function openPit($keepAlive = '5m')
+ public function openPit($keepAlive = '5m'): string
{
return $this->connection->openPit($keepAlive);
}
- public function pitFind($count, $pitId, $after = null, $keepAlive = '5m')
+ public function pitFind(int $count, string $pitId, ?array $after = null, string $keepAlive = '5m'): Results
{
$wheres = $this->compileWheres();
$options = $this->compileOptions();
@@ -1620,37 +1336,269 @@ public function pitFind($count, $pitId, $after = null, $keepAlive = '5m')
return $this->connection->pitFind($wheres, $options, $fields, $pitId, $after, $keepAlive);
}
- public function closePit($id)
+ public function closePit($id): bool
{
return $this->connection->closePit($id);
}
+ //----------------------------------------------------------------------
+ // Pagination overrides
+ //----------------------------------------------------------------------
+
+ protected function _parseWhereNested(array $where): array
+ {
+
+ $boolean = $where['boolean'];
+ // if ($boolean !== 'and') {
+ // throw new RuntimeException('Nested where clause with boolean other than "and" is not supported');
+ // }
+ if ($boolean === 'and not') {
+ $boolean = 'not';
+ }
+ $must = match ($boolean) {
+ 'and' => 'must',
+ 'not', 'or not' => 'must_not',
+ 'or' => 'should',
+ default => throw new RuntimeException($boolean.' is not supported for parameter grouping'),
+ };
+
+ $query = $where['query'];
+ $wheres = $query->compileWheres();
+
+ return [
+ $must => ['group' => ['wheres' => $wheres]],
+ ];
+ }
+
+ protected function _parseWhereQueryNested(array $where): array
+ {
+ return [
+ $where['column'] => [
+ 'innerNested' => [
+ 'wheres' => $where['wheres'],
+ 'options' => $where['options'],
+ ],
+ ],
+ ];
+ }
+
+ protected function _parseWhereIn(array $where): array
+ {
+ $column = $where['column'];
+ $values = $where['values'];
+
+ return [$column => ['in' => array_values($values)]];
+ }
+
+ protected function _parseWhereNotIn(array $where): array
+ {
+ $column = $where['column'];
+ $values = $where['values'];
+
+ return [$column => ['nin' => array_values($values)]];
+ }
+
+ protected function _parseWhereNull(array $where): array
+ {
+ $where['operator'] = 'not_exists';
+ $where['value'] = null;
+ return $this->_parseWhereBasic($where);
+ }
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
- private function _formatTimestamp($value)
+ protected function _parseWhereBasic(array $where): array
+ {
+ $operator = $where['operator'];
+ $column = $where['column'];
+ $value = $where['value'];
+ $boolean = $where['boolean'] ?? null;
+ if ($boolean === 'and not') {
+ $operator = '!=';
+ }
+ if ($boolean === 'or not') {
+ $operator = '!=';
+ }
+ if ($operator === 'not like') {
+ $operator = 'not_like';
+ }
+
+ if (! isset($operator) || $operator == '=') {
+ $query = [$column => $value];
+ } elseif (array_key_exists($operator, $this->conversion)) {
+ $query = [$column => [$this->conversion[$operator] => $value]];
+ } else {
+ if (is_callable($column)) {
+ throw new RuntimeException('Invalid closure for where clause');
+ }
+ $query = [$column => [$operator => $value]];
+ }
+
+ return $query;
+ }
+
+ /**
+ * @return array
+ */
+ protected function _parseWhereNotNull(array $where)
+ {
+ $where['operator'] = 'exists';
+ $where['value'] = null;
+
+ return $this->_parseWhereBasic($where);
+ }
+
+ //----------------------------------------------------------------------
+ // ES query executors
+ //----------------------------------------------------------------------
+
+ protected function _parseWhereBetween(array $where): array
+ {
+ $not = $where['not'] ?? false;
+ $values = $where['values'];
+ $column = $where['column'];
+
+ if ($not) {
+ return [
+ $column => [
+ 'not_between' => [$values[0], $values[1]],
+ ],
+ ];
+ }
+
+ return [
+ $column => [
+ 'between' => [$values[0], $values[1]],
+ ],
+ ];
+ }
+
+ protected function _parseWhereDate(array $where): array
+ {
+ //return a normal where clause
+ return $this->_parseWhereBasic($where);
+ }
+
+ //----------------------------------------------------------------------
+ // ES Search query methods
+ //----------------------------------------------------------------------
+
+ protected function _parseWhereTimestamp(array $where): array
+ {
+ $where['value'] = $this->_formatTimestamp($where['value']);
+
+ return $this->_parseWhereBasic($where);
+ }
+
+ private function _formatTimestamp($value): string|int
{
if (is_numeric($value)) {
// Convert to integer in case it's a string
- $value = (int)$value;
+ $value = (int) $value;
// Check for milliseconds
if ($value > 10000000000) {
return $value;
}
// ES expects seconds as a string
- return (string)Carbon::createFromTimestamp($value)->timestamp;
+ return (string) Carbon::createFromTimestamp($value)->timestamp;
}
// If it's not numeric, assume it's a date string and try to return TS as a string
try {
- return (string)Carbon::parse($value)->timestamp;
+ return (string) Carbon::parse($value)->timestamp;
} catch (\Exception $e) {
throw new LogicException('Invalid date or timestamp');
}
}
+ protected function _parseWhereMonth(array $where): array
+ {
+ throw new LogicException('whereMonth clause is not available yet');
+ }
+
+ protected function _parseWhereDay(array $where): array
+ {
+ throw new LogicException('whereDay clause is not available yet');
+ }
+
+ protected function _parseWhereYear(array $where): array
+ {
+ throw new LogicException('whereYear clause is not available yet');
+ }
+
+ protected function _parseWhereTime(array $where): array
+ {
+ throw new LogicException('whereTime clause is not available yet');
+ }
+
+ protected function _parseWhereRaw(array $where): array
+ {
+ throw new LogicException('whereRaw clause is not available yet');
+ }
+
+ protected function _parseWhereRegex(array $where): array
+ {
+ $value = $where['expression'];
+ $column = $where['column'];
+
+ return [$column => ['regex' => $value]];
+ }
+
+ //----------------------------------------------------------------------
+ // PIT API
+ //----------------------------------------------------------------------
+
+ protected function _parseWhereNestedObject(array $where): array
+ {
+ $wheres = $where['wheres'];
+ $column = $where['column'];
+ $scoreMode = $where['score_mode'];
+
+ return [
+ $column => ['nested' => ['wheres' => $wheres, 'score_mode' => $scoreMode]],
+ ];
+ }
+
+ protected function _parseWhereNotNestedObject(array $where): array
+ {
+ $wheres = $where['wheres'];
+ $column = $where['column'];
+ $scoreMode = $where['score_mode'];
+
+ return [
+ $column => ['not_nested' => ['wheres' => $wheres, 'score_mode' => $scoreMode]],
+ ];
+ }
+
+ protected function runPaginationCountQuery($columns = ['*']): Closure|array
+ {
+ if ($this->distinctType) {
+ $clone = $this->cloneForPaginationCount();
+ $currentCloneCols = $clone->columns;
+ if ($columns && $columns !== ['*']) {
+ $currentCloneCols = array_merge($currentCloneCols, $columns);
+ }
+
+ return $clone->setAggregate('count', $currentCloneCols)->get()->all();
+ }
+
+ $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
+
+ return $this->cloneWithout($without)->cloneWithoutBindings($this->unions ? ['order'] : [
+ 'select',
+ 'order',
+ ])->setAggregate('count', $this->withoutSelectAliases($columns))->get()->all();
+ }
+
+ //----------------------------------------------------------------------
+ // Helpers
+ //----------------------------------------------------------------------
+ public function all($columns = []): Collection
+ {
+ return $this->_processGet($columns);
+ }
}
diff --git a/src/Query/Grammar.php b/src/Query/Grammar.php
index 019c0b1..1c4962d 100644
--- a/src/Query/Grammar.php
+++ b/src/Query/Grammar.php
@@ -1,5 +1,7 @@
getOwnerKey();
+ return $this->ownerKey;
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function addConstraints()
+ public function addConstraints(): void
{
if (static::$constraints) {
- $this->query->where($this->getOwnerKey(), '=', $this->parent->{$this->foreignKey});
+ $this->query->where($this->ownerKey, '=', $this->parent->{$this->foreignKey});
}
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function addEagerConstraints(array $models)
+ public function addEagerConstraints(array $models): void
{
- $key = $this->getOwnerKey();
-
- $this->query->whereIn($key, $this->getEagerModelKeys($models));
+ $this->query->whereIn($this->ownerKey, $this->getEagerModelKeys($models));
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+ public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*']): Builder
{
return $query;
}
- /**
- * Get the owner key with backwards compatible support.
- *
- * @return string
- */
- public function getOwnerKey()
- {
- return property_exists($this, 'ownerKey') ? $this->ownerKey : $this->otherKey;
- }
-
- protected function whereInMethod(EloquentModel $model, $key)
+ protected function whereInMethod(EloquentModel $model, $key): string
{
return 'whereIn';
}
diff --git a/src/Relations/BelongsToMany.php b/src/Relations/BelongsToMany.php
index af27a04..4cecf34 100644
--- a/src/Relations/BelongsToMany.php
+++ b/src/Relations/BelongsToMany.php
@@ -1,13 +1,12 @@
foreignKey;
+ $foreignKey = $this->getHasCompareKey();
+
+ //@phpstan-ignore-next-line
+ return $query->select($foreignKey)->where($foreignKey, 'exists', true);
}
-
+
/**
* Get the key for comparing against the parent key in "has" query.
- *
- * @return string
*/
- public function getHasCompareKey()
+ public function getHasCompareKey(): string
{
return $this->getForeignKeyName();
}
/**
- * @inheritdoc
+ * Get the plain foreign key.
*/
- public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
+ public function getForeignKeyName(): string
{
- $foreignKey = $this->getHasCompareKey();
-
- return $query->select($foreignKey)->where($foreignKey, 'exists', true);
+ return $this->foreignKey;
}
-
- protected function whereInMethod(EloquentModel $model, $key)
+ protected function whereInMethod(EloquentModel $model, $key): string
{
return 'whereIn';
}
diff --git a/src/Relations/HasOne.php b/src/Relations/HasOne.php
index 9e08597..c6cb99e 100644
--- a/src/Relations/HasOne.php
+++ b/src/Relations/HasOne.php
@@ -1,5 +1,7 @@
foreignKey;
+ return $this->getForeignKeyName();
}
- public function getHasCompareKey()
+ public function getForeignKeyName(): string
{
- return $this->getForeignKeyName();
+ return $this->foreignKey;
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
{
$foreignKey = $this->getForeignKeyName();
-
+ //@phpstan-ignore-next-line
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
}
-
- protected function whereInMethod(EloquentModel $model, $key)
+ protected function whereInMethod(EloquentModel $model, $key): string
{
return 'whereIn';
}
diff --git a/src/Relations/MorphMany.php b/src/Relations/MorphMany.php
index d675bf7..0dafb69 100644
--- a/src/Relations/MorphMany.php
+++ b/src/Relations/MorphMany.php
@@ -1,5 +1,7 @@
query->where($this->getOwnerKey(), '=', $this->parent->{$this->foreignKey});
+ $this->query->where($this->ownerKey, '=', $this->getForeignKeyFrom($this->parent));
}
}
- /**
- * @inheritdoc
- */
- protected function getResultsByType($type)
+ /** {@inheritdoc} */
+ protected function getResultsByType($type): Collection
{
$instance = $this->createModelByType($type);
@@ -30,13 +29,8 @@ protected function getResultsByType($type)
return $query->whereIn($key, $this->gatherKeysByType($type, $instance->getKeyType()))->get();
}
-
- public function getOwnerKey()
- {
- return property_exists($this, 'ownerKey') ? $this->ownerKey : $this->otherKey;
- }
- protected function whereInMethod(EloquentModel $model, $key)
+ protected function whereInMethod(EloquentModel $model, $key): string
{
return 'whereIn';
}
diff --git a/src/Schema/AnalyzerBlueprint.php b/src/Schema/AnalyzerBlueprint.php
index 9ba02c8..967b8fa 100644
--- a/src/Schema/AnalyzerBlueprint.php
+++ b/src/Schema/AnalyzerBlueprint.php
@@ -1,22 +1,22 @@
addProperty('analyzer', $name);
}
- public function tokenizer($type): Definitions\AnalyzerPropertyDefinition
+ protected function addProperty($config, $name, array $parameters = [])
{
- return $this->addProperty('tokenizer', $type);
+ return $this->addPropertyDefinition(new Definitions\AnalyzerPropertyDefinition(
+ array_merge(compact('config', 'name'), $parameters)
+ ));
}
- public function charFilter($type): Definitions\AnalyzerPropertyDefinition
+ protected function addPropertyDefinition($definition)
{
- return $this->addProperty('char_filter', $type);
+ $this->parameters['analysis'][] = $definition;
+
+ return $definition;
}
- public function filter($type): Definitions\AnalyzerPropertyDefinition
+ public function tokenizer($type): Definitions\AnalyzerPropertyDefinition
{
- return $this->addProperty('filter', $type);
+ return $this->addProperty('tokenizer', $type);
}
-
//----------------------------------------------------------------------
// Definitions
//----------------------------------------------------------------------
- protected function addProperty($config, $name, array $parameters = [])
+ public function charFilter($type): Definitions\AnalyzerPropertyDefinition
{
- return $this->addPropertyDefinition(new Definitions\AnalyzerPropertyDefinition(
- array_merge(compact('config', 'name'), $parameters)
- ));
+ return $this->addProperty('char_filter', $type);
}
- protected function addPropertyDefinition($definition)
+ public function filter($type): Definitions\AnalyzerPropertyDefinition
{
- $this->parameters['analysis'][] = $definition;
-
- return $definition;
+ return $this->addProperty('filter', $type);
}
//----------------------------------------------------------------------
// Builders
//----------------------------------------------------------------------
- public function buildIndexAnalyzerSettings(Connection $connection)
+ public function buildIndexAnalyzerSettings(Connection $connection): bool
{
$connection->setIndex($this->index);
if ($this->parameters) {
$this->_formatParams();
$connection->indexAnalyzerSettings($this->parameters);
}
+
+ return false;
}
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
- private function _formatParams()
+ private function _formatParams(): void
{
if ($this->parameters) {
- if (!empty($this->parameters['analysis'])) {
+ if (! empty($this->parameters['analysis'])) {
$properties = [];
foreach ($this->parameters['analysis'] as $property) {
if ($property instanceof Fluent) {
@@ -98,5 +99,4 @@ private function _formatParams()
}
}
}
-
}
diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php
index 229f2d7..185f5ab 100644
--- a/src/Schema/Builder.php
+++ b/src/Schema/Builder.php
@@ -1,15 +1,17 @@
connection->getIndices(false);
- }
-
-
public function overridePrefix($value): Builder
{
$this->connection->setIndexPrefix($value);
@@ -33,7 +29,14 @@ public function overridePrefix($value): Builder
return $this;
}
- public function getIndex($index)
+ public function getSettings($index): array
+ {
+ $this->connection->setIndex($index);
+
+ return $this->connection->indexSettings($this->connection->getIndex());
+ }
+
+ public function getIndex($index): array
{
if ($this->hasIndex($index)) {
$this->connection->setIndex($index);
@@ -45,24 +48,23 @@ public function getIndex($index)
}
- public function getMappings($index)
+ public function hasIndex($index): bool
{
- $this->connection->setIndex($index);
+ $index = $this->connection->setIndex($index);
- return $this->connection->indexMappings($this->connection->getIndex());
+ return $this->connection->indexExists($index);
}
- public function getSettings($index)
+ public function getIndices(): array
{
- $this->connection->setIndex($index);
-
- return $this->connection->indexSettings($this->connection->getIndex());
+ return $this->connection->getIndices(false);
}
//----------------------------------------------------------------------
// Create Index
//----------------------------------------------------------------------
- public function create($index, Closure $callback)
+
+ public function create($index, Closure $callback): array
{
$this->builder('buildIndexCreate', tap(new IndexBlueprint($index), function ($blueprint) use ($callback) {
$callback($blueprint);
@@ -71,7 +73,16 @@ public function create($index, Closure $callback)
return $this->getIndex($index);
}
- public function createIfNotExists($index, Closure $callback)
+ protected function builder($builder, IndexBlueprint $blueprint): void
+ {
+ $blueprint->{$builder}($this->connection);
+ }
+
+ //----------------------------------------------------------------------
+ // Reindex
+ //----------------------------------------------------------------------
+
+ public function createIfNotExists($index, Closure $callback): array
{
if ($this->hasIndex($index)) {
return $this->getIndex($index);
@@ -84,20 +95,19 @@ public function createIfNotExists($index, Closure $callback)
}
//----------------------------------------------------------------------
- // Reindex
+ // Modify Index
//----------------------------------------------------------------------
- public function reIndex($from, $to)
+ public function reIndex($from, $to): Results
{
return $this->connection->reIndex($from, $to);
}
-
//----------------------------------------------------------------------
- // Modify Index
+ // Delete Index
//----------------------------------------------------------------------
- public function modify($index, Closure $callback)
+ public function modify($index, Closure $callback): array
{
$this->builder('buildIndexModify', tap(new IndexBlueprint($index), function ($blueprint) use ($callback) {
$callback($blueprint);
@@ -106,18 +116,18 @@ public function modify($index, Closure $callback)
return $this->getIndex($index);
}
- //----------------------------------------------------------------------
- // Delete Index
- //----------------------------------------------------------------------
-
- public function delete($index)
+ public function delete($index): bool
{
$this->connection->setIndex($index);
return $this->connection->indexDelete();
}
- public function deleteIfExists($index)
+ //----------------------------------------------------------------------
+ // Index template
+ //----------------------------------------------------------------------
+
+ public function deleteIfExists($index): bool
{
if ($this->hasIndex($index)) {
$this->connection->setIndex($index);
@@ -129,18 +139,19 @@ public function deleteIfExists($index)
}
//----------------------------------------------------------------------
- // Index template
+ // Analysers
//----------------------------------------------------------------------
+
public function createTemplate($name, Closure $callback)
{
//TODO
}
//----------------------------------------------------------------------
- // Analysers
+ // Index ops
//----------------------------------------------------------------------
- public function setAnalyser($index, Closure $callback)
+ public function setAnalyser($index, Closure $callback): array
{
$this->analyzerBuilder('buildIndexAnalyzerSettings', tap(new AnalyzerBlueprint($index), function ($blueprint) use ($callback) {
$callback($blueprint);
@@ -149,11 +160,12 @@ public function setAnalyser($index, Closure $callback)
return $this->getIndex($index);
}
- //----------------------------------------------------------------------
- // Index ops
- //----------------------------------------------------------------------
+ protected function analyzerBuilder($builder, AnalyzerBlueprint $blueprint): void
+ {
+ $blueprint->{$builder}($this->connection);
+ }
- public function hasField($index, $field)
+ public function hasField($index, $field): bool
{
$index = $this->connection->setIndex($index);
@@ -173,63 +185,22 @@ public function hasField($index, $field)
}
- public function hasFields($index, array $fields)
- {
- $index = $this->connection->setIndex($index);
-
- try {
- $mappings = $this->getMappings($index);
- $props = $mappings[$index]['mappings']['properties'];
- $props = $this->_flattenFields($props);
- $fileList = $this->_sanitizeFlatFields($props);
- $allFound = true;
- foreach ($fields as $field) {
- if (!in_array($field, $fileList)) {
- $allFound = false;
- }
- }
-
- return $allFound;
- } catch (Exception $e) {
- return false;
- }
-
- }
-
- public function hasIndex($index)
- {
- $index = $this->connection->setIndex($index);
-
- return $this->connection->indexExists($index);
- }
-
//----------------------------------------------------------------------
// Manual
//----------------------------------------------------------------------
- public function dsl($method, $params)
+ public function getMappings($index): array
{
- return $this->connection->indicesDsl($method, $params);
+ $this->connection->setIndex($index);
+
+ return $this->connection->indexMappings($this->connection->getIndex());
}
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
- function flatten($array, $prefix = '')
- {
- $result = array();
- foreach ($array as $key => $value) {
- if (is_array($value)) {
- $result = $result + flatten($value, $prefix.$key.'.');
- } else {
- $result[$prefix.$key] = $value;
- }
- }
-
- return $result;
- }
- private function _flattenFields($array, $prefix = '')
+ private function _flattenFields($array, $prefix = ''): array
{
$result = [];
@@ -244,7 +215,7 @@ private function _flattenFields($array, $prefix = '')
return $result;
}
- private function _sanitizeFlatFields($flatFields)
+ private function _sanitizeFlatFields($flatFields): array
{
$fields = [];
if ($flatFields) {
@@ -264,25 +235,59 @@ private function _sanitizeFlatFields($flatFields)
return $fields;
}
+ public function hasFields($index, array $fields): bool
+ {
+ $index = $this->connection->setIndex($index);
+
+ try {
+ $mappings = $this->getMappings($index);
+ $props = $mappings[$index]['mappings']['properties'];
+ $props = $this->_flattenFields($props);
+ $fileList = $this->_sanitizeFlatFields($props);
+ $allFound = true;
+ foreach ($fields as $field) {
+ if (! in_array($field, $fileList)) {
+ $allFound = false;
+ }
+ }
+
+ return $allFound;
+ } catch (Exception $e) {
+ return false;
+ }
+
+ }
+
//----------------------------------------------------------------------
// Internal Laravel init migration catchers
// *Case for when ES is the only datasource
//----------------------------------------------------------------------
- public function hasTable($table)
+
+ public function dsl($method, $params): Results
{
- return $this->getIndex($table);
+ return $this->connection->indicesDsl($method, $params);
}
//----------------------------------------------------------------------
// Builders
//----------------------------------------------------------------------
- protected function builder($builder, IndexBlueprint $blueprint)
+
+ public function flatten($array, $prefix = ''): array
{
- $blueprint->{$builder}($this->connection);
+ $result = [];
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ $result = $result + $this->flatten($value, $prefix.$key.'.');
+ } else {
+ $result[$prefix.$key] = $value;
+ }
+ }
+
+ return $result;
}
- protected function analyzerBuilder($builder, AnalyzerBlueprint $blueprint)
+ public function hasTable($table): array
{
- $blueprint->{$builder}($this->connection);
+ return $this->getIndex($table);
}
}
diff --git a/src/Schema/Definitions/AnalyzerPropertyDefinition.php b/src/Schema/Definitions/AnalyzerPropertyDefinition.php
index 1300876..b68b32c 100644
--- a/src/Schema/Definitions/AnalyzerPropertyDefinition.php
+++ b/src/Schema/Definitions/AnalyzerPropertyDefinition.php
@@ -1,5 +1,7 @@
addField('text', $field);
}
+ protected function addField($type, $field, array $parameters = [])
+ {
+ return $this->addFieldDefinition(new Definitions\FieldDefinition(
+ array_merge(compact('type', 'field'), $parameters)
+ ));
+ }
+
+ protected function addFieldDefinition($definition)
+ {
+ $this->parameters['properties'][] = $definition;
+
+ return $definition;
+ }
+
public function array($field): Definitions\FieldDefinition
{
return $this->addField('text', $field);
}
+ //----------------------------------------------------------------------
+ // Numeric Types
+ //----------------------------------------------------------------------
+
public function boolean($field): Definitions\FieldDefinition
{
return $this->addField('boolean', $field);
@@ -53,11 +68,6 @@ public function keyword($field): Definitions\FieldDefinition
return $this->addField('keyword', $field);
}
-
- //----------------------------------------------------------------------
- // Numeric Types
- //----------------------------------------------------------------------
-
public function long($field): Definitions\FieldDefinition
{
return $this->addField('long', $field);
@@ -93,6 +103,8 @@ public function halfFloat($field): Definitions\FieldDefinition
return $this->addField('half_float', $field);
}
+ //----------------------------------------------------------------------
+
public function scaledFloat($field, $scalingFactor = 100): Definitions\FieldDefinition
{
return $this->addField('scaled_float', $field, [
@@ -105,8 +117,6 @@ public function unsignedLong($field): Definitions\FieldDefinition
return $this->addField('unsigned_long', $field);
}
- //----------------------------------------------------------------------
-
public function date($field, $format = null): Definitions\FieldDefinition
{
if ($format) {
@@ -147,40 +157,25 @@ public function settings($key, $value): void
$this->parameters['settings'][$key] = $value;
}
- public function map($key, $value): void
- {
- $this->parameters['map'][$key] = $value;
- }
-
- public function field($type, $field, array $parameters = [])
- {
- return $this->addField($type, $field, $parameters);
- }
-
//----------------------------------------------------------------------
// Definitions
//----------------------------------------------------------------------
- protected function addField($type, $field, array $parameters = [])
+ public function map($key, $value): void
{
- return $this->addFieldDefinition(new Definitions\FieldDefinition(
- array_merge(compact('type', 'field'), $parameters)
- ));
+ $this->parameters['map'][$key] = $value;
}
- protected function addFieldDefinition($definition)
+ public function field($type, $field, array $parameters = [])
{
- $this->parameters['properties'][] = $definition;
-
- return $definition;
+ return $this->addField($type, $field, $parameters);
}
//======================================================================
// Builders
//======================================================================
-
- public function buildIndexCreate(Connection $connection)
+ public function buildIndexCreate(Connection $connection): void
{
$connection->setIndex($this->index);
if ($this->parameters) {
@@ -189,55 +184,53 @@ public function buildIndexCreate(Connection $connection)
}
}
- public function buildReIndex(Connection $connection)
- {
- return $connection->reIndex($this->index, $this->newIndex);
- }
-
- public function buildIndexModify(Connection $connection)
+ private function _formatParams(): void
{
- $connection->setIndex($this->index);
if ($this->parameters) {
- $this->_formatParams();
- $connection->indexModify($this->parameters);
+ if (! empty($this->parameters['properties'])) {
+ $properties = [];
+ foreach ($this->parameters['properties'] as $property) {
+ if ($property instanceof Fluent) {
+ $properties[] = $property->toArray();
+ } else {
+ $properties[] = $property;
+ }
+ }
+ $this->parameters['properties'] = $properties;
+ }
}
}
+ // public function buildReIndex(Connection $connection): void
+ // {
+ // return $connection->reIndex($this->index, $this->newIndex);
+ // }
//----------------------------------------------------------------------
// Internal Laravel init migration catchers
// *Case for when ES is the only datasource
//----------------------------------------------------------------------
- public function increments($column)
+ public function buildIndexModify(Connection $connection): void
{
- return $this->addField('keyword', $column);
+ $connection->setIndex($this->index);
+ if ($this->parameters) {
+ $this->_formatParams();
+ $connection->indexModify($this->parameters);
+ }
}
- public function string($column)
+ public function increments($column): Definitions\FieldDefinition
{
return $this->addField('keyword', $column);
}
-
-
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
- private function _formatParams()
+
+ public function string($column): Definitions\FieldDefinition
{
- if ($this->parameters) {
- if (!empty($this->parameters['properties'])) {
- $properties = [];
- foreach ($this->parameters['properties'] as $property) {
- if ($property instanceof Fluent) {
- $properties[] = $property->toArray();
- } else {
- $properties[] = $property;
- }
- }
- $this->parameters['properties'] = $properties;
- }
- }
+ return $this->addField('keyword', $column);
}
}
diff --git a/src/Schema/Schema.php b/src/Schema/Schema.php
index a684d0c..fcfc798 100644
--- a/src/Schema/Schema.php
+++ b/src/Schema/Schema.php
@@ -1,8 +1,9 @@
connection($name)->getSchemaBuilder();
}
- public static function on($name)
- {
- return static::connection($name);
- }
-
/**
* Get a schema builder instance for the default connection.
- *
- * @return Builder
*/
- protected static function getFacadeAccessor()
+ protected static function getFacadeAccessor(): Builder
{
return static::$app['db']->connection('elasticsearch')->getSchemaBuilder();
}
-//
public static function __callStatic($method, $args)
{
$instance = static::getFacadeAccessor();
return $instance->$method(...$args);
}
-
}
diff --git a/testbench.yaml b/testbench.yaml
new file mode 100644
index 0000000..19ff6f9
--- /dev/null
+++ b/testbench.yaml
@@ -0,0 +1,21 @@
+providers:
+ # - Workbench\App\Providers\WorkbenchServiceProvider
+
+migrations:
+ - workbench/database/migrations
+
+seeders:
+ - Workbench\Database\Seeders\DatabaseSeeder
+
+workbench:
+ start: '/'
+ install: true
+ discovers:
+ web: true
+ api: false
+ commands: false
+ components: false
+ views: false
+ build: []
+ assets: []
+ sync: []
diff --git a/tests/ArchitectureTest.php b/tests/ArchitectureTest.php
new file mode 100644
index 0000000..1e378a1
--- /dev/null
+++ b/tests/ArchitectureTest.php
@@ -0,0 +1,8 @@
+expect(['dd', 'dump', 'ray', 'var_dump'])
+ ->not->toBeUsed();
diff --git a/tests/Eloquent/AggregationTest.php b/tests/Eloquent/AggregationTest.php
new file mode 100644
index 0000000..d6e427c
--- /dev/null
+++ b/tests/Eloquent/AggregationTest.php
@@ -0,0 +1,43 @@
+state(['status' => 1])->count(3)->create();
+ Product::factory()->state(['status' => 2])->count(2)->create();
+ $statuses = Product::select('status')->distinct()->get();
+ expect($statuses)->toHaveCount(2);
+});
+
+test('group products by status', function () {
+ Product::factory()->state(['status' => 1])->count(3)->create();
+ Product::factory()->state(['status' => 2])->count(2)->create();
+ $grouped = Product::groupBy('status')->get();
+ expect($grouped)->toBeCollection()
+ ->and($grouped)->toHaveCount(2);
+});
+
+test('retrieve distinct products with multiple fields', function () {
+ Product::factory()->state(['status' => 1, 'color' => 'blue'])->create();
+ Product::factory()->state(['status' => 1, 'color' => 'red'])->create();
+ $products = Product::distinct()->get(['status', 'color']);
+ expect($products)->toHaveCount(2);
+});
+
+test('order products by the count of distinct status', function () {
+ Product::factory()->state(['status' => 1])->count(5)->create();
+ Product::factory()->state(['status' => 2])->count(2)->create();
+ $products = Product::select('status')->distinct()->orderBy('status_count')->get();
+ expect($products->first()->status)->toEqual(1); // Assuming it orders with the least first
+});
+
+test('get distinct statuses with their counts', function () {
+ Product::factory()->state(['status' => 1])->count(5)->create();
+ Product::factory()->state(['status' => 2])->count(3)->create();
+ $statuses = Product::select('status')->distinct(true)->orderByDesc('status_count')->get();
+
+ expect($statuses->first()->status_count)->toEqual(5);
+});
diff --git a/tests/Eloquent/ChunkingTest.php b/tests/Eloquent/ChunkingTest.php
new file mode 100644
index 0000000..4e23bca
--- /dev/null
+++ b/tests/Eloquent/ChunkingTest.php
@@ -0,0 +1,54 @@
+state(['price' => 50])->make();
+ Product::insert($products->toArray());
+
+ Product::chunk(10, function ($products) {
+ foreach ($products as $product) {
+ $product->price *= 1.1;
+ $product->saveWithoutRefresh();
+ }
+ });
+ sleep(3);
+
+ $updatedProduct = Product::first();
+ expect($updatedProduct->price)->toBeGreaterThan(50);
+});
+
+test('process large dataset using basic chunking with extended keepAlive', function () {
+ $products = Product::factory(100)->state(['price' => 50])->make();
+ Product::insert($products->toArray());
+
+ Product::chunk(1000, function ($products) {
+ foreach ($products as $product) {
+ $product->price *= 1.1;
+ $product->saveWithoutRefresh();
+ }
+ }, '20m'); // Using an extended keepAlive period
+ sleep(3);
+
+ $updatedProduct = Product::first();
+ expect($updatedProduct->price)->toBeGreaterThan(50);
+});
+
+test('chunk by ID on a specific column with custom keepAlive', function () {
+ $products = Product::factory(100)->state(['price' => 50])->make();
+ Product::insert($products->toArray());
+
+ // Assuming 'product_id' is a unique identifier in the dataset
+ Product::chunkById(1000, function ($products) {
+ foreach ($products as $product) {
+ $product->price *= 1.1;
+ $product->saveWithoutRefresh();
+ }
+ }, 'product_id.keyword', null, '5m');
+ sleep(3);
+
+ $updatedProduct = Product::first();
+ expect($updatedProduct->price)->toBeGreaterThan(50);
+});
diff --git a/tests/Eloquent/DeletionTest.php b/tests/Eloquent/DeletionTest.php
new file mode 100644
index 0000000..a5dd6e8
--- /dev/null
+++ b/tests/Eloquent/DeletionTest.php
@@ -0,0 +1,88 @@
+create();
+ $retrieved = Product::find($product->_id);
+ $retrieved->delete();
+ $deleted = Product::find($product->_id);
+ expect($deleted)->toBeNull();
+});
+
+test('mass deletion of models where color is null', function () {
+ Product::factory(5)->state(['color' => null])->create();
+ Product::factory(3)->state(['color' => 'blue'])->create();
+ Product::whereNull('color')->delete();
+ $products = Product::all();
+ expect($products)->toHaveCount(3);
+});
+
+test('truncate all documents from an index', function () {
+ Product::factory(10)->create();
+ Product::truncate();
+ sleep(3);
+
+ $products = Product::all();
+ expect($products)->toBeEmpty();
+});
+
+test('destroy a product by _id', function () {
+ $product = Product::factory()->create();
+ Product::destroy($product->_id);
+ $deleted = Product::find($product->_id);
+ expect($deleted)->toBeNull();
+});
+
+test('destroy multiple products by _ids', function () {
+ $product1 = Product::factory()->create();
+ $product2 = Product::factory()->create();
+ Product::destroy([$product1->_id, $product2->_id]);
+ $deleted1 = Product::find($product1->_id);
+ $deleted2 = Product::find($product2->_id);
+ expect($deleted1)->toBeNull()
+ ->and($deleted2)->toBeNull();
+});
+
+test('soft deletes a product and restores it', function () {
+ $product = Product::factory()->create();
+ $product->delete();
+ $trashed = Product::withTrashed()->find($product->_id);
+ expect($trashed->trashed())->toBeTrue();
+ $trashed->restore();
+ $restored = Product::find($product->_id);
+ expect($restored->trashed())->toBeFalse();
+});
+
+test('ensure deletion of models with a specific status', function () {
+ Product::factory(3)->state(['status' => 5])->create();
+ Product::factory(2)->state(['status' => 1])->create();
+ Product::where('status', 5)->delete();
+ $remainingProducts = Product::all();
+ expect($remainingProducts)->toHaveCount(2)
+ ->and($remainingProducts->pluck('status')->contains(5))->toBeFalse();
+});
+
+test('delete multiple models by complex query', function () {
+ Product::factory()->state(['is_active' => true, 'color' => 'blue'])->create();
+ Product::factory()->state(['is_active' => false, 'color' => 'blue'])->create();
+ Product::where('is_active', true)->where('color', 'blue')->delete();
+ $activeBlue = Product::where('is_active', true)->where('color', 'blue')->first();
+ expect($activeBlue)->toBeNull();
+ $inactiveBlue = Product::where('is_active', false)->where('color', 'blue')->first();
+ expect($inactiveBlue)->not()->toBeNull();
+});
+
+test('test soft deletion query visibility', function () {
+ $product = Product::factory()->create();
+ $product->delete();
+ $visibleProduct = Product::find($product->_id);
+ expect($visibleProduct)->toBeNull();
+ $trashedProduct = Product::withTrashed()->find($product->_id);
+ expect($trashedProduct)->not()->toBeNull()
+ ->and($trashedProduct->trashed())->toBeTrue();
+});
diff --git a/tests/Eloquent/DynamicIndicesTest.php b/tests/Eloquent/DynamicIndicesTest.php
new file mode 100644
index 0000000..4224bd8
--- /dev/null
+++ b/tests/Eloquent/DynamicIndicesTest.php
@@ -0,0 +1,82 @@
+each(function (string $index) {
+ Schema::deleteIfExists('page_hits_'.$index);
+ });
+});
+
+test('retrieve page hits across dynamic indices', function () {
+ PageHit::factory()->count(15)->state(['page_id' => 1])->make()->each(function ($pageHit) {
+ $pageHit->setIndex('page_hits_'.$pageHit->date);
+ $pageHit->saveWithoutRefresh();
+ });
+ sleep(2);
+
+ $pageHitsSearch = PageHit::where('page_id', 1)->get();
+ expect($pageHitsSearch)->toHaveCount(15);
+
+});
+
+test('create a page hit record with dynamic index', function () {
+ $pageHit = new PageHit;
+ $pageHit->ip = '192.168.1.1';
+ $pageHit->page_id = 4;
+ $pageHit->date = '2021-01-01';
+ $pageHit->setIndex('page_hits_'.$pageHit->date);
+ $pageHit->save();
+
+ $retrievedHits = PageHit::where('page_id', 4)->get();
+ expect($retrievedHits)->toHaveCount(1)
+ ->and($retrievedHits->first()->ip)->toEqual('192.168.1.1');
+});
+
+test('retrieve current record index', function () {
+ $pageHit = new PageHit;
+ $pageHit->ip = '192.168.1.100';
+ $pageHit->page_id = 5;
+ $pageHit->date = '2021-01-01';
+ $pageHit->setIndex('page_hits_'.$pageHit->date);
+ $pageHit->save();
+
+ $indexName = $pageHit->getRecordIndex();
+ expect($indexName)->toEqual('page_hits_*');
+});
+
+test('search within a specific dynamic index', function () {
+ $pageHit = new PageHit;
+ $pageHit->ip = '192.168.1.100';
+ $pageHit->page_id = 3;
+ $pageHit->date = '2021-01-02';
+ $pageHit->setIndex('page_hits_'.$pageHit->date);
+ $pageHit->save();
+
+ $pageHit = new PageHit;
+ $pageHit->ip = '192.168.1.100';
+ $pageHit->page_id = 3;
+ $pageHit->date = '2021-01-01';
+ $pageHit->setIndex('page_hits_'.$pageHit->date);
+ $pageHit->save();
+
+ $model = new PageHit;
+ $model->setIndex('page_hits_2021-01-01');
+
+ $pageHits = $model->where('page_id', 3)->get();
+ expect($pageHits)->toHaveCount(1);
+});
diff --git a/tests/Eloquent/ElasticsearchSpecificTest.php b/tests/Eloquent/ElasticsearchSpecificTest.php
new file mode 100644
index 0000000..98222ff
--- /dev/null
+++ b/tests/Eloquent/ElasticsearchSpecificTest.php
@@ -0,0 +1,115 @@
+count(3)->state(['status' => 7, 'manufacturer' => ['location' => ['lat' => 5, 'lon' => 5]]])->create();
+ Product::factory()->count(2)->state(['status' => 7, 'manufacturer' => ['location' => ['lat' => 15, 'lon' => -15]]])->create();
+ $topLeft = [-10, 10];
+ $bottomRight = [10, -10];
+ $products = Product::where('status', 7)->filterGeoBox('manufacturer.location', $topLeft, $bottomRight)->get();
+ expect($products)->toHaveCount(3); // Expecting only the first three within the box
+});
+
+test('filter products close to a specific point', function () {
+
+ $first = Product::factory()->state(['status' => 7])->create();
+ Product::factory()->count(5)->state(['status' => 7])->create();
+ $first->manufacturer = ['location' => ['lat' => 0, 'lon' => 0]];
+ $first->save();
+ $point = [0, 0];
+ $distance = '1m';
+ $products = Product::where('status', 7)->filterGeoPoint('manufacturer.location', $distance, $point)->get();
+ expect($products)->toHaveCount(1);
+
+});
+
+test('search for products by exact name', function () {
+ Product::factory()->state(['name' => 'John Smith'])->create();
+
+ $products = Product::whereExact('name', 'John Smith')->get();
+ expect($products->first()->name)->toEqual('John Smith');
+});
+
+test('search for products by phrase in description', function () {
+ Product::factory()->state(['description' => 'loves espressos'])->create();
+
+ $products = Product::wherePhrase('description', 'loves espressos')->get();
+ expect($products->first()->description)->toContain('loves espressos');
+});
+
+test('search for products where description starts with a prefix', function () {
+ Product::factory()->state(['description' => 'loves espresso beans'])->create();
+
+ $products = Product::wherePhrasePrefix('description', 'loves es')->get();
+ expect($products->first()->description)->toContain('loves espresso beans');
+});
+
+test('query products by timestamp', function () {
+ $timestamp = 1713911889521;
+ Product::factory()->state(['last_order_ts' => $timestamp])->create();
+
+ $products = Product::whereTimestamp('last_order_ts', '<=', $timestamp)->get();
+ expect($products)->toHaveCount(1);
+});
+
+test('search for products using regex on color', function () {
+ Product::factory()->state(['color' => 'blue'])->create();
+ Product::factory()->state(['color' => 'black'])->create();
+
+ $regexProducts = Product::whereRegex('color', 'bl(ue)?(ack)?')->get();
+ expect($regexProducts)->toHaveCount(2);
+});
+
+test('execute raw DSL query on products', function () {
+ Product::factory()->state(['color' => 'silver'])->create();
+
+ $bodyParams = [
+ 'query' => [
+ 'match' => [
+ 'color' => 'silver',
+ ],
+ ],
+ ];
+ $products = Product::rawSearch($bodyParams);
+ expect($products)->toHaveCount(1);
+});
+
+test('perform raw aggregation query', function () {
+ Product::factory()->state(['price' => 50])->create();
+ Product::factory()->state(['price' => 300])->create();
+ Product::factory()->state(['price' => 700])->create();
+ Product::factory()->state(['price' => 1200])->create();
+
+ $bodyParams = [
+ 'aggs' => [
+ 'price_ranges' => [
+ 'range' => [
+ 'field' => 'price',
+ 'ranges' => [
+ ['to' => 100],
+ ['from' => 100, 'to' => 500],
+ ['from' => 500, 'to' => 1000],
+ ['from' => 1000],
+ ],
+
+ ],
+ ],
+ ],
+ ];
+ $priceBuckets = Product::rawAggregation($bodyParams);
+ expect($priceBuckets['price_ranges'][0]['doc_count'])->toBe(1)
+ ->and($priceBuckets['price_ranges'][1]['doc_count'])->toBe(1)
+ ->and($priceBuckets['price_ranges'][2]['doc_count'])->toBe(1)
+ ->and($priceBuckets['price_ranges'][3]['doc_count'])->toBe(1);
+});
+
+test('convert query to DSL', function () {
+ $dslQuery = Product::where('price', '>', 100)->toDSL();
+
+ expect($dslQuery)->toBeArray()
+ ->and($dslQuery['body']['query']['bool']['must'][0]['range'])->toBeArray();
+});
diff --git a/tests/Eloquent/FullTextSearchTest.php b/tests/Eloquent/FullTextSearchTest.php
new file mode 100644
index 0000000..76121ca
--- /dev/null
+++ b/tests/Eloquent/FullTextSearchTest.php
@@ -0,0 +1,67 @@
+create([
+ 'name' => 'Espresso Machine',
+ 'description' => 'Automatic espresso machine with fine control over brew temperature.',
+ 'manufacturer' => [
+ 'name' => 'Coffee Inc.',
+ 'location' => ['lat' => 40.7128, 'lon' => -74.0060],
+ ],
+ ]);
+});
+
+test('term search across all fields', function () {
+ $results = Product::term('Espresso')->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('phrase search across all fields', function () {
+ $results = Product::phrase('Espresso Machine')->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('combining multiple terms with logical operators', function () {
+ $results = Product::term('Espresso')->orTerm('Machine')->andTerm('Automatic')->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('boosting terms in search', function () {
+ $results = Product::term('Espresso', 2)->orTerm('Brew')->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('limiting search to specific fields', function () {
+ $results = Product::term('Espresso')->fields(['name', 'description'])->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('minimum should match in search', function () {
+ $results = Product::term('Espresso')->orTerm('Brew')->orTerm('Machine')->minShouldMatch(2)->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('minimum score for search results', function () {
+ $results = Product::term('Espresso')->minScore(0.1)->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('fuzzy searches for similar terms', function () {
+ $results = Product::fuzzyTerm('espreso')->orFuzzyTerm('mchine')->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('regex search on product fields', function () {
+ $results = Product::regEx('espresso*')->search();
+ expect($results)->toHaveCount(1);
+});
+
+test('highlighting search results', function () {
+ $results = Product::term('Espresso')->highlight(['description'], '', '')->search();
+ $highlighted = $results->first()->searchHighlights->description ?? [];
+ expect($highlighted)->toContain('');
+});
diff --git a/tests/Eloquent/InsertTest.php b/tests/Eloquent/InsertTest.php
new file mode 100644
index 0000000..88fd664
--- /dev/null
+++ b/tests/Eloquent/InsertTest.php
@@ -0,0 +1,23 @@
+make();
+ $result = Product::insert($products->toArray(), true);
+
+ expect($result)->toBeInstanceOf(ElasticCollection::class)
+ ->and($result->getQueryMetaAsArray())->toBeArray();
+});
+
+test('bulk insert without refresh', function () {
+ $products = Product::factory(1000)->make();
+ $result = Product::insertWithoutRefresh($products->toArray());
+ expect($result)->toBeInstanceOf(ElasticCollection::class)
+ ->and($result->getQueryMetaAsArray())->toBeArray();
+ sleep(2);
+ expect(Product::count())->toBe(1000);
+});
diff --git a/tests/Eloquent/NestedTest.php b/tests/Eloquent/NestedTest.php
new file mode 100644
index 0000000..1134408
--- /dev/null
+++ b/tests/Eloquent/NestedTest.php
@@ -0,0 +1,85 @@
+create([
+ 'comments' => [
+ ['name' => 'John Doe', 'country' => 'Peru', 'likes' => 5],
+ ['name' => 'Jane Smith', 'country' => 'USA', 'likes' => 3]
+ ]
+ ]);
+
+ $posts = BlogPost::whereNestedObject('comments', function ($query) {
+ $query->where('country', 'Peru')->where('likes', 5);
+ })->get();
+
+ expect($posts)->toHaveCount(1)
+ ->and($posts->first()->comments[0]['country'])->toEqual('Peru')
+ ->and($posts->first()->comments[0]['likes'])->toEqual(5);
+ });
+
+ test('exclude blog posts with comments from a specific country', function () {
+ BlogPost::factory()->create([
+ 'comments' => [
+ ['name' => 'John Doe', 'country' => 'Peru', 'likes' => 5]
+ ]
+ ]);
+
+ $posts = BlogPost::whereNotNestedObject('comments', function ($query) {
+ $query->where('country', 'Peru');
+ })->get();
+
+ expect($posts->isNotEmpty())->toBeTrue();
+ });
+
+ test('order blog posts by comments likes descending', function () {
+ BlogPost::factory()->create([
+ 'status' => 1,
+ 'comments' => [
+ ['name' => 'John Doe', 'country' => 'Peru', 'likes' => 5],
+ ['name' => 'Jane Smith', 'country' => 'USA', 'likes' => 8]
+ ]
+ ]);
+
+ // FIXME: @pdphilip I can't get this to sort for the life of me not sure what I am doing wrong.
+ $posts = BlogPost::where('status', 1)->orderByNested('comments.likes', 'desc', 'sum')->get();
+ expect($posts->first()->comments[0]['likes'])->toEqual(8);
+ })->todo();
+
+ test('filter blog posts by comments from Switzerland ordered by likes', function () {
+ BlogPost::factory()->create([
+ 'status' => 5,
+ 'comments' => [
+ ['name' => 'April Von', 'country' => 'Switzerland', 'likes' => 10],
+ ['name' => 'Mabelle Schinner', 'country' => 'Switzerland', 'likes' => 7]
+ ]
+ ]);
+
+ $post = BlogPost::where('status', 5)->queryNested('comments', function ($query) {
+ $query->where('country', 'Switzerland')->orderBy('likes');
+ })->first();
+
+ expect($post->comments[0]['name'])->toEqual('Mabelle Schinner')
+ ->and($post->comments[0]['likes'])->toEqual(7)
+ ->and($post->comments[1]['likes'])->toEqual(10);
+ });
+
+ test('filter comments with likes greater than or equal to 5, limit 2', function () {
+ BlogPost::factory()->create([
+ 'status' => 5,
+ 'comments' => [
+ ['name' => 'Damaris Ondricka', 'country' => 'Peru', 'likes' => 5],
+ ['name' => 'April Von', 'country' => 'Switzerland', 'likes' => 10],
+ ['name' => 'Third Comment', 'country' => 'USA', 'likes' => 2]
+ ]
+ ]);
+
+ $post = BlogPost::where('status', 5)->queryNested('comments', function ($query) {
+ $query->where('likes', '>=', 5)->limit(2);
+ })->first();
+
+ expect($post->comments)->toHaveCount(2)
+ ->and($post->comments[0]['likes'])->toBeGreaterThanOrEqual(5);
+ });
diff --git a/tests/Eloquent/OrderAndPaginationTest.php b/tests/Eloquent/OrderAndPaginationTest.php
new file mode 100644
index 0000000..38c488f
--- /dev/null
+++ b/tests/Eloquent/OrderAndPaginationTest.php
@@ -0,0 +1,79 @@
+pluck($key)->toArray();
+ for ($i = 0; $i < count($values) - 1; $i++) {
+ if ($descending) {
+ if ($values[$i] < $values[$i + 1]) {
+ return false;
+ }
+ } else {
+ if ($values[$i] > $values[$i + 1]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+test('products are ordered by status', function () {
+ $products = Product::factory(50)->make();
+ Product::insert($products->toArray());
+
+ $products = Product::orderBy('status')->get();
+ expect(isSorted($products, 'status'))->toBeTrue();
+});
+
+// not sure why this is failing
+test('products are ordered by created_at descending', function () {
+
+ $products = Product::factory(10)->make();
+ Product::insert($products->toArray());
+
+ $products = Product::orderBy('created_at', 'desc')->get();
+ expect(isSorted($products, 'created_at', true))->toBeTrue();
+})->todo();
+
+test('products are ordered by name using keyword subfield', function () {
+ $products = Product::factory(50)->make();
+ Product::insert($products->toArray());
+
+ $products = Product::orderBy('name.keyword')->get();
+ expect(isSorted($products, 'name'))->toBeTrue();
+});
+
+test('products are paginated', function () {
+ $products = Product::factory(50)->make();
+ Product::insert($products->toArray());
+
+ $products = Product::where('is_active', true)->paginate(10);
+ expect($products)->toHaveCount(10);
+});
+
+test('sort products by color with missing values treated as first', function () {
+ Product::factory()->state(['color' => null])->create();
+ Product::factory()->state(['color' => 'blue'])->create();
+ $products = Product::orderBy('color', 'desc', null, '_first')->get();
+ expect($products->first()->color)->toBeNull();
+});
+
+test('sort products by geographic location closest to London', function () {
+ Product::factory()->state(['manufacturer' => ['location' => ['lat' => 51.50853, 'lon' => -0.12574]]])->create(); // London
+ $products = Product::orderByGeo('manufacturer.location', [-0.12574, 51.50853])->get();
+ expect(! empty($products))->toBeTrue();
+})->todo();
+
+test('sort products by geographic location farthest from Paris using multiple points and plane type', function () {
+ Product::factory()->state(['manufacturer' => ['location' => ['lat' => 48.85341, 'lon' => 2.3488]]])->create(); // Paris
+ $products = Product::orderByGeo('manufacturer.location', [[2.3488, 48.85341], [-0.12574, 51.50853]], 'desc', 'km', 'avg', 'plane')->get();
+ expect(! empty($products))->toBeTrue();
+})->todo();
diff --git a/tests/Eloquent/QueryingTest.php b/tests/Eloquent/QueryingTest.php
new file mode 100644
index 0000000..c2645fc
--- /dev/null
+++ b/tests/Eloquent/QueryingTest.php
@@ -0,0 +1,126 @@
+count(5)->create();
+ $products = Product::all();
+ expect($products)->toBeCollection();
+});
+
+test('find a product by primary key', function () {
+ $product = Product::factory()->create();
+ $found = Product::find($product->_id);
+ expect($found)->toBeInstanceOf(Product::class);
+});
+
+test('fail to find a product and get null', function () {
+ $product = Product::find('nonexistent');
+ expect($product)->toBeNull();
+});
+
+test('fail to find a product and get exception', function () {
+ Product::findOrFail('nonexistent');
+})->throws(Exception::class);
+
+test('retrieve first product by status', function () {
+ $product = Product::factory()->state(['status' => 1])->create();
+ $found = Product::where('status', 1)->first();
+ expect($found)->toBeInstanceOf(Product::class)
+ ->and($found->status)->toEqual(1);
+});
+
+test('retrieve and count products using where condition', function () {
+ Product::factory(5)->state(['status' => 1])->create();
+ $products = Product::where('status', 1)->get();
+ expect($products)->toHaveCount(5);
+});
+
+test('exclude products with specific status using whereNot', function () {
+ Product::factory()->state(['status' => 1])->create();
+ Product::factory()->state(['status' => 2])->create();
+ $products = Product::whereNot('status', 1)->get();
+ expect($products->first()->status)->not()->toEqual(1);
+});
+
+test('chain multiple conditions', function () {
+ Product::factory()->state(['is_active' => true, 'in_stock' => 50])->create();
+ $products = Product::where('is_active', true)->where('in_stock', '<=', 50)->get();
+ expect($products)->toHaveCount(1);
+});
+
+test('use OR conditions', function () {
+ Product::factory()->state(['is_active' => false, 'in_stock' => 150])->create();
+ $products = Product::where('is_active', false)->orWhere('in_stock', '>=', 100)->get();
+ expect($products)->toHaveCount(1);
+});
+
+test('check inclusion with whereIn', function () {
+ Product::factory(3)->state(['status' => 1])->create();
+ Product::factory(2)->state(['status' => 5])->create();
+ $products = Product::whereIn('status', [1, 5])->get();
+ expect($products)->toHaveCount(5);
+});
+
+test('check exclusion with whereNotIn', function () {
+ Product::factory()->state(['color' => 'red'])->create();
+ Product::factory()->state(['color' => 'green'])->create();
+ $products = Product::whereNotIn('color', ['red', 'green'])->get();
+ expect($products)->toBeEmpty();
+});
+
+test('query non-existent color field', function () {
+ $products = Product::whereNull('color')->get();
+ expect($products)->toBeCollection();
+});
+
+test('query products where color field exists', function () {
+ Product::factory()->state(['color' => 'blue'])->create();
+ $products = Product::whereNotNull('color')->get();
+ expect($products->first()->color)->toEqual('blue');
+});
+
+test('filter products based on a date range', function () {
+ Product::factory()->state(['created_at' => now()->subDays(5)])->create();
+ $products = Product::whereBetween('created_at', [now()->subWeek(), now()])->get();
+ expect($products)->toHaveCount(1);
+});
+
+test('retrieve products with no stock', function () {
+ Product::factory()->state(['in_stock' => 0])->create();
+ $products = Product::where('in_stock', 0)->get();
+ expect($products)->toHaveCount(1);
+});
+
+test('calculate average orders correctly', function () {
+ Product::factory()->state(['order_values' => [10, 20, 30]])->create();
+ $product = Product::first();
+ expect($product->getAvgOrdersAttribute())->toEqual(20);
+});
+
+test('search for products with partial text match', function () {
+ Product::factory()->state(['name' => 'Black Coffee'])->create();
+ $products = Product::where('name', 'like', 'bl')->orderBy('name.keyword')->get();
+ expect($products)->toHaveCount(1)
+ ->and($products->first()->name)->toEqual('Black Coffee');
+});
+
+test('complex query chaining', function () {
+ Product::factory()->state(['type' => 'coffee', 'is_approved' => true])->create();
+ Product::factory()->state(['type' => 'tea', 'is_approved' => false])->create();
+ $products = Product::where('type', 'coffee')
+ ->where('is_approved', true)
+ ->orWhere('type', 'tea')
+ ->where('is_approved', false)
+ ->get();
+ expect($products)->toHaveCount(2);
+});
+
+test('date query on product creation', function () {
+ Product::factory()->state(['created_at' => now()->subDay()])->create();
+ $products = Product::whereDate('created_at', now()->subDay()->toDateString())->get();
+ expect($products)->toHaveCount(1);
+});
diff --git a/tests/Eloquent/RelationshipTest.php b/tests/Eloquent/RelationshipTest.php
new file mode 100644
index 0000000..fb221bf
--- /dev/null
+++ b/tests/Eloquent/RelationshipTest.php
@@ -0,0 +1,45 @@
+create();
+ $logs = CompanyLog::factory(3)->create(['company_id' => $company->_id]);
+ $fetchedLogs = $company->companyLogs;
+
+ expect($fetchedLogs)->toHaveCount(3)
+ ->and($fetchedLogs->first())->toBeInstanceOf(CompanyLog::class);
+ });
+
+ test('company has one company profile', function () {
+ $company = Company::factory()->create();
+ $profile = CompanyProfile::factory()->create(['company_id' => $company->_id]);
+ $fetchedProfile = $company->companyProfile;
+
+ expect($fetchedProfile)->toBeInstanceOf(CompanyProfile::class)
+ ->and($fetchedProfile->_id)->toEqual($profile->_id);
+ });
+
+ test('company has one avatar using morphOne', function () {
+ $company = Company::factory()->create();
+ $avatar = Avatar::factory()->create(['imageable_id' => $company->_id, 'imageable_type' => Company::class]);
+ $fetchedAvatar = $company->avatar;
+
+ expect($fetchedAvatar)->toBeInstanceOf(Avatar::class)
+ ->and($fetchedAvatar->_id)->toEqual($avatar->_id);
+ });
+
+ test('company has many photos using morphMany', function () {
+ $company = Company::factory()->create();
+ $photos = Photo::factory(5)->create(['photoable_id' => $company->_id, 'photoable_type' => Company::class]);
+ $fetchedPhotos = $company->photos;
+
+ expect($fetchedPhotos)->toHaveCount(5)
+ ->and($fetchedPhotos->first())->toBeInstanceOf(Photo::class);
+ });
diff --git a/tests/Eloquent/SaveTest.php b/tests/Eloquent/SaveTest.php
new file mode 100644
index 0000000..ac9492d
--- /dev/null
+++ b/tests/Eloquent/SaveTest.php
@@ -0,0 +1,121 @@
+name = 'New Product';
+ $product->price = 199.99;
+ $product->status = 1;
+ $product->save();
+
+ $found = Product::first();
+ expect($found)->toBeInstanceOf(Product::class)
+ ->and($found->name)->toEqual('New Product')
+ ->and($found->price)->toEqual(199.99);
+});
+
+test('create a new product using mass assignment', function () {
+ Product::create([
+ 'name' => 'Mass Assigned Product',
+ 'price' => 299.99,
+ 'status' => 1,
+ ]);
+
+ $found = Product::first();
+ expect($found)->toBeInstanceOf(Product::class)
+ ->and($found->name)->toEqual('Mass Assigned Product')
+ ->and($found->price)->toEqual(299.99);
+});
+
+test('update a product attribute and save', function () {
+ $product = Product::factory()->create(['status' => 1]);
+ $product->status = 2;
+ $product->save();
+
+ $updated = Product::find($product->_id);
+ expect($updated->status)->toEqual(2);
+});
+
+test('mass update products matching a condition', function () {
+ Product::factory(5)->state(['status' => 1])->create();
+ $updates = Product::where('status', 1)->update(['status' => 4]);
+
+ $updatedCount = Product::where('status', 4)->count();
+ expect($updates)->toEqual(5)
+ ->and($updatedCount)->toEqual(5);
+});
+
+test('save product without waiting for index refresh', function () {
+ $product = new Product();
+ $product->name = 'Fast Save Product';
+ $product->status = 1;
+ $product->saveWithoutRefresh();
+
+ // Note: Can't directly test the non-wait state, this would typically be tested with integration tests
+ expect($product->wasRecentlyCreated)->toBeTrue();
+});
+
+test('first or create product based on unique attributes', function () {
+ Product::factory()->create(['name' => 'Unique Product', 'status' => 1]);
+
+ $product = Product::firstOrCreate(
+ ['name' => 'Unique Product', 'status' => 1],
+ ['price' => 99.99]
+ );
+
+ expect($product->wasRecentlyCreated)->toBeFalse()
+ ->and($product->name)->toEqual('Unique Product');
+});
+
+test('first or create without refresh', function () {
+ $product = Product::firstOrCreateWithoutRefresh(
+ ['name' => 'Non-Refresh Product', 'status' => 1],
+ ['price' => 109.99]
+ );
+
+ // Note: Similar to the fast save test, the non-refresh state is an integration aspect
+ expect($product->wasRecentlyCreated)->toBeTrue()
+ ->and($product->name)->toEqual('Non-Refresh Product');
+});
+
+test('validate saving a model with a unique constraint on name', function () {
+ Product::create(['name' => 'Unique Gadget', 'price' => 100]);
+ $duplicateProductAttempt = Product::firstOrCreate(
+ ['name' => 'Unique Gadget'],
+ ['price' => 200]
+ );
+
+ // Assert it didn't overwrite the existing product
+ expect($duplicateProductAttempt->price)->toEqual(100)
+ // Ensure no duplicate was created
+ ->and(Product::count())->toEqual(1);
+
+});
+
+test('ensure save without refresh accurately models elastic behavior', function () {
+ $product = new Product();
+ $product->name = 'Delayed Visibility Product';
+ $product->price = 150;
+ $product->saveWithoutRefresh();
+
+ $foundImmediately = Product::where('name', 'Delayed Visibility Product')->first();
+ expect($foundImmediately)->toBeNull(); // Not immediately available
+});
+
+test('query using firstOrCreate to simulate inventory addition', function () {
+ Product::factory()->create(['name' => 'Gadget', 'status' => 1]);
+
+ $newOrExistingProduct = Product::firstOrCreate(
+ ['name' => 'Gadget'],
+ ['status' => 1, 'price' => 99.99]
+ );
+
+ expect($newOrExistingProduct->wasRecentlyCreated)->toBeFalse()
+ // Price won't be 99.99 if it already existed
+ ->and($newOrExistingProduct->price)->not()->toEqual(99.99);
+
+});
diff --git a/tests/Eloquent/SearchAfterPaginationTest.php b/tests/Eloquent/SearchAfterPaginationTest.php
new file mode 100644
index 0000000..dd8af08
--- /dev/null
+++ b/tests/Eloquent/SearchAfterPaginationTest.php
@@ -0,0 +1,87 @@
+push([
+ 'title' => fake()->name(),
+ 'slug' => fake()->uuid(),
+ 'content' => fake()->realTextBetween(5, 15),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ]);
+ }
+
+ Post::insert($collectionToInsert->toArray());
+
+ $perPage = 100;
+ $totalFetched = 0;
+ $totalProducts = Post::count();
+
+ // Fetch the first page of posts
+ $paginator = Post::orderBy('slug.keyword')->cursorPaginate($perPage)->withQueryString();
+
+ do {
+
+ // Count the number of posts fetched in the current page
+ $totalFetched += $paginator->count();
+
+ // Move to the next page if possible
+ if ($paginator->hasMorePages()) {
+ $cursor = $paginator->nextCursor();
+ $paginator = Post::orderBy('slug.keyword')->cursorPaginate($perPage, ['*'], 'cursor', $cursor)->withQueryString();
+ }
+ } while ($paginator->hasMorePages());
+
+ // Include the last page count if not empty
+ $totalFetched += $paginator->count();
+
+ // Check if all products were fetched
+ expect($totalFetched)->toEqual($totalProducts);
+
+});
+
+it('can paginate a small amount of records', function () {
+
+ Post::truncate();
+
+ //Generate a massive amount of data to paginate over.
+ $collectionToInsert = collect([]);
+ $numberOfEntries = 100;
+ for ($i = 1; $i <= $numberOfEntries; $i++) {
+ $collectionToInsert->push([
+ 'title' => fake()->name(),
+ 'slug' => fake()->uuid(),
+ 'content' => fake()->realTextBetween(5, 15),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ]);
+ }
+ Post::insert($collectionToInsert->toArray());
+
+ // Fetch the first page of posts
+ $paginator = Post::orderBy('slug.keyword')->cursorPaginate(200)->withQueryString();
+
+ expect($paginator->hasMorePages())->toBeFalse()
+ ->and($paginator->count())->toBe(100);
+
+});
+
+test('throws an exception when there is no ordering search_after', function () {
+
+ // Fetch the first page of posts
+ StaticPage::cursorPaginate(100)->withQueryString();
+
+})->throws(MissingOrderException::class);
diff --git a/tests/ModelTest.php b/tests/ModelTest.php
new file mode 100644
index 0000000..2190bda
--- /dev/null
+++ b/tests/ModelTest.php
@@ -0,0 +1,444 @@
+assertInstanceOf(Connection::class, $product->getConnection());
+ $this->assertFalse($product->exists);
+ $this->assertEquals('products', $product->getTable());
+ $this->assertEquals('_id', $product->getKeyName());
+ });
+
+ test('Insert', function () {
+ $product = new Product();
+ $product['name'] = 'John Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['product_id'] = 'c1b5f730-7e5c-11e9-8f9e-2a86e4085a59';
+ $product['in_stock'] = 25;
+
+ $product->save();
+
+ $this->assertTrue($product->exists);
+ $this->assertEquals(1, Product::count());
+
+ $this->assertTrue(isset($product->id));
+ $this->assertIsString($product->id);
+ $this->assertNotEquals('', (string) $product->id);
+ $this->assertNotEquals(0, strlen((string) $product->id));
+ $this->assertInstanceOf(Carbon::class, $product->created_at);
+
+
+ $this->assertEquals('John Doe', $product->name);
+ $this->assertEquals(25, $product->in_stock);
+ });
+
+ test('Update', function () {
+ $product = new Product();
+ $product['name'] = 'John Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['product_id'] = 'c1b5f730-7e5c-11e9-8f9e-2a86e4085a59';
+ $product['in_stock'] = 25;
+ $product->save();
+
+ $this->assertTrue($product->exists);
+ $this->assertTrue(isset($product->id));
+
+ $check = Product::find($product->id);
+ $this->assertInstanceOf(Product::class, $check);
+ $check->in_stock = 36;
+ $check->save();
+
+
+ $this->assertTrue($check->exists);
+ $this->assertInstanceOf(Carbon::class, $check->created_at);
+ $this->assertInstanceOf(Carbon::class, $check->updated_at);
+ $this->assertEquals(1, Product::count());
+
+ $this->assertEquals('John Doe', $check->name);
+ $this->assertEquals(36, $check->in_stock);
+
+ $product->update(['in_stock' => 20]);
+
+ $check = Product::find($product->id);
+ $this->assertEquals(20, $check->in_stock);
+
+ $check->in_stock = 24;
+ $check->color = 'blue'; // new field
+ $check->save();
+
+ $check = Product::find($product->id);
+ $this->assertEquals(24, $check->in_stock);
+ $this->assertEquals('blue', $check->color);
+ });
+
+ test('Delete', function () {
+ $product = new Product();
+ $product['name'] = 'John Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['product_id'] = 'c1b5f730-7e5c-11e9-8f9e-2a86e4085a59';
+ $product['in_stock'] = 25;
+ $product->save();
+
+ $this->assertTrue($product->exists);
+ $this->assertEquals(1, Product::count());
+
+ $product->delete();
+
+ $this->assertEquals(0, Product::count());
+
+ });
+
+ test('All', function () {
+ $product = new Product();
+ $product['name'] = 'John Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['in_stock'] = 24;
+ $product->save();
+
+ $product = new Product();
+ $product['name'] = 'Jane Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['in_stock'] = 35;
+ $product->save();
+
+ $all = Product::all();
+
+ $this->assertCount(2, $all);
+ $this->assertContains('John Doe', $all->pluck('name'));
+ $this->assertContains('Jane Doe', $all->pluck('name'));
+
+ });
+
+ test('Find', function () {
+ $product = new Product();
+ $product['name'] = 'John Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['in_stock'] = 35;
+ $product->save();
+
+ $check = Product::find($product->id);
+ $this->assertInstanceOf(Product::class, $check);
+ $this->assertTrue($check->exists);
+ $this->assertEquals($product->id, $check->id);
+
+ $this->assertEquals('John Doe', $check->name);
+ $this->assertEquals(35, $check->in_stock);
+ });
+
+ test('Get', function () {
+ //this also test bulk insert yay!
+ Product::insert([
+ ['name' => 'John Doe'],
+ ['name' => 'Jane Doe'],
+ ]);
+
+ $products = Product::get();
+ $this->assertCount(2, $products);
+ $this->assertInstanceOf(EloquentCollection::class, $products);
+ $this->assertInstanceOf(Product::class, $products[0]);
+ });
+
+ test('First', function () {
+ //this also test bulk insert yay!
+ Product::insert([
+ ['name' => 'John Doe'],
+ ['name' => 'Jane Doe'],
+ ]);
+
+ $product = Product::first();
+ $this->assertInstanceOf(Product::class, $product);
+ $this->assertEquals('John Doe', $product->name);
+ });
+
+ test('No Document', function () {
+ $items = Product::where('name', 'nothing')->get();
+ $this->assertInstanceOf(EloquentCollection::class, $items);
+ $this->assertEquals(0, $items->count());
+
+ $item = Product::where('name', 'nothing')->first();
+ $this->assertNull($item);
+
+ $item = Product::find('51c33d8981fec6813e00000a');
+ $this->assertNull($item);
+
+ });
+
+ test('Find Or Fail', function () {
+ $this->expectException(ModelNotFoundException::class);
+ Product::findOrFail('51c33d8981fec6813e00000a');
+
+ });
+
+ test('Create', function () {
+ $product = Product::create(['name' => 'Jane Poe']);
+ $this->assertInstanceOf(Product::class, $product);
+
+ $this->assertTrue($product->exists);
+ $this->assertEquals('Jane Poe', $product->name);
+
+ $check = Product::where('name', 'Jane Poe')->first();
+ $this->assertInstanceOf(Product::class, $check);
+ $this->assertEquals($product->id, $check->id);
+ });
+
+ test('Destroy', function () {
+ $product = new Product();
+ $product['name'] = 'John Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['in_stock'] = 35;
+ $product->save();
+
+ Product::destroy((string) $product->id);
+ $this->assertEquals(0, Product::count());
+ });
+
+ test('Touch', function () {
+ $product = new Product();
+ $product['name'] = 'John Doe';
+ $product['description'] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.';
+ $product['in_stock'] = 35;
+ $product->save();
+
+ $old = $product->updated_at;
+ sleep(1);
+ $product->touch();
+
+ $check = Product::find($product->id);
+ $this->assertInstanceOf(Product::class, $check);
+
+ $this->assertNotEquals($old, $check->updated_at);
+ });
+
+ test('Soft Delete', function () {
+ Soft::create(['name' => 'John Doe']);
+ Soft::create(['name' => 'Jane Doe']);
+
+ $this->assertEquals(2, Soft::count());
+
+ $object = Soft::where('name', 'John Doe')->first();
+ $this->assertInstanceOf(Soft::class, $object);
+ $this->assertTrue($object->exists);
+ $this->assertFalse($object->trashed());
+ $this->assertNull($object->deleted_at);
+
+ $object->delete();
+ $this->assertTrue($object->trashed());
+ $this->assertNotNull($object->deleted_at);
+
+ $object = Soft::where('name', 'John Doe')->first();
+ $this->assertNull($object);
+
+ $this->assertEquals(1, Soft::count());
+ $this->assertEquals(2, Soft::withTrashed()->count());
+
+ $object = Soft::withTrashed()->where('name', 'John Doe')->first();
+ $this->assertNotNull($object);
+ $this->assertInstanceOf(Carbon::class, $object->deleted_at);
+ $this->assertTrue($object->trashed());
+
+ $object->restore();
+ $this->assertEquals(2, Soft::count());
+
+ })->todo();
+
+ test('Scope', function () {
+ Product::insert([
+ ['name' => 'knife', 'color' => 'green'],
+ ['name' => 'spoon', 'color' => 'red'],
+ ]);
+
+ $green = Product::green()->get();
+ $this->assertEquals(1, $green->count());
+ });
+
+ test('To Array', function () {
+ $product = Product::create(['name' => 'fork', 'color' => 'green']);
+
+ $array = $product->toArray();
+ $keys = array_keys($array);
+ sort($keys);
+ $this->assertEquals(['_id', 'color', 'created_at', 'name', 'updated_at'], $keys);
+ $this->assertIsString($array['created_at']);
+ $this->assertIsString($array['updated_at']);
+ $this->assertIsString($array['_id']);
+ });
+
+ test('Dot Notation', function () {
+
+ $product = Product::create([
+ 'name' => 'John Doe',
+ 'manufacturer' => [
+ 'name' => 'Paris',
+ 'country' => 'France',
+ ],
+ ]);
+
+ $this->assertEquals('Paris', $product->getAttribute('manufacturer.name'));
+ $this->assertEquals('Paris', $product['manufacturer.name']);
+ $this->assertEquals('Paris', $product->{'manufacturer.name'});
+
+ // Fill
+ //TODO: Fix this it's not working correctly
+// $product->fill(['manufacturer.name' => 'Strasbourg']);
+//
+// $this->assertEquals('Strasbourg', $product['manufacturer.name']);
+ });
+
+
+ test('Truncate Model', function () {
+ Product::create(['name' => 'John Doe']);
+
+ Product::truncate();
+ sleep(2);
+
+ $this->assertEquals(0, Product::count());
+
+ });
+
+ test('Chunk By Id', function () {
+
+ Product::create(['name' => 'fork', 'order_values' => [10, 20]]);
+ Product::create(['name' => 'spork', 'order_values' => [10, 35, 20, 30]]);
+ Product::create(['name' => 'spoon', 'order_values' => [20, 30]]);
+
+ $names = [];
+ Product::chunkById(2, function (EloquentCollection $items) use (&$names) {
+ $names = array_merge($names, $items->pluck('name')->all());
+ });
+
+ $this->assertEquals(['fork', 'spork', 'spoon'], $names);
+
+ });
+
+ test('Guarded Model', function () {
+ $model = new Guarded();
+
+ // foobar is properly guarded
+ $model->fill(['foobar' => 'ignored', 'name' => 'John Doe']);
+ $this->assertFalse(isset($model->foobar));
+ $this->assertSame('John Doe', $model->name);
+
+ // foobar is guarded to any level
+ $model->fill(['foobar->level2' => 'v2']);
+ $this->assertNull($model->getAttribute('foobar->level2'));
+
+ // multi level statement also guarded
+ $model->fill(['level1->level2' => 'v1']);
+ $this->assertNull($model->getAttribute('level1->level2'));
+
+ // level1 is still writable
+ $dataValues = ['array', 'of', 'values'];
+ $model->fill(['level1' => $dataValues]);
+ $this->assertEquals($dataValues, $model->getAttribute('level1'));
+
+ });
+
+ test('First Or Create', function () {
+ $name = 'Jane Poe';
+
+ $user = Product::where('name', $name)->first();
+ $this->assertNull($user);
+
+ $user = Product::firstOrCreate(['name' => $name]);
+ $this->assertInstanceOf(Product::class, $user);
+ $this->assertTrue($user->exists);
+ $this->assertEquals($name, $user->name);
+
+ $check = Product::where('name', $name)->first();
+ $this->assertInstanceOf(Product::class, $check);
+ $this->assertEquals($user->id, $check->id);
+
+ });
+
+ test('Update Or Create', function () {
+ // Insert data to ensure we filter on the correct criteria, and not getting
+ // the first document randomly.
+ Product::insert([
+ ['name' => 'fixture@example.com'],
+ ['name' => 'john.doe@example.com'],
+ ]);
+
+ Carbon::setTestNow('2010-01-01');
+ $createdAt = Carbon::now()->getTimestamp();
+ $events = [];
+ registerModelEvents(Product::class, $events);
+
+ // Create
+ $product = Product::updateOrCreate(
+ ['name' => 'bar'],
+ ['name' => 'bar', 'in_stock' => 30],
+ );
+
+ $this->assertInstanceOf(Product::class, $product);
+ $this->assertEquals('bar', $product->name);
+ $this->assertEquals(30, $product->in_stock);
+ $this->assertEquals($createdAt, $product->created_at->getTimestamp());
+ $this->assertEquals($createdAt, $product->updated_at->getTimestamp());
+ $this->assertEquals(['saving', 'creating', 'created', 'saved'], $events);
+ Carbon::setTestNow('2010-02-01');
+ $updatedAt = Carbon::now()->getTimestamp();
+
+ // Update
+ $events = [];
+ $product = Product::updateOrCreate(
+ ['name' => 'bar'],
+ ['in_stock' => 25]
+ );
+
+ $this->assertInstanceOf(Product::class, $product);
+ $this->assertEquals('bar', $product->name);
+ $this->assertEquals(25, $product->in_stock);
+ $this->assertEquals($createdAt, $product->created_at->getTimestamp());
+ $this->assertEquals($updatedAt, $product->updated_at->getTimestamp());
+ $this->assertEquals(['saving', 'updating', 'updated', 'saved'], $events);
+
+ // Stored data
+ $checkProduct = Product::where(['name' => 'bar'])->first();
+ $this->assertInstanceOf(Product::class, $checkProduct);
+ $this->assertEquals('bar', $checkProduct->name);
+ $this->assertEquals(25, $checkProduct->in_stock);
+ $this->assertEquals($createdAt, $checkProduct->created_at->getTimestamp());
+ $this->assertEquals($updatedAt, $checkProduct->updated_at->getTimestamp());
+ });
+
+
+ test('Create With Null Id', function (string $id) {
+ $product = Product::create([$id => null, 'email' => 'foo@bar']);
+ $this->assertNotNull($product->id);
+ $this->assertSame(1, Product::count());
+ })->with([
+ 'id',
+// #TODO: this fails.
+// '_id'
+ ]);
+
+ function registerModelEvents(string $modelClass, array &$events): void
+ {
+ $modelClass::creating(function () use (&$events) {
+ $events[] = 'creating';
+ });
+ $modelClass::created(function () use (&$events) {
+ $events[] = 'created';
+ });
+ $modelClass::updating(function () use (&$events) {
+ $events[] = 'updating';
+ });
+ $modelClass::updated(function () use (&$events) {
+ $events[] = 'updated';
+ });
+ $modelClass::saving(function () use (&$events) {
+ $events[] = 'saving';
+ });
+ $modelClass::saved(function () use (&$events) {
+ $events[] = 'saved';
+ });
+ }
diff --git a/tests/Pest.php b/tests/Pest.php
new file mode 100644
index 0000000..db0f604
--- /dev/null
+++ b/tests/Pest.php
@@ -0,0 +1,8 @@
+in(__DIR__);
diff --git a/tests/Schema/IndexBlueprintTest.php b/tests/Schema/IndexBlueprintTest.php
new file mode 100644
index 0000000..330090d
--- /dev/null
+++ b/tests/Schema/IndexBlueprintTest.php
@@ -0,0 +1,105 @@
+text('info');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['info']['type'])->toEqual('text');
+});
+
+it('validates keyword field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->keyword('tag');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['tag']['type'])->toEqual('keyword');
+});
+
+it('validates integer field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->integer('age');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['age']['type'])->toEqual('integer');
+});
+
+it('validates float field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->float('price');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['price']['type'])->toEqual('float');
+});
+
+it('validates date field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->date('birthdate');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['birthdate']['type'])->toEqual('date');
+});
+
+it('validates boolean field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->boolean('is_active');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['is_active']['type'])->toEqual('boolean');
+});
+
+it('validates geo_point field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->geo('location');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['location']['type'])->toEqual('geo_point');
+});
+
+it('validates ip field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->ip('user_ip');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['user_ip']['type'])->toEqual('ip');
+});
+
+it('validates nested field type', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->nested('user', [
+ 'properties' => [
+ 'name' => [
+ 'type' => 'text',
+ ],
+ 'age' => [
+ 'type' => 'integer',
+ ],
+ ],
+ ]);
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['user']['type'])->toEqual('nested')
+ ->and($mappings['test_index']['mappings']['properties']['user']['properties']['name']['type'])->toEqual('text')
+ ->and($mappings['test_index']['mappings']['properties']['user']['properties']['age']['type'])->toEqual('integer');
+});
+
+it('validates object field type with dot notation', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->text('user.name');
+ $index->integer('user.age');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['user']['properties']['name']['type'])->toEqual('text')
+ ->and($mappings['test_index']['mappings']['properties']['user']['properties']['age']['type'])->toEqual('integer');
+});
diff --git a/tests/Schema/MigrationsTest.php b/tests/Schema/MigrationsTest.php
new file mode 100644
index 0000000..92e488c
--- /dev/null
+++ b/tests/Schema/MigrationsTest.php
@@ -0,0 +1,95 @@
+text('name');
+ $index->integer('age');
+ $index->settings('number_of_shards', 1);
+ $index->settings('number_of_replicas', 1);
+ });
+ $exists = Schema::hasIndex('test_index');
+ expect($exists)->toBeTrue();
+});
+
+it('deletes an index if it exists', function () {
+ Schema::createIfNotExists('test_index', function (IndexBlueprint $index) {
+ $index->text('description');
+ });
+ $deleted = Schema::deleteIfExists('test_index');
+ expect($deleted)->toBeTrue();
+ $exists = Schema::hasIndex('test_index');
+ expect($exists)->toBeFalse();
+});
+
+it('modifies an existing index by adding a new field', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->text('title');
+ });
+ Schema::modify('test_index', function (IndexBlueprint $index) {
+ $index->integer('year');
+ });
+ $hasField = Schema::hasField('test_index', 'year');
+ expect($hasField)->toBeTrue();
+});
+
+it('sets a custom analyzer on an index', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->text('content');
+ });
+ Schema::setAnalyser('test_index', function (AnalyzerBlueprint $settings) {
+ $settings->analyzer('custom_analyzer')
+ ->type('custom')
+ ->tokenizer('standard')
+ ->filter(['lowercase', 'asciifolding']);
+ });
+ sleep(1);
+ $settings = Schema::getSettings('test_index');
+ expect($settings['test_index']['settings']['index']['analysis']['analyzer']['custom_analyzer'])->toBeArray();
+});
+
+it('retrieves mappings of an index', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->text('info');
+ $index->keyword('tag');
+ });
+ $mappings = Schema::getMappings('test_index');
+ expect($mappings['test_index']['mappings']['properties']['info']['type'])->toEqual('text')
+ ->and($mappings['test_index']['mappings']['properties']['tag']['type'])->toEqual('keyword');
+});
+
+it('checks if an index has specific fields', function () {
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->text('name');
+ $index->integer('age');
+ });
+ $hasFields = Schema::hasFields('test_index', ['name', 'age']);
+ expect($hasFields)->toBeTrue();
+});
+
+it('fails to delete a non-existent index', function () {
+ $deleted = Schema::deleteIfExists('nonexistent_index');
+ expect($deleted)->toBeFalse();
+});
+
+it('overrides index prefix for operations', function () {
+ Schema::overridePrefix('test_prefix');
+ Schema::create('test_index', function (IndexBlueprint $index) {
+ $index->text('message');
+ });
+ $exists = Schema::hasIndex('test_prefix_test_index');
+ expect($exists)->toBeTrue();
+ Schema::deleteIfExists('test_prefix_test_index');
+});
diff --git a/tests/Schema/ReindexTest.php b/tests/Schema/ReindexTest.php
new file mode 100644
index 0000000..83a181b
--- /dev/null
+++ b/tests/Schema/ReindexTest.php
@@ -0,0 +1,102 @@
+ text('name');
+ $index->float('price');
+ $index->integer('status');
+ $index->date('created_at');
+ $index->date('updated_at');
+ });
+
+ $productsHoldingSchema = Schema::create('holding_products', function (IndexBlueprint $index) {
+ $index->text('name');
+ $index->float('price');
+ $index->integer('status');
+ $index->geo('manufacturer.location');
+ $index->date('created_at');
+ $index->date('updated_at');
+ });
+
+ expect(! empty($productsSchema['products']['mappings']))->toBeTrue()
+ ->and(! empty($productsSchema['products']['settings']))->toBeTrue()
+ ->and(! empty($productsHoldingSchema['holding_products']['mappings']))->toBeTrue()
+ ->and(! empty($productsHoldingSchema['holding_products']['mappings']['properties']['manufacturer']['properties']['location']['type'] == 'geo_point'))->toBeTrue()
+ ->and(! empty($productsHoldingSchema['holding_products']['settings']))->toBeTrue();
+
+ $pf = Product::factory()->count(100)->make();
+ $pf->each(function ($product) {
+ $product->saveWithoutRefresh();
+ });
+ sleep(2);
+ $find = Product::all();
+
+ expect(count($find) === 100)->toBeTrue();
+
+ try {
+ Product::filterGeoPoint('manufacturer.location', '10000km', [0, 0])->get();
+ } catch (QueryException $exception) {
+ expect($exception->getMessage())->toContain('failed to find geo field');
+ }
+
+ $reindex = Schema::reIndex('products', 'holding_products');
+ expect($reindex->data['created'] == 100)->toBeTrue();
+
+ sleep(2);
+ $findOld = DB::connection('elasticsearch')->table('products')->count();
+ $findNew = DB::connection('elasticsearch')->table('holding_products')->count();
+
+ expect($findOld === 100)->toBeTrue()
+ ->and($findNew === 100)->toBeTrue();
+
+ Schema::deleteIfExists('products');
+ expect(Schema::hasIndex('products'))->toBeFalse();
+
+ sleep(2);
+ //Now let's create the products index again but with proper mapping
+ $product = Schema::create('products', function (IndexBlueprint $index) {
+ $index->text('name');
+ $index->float('price');
+ $index->integer('status');
+ $index->geo('manufacturer.location');
+ $index->date('created_at');
+ $index->date('updated_at');
+ });
+
+ expect(! empty($product['products']['mappings']))->toBeTrue()
+ ->and(! empty($product['products']['settings']))->toBeTrue();
+
+ //now we move new to old.
+ $reindex = Schema::reIndex('holding_products', 'products');
+ expect($reindex->data['created'] == 100)->toBeTrue();
+ //Sleep to allow ES to catch up
+ sleep(2);
+
+ $countOriginal = DB::connection('elasticsearch')->table('products')->count();
+ $countHolding = DB::connection('elasticsearch')->table('holding_products')->count();
+
+ expect($countOriginal === 100)->toBeTrue()
+ ->and($countHolding === 100)->toBeTrue();
+
+ $found = Product::filterGeoPoint('manufacturer.location', '10000km', [0, 0])->get();
+ expect($found->isNotEmpty())->toBeTrue();
+
+ //Cleanup
+ Schema::deleteIfExists('products');
+ Schema::deleteIfExists('holding_products');
+
+ expect(Schema::hasIndex('products'))->toBeFalse()
+ ->and(Schema::hasIndex('holding_products'))->toBeFalse();
+
+})->group('schema')->todo();
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..4f547de
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,50 @@
+loadMigrationsFrom(
+ workbench_path('database/migrations')
+ );
+ }
+
+ protected function defineDatabaseSeeders(): void
+ {
+ $this->seed(DatabaseSeeder::class);
+ }
+
+ protected function getEnvironmentSetUp($app): void
+ {
+ $app['config']->set('database.default', 'testing');
+ $app['config']->set('database.connections.elasticsearch', [
+ 'driver' => 'elasticsearch',
+ 'auth_type' => 'http',
+ 'hosts' => ['http://localhost:9200'],
+ ]);
+ }
+}
diff --git a/workbench/app/Models/Avatar.php b/workbench/app/Models/Avatar.php
new file mode 100644
index 0000000..1c5aed5
--- /dev/null
+++ b/workbench/app/Models/Avatar.php
@@ -0,0 +1,48 @@
+morphTo();
+ }
+
+ public static function newFactory(): AvatarFactory
+ {
+ return AvatarFactory::new();
+ }
+
+ }
diff --git a/workbench/app/Models/BlogPost.php b/workbench/app/Models/BlogPost.php
new file mode 100644
index 0000000..87cc54f
--- /dev/null
+++ b/workbench/app/Models/BlogPost.php
@@ -0,0 +1,58 @@
+ encrypt($value)];
+ }
+ }
diff --git a/workbench/app/Models/Client.php b/workbench/app/Models/Client.php
new file mode 100644
index 0000000..aa4f276
--- /dev/null
+++ b/workbench/app/Models/Client.php
@@ -0,0 +1,76 @@
+ [
+ 'name' => 'New',
+ 'level' => 1,
+ 'color' => 'text-neutral-500',
+ 'time_model' => 'created_at',
+ ],
+
+ ];
+
+
+ //Relationships =====================================
+
+ public function clientLogs()
+ {
+ return $this->hasMany(ClientLog::class);
+ }
+
+ public function clientProfile()
+ {
+ return $this->hasOne(ClientProfile::class);
+ }
+
+ public function company()
+ {
+ return $this->belongsTo(Company::class);
+ }
+
+ public static function newFactory(): ClientFactory
+ {
+ return ClientFactory::new();
+ }
+
+ }
diff --git a/workbench/app/Models/ClientLog.php b/workbench/app/Models/ClientLog.php
new file mode 100644
index 0000000..2cb402b
--- /dev/null
+++ b/workbench/app/Models/ClientLog.php
@@ -0,0 +1,51 @@
+belongsTo(Client::class);
+ }
+
+
+ public static function newFactory(): ClientLogFactory
+ {
+ return ClientLogFactory::new();
+ }
+ }
diff --git a/workbench/app/Models/ClientProfile.php b/workbench/app/Models/ClientProfile.php
new file mode 100644
index 0000000..25c713a
--- /dev/null
+++ b/workbench/app/Models/ClientProfile.php
@@ -0,0 +1,50 @@
+belongsTo(Client::class);
+ }
+ public static function newFactory(): ClientProfileFactory
+ {
+ return ClientProfileFactory::new();
+ }
+
+ }
diff --git a/workbench/app/Models/Company.php b/workbench/app/Models/Company.php
new file mode 100644
index 0000000..61d2d5f
--- /dev/null
+++ b/workbench/app/Models/Company.php
@@ -0,0 +1,98 @@
+ [
+ 'name' => 'New',
+ 'level' => 1,
+ 'color' => 'text-neutral-500',
+ 'time_model' => 'created_at',
+ ],
+
+ ];
+
+
+ //Relationships =====================================
+
+ public function users()
+ {
+ return $this->hasMany(User::class);
+ }
+
+ public function userLogs()
+ {
+ return $this->hasMany(UserLog::class);
+ }
+
+ public function companyLogs()
+ {
+ return $this->hasMany(CompanyLog::class);
+ }
+
+ public function companyProfile()
+ {
+ return $this->hasOne(CompanyProfile::class);
+ }
+
+ public function avatar()
+ {
+ return $this->morphOne(Avatar::class, 'imageable');
+ }
+
+ public function photos()
+ {
+ return $this->morphMany(Photo::class, 'photoable');
+ }
+
+ public function clients()
+ {
+ return $this->hasMany(Client::class);
+ }
+
+ public static function newFactory(): CompanyFactory
+ {
+ return CompanyFactory::new();
+ }
+
+ }
diff --git a/workbench/app/Models/CompanyLog.php b/workbench/app/Models/CompanyLog.php
new file mode 100644
index 0000000..ed80f82
--- /dev/null
+++ b/workbench/app/Models/CompanyLog.php
@@ -0,0 +1,46 @@
+belongsTo(Company::class);
+ }
+
+
+ public static function newFactory(): CompanyLogFactory
+ {
+ return CompanyLogFactory::new();
+ }
+
+ }
diff --git a/workbench/app/Models/CompanyProfile.php b/workbench/app/Models/CompanyProfile.php
new file mode 100644
index 0000000..59b2c90
--- /dev/null
+++ b/workbench/app/Models/CompanyProfile.php
@@ -0,0 +1,46 @@
+belongsTo(Company::class);
+ }
+
+ public static function newFactory(): CompanyProfileFactory
+ {
+ return CompanyProfileFactory::new();
+ }
+}
diff --git a/workbench/app/Models/EsPhoto.php b/workbench/app/Models/EsPhoto.php
new file mode 100644
index 0000000..0cce0ee
--- /dev/null
+++ b/workbench/app/Models/EsPhoto.php
@@ -0,0 +1,26 @@
+morphTo();
+ }
+
+ }
diff --git a/workbench/app/Models/Guarded.php b/workbench/app/Models/Guarded.php
new file mode 100644
index 0000000..2345dae
--- /dev/null
+++ b/workbench/app/Models/Guarded.php
@@ -0,0 +1,16 @@
+level2'];
+}
diff --git a/workbench/app/Models/PageHit.php b/workbench/app/Models/PageHit.php
new file mode 100644
index 0000000..7900421
--- /dev/null
+++ b/workbench/app/Models/PageHit.php
@@ -0,0 +1,22 @@
+morphTo();
+ }
+
+ public static function newFactory(): PhotoFactory
+ {
+ return PhotoFactory::new();
+ }
+}
diff --git a/workbench/app/Models/Post.php b/workbench/app/Models/Post.php
new file mode 100644
index 0000000..7bf7fdf
--- /dev/null
+++ b/workbench/app/Models/Post.php
@@ -0,0 +1,37 @@
+in_stock > 0) {
+ return 'yes';
+ }
+
+ return 'no';
+ }
+
+ public function scopeGreen(Builder $query): Builder
+ {
+ return $query->where('color', 'green');
+ }
+
+ public function getAvgOrdersAttribute(): float|int
+ {
+ $orders = array_filter($this->order_values);
+ $avg = 0;
+ if (count($orders)) {
+ $avg = round(array_sum($orders) / count($orders));
+ }
+
+ return $avg;
+ }
+
+ //Relationships =====================================
+
+ public function user(): \PDPhilip\Elasticsearch\Relations\BelongsTo
+ {
+ return $this->belongsTo(User::class);
+ }
+
+ public static function newFactory(): ProductFactory
+ {
+ return ProductFactory::new();
+ }
+}
diff --git a/workbench/app/Models/Soft.php b/workbench/app/Models/Soft.php
new file mode 100644
index 0000000..56e6542
--- /dev/null
+++ b/workbench/app/Models/Soft.php
@@ -0,0 +1,18 @@
+ 'datetime'];
+
+ }
diff --git a/workbench/app/Models/StaticPage.php b/workbench/app/Models/StaticPage.php
new file mode 100644
index 0000000..61525ac
--- /dev/null
+++ b/workbench/app/Models/StaticPage.php
@@ -0,0 +1,36 @@
+
+ */
+ protected $fillable = [
+ 'name',
+ 'email',
+ 'password',
+ ];
+
+ /**
+ * The attributes that should be hidden for serialization.
+ *
+ * @var array
+ */
+ protected $hidden = [
+ 'password',
+ 'remember_token',
+ ];
+
+ /**
+ * Get the attributes that should be cast.
+ *
+ * @return array
+ */
+ protected function casts(): array
+ {
+ return [
+ 'email_verified_at' => 'datetime',
+ 'password' => 'hashed',
+ ];
+ }
+
+ public function userLogs()
+ {
+ return $this->hasMany(UserLog::class);
+ }
+
+ public function company()
+ {
+ return $this->belongsTo(Company::class);
+ }
+
+ public function userProfile()
+ {
+ return $this->hasOne(UserProfile::class);
+ }
+
+ public function avatar()
+ {
+ return $this->morphOne(Avatar::class, 'imageable');
+ }
+
+ public function photos()
+ {
+ return $this->morphMany(Photo::class, 'photoable');
+ }
+
+ public function getFullNameAttribute()
+ {
+ return $this->first_name.' '.$this->last_name;
+ }
+
+ public function getFirstNameAttribute($value)
+ {
+ return strtoupper($value);
+ }
+
+ public static function newFactory(): UserFactory
+ {
+ return UserFactory::new();
+ }
+
+ }
diff --git a/workbench/app/Models/UserLog.php b/workbench/app/Models/UserLog.php
new file mode 100644
index 0000000..8bd2c6c
--- /dev/null
+++ b/workbench/app/Models/UserLog.php
@@ -0,0 +1,78 @@
+belongsTo(User::class);
+ }
+
+ public function company()
+ {
+ return $this->belongsTo(Company::class);
+ }
+
+ protected $casts = [
+ 'secret' => EncryptCast::class,
+ ];
+
+ public function getCodeAttribute($value)
+ {
+ return $value + 1000000;
+ }
+
+ public function getTitleAttribute($value)
+ {
+ return 'MR '.ucfirst($value);
+ }
+
+ public static function newFactory(): UserLogFactory
+ {
+ return UserLogFactory::new();
+ }
+}
diff --git a/workbench/app/Models/UserProfile.php b/workbench/app/Models/UserProfile.php
new file mode 100644
index 0000000..efec0cc
--- /dev/null
+++ b/workbench/app/Models/UserProfile.php
@@ -0,0 +1,53 @@
+belongsTo(User::class);
+ }
+ public static function newFactory(): UserProfileFactory
+ {
+ return UserProfileFactory::new();
+ }
+
+ }
diff --git a/workbench/database/factories/AvatarFactory.php b/workbench/database/factories/AvatarFactory.php
new file mode 100644
index 0000000..5101422
--- /dev/null
+++ b/workbench/database/factories/AvatarFactory.php
@@ -0,0 +1,24 @@
+ $this->faker->imageUrl,
+ 'imageable_id' => null, // To be set when creating instances
+ 'imageable_type' => null, // To be set when creating instances
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ];
+ }
+}
diff --git a/workbench/database/factories/BlogPostFactory.php b/workbench/database/factories/BlogPostFactory.php
new file mode 100644
index 0000000..452ddc1
--- /dev/null
+++ b/workbench/database/factories/BlogPostFactory.php
@@ -0,0 +1,55 @@
+
+ */
+ class BlogPostFactory extends Factory
+ {
+ protected $model = BlogPost::class;
+
+ /**
+ * Generates an array of random comments.
+ *
+ * @param int $count The number of comments to generate.
+ * @return array An array of comment data.
+ */
+ public function generateComments(int $count): array
+ {
+ return collect(range(1, $count))->map(function () {
+ return [
+ 'name' => fake()->name(),
+ 'comment' => fake()->text(),
+ 'country' => fake()->country(),
+ 'likes' => fake()->numberBetween(0, 10),
+ ];
+ })->all();
+ }
+
+ /**
+ * Defines the default state for the BlogPost model.
+ *
+ * @return array
+ */
+ public function definition(): array
+ {
+ return [
+ 'title' => fake()->sentence(),
+ 'content' => fake()->text(),
+ 'comments' => $this->generateComments(fake()->numberBetween(5, 20)),
+ 'status' => fake()->numberBetween(1, 5),
+ 'active' => fake()->boolean(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+ }
diff --git a/workbench/database/factories/ClientFactory.php b/workbench/database/factories/ClientFactory.php
new file mode 100644
index 0000000..bdf28f4
--- /dev/null
+++ b/workbench/database/factories/ClientFactory.php
@@ -0,0 +1,23 @@
+ '',
+ 'name' => fake()->name(),
+ 'status' => fake()->randomNumber(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+ }
diff --git a/workbench/database/factories/ClientLogFactory.php b/workbench/database/factories/ClientLogFactory.php
new file mode 100644
index 0000000..7656071
--- /dev/null
+++ b/workbench/database/factories/ClientLogFactory.php
@@ -0,0 +1,24 @@
+ '',
+ 'title' => fake()->word(),
+ 'desc' => fake()->sentence(),
+ 'status' => fake()->randomNumber(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+ }
diff --git a/workbench/database/factories/ClientProfileFactory.php b/workbench/database/factories/ClientProfileFactory.php
new file mode 100644
index 0000000..cf8d874
--- /dev/null
+++ b/workbench/database/factories/ClientProfileFactory.php
@@ -0,0 +1,28 @@
+ '',
+ 'company_id' => function () {
+ return Company::factory()->create()->_id;
+ },
+ 'contact_name' => fake()->name(),
+ 'contact_email' => fake()->email(),
+ 'website' => fake()->url(),
+ 'status' => fake()->randomNumber(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+ }
diff --git a/workbench/database/factories/CompanyFactory.php b/workbench/database/factories/CompanyFactory.php
new file mode 100644
index 0000000..30ce309
--- /dev/null
+++ b/workbench/database/factories/CompanyFactory.php
@@ -0,0 +1,23 @@
+ fake()->company(),
+ 'status' => fake()->randomNumber(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+
+ }
diff --git a/workbench/database/factories/CompanyLogFactory.php b/workbench/database/factories/CompanyLogFactory.php
new file mode 100644
index 0000000..3f1df6f
--- /dev/null
+++ b/workbench/database/factories/CompanyLogFactory.php
@@ -0,0 +1,26 @@
+ function () {
+ return Company::factory()->create()->_id;
+ },
+ 'title' => fake()->word(),
+ 'desc' => fake()->sentence(),
+ 'status' => fake()->randomNumber(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+ }
diff --git a/workbench/database/factories/CompanyProfileFactory.php b/workbench/database/factories/CompanyProfileFactory.php
new file mode 100644
index 0000000..c3bea66
--- /dev/null
+++ b/workbench/database/factories/CompanyProfileFactory.php
@@ -0,0 +1,25 @@
+ fake()->address(),
+ 'website' => fake()->url(),
+ 'status' => fake()->randomNumber(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+}
diff --git a/workbench/database/factories/PageHitFactory.php b/workbench/database/factories/PageHitFactory.php
new file mode 100644
index 0000000..9895010
--- /dev/null
+++ b/workbench/database/factories/PageHitFactory.php
@@ -0,0 +1,34 @@
+ fake()->ipv4(),
+ 'page_id' => fake()->numberBetween(1, 9),
+ 'date' => fake()->randomElement([
+ '2021-01-01',
+ '2021-01-02',
+ '2021-01-03',
+ '2021-01-04',
+ '2021-01-05',
+ '2021-01-06',
+ '2021-01-07',
+ '2021-01-08',
+ '2021-01-09',
+ '2021-01-10',
+ ]),
+ ];
+ }
+}
diff --git a/workbench/database/factories/PersonFactory.php b/workbench/database/factories/PersonFactory.php
new file mode 100644
index 0000000..0e7c3b4
--- /dev/null
+++ b/workbench/database/factories/PersonFactory.php
@@ -0,0 +1,24 @@
+ fake()->name(),
+ 'jobs' => fake()->words(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+}
diff --git a/workbench/database/factories/PhotoFactory.php b/workbench/database/factories/PhotoFactory.php
new file mode 100644
index 0000000..36406c7
--- /dev/null
+++ b/workbench/database/factories/PhotoFactory.php
@@ -0,0 +1,24 @@
+ $this->faker->imageUrl,
+ 'photoable_id' => null, // To be set when creating instances
+ 'photoable_type' => null, // To be set when creating instances
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ];
+ }
+}
diff --git a/workbench/database/factories/PostFactory.php b/workbench/database/factories/PostFactory.php
new file mode 100644
index 0000000..c9ae6ac
--- /dev/null
+++ b/workbench/database/factories/PostFactory.php
@@ -0,0 +1,25 @@
+ fake()->name(),
+ 'slug' => fake()->slug(),
+ 'content' => fake()->realTextBetween(100),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+}
diff --git a/workbench/database/factories/ProductFactory.php b/workbench/database/factories/ProductFactory.php
new file mode 100644
index 0000000..bde12a4
--- /dev/null
+++ b/workbench/database/factories/ProductFactory.php
@@ -0,0 +1,84 @@
+ fake()->name(),
+ 'description' => fake()->realTextBetween(100),
+ 'product_id' => fake()->uuid(),
+ 'in_stock' => fake()->numberBetween(0, 100),
+ 'status' => fake()->numberBetween(1, 9),
+ 'color' => fake()->safeColorName(),
+ 'is_active' => fake()->boolean(),
+ 'price' => fake()->randomFloat(2, 0, 2000),
+ 'orders' => fake()->numberBetween(0, 250),
+ 'order_values' => $this->randomArrayOfInts(),
+
+ 'manufacturer' => [
+ 'location' => [
+ 'lat' => fake()->latitude(),
+ 'lon' => fake()->longitude(),
+ ],
+ 'name' => fake()->company(),
+ 'country' => fake()->country(),
+ 'owned_by' => [
+ 'name' => fake()->name(),
+ 'country' => fake()->country(),
+ ],
+ ],
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ 'deleted_at' => null,
+ ];
+ }
+
+ public function randomArrayOfInts()
+ {
+ $array = [];
+ $i = 0;
+ while ($i < rand(0, 50)) {
+ $array[] = rand(5, 200);
+ $i++;
+ }
+
+ return $array;
+ }
+
+ public function definitionUSA()
+ {
+ return [
+ 'name' => fake()->name(),
+ 'product_id' => fake()->uuid(),
+ 'in_stock' => fake()->numberBetween(0, 100),
+ 'status' => fake()->numberBetween(1, 9),
+ 'color' => fake()->safeColorName(),
+ 'is_active' => fake()->boolean(),
+ 'price' => fake()->randomFloat(2, 0, 2000),
+ 'orders' => fake()->numberBetween(0, 250),
+ 'manufacturer' => [
+ 'location' => [
+ 'lat' => fake()->latitude(),
+ 'lon' => fake()->longitude(),
+ ],
+ 'name' => fake()->company(),
+ 'country' => 'United States of America',
+ 'owned_by' => [
+ 'name' => fake()->name(),
+ 'country' => fake()->country(),
+ ],
+ ],
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+}
diff --git a/workbench/database/factories/StaticPageFactory.php b/workbench/database/factories/StaticPageFactory.php
new file mode 100644
index 0000000..4b57b74
--- /dev/null
+++ b/workbench/database/factories/StaticPageFactory.php
@@ -0,0 +1,31 @@
+
+ */
+ class StaticPageFactory extends Factory
+ {
+ protected $model = StaticPage::class;
+
+ /**
+ * Defines the default state for the BlogPost model.
+ *
+ * @return array
+ */
+ public function definition(): array
+ {
+ return [
+ 'title' => fake()->sentence(),
+ 'content' => fake()->text(),
+ ];
+ }
+ }
diff --git a/workbench/database/factories/UserFactory.php b/workbench/database/factories/UserFactory.php
new file mode 100644
index 0000000..77d96ba
--- /dev/null
+++ b/workbench/database/factories/UserFactory.php
@@ -0,0 +1,46 @@
+ fake()->firstName(),
+ 'last_name' => fake()->lastName(),
+ 'email' => fake()->unique()->safeEmail(),
+ 'email_verified_at' => now(),
+ 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
+ 'remember_token' => Str::random(10),
+ 'status' => fake()->numberBetween(1, 9),
+ ];
+ }
+
+ /**
+ * Indicate that the model's email address should be unverified.
+ *
+ * @return \Illuminate\Database\Eloquent\Factories\Factory
+ */
+ public function unverified()
+ {
+ return $this->state(function (array $attributes) {
+ return [
+ 'email_verified_at' => null,
+ ];
+ });
+ }
+}
diff --git a/workbench/database/factories/UserLogFactory.php b/workbench/database/factories/UserLogFactory.php
new file mode 100644
index 0000000..7d738fa
--- /dev/null
+++ b/workbench/database/factories/UserLogFactory.php
@@ -0,0 +1,41 @@
+ fake()->word(),
+ 'score' => fake()->word(),
+ 'secret' => fake()->word(),
+ 'code' => fake()->numberBetween(1,5),
+ 'meta' => [],
+ 'agent' => [
+ 'ip' => fake()->ipv4(),
+ 'source' => fake()->url(),
+ 'method' => 'GET',
+ 'browser' => fake()->chrome(),
+ 'device' => fake()->chrome(),
+ 'deviceType' => fake()->randomElement(['desktop', 'mobile', 'tablet']),
+ 'geo' => [
+ 'lat' => fake()->latitude(),
+ 'lon' => fake()->longitude(),
+ ],
+ 'countryCode' => fake()->countryCode(),
+ 'city' => fake()->city(),
+ ],
+ 'status' => fake()->numberBetween(1,9),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+ }
diff --git a/workbench/database/factories/UserProfileFactory.php b/workbench/database/factories/UserProfileFactory.php
new file mode 100644
index 0000000..ed96568
--- /dev/null
+++ b/workbench/database/factories/UserProfileFactory.php
@@ -0,0 +1,25 @@
+ fake()->word(),
+ 'facebook' => fake()->word(),
+ 'address' => fake()->address(),
+ 'timezone' => fake()->timezone(),
+ 'status' => fake()->randomNumber(),
+ 'created_at' => Carbon::now(),
+ 'updated_at' => Carbon::now(),
+ ];
+ }
+ }
diff --git a/workbench/database/migrations/0000_00_00_000001_create_posts_table.php b/workbench/database/migrations/0000_00_00_000001_create_posts_table.php
new file mode 100644
index 0000000..4b38be6
--- /dev/null
+++ b/workbench/database/migrations/0000_00_00_000001_create_posts_table.php
@@ -0,0 +1,39 @@
+text('title');
+ $index->keyword('title');
+
+ $index->text('content');
+
+ $index->nested('comments');
+
+ $index->integer('status');
+ $index->boolean('active');
+
+
+ $index->date('created_at');
+ $index->date('updated_at');
+ $index->date('deleted_at');
+
+ });
+
+ }
+
+ public function down(): void
+ {
+ Schema::deleteIfExists('posts');
+ }
+ };
diff --git a/workbench/database/migrations/0000_00_00_000002_create_static_pages_table.php b/workbench/database/migrations/0000_00_00_000002_create_static_pages_table.php
new file mode 100644
index 0000000..98d01d3
--- /dev/null
+++ b/workbench/database/migrations/0000_00_00_000002_create_static_pages_table.php
@@ -0,0 +1,29 @@
+text('title');
+ $index->keyword('title');
+
+ $index->text('content');
+
+ });
+
+ }
+
+ public function down(): void
+ {
+ Schema::deleteIfExists('static_pages');
+ }
+};
diff --git a/workbench/database/migrations/0000_00_00_000003_create_softs_table.php b/workbench/database/migrations/0000_00_00_000003_create_softs_table.php
new file mode 100644
index 0000000..8335f53
--- /dev/null
+++ b/workbench/database/migrations/0000_00_00_000003_create_softs_table.php
@@ -0,0 +1,31 @@
+text('name');
+ $index->keyword('name');
+
+ $index->date('created_at');
+ $index->date('updated_at');
+ $index->date('deleted_at');
+
+ });
+
+ }
+
+ public function down(): void
+ {
+ Schema::deleteIfExists('softs');
+ }
+};
diff --git a/workbench/database/migrations/2024_08_01_000000_create_users_table.php b/workbench/database/migrations/2024_08_01_000000_create_users_table.php
new file mode 100644
index 0000000..a29e1bb
--- /dev/null
+++ b/workbench/database/migrations/2024_08_01_000000_create_users_table.php
@@ -0,0 +1,53 @@
+down();
+
+ Schema::create('users', function (Blueprint $table) {
+ $table->id();
+ $table->string('first_name');
+ $table->string('last_name');
+ $table->string('email')->unique();
+ $table->string('company_id')->nullable();
+ $table->timestamp('email_verified_at')->nullable();
+ $table->string('password');
+ $table->integer('status');
+ $table->rememberToken();
+ $table->timestamps();
+ });
+
+ Schema::create('photos', function (Blueprint $table) {
+ $table->id();
+ $table->string('url');
+ $table->string('photoable_id');
+ $table->string('photoable_type');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('users');
+ Schema::dropIfExists('photos');
+ }
+
+ public function hasTables(): bool
+ {
+ return Schema::hasTable('users') && Schema::hasTable('photos');
+ }
+};
diff --git a/workbench/database/migrations/2024_08_01_014350_create_products_table.php b/workbench/database/migrations/2024_08_01_014350_create_products_table.php
new file mode 100644
index 0000000..86ae031
--- /dev/null
+++ b/workbench/database/migrations/2024_08_01_014350_create_products_table.php
@@ -0,0 +1,65 @@
+text('name');
+ $index->keyword('name');
+
+ $index->text('description');
+ $index->keyword('description');
+
+ $index->text('product_id');
+ $index->keyword('product_id');
+
+ $index->integer('in_stock');
+
+ $index->keyword('color');
+ $index->integer('status');
+
+ $index->boolean('is_active');
+
+ $index->boolean('is_approved');
+
+ $index->float('price');
+
+ $index->integer('orders');
+ $index->integer('order_values');
+
+ $index->date('last_order_datetime');
+ $index->date('last_order_ts');
+ $index->date('last_order_ms');
+
+ $index->geo('manufacturer.location');
+
+ $index->keyword('manufacturer.name');
+ $index->keyword('manufacturer.country');
+
+ $index->keyword('manufacturer.owned_by.name');
+ $index->keyword('manufacturer.owned_by.country');
+
+ $index->keyword('type');
+
+ $index->date('created_at');
+ $index->date('updated_at');
+ $index->date('deleted_at');
+
+ });
+
+ }
+
+ public function down(): void
+ {
+ Schema::deleteIfExists('products');
+ }
+ };
diff --git a/workbench/database/migrations/2024_08_03_161126_create_blog_posts_table.php b/workbench/database/migrations/2024_08_03_161126_create_blog_posts_table.php
new file mode 100644
index 0000000..d659492
--- /dev/null
+++ b/workbench/database/migrations/2024_08_03_161126_create_blog_posts_table.php
@@ -0,0 +1,39 @@
+text('title');
+ $index->keyword('title');
+
+ $index->text('content');
+
+ $index->nested('comments');
+
+ $index->integer('status');
+ $index->boolean('active');
+
+
+ $index->date('created_at');
+ $index->date('updated_at');
+ $index->date('deleted_at');
+
+ });
+
+ }
+
+ public function down(): void
+ {
+ Schema::deleteIfExists('blog_posts');
+ }
+ };
diff --git a/workbench/database/migrations/2024_08_03_171115_create_companies_tables.php b/workbench/database/migrations/2024_08_03_171115_create_companies_tables.php
new file mode 100644
index 0000000..8cae2bc
--- /dev/null
+++ b/workbench/database/migrations/2024_08_03_171115_create_companies_tables.php
@@ -0,0 +1,56 @@
+text('name');
+ $index->integer('status');
+ $index->date('created_at');
+ $index->date('updated_at');
+ });
+
+ Schema::create('company_logs', function ($index) {
+ $index->text('company_id');
+ $index->text('title');
+ $index->integer('code');
+ $index->date('created_at');
+ $index->date('updated_at');
+ });
+
+ Schema::create('avatars', function ($index) {
+ $index->text('url');
+ $index->text('imageable_id');
+ $index->text('imageable_type');
+ $index->date('created_at');
+ $index->date('updated_at');
+ });
+
+ Schema::create('photos', function ($index) {
+ $index->text('url');
+ $index->text('photoable_id');
+ $index->text('photoable_type');
+ $index->date('created_at');
+ $index->date('updated_at');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::deleteIfExists('companies');
+ Schema::deleteIfExists('company_logs');
+ Schema::deleteIfExists('avatars');
+ Schema::deleteIfExists('photos');
+ }
+};
diff --git a/workbench/database/seeders/DatabaseSeeder.php b/workbench/database/seeders/DatabaseSeeder.php
new file mode 100644
index 0000000..de507a6
--- /dev/null
+++ b/workbench/database/seeders/DatabaseSeeder.php
@@ -0,0 +1,15 @@
+get('/user', function (Request $request) {
+// return $request->user();
+// });
diff --git a/workbench/routes/console.php b/workbench/routes/console.php
new file mode 100644
index 0000000..3c0324c
--- /dev/null
+++ b/workbench/routes/console.php
@@ -0,0 +1,19 @@
+comment(Inspiring::quote());
+// })->purpose('Display an inspiring quote');
diff --git a/workbench/routes/web.php b/workbench/routes/web.php
new file mode 100644
index 0000000..bdc59b1
--- /dev/null
+++ b/workbench/routes/web.php
@@ -0,0 +1,20 @@
+