diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b0876df
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/vendor/
+.phpunit*
+.idea
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..ffb4734
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: php
+php: 8.0
+services:
+ - docker
+
+notifications:
+ email:
+ - team@appwrite.io
+
+before_script:
+ - composer install --ignore-platform-reqs
+
+script:
+ - docker-compose up -d
+ - docker-compose exec tests vendor/bin/pint --test
+ - docker-compose exec tests vendor/bin/phpstan analyse
+ - docker-compose exec tests vendor/bin/phpunit
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..6181e23
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to make participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity, expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at team@appwrite.io. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..29eb794
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,101 @@
+# Contributing
+
+We would ❤️ for you to contribute to Utopia-php and help make it better! We want contributing to Utopia-php to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, new docs as well as updates and tweaks, blog posts, workshops, and more.
+
+## How to Start?
+
+If you are worried or don’t know where to start, check out our next section explaining what kind of help we could use and where can you get involved. You can reach out with questions to [Eldad Fux (@eldadfux)](https://twitter.com/eldadfux) or anyone from the [Appwrite team on Discord](https://discord.gg/GSeTUeA). You can also submit an issue, and a maintainer can guide you!
+
+## Code of Conduct
+
+Help us keep Utopia-php open and inclusive. Please read and follow our [Code of Conduct](/CODE_OF_CONDUCT.md).
+
+## Submit a Pull Request 🚀
+
+Branch naming convention is as following
+
+`TYPE-ISSUE_ID-DESCRIPTION`
+
+example:
+
+```
+doc-548-submit-a-pull-request-section-to-contribution-guide
+```
+
+When `TYPE` can be:
+
+- **feat** - is a new feature
+- **doc** - documentation only changes
+- **cicd** - changes related to CI/CD system
+- **fix** - a bug fix
+- **refactor** - code change that neither fixes a bug nor adds a feature
+
+**All PRs must include a commit message with the changes description!**
+
+For the initial start, fork the project and use git clone command to download the repository to your computer. A standard procedure for working on an issue would be to:
+
+1. `git pull`, before creating a new branch, pull the changes from upstream. Your master needs to be up to date.
+
+```
+$ git pull
+```
+
+2. Create new branch from `master` like: `doc-548-submit-a-pull-request-section-to-contribution-guide`
+
+```
+$ git checkout -b [name_of_your_new_branch]
+```
+
+3. Work - commit - repeat ( be sure to be in your branch )
+
+4. Push changes to GitHub
+
+```
+$ git push origin [name_of_your_new_branch]
+```
+
+5. Submit your changes for review
+ If you go to your repository on GitHub, you'll see a `Compare & pull request` button. Click on that button.
+6. Start a Pull Request
+ Now submit the pull request and click on `Create pull request`.
+7. Get a code review approval/reject
+8. After approval, merge your PR
+9. GitHub will automatically delete the branch after the merge is done. (they can still be restored).
+
+## Introducing New Features
+
+We would 💖 you to contribute to Utopia-php, but we would also like to make sure Utopia-php is as great as possible and loyal to its vision and mission statement 🙏.
+
+For us to find the right balance, please open an issue explaining your ideas before introducing a new pull request.
+
+This will allow the Utopia-php community to have sufficient discussion about the new feature value and how it fits in the product roadmap and vision.
+
+This is also important for the Utopia-php lead developers to be able to give technical input and different emphasis regarding the feature design and architecture. Some bigger features might need to go through our [RFC process](https://github.com/appwrite/rfc).
+
+## Other Ways to Help
+
+Pull requests are great, but there are many other areas where you can help Utopia-php.
+
+### Blogging & Speaking
+
+Blogging, speaking about, or creating tutorials about one of Utopia-php’s many features is great way to contribute and help our project grow.
+
+### Presenting at Meetups
+
+Presenting at meetups and conferences about your Utopia-php projects. Your unique challenges and successes in building things with Utopia-php can provide great speaking material. We’d love to review your talk abstract/CFP, so get in touch with us if you’d like some help!
+
+### Sending Feedbacks & Reporting Bugs
+
+Sending feedback is a great way for us to understand your different use cases of Utopia-php better. If you had any issues, bugs, or want to share about your experience, feel free to do so on our GitHub issues page or at our [Discord channel](https://discord.gg/GSeTUeA).
+
+### Submitting New Ideas
+
+If you think Utopia-php could use a new feature, please open an issue on our GitHub repository, stating as much information as you can think about your new idea and it's implications. We would also use this issue to gather more information, get more feedback from the community, and have a proper discussion about the new feature.
+
+### Improving Documentation
+
+Submitting documentation updates, enhancements, designs, or bug fixes. Spelling or grammar fixes will be very much appreciated.
+
+### Helping Someone
+
+Searching for Utopia-php, GitHub or StackOverflow and helping someone else who needs help. You can also help by teaching others how to contribute to Utopia-php's repo!
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..da9052f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,25 @@
+FROM composer:2.0 as composer
+
+WORKDIR /usr/local/src/
+
+COPY composer.lock /usr/local/src/
+COPY composer.json /usr/local/src/
+
+RUN composer install --ignore-platform-reqs
+
+FROM phpswoole/swoole:php8.1-alpine
+
+WORKDIR /usr/local/src/
+
+RUN apk add autoconf build-base
+
+RUN set -ex \
+ && pecl update-channels \
+ && pecl install redis-stable \
+ && docker-php-ext-enable redis
+
+COPY . .
+
+COPY --from=composer /usr/local/src/vendor /usr/local/src/vendor
+
+CMD ["sleep","3600"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ac621e7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 utopia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..033096e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+# Utopia Queue
+
+[](https://travis-ci.com/utopia-php/queue)
+
+[](https://appwrite.io/discord)
+
+Utopia Queue is a powerful Queue library. This library is aiming to be as simple and easy to learn and use. This library is maintained by the [Appwrite team](https://appwrite.io).
+
+Although this library is part of the [Utopia Framework](https://github.com/utopia-php/framework) project it is dependency free and can be used as standalone with any other PHP project or framework.
+
+## Getting Started
+
+Install using composer:
+```bash
+composer require utopia-php/queue
+```
+
+Init in your application:
+```php
+=8.0",
+ "utopia-php/cli": "0.12.*"
+ },
+ "require-dev": {
+ "swoole/ide-helper": "4.8.8",
+ "phpunit/phpunit": "^9.5.5",
+ "laravel/pint": "^0.2.3",
+ "workerman/workerman": "^4.0",
+ "phpstan/phpstan": "^1.8"
+ },
+ "suggest": {
+ "ext-swoole": "Needed to support Swoole.",
+ "workerman/workerman": "Needed to support Workerman."
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..5ccd929
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,2369 @@
+{
+ "_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": "d2e6f5c1f9e46b331870f6b98ca42359",
+ "packages": [
+ {
+ "name": "utopia-php/cli",
+ "version": "0.12.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/utopia-php/cli.git",
+ "reference": "6d164b752efeb1ca089e3a517bc274d8b383474b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/utopia-php/cli/zipball/6d164b752efeb1ca089e3a517bc274d8b383474b",
+ "reference": "6d164b752efeb1ca089e3a517bc274d8b383474b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4",
+ "utopia-php/framework": "0.*.*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3",
+ "vimeo/psalm": "4.0.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Utopia\\CLI\\": "src/CLI"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eldad Fux",
+ "email": "eldad@appwrite.io"
+ }
+ ],
+ "description": "A simple CLI library to manage command line applications",
+ "keywords": [
+ "cli",
+ "command line",
+ "framework",
+ "php",
+ "upf",
+ "utopia"
+ ],
+ "support": {
+ "issues": "https://github.com/utopia-php/cli/issues",
+ "source": "https://github.com/utopia-php/cli/tree/0.12.0"
+ },
+ "time": "2022-02-18T22:10:41+00:00"
+ },
+ {
+ "name": "utopia-php/framework",
+ "version": "0.19.21",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/utopia-php/framework.git",
+ "reference": "3b7bd8e4acf84fd7d560ced8e0142221d302575d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/utopia-php/framework/zipball/3b7bd8e4acf84fd7d560ced8e0142221d302575d",
+ "reference": "3b7bd8e4acf84fd7d560ced8e0142221d302575d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5.10",
+ "vimeo/psalm": "4.13.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Utopia\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eldad Fux",
+ "email": "eldad@appwrite.io"
+ }
+ ],
+ "description": "A simple, light and advanced PHP framework",
+ "keywords": [
+ "framework",
+ "php",
+ "upf"
+ ],
+ "support": {
+ "issues": "https://github.com/utopia-php/framework/issues",
+ "source": "https://github.com/utopia-php/framework/tree/0.19.21"
+ },
+ "time": "2022-05-12T18:42:28+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^0.16 || ^1",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "vimeo/psalm": "^4.22"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.4.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%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-03T08:28:38+00:00"
+ },
+ {
+ "name": "laravel/pint",
+ "version": "v0.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/pint.git",
+ "reference": "d0829631687c1238abdd660daac3d7218254b65c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/pint/zipball/d0829631687c1238abdd660daac3d7218254b65c",
+ "reference": "d0829631687c1238abdd660daac3d7218254b65c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-tokenizer": "*",
+ "ext-xml": "*",
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.8.0",
+ "illuminate/view": "^9.17.0",
+ "laravel-zero/framework": "^9.1.1",
+ "mockery/mockery": "^1.5.0",
+ "nunomaduro/larastan": "^2.1.11",
+ "nunomaduro/termwind": "^1.10.1",
+ "pestphp/pest": "^1.21.3"
+ },
+ "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": "2022-07-04T16:04:06+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
+ "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",
+ "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.11.0"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-03T13:19:32+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1",
+ "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-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/v4.14.0"
+ },
+ "time": "2022-05-31T20:59:12+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "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.3"
+ },
+ "time": "2021-07-20T11:28:43+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": "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.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^7.2 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^1.3",
+ "webmozart/assert": "^1.9.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.2",
+ "psalm/phar": "^4.8"
+ },
+ "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": "account@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.3.0"
+ },
+ "time": "2021-10-19T17:43:47+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "1.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "77a32518733312af16a44300404e945338981de3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3",
+ "reference": "77a32518733312af16a44300404e945338981de3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*",
+ "psalm/phar": "^4.8"
+ },
+ "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.6.1"
+ },
+ "time": "2022-03-15T21:29:03+00:00"
+ },
+ {
+ "name": "phpspec/prophecy",
+ "version": "v1.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/prophecy.git",
+ "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
+ "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.2",
+ "php": "^7.2 || ~8.0, <8.2",
+ "phpdocumentor/reflection-docblock": "^5.2",
+ "sebastian/comparator": "^3.0 || ^4.0",
+ "sebastian/recursion-context": "^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "^6.0 || ^7.0",
+ "phpunit/phpunit": "^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Prophecy\\": "src/Prophecy"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Konstantin Kudryashov",
+ "email": "ever.zet@gmail.com",
+ "homepage": "http://everzet.com"
+ },
+ {
+ "name": "Marcello Duarte",
+ "email": "marcello.duarte@gmail.com"
+ }
+ ],
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
+ "homepage": "https://github.com/phpspec/prophecy",
+ "keywords": [
+ "Double",
+ "Dummy",
+ "fake",
+ "mock",
+ "spy",
+ "stub"
+ ],
+ "support": {
+ "issues": "https://github.com/phpspec/prophecy/issues",
+ "source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
+ },
+ "time": "2021-12-08T12:19:24+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b7648d4ee9321665acaf112e49da9fd93df8fbd5",
+ "reference": "b7648d4ee9321665acaf112e49da9fd93df8fbd5",
+ "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",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "source": "https://github.com/phpstan/phpstan/tree/1.8.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/phpstan",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-06-29T08:53:31+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "9.2.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.13.0",
+ "php": ">=7.3",
+ "phpunit/php-file-iterator": "^3.0.3",
+ "phpunit/php-text-template": "^2.0.2",
+ "sebastian/code-unit-reverse-lookup": "^2.0.2",
+ "sebastian/complexity": "^2.0",
+ "sebastian/environment": "^5.1.2",
+ "sebastian/lines-of-code": "^1.0.3",
+ "sebastian/version": "^3.0.1",
+ "theseer/tokenizer": "^1.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-pcov": "*",
+ "ext-xdebug": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.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 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",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-03-07T09:28:20+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "3.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "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",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-02T12:48:52+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "3.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-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/3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:58:55+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T05:33:50+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "5.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.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/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:16:10+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "9.5.21",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e32b76be457de00e83213528f6bb37e2a38fcb1",
+ "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.3.1",
+ "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": ">=7.3",
+ "phpspec/prophecy": "^1.12.1",
+ "phpunit/php-code-coverage": "^9.2.13",
+ "phpunit/php-file-iterator": "^3.0.5",
+ "phpunit/php-invoker": "^3.1.1",
+ "phpunit/php-text-template": "^2.0.3",
+ "phpunit/php-timer": "^5.0.2",
+ "sebastian/cli-parser": "^1.0.1",
+ "sebastian/code-unit": "^1.0.6",
+ "sebastian/comparator": "^4.0.5",
+ "sebastian/diff": "^4.0.3",
+ "sebastian/environment": "^5.1.3",
+ "sebastian/exporter": "^4.0.3",
+ "sebastian/global-state": "^5.0.1",
+ "sebastian/object-enumerator": "^4.0.3",
+ "sebastian/resource-operations": "^3.0.3",
+ "sebastian/type": "^3.0",
+ "sebastian/version": "^3.0.2"
+ },
+ "require-dev": {
+ "phpspec/prophecy-phpunit": "^2.0.1"
+ },
+ "suggest": {
+ "ext-soap": "*",
+ "ext-xdebug": "*"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.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",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.21"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-06-19T12:14:25+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.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",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:08:49+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.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/1.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:08:54+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.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/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:30:19+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "4.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "55f4261989e546dc112258c7a75935a81a7ce382"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382",
+ "reference": "55f4261989e546dc112258c7a75935a81a7ce382",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/diff": "^4.0",
+ "sebastian/exporter": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T15:49:45+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.7",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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 calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T15:52:27+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-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",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:10:38+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "5.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.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": "http://www.github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-04-03T09:37:03+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-mbstring": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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": "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",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-11-11T14:18:36+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "5.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-uopz": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-02-14T08:28:10+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.6",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.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",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-28T06:42:11+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:12:34+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.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/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:14:26+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.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": "http://www.github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:17:30+00:00"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:45:17+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
+ "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "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/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-03-15T09:54:48+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "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": "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/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:39:44+00:00"
+ },
+ {
+ "name": "swoole/ide-helper",
+ "version": "4.8.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/swoole/ide-helper.git",
+ "reference": "dd87843a5040831f9ad40b68fb57879b7342ef61"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/swoole/ide-helper/zipball/dd87843a5040831f9ad40b68fb57879b7342ef61",
+ "reference": "dd87843a5040831f9ad40b68fb57879b7342ef61",
+ "shasum": ""
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Team Swoole",
+ "email": "team@swoole.com"
+ }
+ ],
+ "description": "IDE help files for Swoole.",
+ "support": {
+ "issues": "https://github.com/swoole/ide-helper/issues",
+ "source": "https://github.com/swoole/ide-helper/tree/4.8.8"
+ },
+ "funding": [
+ {
+ "url": "https://gitee.com/swoole/swoole?donate=true",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/swoole",
+ "type": "github"
+ }
+ ],
+ "time": "2022-03-17T18:24:39+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "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.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-28T10:34:58+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"
+ },
+ {
+ "name": "workerman/workerman",
+ "version": "v4.0.39",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/walkor/workerman.git",
+ "reference": "97ff0d0d3d20ce9abfd0f3c87e5e3481d3bf16d9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/walkor/workerman/zipball/97ff0d0d3d20ce9abfd0f3c87e5e3481d3bf16d9",
+ "reference": "97ff0d0d3d20ce9abfd0f3c87e5e3481d3bf16d9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4"
+ },
+ "suggest": {
+ "ext-event": "For better performance. "
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Workerman\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "walkor",
+ "email": "walkor@workerman.net",
+ "homepage": "http://www.workerman.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
+ "homepage": "http://www.workerman.net",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "support": {
+ "email": "walkor@workerman.net",
+ "forum": "http://wenda.workerman.net/",
+ "issues": "https://github.com/walkor/workerman/issues",
+ "source": "https://github.com/walkor/workerman",
+ "wiki": "http://doc.workerman.net/"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/workerman",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/walkor",
+ "type": "patreon"
+ }
+ ],
+ "time": "2022-06-16T14:11:47+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=8.0"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.3.0"
+}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..79b626b
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,36 @@
+version: '3.1'
+
+services:
+ tests:
+ container_name: tests
+ build: .
+ volumes:
+ - ./:/usr/local/src
+ depends_on:
+ - redis
+ - swoole
+ - workerman
+
+ swoole:
+ container_name: swoole
+ build: ./tests/Queue/servers/Swoole/.
+ command: php /usr/src/code/tests/Queue/servers/Swoole/worker.php
+ volumes:
+ - ./:/usr/src/code
+ depends_on:
+ - redis
+
+ workerman:
+ container_name: workerman
+ build: ./tests/Queue/servers/Workerman/.
+ command: php /usr/src/code/tests/Queue/servers/Workerman/worker.php start
+ volumes:
+ - ./:/usr/src/code
+ depends_on:
+ - redis
+
+ redis:
+ container_name: redis
+ image: "redis:alpine"
+ ports:
+ - "6379:6379"
\ No newline at end of file
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..6852b4c
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,9 @@
+parameters:
+ level: 2
+
+ paths:
+ - src
+ - tests
+
+ scanDirectories:
+ - vendor/swoole
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..b0e7e34
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,16 @@
+
+
+
+ ./tests/Queue/e2e/
+
+
+
\ No newline at end of file
diff --git a/pint.json b/pint.json
new file mode 100644
index 0000000..c781933
--- /dev/null
+++ b/pint.json
@@ -0,0 +1,3 @@
+{
+ "preset": "psr12"
+}
\ No newline at end of file
diff --git a/src/Queue/Adapter.php b/src/Queue/Adapter.php
new file mode 100644
index 0000000..bb99b02
--- /dev/null
+++ b/src/Queue/Adapter.php
@@ -0,0 +1,67 @@
+
+ * @version 1.0 RC1
+ * @license The MIT License (MIT)
+ */
+abstract class Adapter
+{
+ public int $workerNum;
+ public string $queue;
+ public string $namespace;
+ public Connection $connection;
+
+ public function __construct(int $workerNum, string $queue, string $namespace = 'utopia-queue')
+ {
+ $this->workerNum = $workerNum;
+ $this->queue = $queue;
+ $this->namespace = $namespace;
+ }
+
+ /**
+ * Starts the Server.
+ * @return void
+ */
+ abstract public function start(): void;
+
+ /**
+ * Shuts down the Server.
+ * @return void
+ */
+ abstract public function shutdown(): void;
+
+ /**
+ * Is called when the Server starts.
+ * @param callable $callback
+ * @return self
+ */
+ abstract public function onStart(callable $callback): self;
+
+ /**
+ * Is called when a Worker receives a Job.
+ * @param callable $callback
+ * @return self
+ */
+ abstract public function onJob(callable $callback): self;
+
+ /**
+ * Is called when a Worker starts.
+ * @param callable $callback
+ * @return self
+ */
+ abstract public function onWorkerStart(callable $callback): self;
+
+ /**
+ * Returns the native server object from the Adapter.
+ * @return mixed
+ */
+ abstract public function getNative(): mixed;
+}
diff --git a/src/Queue/Adapter/Swoole.php b/src/Queue/Adapter/Swoole.php
new file mode 100644
index 0000000..136cfe6
--- /dev/null
+++ b/src/Queue/Adapter/Swoole.php
@@ -0,0 +1,65 @@
+connection = $connection;
+ $this->pool = new Pool($workerNum);
+ }
+
+ public function start(): void
+ {
+ $this->pool->set(['enable_coroutine' => true]);
+ $this->pool->start();
+ }
+
+ public function shutdown(): void
+ {
+ $this->pool->shutdown();
+ }
+
+ public function onStart(callable $callback): self
+ {
+ $this->pool->on('start', function () use ($callback) {
+ call_user_func($callback);
+ });
+
+ return $this;
+ }
+
+ public function onWorkerStart(callable $callback): self
+ {
+ $this->onWorkerStartCallback = $callback;
+
+ return $this;
+ }
+
+ public function onJob(callable $callback): self
+ {
+ $this->pool->on('WorkerStart', function (Pool $pool, string $workerId) use ($callback) {
+ if (!is_null($this->onWorkerStartCallback)) {
+ call_user_func($this->onWorkerStartCallback);
+ }
+ call_user_func($callback);
+ });
+
+ return $this;
+ }
+
+ public function getNative(): Pool
+ {
+ return $this->pool;
+ }
+}
diff --git a/src/Queue/Adapter/Workerman.php b/src/Queue/Adapter/Workerman.php
new file mode 100644
index 0000000..9387a05
--- /dev/null
+++ b/src/Queue/Adapter/Workerman.php
@@ -0,0 +1,65 @@
+worker = new Worker();
+ $this->worker->count = $workerNum;
+ $this->connection = $connection;
+ }
+
+ public function start(): void
+ {
+ Worker::runAll();
+ call_user_func($this->onStartCallback);
+ }
+
+ public function shutdown(): void
+ {
+ Worker::stopAll();
+ }
+
+ public function onStart(callable $callback): self
+ {
+ $this->onStartCallback = $callback;
+
+ return $this;
+ }
+
+ public function onWorkerStart(callable $callback): self
+ {
+ $this->onWorkerStartCallback = $callback;
+
+ return $this;
+ }
+
+ public function onJob(callable $callback): self
+ {
+ $this->worker->onWorkerStart = function () use ($callback) {
+ if (!is_null($this->onWorkerStartCallback)) {
+ call_user_func($this->onWorkerStartCallback);
+ }
+ call_user_func($callback);
+ };
+
+ return $this;
+ }
+
+ public function getNative(): Worker
+ {
+ return $this->worker;
+ }
+}
diff --git a/src/Queue/Client.php b/src/Queue/Client.php
new file mode 100644
index 0000000..639adcf
--- /dev/null
+++ b/src/Queue/Client.php
@@ -0,0 +1,77 @@
+queue = $queue;
+ $this->namespace = $namespace;
+ $this->connection = $connection;
+ }
+
+ public function enqueue(array $payload): bool
+ {
+ $payload = [
+ 'pid' => \uniqid(more_entropy: true),
+ 'queue' => $this->queue,
+ 'timestamp' => time(),
+ 'payload' => $payload
+ ];
+
+ return $this->connection->leftPushArray("{$this->namespace}.queue.{$this->queue}", $payload);
+ }
+
+ public function getJob(string $pid): Job|false
+ {
+ $job = $this->connection->get("{$this->namespace}.jobs.{$this->queue}.{$pid}");
+
+ if ($job === false) {
+ return false;
+ }
+
+ return new Job($job);
+ }
+
+ public function listJobs(int $total = 50, int $offset = 0): array
+ {
+ return $this->connection->listRange("{$this->namespace}.queue.{$this->queue}", $total, $offset);
+ }
+
+ public function getQueueSize(): int
+ {
+ return $this->connection->listSize("{$this->namespace}.queue.{$this->queue}");
+ }
+
+ public function sumTotalJobs(): int
+ {
+ return \intval($this->connection->get("{$this->namespace}.stats.{$this->queue}.total") ?? 0);
+ }
+
+ public function sumSuccessfulJobs(): int
+ {
+ return \intval($this->connection->get("{$this->namespace}.stats.{$this->queue}.success") ?? 0);
+ }
+
+ public function sumFailedJobs(): int
+ {
+ return \intval($this->connection->get("{$this->namespace}.stats.{$this->queue}.failed") ?? 0);
+ }
+
+ public function sumProcessingJobs(): int
+ {
+ return \intval($this->connection->get("{$this->namespace}.stats.{$this->queue}.processing") ?? 0);
+ }
+
+ public function resetStats(): void
+ {
+ $this->connection->set("{$this->namespace}.stats.{$this->queue}.total", 0);
+ $this->connection->set("{$this->namespace}.stats.{$this->queue}.success", 0);
+ $this->connection->set("{$this->namespace}.stats.{$this->queue}.failed", 0);
+ $this->connection->set("{$this->namespace}.stats.{$this->queue}.processing", 0);
+ }
+}
diff --git a/src/Queue/Connection.php b/src/Queue/Connection.php
new file mode 100644
index 0000000..2ceae35
--- /dev/null
+++ b/src/Queue/Connection.php
@@ -0,0 +1,37 @@
+
+ * @version 1.0 RC1
+ * @license The MIT License (MIT)
+ */
+interface Connection
+{
+ public function rightPushArray(string $queue, array $payload): bool;
+ public function rightPopArray(string $queue, int $timeout): array|false;
+ public function rightPopLeftPushArray(string $queue, string $destination, int $timeout): array|false;
+ public function leftPushArray(string $queue, array $payload): bool;
+ public function leftPopArray(string $queue, int $timeout): array|false;
+ public function rightPush(string $queue, string $payload): bool;
+ public function rightPop(string $queue, int $timeout): string|false;
+ public function rightPopLeftPush(string $queue, string $destination, int $timeout): string|false;
+ public function leftPush(string $queue, string $payload): bool;
+ public function leftPop(string $queue, int $timeout): string|false;
+ public function listRemove(string $queue, string $key): bool;
+ public function listSize(string $key): int;
+ public function listRange(string $key, int $total, int $offset): array;
+ public function remove(string $key): bool;
+ public function move(string $queue, string $destination): bool;
+ public function set(string $key, string $value): bool;
+ public function get(string $key): array|string|null;
+ public function setArray(string $key, array $value): bool;
+ public function increment(string $key): int;
+ public function decrement(string $key): int;
+}
diff --git a/src/Queue/Connection/Redis.php b/src/Queue/Connection/Redis.php
new file mode 100644
index 0000000..9798471
--- /dev/null
+++ b/src/Queue/Connection/Redis.php
@@ -0,0 +1,174 @@
+host = $host;
+ $this->port = $port;
+ $this->user = $user;
+ $this->password = $password;
+ }
+
+ public function rightPopLeftPushArray(string $queue, string $destination, int $timeout): array|false
+ {
+ $response = $this->rightPopLeftPush($queue, $destination, $timeout);
+
+ if (!$response) {
+ return false;
+ }
+
+ return json_decode($response, true);
+ }
+ public function rightPopLeftPush(string $queue, string $destination, int $timeout): string|false
+ {
+ $response = $this->getRedis()->bRPopLPush($queue, $destination, $timeout);
+
+ if (!$response) {
+ return false;
+ }
+
+ return $response;
+ }
+ public function rightPushArray(string $queue, array $value): bool
+ {
+ return !!$this->getRedis()->rPush($queue, json_encode($value));
+ }
+
+ public function rightPush(string $queue, string $value): bool
+ {
+ return !!$this->getRedis()->rPush($queue, $value);
+ }
+
+ public function leftPushArray(string $queue, array $value): bool
+ {
+ return !!$this->getRedis()->lPush($queue, json_encode($value));
+ }
+
+ public function leftPush(string $queue, string $value): bool
+ {
+ return !!$this->getRedis()->lPush($queue, $value);
+ }
+
+ public function rightPopArray(string $queue, int $timeout): array|false
+ {
+ $response = $this->rightPop($queue, $timeout);
+
+ if ($response === false) {
+ return false;
+ }
+
+ return json_decode($response, true);
+ }
+
+ public function rightPop(string $queue, int $timeout): string|false
+ {
+ $response = $this->getRedis()->brPop([$queue], $timeout);
+
+ if (empty($response)) {
+ return false;
+ }
+
+ return $response[1];
+ }
+
+ public function leftPopArray(string $queue, int $timeout): array|false
+ {
+ $response = $this->getRedis()->blPop($queue, $timeout);
+
+ if (empty($response)) {
+ return false;
+ }
+
+ return json_decode($response[1], true);
+ }
+
+ public function leftPop(string $queue, int $timeout): string|false
+ {
+ $response = $this->getRedis()->blPop($queue, $timeout);
+
+ if (empty($response)) {
+ return false;
+ }
+
+ return $response[1];
+ }
+
+ public function listRemove(string $queue, string $key): bool
+ {
+ return !!$this->getRedis()->lRem($queue, $key, 1);
+ }
+
+ public function remove(string $key): bool
+ {
+ return !!$this->getRedis()->del($key);
+ }
+
+ public function move(string $queue, string $destination): bool
+ {
+ return $this->getRedis()->move($queue, $destination);
+ }
+
+ public function setArray(string $key, array $value): bool
+ {
+ return $this->set($key, json_encode($value));
+ }
+
+ public function set(string $key, string $value): bool
+ {
+ return $this->getRedis()->set($key, $value);
+ }
+
+ public function get(string $key): array|string|null
+ {
+ return $this->getRedis()->get($key);
+ }
+
+ public function listSize(string $key): int
+ {
+ return $this->getRedis()->lLen($key);
+ }
+
+ public function increment(string $key): int
+ {
+ return $this->getRedis()->incr($key);
+ }
+
+ public function decrement(string $key): int
+ {
+ return $this->getRedis()->decr($key);
+ }
+
+ public function listRange(string $key, int $total, int $offset): array
+ {
+ $start = $offset - 1;
+ $end = ($total + $offset) -1;
+ $results = $this->getRedis()->lrange($key, $start, $end);
+
+ return array_map(fn (array $job) => new Job($job), $results);
+ }
+
+ protected function getRedis(): \Redis
+ {
+ if ($this->redis) {
+ return $this->redis;
+ }
+
+ $this->redis = new \Redis();
+
+ $this->redis->connect($this->host, $this->port);
+
+ return $this->getRedis();
+ }
+}
diff --git a/src/Queue/Connection/RedisSwoole.php b/src/Queue/Connection/RedisSwoole.php
new file mode 100644
index 0000000..96a93de
--- /dev/null
+++ b/src/Queue/Connection/RedisSwoole.php
@@ -0,0 +1,174 @@
+host = $host;
+ $this->port = $port;
+ $this->user = $user;
+ $this->password = $password;
+ }
+
+ public function rightPopLeftPushArray(string $queue, string $destination, int $timeout): array|false
+ {
+ $response = $this->rightPopLeftPush($queue, $destination, $timeout);
+
+ if (!$response) {
+ return false;
+ }
+
+ return json_decode($response, true);
+ }
+ public function rightPopLeftPush(string $queue, string $destination, int $timeout): string|false
+ {
+ $response = $this->getRedis()->bRPopLPush($queue, $destination, $timeout);
+
+ if (($response ?? false) === false) {
+ return false;
+ }
+
+ return $response;
+ }
+ public function rightPushArray(string $queue, array $value): bool
+ {
+ return !!$this->getRedis()->rPush($queue, json_encode($value));
+ }
+
+ public function rightPush(string $queue, string $value): bool
+ {
+ return !!$this->getRedis()->rPush($queue, $value);
+ }
+
+ public function leftPushArray(string $queue, array $value): bool
+ {
+ return !!$this->getRedis()->lPush($queue, json_encode($value));
+ }
+
+ public function leftPush(string $queue, string $value): bool
+ {
+ return !!$this->getRedis()->lPush($queue, $value);
+ }
+
+ public function rightPopArray(string $queue, int $timeout): array|false
+ {
+ $response = $this->rightPop($queue, $timeout);
+
+ if ($response === false) {
+ return false;
+ }
+
+ return json_decode($response, true);
+ }
+
+ public function rightPop(string $queue, int $timeout): string|false
+ {
+ $response = $this->getRedis()->brPop([$queue], $timeout);
+
+ if (($response ?? false) === false) {
+ return false;
+ }
+ return $response[1];
+ }
+
+ public function leftPopArray(string $queue, int $timeout): array|false
+ {
+ $response = $this->getRedis()->blPop($queue, $timeout);
+
+ if ($response === false) {
+ return false;
+ }
+
+ return json_decode($response, true);
+ }
+
+ public function leftPop(string $queue, int $timeout): string|false
+ {
+ $response = $this->getRedis()->blPop($queue, $timeout);
+
+ if (($response ?? false) === false) {
+ return false;
+ }
+
+ return $response[1];
+ }
+
+ public function listRemove(string $queue, string $key): bool
+ {
+ return !!$this->getRedis()->lRem($queue, $key, 1);
+ }
+
+ public function remove(string $key): bool
+ {
+ return !!$this->getRedis()->del($key);
+ }
+
+ public function move(string $queue, string $destination): bool
+ {
+ return $this->getRedis()->move($queue, $destination);
+ }
+
+ public function setArray(string $key, array $value): bool
+ {
+ return $this->set($key, json_encode($value));
+ }
+
+ public function set(string $key, string $value): bool
+ {
+ return $this->getRedis()->set($key, $value);
+ }
+
+ public function get(string $key): array|string|null
+ {
+ return $this->getRedis()->get($key);
+ }
+
+ public function listSize(string $key): int
+ {
+ return $this->getRedis()->lSize($key);
+ }
+
+ public function increment(string $key): int
+ {
+ return $this->getRedis()->incr($key);
+ }
+
+ public function decrement(string $key): int
+ {
+ return $this->getRedis()->decr($key);
+ }
+
+ public function listRange(string $key, int $total, int $offset): array
+ {
+ $start = $offset - 1;
+ $end = ($total + $offset) - 1;
+ $results = $this->getRedis()->lrange($key, $start, $end);
+
+ return array_map(fn (array $job) => new Job($job), $results);
+ }
+
+ protected function getRedis(): Redis
+ {
+ if ($this->redis) {
+ return $this->redis;
+ }
+
+ $this->redis = new Redis();
+
+ $this->redis->connect($this->host, $this->port);
+
+ return $this->getRedis();
+ }
+}
diff --git a/src/Queue/Job.php b/src/Queue/Job.php
new file mode 100644
index 0000000..dff7667
--- /dev/null
+++ b/src/Queue/Job.php
@@ -0,0 +1,63 @@
+pid = $array['pid'];
+ $this->queue = $array['queue'];
+ $this->timestamp = $array['timestamp'];
+ $this->payload = $array['payload'];
+ }
+ public function setPid(string $pid): self
+ {
+ $this->pid = $pid;
+
+ return $this;
+ }
+ public function setQueue(string $queue): self
+ {
+ $this->queue = $queue;
+
+ return $this;
+ }
+ public function setTimestamp(int $timestamp): self
+ {
+ $this->timestamp = $timestamp;
+
+ return $this;
+ }
+ public function setPayload(array $payload): self
+ {
+ $this->payload = $payload;
+
+ return $this;
+ }
+ public function getPid(): string
+ {
+ return $this->pid;
+ }
+ public function getQueue(): string
+ {
+ return $this->queue;
+ }
+ public function getTimestamp(): int
+ {
+ return $this->timestamp;
+ }
+ public function getPayload(): array
+ {
+ return $this->payload;
+ }
+}
diff --git a/src/Queue/Server.php b/src/Queue/Server.php
new file mode 100644
index 0000000..ea147c8
--- /dev/null
+++ b/src/Queue/Server.php
@@ -0,0 +1,212 @@
+
+ * @version 1.0 RC1
+ * @license The MIT License (MIT)
+ */
+class Server
+{
+ /**
+ * Callbacks that will be executed when an error occurs
+ *
+ * @var array
+ */
+ protected array $errorCallbacks = [];
+ protected Adapter $adapter;
+
+ /**
+ * Creates an instance of a Queue server.
+ * @param Adapter $adapter
+ */
+ public function __construct(Adapter $adapter)
+ {
+ $this->adapter = $adapter;
+ }
+
+ /**
+ * Starts the Queue server.
+ * @return void
+ */
+ public function start(): void
+ {
+ try {
+ $this->adapter->start();
+ } catch (Throwable $error) {
+ foreach ($this->errorCallbacks as $errorCallback) {
+ $errorCallback($error, "start");
+ }
+ }
+ }
+
+ /**
+ * Shuts down the Queue server.
+ * @return void
+ */
+ public function shutdown(): void
+ {
+ try {
+ $this->adapter->shutdown();
+ } catch (Throwable $error) {
+ foreach ($this->errorCallbacks as $errorCallback) {
+ $errorCallback($error, "shutdown");
+ }
+ }
+ }
+
+ /**
+ * Is called when the Server starts.
+ * @param callable $callback
+ * @return self
+ */
+ public function onStart(callable $callback): self
+ {
+ try {
+ $this->adapter->onStart(function () use ($callback) {
+ Console::success("[Worker] Queue Workers are starting");
+ call_user_func($callback);
+ });
+ } catch (Throwable $error) {
+ foreach ($this->errorCallbacks as $errorCallback) {
+ $errorCallback($error, "onStart");
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Is called when a Worker starts.
+ * @param callable $callback
+ * @return self
+ */
+ public function onWorkerStart(callable $callback): self
+ {
+ try {
+ $this->adapter->onWorkerStart(function (string $workerId) use ($callback) {
+ Console::success("[Worker] Worker {$workerId} is ready!");
+ call_user_func($callback);
+ });
+ } catch (Throwable $error) {
+ foreach ($this->errorCallbacks as $errorCallback) {
+ $errorCallback($error, "onWorkerStart");
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Is called when a Worker receives a Job.
+ * @param callable $callback
+ * @return self
+ */
+ public function onJob(callable $callback): self
+ {
+ try {
+ $this->adapter->onJob(function () use ($callback) {
+ while (true) {
+ /**
+ * Waiting for next Job.
+ */
+ $nextJob = $this->adapter->connection->rightPopArray("{$this->adapter->namespace}.queue.{$this->adapter->queue}", 5);
+
+ if (!$nextJob) {
+ continue;
+ }
+
+ $job = new Job();
+ $job
+ ->setPid($nextJob['pid'])
+ ->setQueue($nextJob['queue'])
+ ->setTimestamp(\intval($nextJob['timestamp']))
+ ->setPayload($nextJob['payload']);
+
+ Console::info("[Job] Received Job ({$job->getPid()}).");
+
+ /**
+ * Move Job to Jobs and it's PID to the processing list.
+ */
+ $this->adapter->connection->setArray("{$this->adapter->namespace}.jobs.{$this->adapter->queue}.{$job->getPid()}", $nextJob);
+ $this->adapter->connection->leftPush("{$this->adapter->namespace}.processing.{$this->adapter->queue}", $job->getPid());
+
+ /**
+ * Increment Total Jobs Received from Stats.
+ */
+ $this->adapter->connection->increment("{$this->adapter->namespace}.stats.{$this->adapter->queue}.total");
+
+ try {
+ /**
+ * Increment Processing Jobs from Stats.
+ */
+ $this->adapter->connection->increment("{$this->adapter->namespace}.stats.{$this->adapter->queue}.processing");
+
+
+ call_user_func($callback, $job);
+
+ /**
+ * Remove Jobs if successful.
+ */
+ $this->adapter->connection->remove("{$this->adapter->namespace}.jobs.{$this->adapter->queue}.{$job->getPid()}");
+
+ /**
+ * Increment Successful Jobs from Stats.
+ */
+ $this->adapter->connection->increment("{$this->adapter->namespace}.stats.{$this->adapter->queue}.success");
+
+ Console::success("[Job] ({$job->getPid()}) successfully run.");
+ } catch (\Throwable $th) {
+ /**
+ * Move failed Job to Failed list.
+ */
+ $this->adapter->connection->leftPush("{$this->adapter->namespace}.failed.{$this->adapter->queue}", $job->getPid());
+
+ /**
+ * Increment Failed Jobs from Stats.
+ */
+ $this->adapter->connection->increment("{$this->adapter->namespace}.stats.{$this->adapter->queue}.failed");
+
+ Console::error("[Job] ({$job->getPid()}) failed to run.");
+ Console::error("[Job] ({$job->getPid()}) {$th->getMessage()}");
+ } finally {
+ /**
+ * Remove Job from Processing.
+ */
+ $this->adapter->connection->listRemove("{$this->adapter->namespace}.processing.{$this->adapter->queue}", $job->getPid());
+
+ /**
+ * Decrease Processing Jobs from Stats.
+ */
+ $this->adapter->connection->decrement("{$this->adapter->namespace}.stats.{$this->adapter->queue}.processing");
+ }
+ }
+ });
+ } catch (Throwable $error) {
+ foreach ($this->errorCallbacks as $errorCallback) {
+ $errorCallback($error, "onJob");
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Register callback. Will be executed when error occurs.
+ * @param callable $callback
+ * @return self
+ */
+ public function error(callable $callback): self
+ {
+ \array_push($this->errorCallbacks, $callback);
+ return $this;
+ }
+}
diff --git a/tests/Queue/e2e/AdapterTest.php b/tests/Queue/e2e/AdapterTest.php
new file mode 100644
index 0000000..be59eed
--- /dev/null
+++ b/tests/Queue/e2e/AdapterTest.php
@@ -0,0 +1,146 @@
+resetStats();
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_string',
+ 'value' => 'lorem ipsum'
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_number',
+ 'value' => 123
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_number',
+ 'value' => 123.456
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_bool',
+ 'value' => true
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_null',
+ 'value' => null
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_array',
+ 'value' => [
+ 1,
+ 2,
+ 3
+ ]
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_assoc',
+ 'value' => [
+ 'string' => 'ipsum',
+ 'number' => 123,
+ 'bool' => true,
+ 'null' => null
+ ]
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_exception'
+ ]));
+
+ sleep(1);
+
+ $this->assertEquals(8, $client->sumTotalJobs());
+ $this->assertEquals(0, $client->getQueueSize());
+ $this->assertEquals(0, $client->sumProcessingJobs());
+ $this->assertEquals(1, $client->sumFailedJobs());
+ $this->assertEquals(7, $client->sumSuccessfulJobs());
+ }
+
+ public function testSwoole(): void
+ {
+ $connection = new RedisSwoole('redis', 6379);
+
+ run(function () use ($connection) {
+ $client = new Client('swoole', $connection);
+ go(function () use ($client) {
+ $client->resetStats();
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_string',
+ 'value' => 'lorem ipsum'
+ ]));
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_number',
+ 'value' => 123
+ ]));
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_number',
+ 'value' => 123.456
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_bool',
+ 'value' => true
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_null',
+ 'value' => null
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_array',
+ 'value' => [
+ 1,
+ 2,
+ 3
+ ]
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_assoc',
+ 'value' => [
+ 'string' => 'ipsum',
+ 'number' => 123,
+ 'bool' => true,
+ 'null' => null
+ ]
+ ]));
+
+ $this->assertTrue($client->enqueue([
+ 'type' => 'test_exception'
+ ]));
+
+ sleep(1);
+
+ $this->assertEquals(8, $client->sumTotalJobs());
+ $this->assertEquals(0, $client->sumProcessingJobs());
+ $this->assertEquals(1, $client->sumFailedJobs());
+ $this->assertEquals(7, $client->sumSuccessfulJobs());
+ });
+ });
+ }
+}
diff --git a/tests/Queue/servers/Swoole/Dockerfile b/tests/Queue/servers/Swoole/Dockerfile
new file mode 100644
index 0000000..6aa0843
--- /dev/null
+++ b/tests/Queue/servers/Swoole/Dockerfile
@@ -0,0 +1,8 @@
+FROM phpswoole/swoole:php8.1-alpine
+
+RUN apk add autoconf build-base
+
+RUN set -ex \
+ && pecl update-channels \
+ && pecl install redis-stable \
+ && docker-php-ext-enable redis
\ No newline at end of file
diff --git a/tests/Queue/servers/Swoole/worker.php b/tests/Queue/servers/Swoole/worker.php
new file mode 100644
index 0000000..c27a4e2
--- /dev/null
+++ b/tests/Queue/servers/Swoole/worker.php
@@ -0,0 +1,21 @@
+error(function ($th) {
+ echo $th->getMessage() . PHP_EOL;
+ })
+ ->onStart(function () {
+ echo "Queue Server started". PHP_EOL;
+ })
+ ->onJob(function (Queue\Job $job) {
+ handleRequest($job);
+ })
+ ->start();
diff --git a/tests/Queue/servers/Workerman/Dockerfile b/tests/Queue/servers/Workerman/Dockerfile
new file mode 100644
index 0000000..e78ea0f
--- /dev/null
+++ b/tests/Queue/servers/Workerman/Dockerfile
@@ -0,0 +1,14 @@
+FROM phpswoole/swoole:php8.1-alpine
+
+RUN apk add autoconf build-base
+
+RUN set -ex \
+ && pecl update-channels \
+ && pecl install redis-stable \
+ && docker-php-ext-enable redis
+
+ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
+
+RUN docker-php-ext-configure pcntl --enable-pcntl
+
+RUN docker-php-ext-install pcntl
\ No newline at end of file
diff --git a/tests/Queue/servers/Workerman/worker.php b/tests/Queue/servers/Workerman/worker.php
new file mode 100644
index 0000000..e6bf1d5
--- /dev/null
+++ b/tests/Queue/servers/Workerman/worker.php
@@ -0,0 +1,21 @@
+error(function ($th) {
+ echo $th->getMessage() . PHP_EOL;
+ })
+ ->onStart(function () {
+ echo "Queue Server started". PHP_EOL;
+ })
+ ->onJob(function (Queue\Job $job) {
+ handleRequest($job);
+ })
+ ->start();
diff --git a/tests/Queue/servers/tests.php b/tests/Queue/servers/tests.php
new file mode 100644
index 0000000..2db5613
--- /dev/null
+++ b/tests/Queue/servers/tests.php
@@ -0,0 +1,50 @@
+ $type, 'value' => $value] = $job->getPayload();
+
+ if (empty($job->getTimestamp())) {
+ throw new Exception();
+ }
+
+ switch ($type) {
+ case 'test_string':
+ assert(is_string($value));
+
+ break;
+ case 'test_number':
+ assert(is_numeric($value));
+
+ break;
+ case 'test_bool':
+ assert(is_bool($value));
+
+ break;
+ case 'test_bool':
+ assert(is_null($value));
+
+ break;
+ case 'test_array':
+ assert(is_array($value));
+ assert(count($value) === 3);
+ assert(empty(array_diff([1, 2, 3], $value)));
+
+ break;
+ case 'test_assoc':
+ assert(is_array($value));
+ assert(count($value) === 4);
+ assert($value['string'] === 'ipsum');
+ assert($value['number'] === 123);
+ assert($value['bool'] === true);
+ assert($value['null'] === null);
+
+ break;
+ case 'test_exception':
+ assert(false);
+
+ break;
+ }
+}