diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml
new file mode 100644
index 00000000..2be9fdef
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug.yaml
@@ -0,0 +1,75 @@
+name: "🐛 Bug Report"
+description: "Submit a bug report to help us improve"
+title: "🐛 Bug Report: "
+labels: [bug]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out our bug report form 🙏
+ - type: textarea
+ id: steps-to-reproduce
+ validations:
+ required: true
+ attributes:
+ label: "👟 Reproduction steps"
+ description: "How do you trigger this bug? Please walk us through it step by step."
+ placeholder: "When I ..."
+ - type: textarea
+ id: expected-behavior
+ validations:
+ required: true
+ attributes:
+ label: "👍 Expected behavior"
+ description: "What did you think would happen?"
+ placeholder: "It should ..."
+ - type: textarea
+ id: actual-behavior
+ validations:
+ required: true
+ attributes:
+ label: "👎 Actual Behavior"
+ description: "What did actually happen? Add screenshots, if applicable."
+ placeholder: "It actually ..."
+ - type: textarea
+ id: version
+ attributes:
+ label: "🎲 Version"
+ description: "What version of are you running?"
+ validations:
+ required: true
+ - type: dropdown
+ id: operating-system
+ attributes:
+ label: "💻 Operating system"
+ description: "What OS is your server / device running on?"
+ options:
+ - Linux
+ - MacOS
+ - Windows
+ - Something else
+ validations:
+ required: true
+ - type: textarea
+ id: environment
+ validations:
+ required: false
+ attributes:
+ label: "🧱 Your Environment"
+ description: "Is your environment customized in any way?"
+ placeholder: "I use Cloudflare for ..."
+ - type: checkboxes
+ id: no-duplicate-issues
+ attributes:
+ label: "👀 Have you spent some time to check if this issue has been raised before?"
+ description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
+ options:
+ - label: "I checked and didn't find similar issue"
+ required: true
+ - type: checkboxes
+ id: read-code-of-conduct
+ attributes:
+ label: "🏢 Have you read the Code of Conduct?"
+ options:
+ - label: "I have read the [Code of Conduct](https://github.com/utopia-php/messaging/blob/HEAD/CODE_OF_CONDUCT.md)"
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/documentation.yaml b/.github/ISSUE_TEMPLATE/documentation.yaml
new file mode 100644
index 00000000..141fbe92
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation.yaml
@@ -0,0 +1,32 @@
+name: "📚 Documentation"
+description: "Report an issue related to documentation"
+title: "📚 Documentation: "
+labels: [documentation]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to make our documentation better 🙏
+ - type: textarea
+ id: issue-description
+ validations:
+ required: true
+ attributes:
+ label: "💭 Description"
+ description: "A clear and concise description of what the issue is."
+ placeholder: "Documentation should not ..."
+ - type: checkboxes
+ id: no-duplicate-issues
+ attributes:
+ label: "👀 Have you spent some time to check if this issue has been raised before?"
+ description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
+ options:
+ - label: "I checked and didn't find similar issue"
+ required: true
+ - type: checkboxes
+ id: read-code-of-conduct
+ attributes:
+ label: "🏢 Have you read the Code of Conduct?"
+ options:
+ - label: "I have read the [Code of Conduct](https://github.com/utopia-php/messaging/blob/HEAD/CODE_OF_CONDUCT.md)"
+ required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml
new file mode 100644
index 00000000..88656130
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature.yaml
@@ -0,0 +1,40 @@
+name: 🚀 Feature
+description: "Submit a proposal for a new feature"
+title: "🚀 Feature: "
+labels: [feature]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out our feature request form 🙏
+ - type: textarea
+ id: feature-description
+ validations:
+ required: true
+ attributes:
+ label: "🔖 Feature description"
+ description: "A clear and concise description of what the feature is."
+ placeholder: "You should add ..."
+ - type: textarea
+ id: pitch
+ validations:
+ required: true
+ attributes:
+ label: "🎤 Pitch"
+ description: "Please explain why this feature should be implemented and how it would be used. Add examples, if applicable."
+ placeholder: "In my use-case, ..."
+ - type: checkboxes
+ id: no-duplicate-issues
+ attributes:
+ label: "👀 Have you spent some time to check if this issue has been raised before?"
+ description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
+ options:
+ - label: "I checked and didn't find similar issue"
+ required: true
+ - type: checkboxes
+ id: read-code-of-conduct
+ attributes:
+ label: "🏢 Have you read the Code of Conduct?"
+ options:
+ - label: "I have read the [Code of Conduct](https://github.com/utopia-php/messaging/blob/HEAD/CODE_OF_CONDUCT.md)"
+ required: true
\ No newline at end of file
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..29458712
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,26 @@
+
+
+## What does this PR do?
+
+(Provide a description of what this PR does.)
+
+## Test Plan
+
+(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
+
+## Related PRs and Issues
+
+(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
+
+### Have you read the [Contributing Guidelines on issues](https://github.com/utopia-php/messaging/blob/master/CONTRIBUTING.md)?
+
+(Write your answer here.)
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
new file mode 100644
index 00000000..4ee16844
--- /dev/null
+++ b/.github/workflows/linter.yml
@@ -0,0 +1,18 @@
+name: "Linter"
+
+on: [pull_request]
+jobs:
+ lint:
+ name: Linter
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 2
+ - run: git checkout HEAD^2
+ - name: Run Linter
+ run: |
+ docker run --rm -v $PWD:/app composer sh -c \
+ "composer install --profile --ignore-platform-reqs && composer lint"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000..901e25bf
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,19 @@
+name: "Tests"
+
+on: [pull_request]
+jobs:
+ tests:
+ name: Unit & E2E
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 2
+ - run: git checkout HEAD^2
+ - name: Run Tests
+ run: |
+ docker compose up -d --build
+ sleep 5
+ docker compose exec tests vendor/bin/phpunit
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..24265e89
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea
+vendor
+.phpunit.result.cache
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..19d90e3a
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,25 @@
+FROM composer:2.0 as composer
+
+ARG TESTING=false
+ENV TESTING=$TESTING
+
+WORKDIR /usr/local/src/
+
+COPY composer.lock /usr/local/src/
+COPY composer.json /usr/local/src/
+
+RUN composer install \
+ --ignore-platform-reqs \
+ --optimize-autoloader \
+ --no-plugins \
+ --no-scripts \
+ --prefer-dist
+
+FROM php:8.0-cli-alpine
+
+WORKDIR /usr/local/src/
+
+COPY --from=composer /usr/local/src/vendor /usr/local/src/vendor
+COPY . /usr/local/src/
+
+CMD [ "tail", "-f", "/dev/null" ]
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..3baa3047
--- /dev/null
+++ b/README.md
@@ -0,0 +1,147 @@
+# Utopia Messaging
+
+[![Build Status](https://travis-ci.org/utopia-php/abuse.svg?branch=master)](https://travis-ci.com/utopia-php/database)
+![Total Downloads](https://img.shields.io/packagist/dt/utopia-php/messaging.svg)
+[![Discord](https://img.shields.io/discord/564160730845151244?label=discord)](https://appwrite.io/discord)
+
+Utopia Messaging library is simple and lite library for sending messages using multiple messaging adapters. 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/messaging
+```
+
+## Email
+
+```php
+Hello World'
+);
+
+$messaging = new Sendgrid('YOUR_API_KEY');
+$messaging->send($message);
+
+$messaging = new Mailgun('YOUR_API_KEY', 'YOUR_DOMAIN');
+$messaging->send($message);
+```
+
+## SMS
+
+```php
+send($message);
+
+$messaging = new Telesign('YOUR_USERNAME', 'YOUR_PASSWORD');
+$messaging->send($message);
+```
+
+## Push
+
+```php
+send($message);
+```
+
+## Adapters
+
+> Want to implement any of the missing adapters or have an idea for another? We would love to hear from you! Please check out our [contribution guide](./CONTRIBUTING.md) and [new adapter guide](./docs/add-new-adapter.md) for more information.
+
+### Email
+- [x] [SendGrid](https://sendgrid.com/)
+- [x] [Mailgun](https://www.mailgun.com/)
+- [ ] [Mailjet](https://www.mailjet.com/)
+- [ ] [Mailchimp](https://www.mailchimp.com/)
+- [ ] [Postmark](https://postmarkapp.com/)
+- [ ] [SparkPost](https://www.sparkpost.com/)
+- [ ] [SendinBlue](https://www.sendinblue.com/)
+- [ ] [MailSlurp](https://www.mailslurp.com/)
+- [ ] [ElasticEmail](https://elasticemail.com/)
+- [ ] [SES](https://aws.amazon.com/ses/)
+
+### SMS
+- [x] [Twilio](https://www.twilio.com/)
+- [x] [Twilio Notify](https://www.twilio.com/notify)
+- [x] [Telesign](https://www.telesign.com/)
+- [x] [TextMagic](https://www.textmagic.com/)
+- [x] [Msg91](https://msg91.com/)
+- [x] [Vonage](https://www.vonage.com/)
+- [ ] [Plivo](https://www.plivo.com/)
+- [ ] [Infobip](https://www.infobip.com/)
+- [ ] [Clickatell](https://www.clickatell.com/)
+- [ ] [AfricasTalking](https://africastalking.com/)
+- [ ] [Sinch](https://www.sinch.com/)
+- [ ] [Sms77](https://www.sms77.io/)
+- [ ] [SmsGlobal](https://www.smsglobal.com/)
+
+### Push
+- [x] [FCM](https://firebase.google.com/docs/cloud-messaging)
+- [ ] [APNS](https://developer.apple.com/documentation/usernotifications)
+- [ ] [OneSignal](https://onesignal.com/)
+- [ ] [Pusher](https://pusher.com/)
+- [ ] [WebPush](https://developer.mozilla.org/en-US/docs/Web/API/Push_API)
+- [ ] [UrbanAirship](https://www.urbanairship.com/)
+- [ ] [Pushwoosh](https://www.pushwoosh.com/)
+- [ ] [PushBullet](https://www.pushbullet.com/)
+- [ ] [Pushy](https://pushy.me/)
+
+## System Requirements
+
+Utopia Messaging requires PHP 8.0 or later. We recommend using the latest PHP version whenever possible.
+
+## Tests
+
+To run all unit tests, use the following Docker command:
+
+```bash
+composer test
+```
+
+To run static code analysis, use the following Psalm command:
+
+```bash
+composer lint
+```
+
+## Authors
+
+**Jake Barnby**
+
++ [https://github.com/abnegate](https://github.com/abnegate)
++ [https://nz.linkedin.com/in/jakebarnby](https://nz.linkedin.com/in/jakebarnby)
+
+## Copyright and license
+
+The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php)
diff --git a/composer.json b/composer.json
new file mode 100644
index 00000000..e94220b3
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "utopia-php/messaging",
+ "description": "A simple, light and advanced PHP messaging library",
+ "type": "library",
+ "keywords": ["php","messaging", "upf", "utopia", "utopia-php", "library"],
+ "license": "MIT",
+ "minimum-stability": "stable",
+ "scripts": {
+ "test": "vendor/bin/phpunit",
+ "lint": "vendor/bin/phpcs",
+ "format": "vendor/bin/phpcbf"
+ },
+ "authors": [
+ {
+ "name": "Jake Barnby",
+ "email": "jake@appwrite.io"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Utopia\\Messaging\\": "src/Utopia/Messaging"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\E2E\\": "tests/e2e",
+ "Tests\\Unit\\": "tests/unit"
+ }
+ },
+ "require": {
+ "php": ">=8.0.0",
+ "ext-curl": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "9.5.*",
+ "phpmailer/phpmailer": "6.6.*",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "config": {
+ "platform": {
+ "php": "8.0"
+ }
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 00000000..88f16b81
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1889 @@
+{
+ "_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": "e33a07c2dd4a4d32669369f3e04a7a21",
+ "packages": [],
+ "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": "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.15.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
+ "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
+ "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.15.1"
+ },
+ "time": "2022-09-04T07:30:47+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": "phpmailer/phpmailer",
+ "version": "v6.6.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPMailer/PHPMailer.git",
+ "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b",
+ "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-filter": "*",
+ "ext-hash": "*",
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "doctrine/annotations": "^1.2",
+ "php-parallel-lint/php-console-highlighter": "^1.0.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3.2",
+ "phpcompatibility/php-compatibility": "^9.3.5",
+ "roave/security-advisories": "dev-latest",
+ "squizlabs/php_codesniffer": "^3.6.2",
+ "yoast/phpunit-polyfills": "^1.0.0"
+ },
+ "suggest": {
+ "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
+ "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
+ "league/oauth2-google": "Needed for Google XOAUTH2 authentication",
+ "psr/log": "For optional PSR-3 debug logging",
+ "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
+ "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPMailer\\PHPMailer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1-only"
+ ],
+ "authors": [
+ {
+ "name": "Marcus Bointon",
+ "email": "phpmailer@synchromedia.co.uk"
+ },
+ {
+ "name": "Jim Jagielski",
+ "email": "jimjag@gmail.com"
+ },
+ {
+ "name": "Andy Prevost",
+ "email": "codeworxtech@users.sourceforge.net"
+ },
+ {
+ "name": "Brent R. Matzelle"
+ }
+ ],
+ "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
+ "support": {
+ "issues": "https://github.com/PHPMailer/PHPMailer/issues",
+ "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Synchro",
+ "type": "github"
+ }
+ ],
+ "time": "2022-08-22T09:22:00+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "9.2.17",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8",
+ "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.14",
+ "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.17"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-08-30T12:24:04+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.25",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
+ "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
+ "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",
+ "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.8",
+ "sebastian/diff": "^4.0.3",
+ "sebastian/environment": "^5.1.3",
+ "sebastian/exporter": "^4.0.5",
+ "sebastian/global-state": "^5.0.1",
+ "sebastian/object-enumerator": "^4.0.3",
+ "sebastian/resource-operations": "^3.0.3",
+ "sebastian/type": "^3.2",
+ "sebastian/version": "^3.0.2"
+ },
+ "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.25"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-09-25T03:44:45+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.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a",
+ "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.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-09-14T12:41:17+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.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
+ "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
+ "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.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-09-14T06:03:37+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.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
+ "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "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.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-09-12T14:47:03+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": "squizlabs/php_codesniffer",
+ "version": "3.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619",
+ "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "support": {
+ "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
+ "source": "https://github.com/squizlabs/PHP_CodeSniffer",
+ "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+ },
+ "time": "2022-06-18T07:21:10+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"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=8.0.0",
+ "ext-curl": "*"
+ },
+ "platform-dev": [],
+ "platform-overrides": {
+ "php": "8.0"
+ },
+ "plugin-api-version": "2.3.0"
+}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..96054433
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,20 @@
+version: '3.9'
+
+services:
+ tests:
+ build:
+ context: .
+ volumes:
+ - ./src:/usr/local/src/src
+ - ./tests:/usr/local/src/tests
+ - ./phpunit.xml:/usr/local/src/phpunit.xml
+
+ maildev:
+ image: appwrite/mailcatcher:1.0.0
+ ports:
+ - '10000:1080'
+
+ request-catcher:
+ image: appwrite/requestcatcher:1.0.0
+ ports:
+ - '10001:5000'
\ No newline at end of file
diff --git a/docs/add-new-adapter.md b/docs/add-new-adapter.md
new file mode 100644
index 00000000..a720bbc0
--- /dev/null
+++ b/docs/add-new-adapter.md
@@ -0,0 +1,150 @@
+# Adding A New Messaging Adapter
+
+This document is a part of Utopia PHP contributors guide. Before you continue reading this document make sure you have read the [Code of Conduct](../CODE_OF_CONDUCT.md) and the [Contribution Guide](../CONTRIBUTING.md).
+
+## Getting Started
+
+Messaging adapter allow utilization of different messaging services in a consistent way. This document will guide you through the process of adding a new messaging adapter to the Utopia PHP Messaging library.
+
+## 1. Prerequisites
+
+It's really easy to contribute to an open source project, but when using GitHub, there are a few steps we need to follow. This section will take you step-by-step through the process of preparing your own local version of `utopia-php/messaging`, where you can make any changes without affecting the upstream repository right away.
+
+> If you are experienced with GitHub or have made a pull request before, you can skip to [Implement A New Messaging Adapter](#2-implement-new-messaging-adapter).
+
+### 1.1 Fork The Repository
+
+Before making any changes, you will need to fork the `utopia-php/messaging` repository to keep branches on the upstream repo clean. To do that, visit the [repository](https://github.com/utopia-php/messaging) and click the **Fork** button.
+
+This will redirect you from `github.com/utopia-php/messaging` to `github.com/YOUR_USERNAME/messaging`, meaning all further changes will reflect on your copy of the repository. Once you are there, click the highlighted `Code` button, copy the URL and clone the repository to your computer using either a Git UI or the `git clone` command:
+
+```shell
+$ git clone COPIED_URL
+```
+
+> To fork a repository, you will need the git-cli binaries installed and a basic understanding of CLI. If you are a beginner, we recommend you to use `Github Desktop`. It is a clean and simple visual Git client.
+
+Finally, you will need to create a `feat-XXX-YYY-messaging-adapter` branch based on the `master` branch and switch to it. The `XXX` should represent the issue ID and `YYY` the Storage adapter name.
+
+## 2. Implement A New Messaging Adapter
+
+In order to start implementing a new messaging adapter, add new file inside `src/Utopia/Messaging/Adapters/XXX/YYY.php` where `XXX` is the type of adapter (**Email**, **SMS** or **Push**), and `YYY` is the name of the messaging provider in `PascalCase` casing. Inside the file you should create a class that extends the base `Email`, `SMS` or `Push` abstract adapter class.
+
+Inside the class, you need to implement four methods:
+
+- `__construct()` - This method should accept all the required parameters for the adapter to work. For example, the `SendGrid` adapter requires an API key, so the constructor should look like this:
+
+```php
+public function __construct(private string $apiKey)
+```
+
+- `getName()` - This method should return the display name of the adapter. For example, the `SendGrid` adapter should return `SendGrid`:
+
+```php
+public function getName(): string
+{
+ return 'SendGrid';
+}
+```
+
+- `getMaxMessagesPerRequest()` - This method should return the maximum number of messages that can be sent in a single request. For example, `SendGrid` can send 1000 messages per request, so this method should return 1000:
+
+```php
+public function getMaxMessagesPerRequest(): int
+{
+ return 1000;
+}
+```
+
+- `process()` - This method should accept a message object of the same type as the base adapter, and send it to the messaging provider, returning the response as a string. For example, the `SendGrid` adapter should accept an `Email` message object and send it to the SendGrid API:
+
+```php
+public function process(Email $message): string
+{
+ // Send message to SendGrid API
+}
+```
+
+The base `Adapter` class includes a helper method called `request()` that can be used to send HTTP requests to the messaging provider. It accepts the following parameters, and returns the response as a string:
+
+- `method` - The HTTP method to use. For example, `POST`, `GET`, `PUT`, `PATCH` or `DELETE`.
+- `url` - The URL to send the request to.
+- `headers` - An array of headers to send with the request.
+- `body` - The body of the request. It can be either a string or an array.
+
+The default content type of the request is `x-www-form-urlencoded`, but you can change it by adding a `Content-Type` header. No encoding is applied to the body, so you need to make sure it is encoded properly before sending the request.
+
+Putting it all together, the `SendGrid` adapter should look like this:
+
+### Full Example
+
+```php
+request(
+ method: 'POST',
+ url: 'https://api.sendgrid.com/v3/mail/send',
+ headers: [
+ 'Content-Type: application/json',
+ 'Authorization: Bearer ' . $this->apiKey,
+ ],
+ body: \json_encode([
+ 'personalizations' => [
+ [
+ 'to' => \array_map(
+ fn($to) => ['email' => $to],
+ $message->getTo()
+ ),
+ 'subject' => $message->getSubject(),
+ ],
+ ],
+ 'from' => [
+ 'email' => $message->getFrom(),
+ ],
+ 'content' => [
+ [
+ 'type' => $message->isHtml() ? 'text/html' : 'text/plain',
+ 'value' => $message->getContent(),
+ ],
+ ],
+ ]),
+ );
+ }
+}
+```
+
+## 3. Test your adapter
+
+After you finish adding your new adapter, you need to ensure that it is usable. Use your newly created adapter to make some sample requests to your messaging service.
+
+If everything goes well, raise a pull request and be ready to respond to any feedback which can arise during code review.
+
+## 4. Raise a pull request
+
+First of all, commit the changes with the message `Added YYY Storage adapter` and push it. This will publish a new branch to your forked version of `utopia-php/messaging`. If you visit it at `github.com/YOUR_USERNAME/messaging`, you will see a new alert saying you are ready to submit a pull request. Follow the steps GitHub provides, and at the end, you will have your pull request submitted.
+
+## 🤕 Stuck ?
+If you need any help with the contribution, feel free to head over to [our discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 00000000..c6f058ea
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,11 @@
+
+
+
+ ./src
+ ./tests
+
+
+
+ *
+
+
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 00000000..8db6586a
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,19 @@
+
+
+
+ ./tests/unit
+
+
+ ./tests/e2e/
+
+
+
\ No newline at end of file
diff --git a/src/Utopia/Messaging/Adapter.php b/src/Utopia/Messaging/Adapter.php
new file mode 100644
index 00000000..83422709
--- /dev/null
+++ b/src/Utopia/Messaging/Adapter.php
@@ -0,0 +1,86 @@
+getName()} Message Sender");
+
+ if (!is_null($body)) {
+ \curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ }
+
+ $response = \curl_exec($ch);
+
+ if (\curl_errno($ch)) {
+ throw new \Exception('Error:' . \curl_error($ch));
+ }
+ if (\curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) {
+ throw new \Exception($response);
+ }
+
+ \curl_close($ch);
+
+ return $response;
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/Email.php b/src/Utopia/Messaging/Adapters/Email.php
new file mode 100644
index 00000000..4cfff3fc
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/Email.php
@@ -0,0 +1,43 @@
+getMessageType())) {
+ throw new \Exception('Invalid message type.');
+ }
+ if (\count($message->getTo()) > $this->getMaxMessagesPerRequest()) {
+ throw new \Exception("{$this->getName()} can only send {$this->getMaxMessagesPerRequest()} messages per request.");
+ }
+ return $this->process($message);
+ }
+
+ /**
+ * Process an email message.
+ *
+ * @param EmailMessage $message Message to process.
+ * @return string The response body.
+ */
+ abstract protected function process(EmailMessage $message): string;
+}
diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php
new file mode 100644
index 00000000..1e022f04
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php
@@ -0,0 +1,51 @@
+request(
+ method: 'POST',
+ url: "https://api.mailgun.net/v3/{$this->domain}/messages",
+ headers: [
+ 'Authorization: Basic ' . base64_encode('api:' . $this->apiKey)
+ ],
+ body: [
+ 'from' => $message->getFrom(),
+ 'to' => \implode(',', $message->getTo()),
+ 'subject' => $message->getSubject(),
+ 'text' => $message->isHtml() ? null : $message->getContent(),
+ 'html' => $message->isHtml() ? $message->getContent() : null,
+ ],
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/Email/Mock.php b/src/Utopia/Messaging/Adapters/Email/Mock.php
new file mode 100644
index 00000000..c13a43fa
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/Email/Mock.php
@@ -0,0 +1,58 @@
+isSMTP();
+ $mail->XMailer = 'Utopia Mailer';
+ $mail->Host = 'maildev';
+ $mail->Port = 1025;
+ $mail->SMTPAuth = false;
+ $mail->Username = '';
+ $mail->Password = '';
+ $mail->SMTPSecure = false;
+ $mail->SMTPAutoTLS = false;
+ $mail->CharSet = 'UTF-8';
+ $mail->Subject = $message->getSubject();
+ $mail->Body = $message->getContent();
+ $mail->AltBody = \strip_tags($message->getContent());
+ $mail->setFrom($message->getFrom(), 'Utopia');
+ $mail->addReplyTo($message->getFrom(), 'Utopia');
+ $mail->isHTML($message->isHtml());
+
+ foreach ($message->getTo() as $to) {
+ $mail->addAddress($to);
+ }
+
+ if (!$mail->send()) {
+ throw new \Exception($mail->ErrorInfo);
+ }
+
+ return true;
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/Email/Sendgrid.php b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php
new file mode 100644
index 00000000..dc0ec8e3
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php
@@ -0,0 +1,56 @@
+request(
+ method: 'POST',
+ url: 'https://api.sendgrid.com/v3/mail/send',
+ headers: [
+ 'Authorization: Bearer ' . $this->apiKey,
+ 'Content-Type: application/json',
+ ],
+ body: \json_encode([
+ 'personalizations' => [
+ [
+ 'to' => \array_map(
+ fn($to) => ['email' => $to],
+ $message->getTo()
+ ),
+ 'subject' => $message->getSubject(),
+ ],
+ ],
+ 'from' => [
+ 'email' => $message->getFrom(),
+ ],
+ 'content' => [
+ [
+ 'type' => $message->isHtml() ? 'text/html' : 'text/plain',
+ 'value' => $message->getContent(),
+ ],
+ ],
+ ]),
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/Push.php b/src/Utopia/Messaging/Adapters/Push.php
new file mode 100644
index 00000000..419fc987
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/Push.php
@@ -0,0 +1,43 @@
+getMessageType())) {
+ throw new \Exception('Invalid message type.');
+ }
+ if (\count($message->getTo()) > $this->getMaxMessagesPerRequest()) {
+ throw new \Exception("{$this->getName()} can only send {$this->getMaxMessagesPerRequest()} messages per request.");
+ }
+ return $this->process($message);
+ }
+
+ /**
+ * Send a push message.
+ *
+ * @param PushMessage $message Message to process.
+ * @return string The response body.
+ */
+ abstract protected function process(PushMessage $message): string;
+}
diff --git a/src/Utopia/Messaging/Adapters/Push/FCM.php b/src/Utopia/Messaging/Adapters/Push/FCM.php
new file mode 100644
index 00000000..4205dc8f
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/Push/FCM.php
@@ -0,0 +1,57 @@
+request(
+ method: 'POST',
+ url: 'https://fcm.googleapis.com/fcm/send',
+ headers: [
+ "Content-Type: application/json",
+ "Authorization: key={$this->serverKey}",
+ ],
+ body: \json_encode([
+ 'registration_ids' => $message->getTo(),
+ 'notification' => [
+ 'title' => $message->getTitle(),
+ 'body' => $message->getBody(),
+ 'click_action' => $message->getAction(),
+ 'icon' => $message->getIcon(),
+ 'badge' => $message->getBadge(),
+ 'color' => $message->getColor(),
+ 'sound' => $message->getSound(),
+ 'tag' => $message->getTag(),
+ ],
+ 'data' => $message->getData(),
+ ])
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS.php b/src/Utopia/Messaging/Adapters/SMS.php
new file mode 100644
index 00000000..7b28dff8
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS.php
@@ -0,0 +1,43 @@
+getMessageType())) {
+ throw new \Exception('Invalid message type.');
+ }
+ if (\count($message->getTo()) > $this->getMaxMessagesPerRequest()) {
+ throw new \Exception("{$this->getName()} can only send {$this->getMaxMessagesPerRequest()} messages per request.");
+ }
+ return $this->process($message);
+ }
+
+ /**
+ * Send an SMS message.
+ *
+ * @param SMSMessage $message Message to send.
+ * @return string The response body.
+ */
+ abstract protected function process(SMSMessage $message): string;
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS/Mock.php b/src/Utopia/Messaging/Adapters/SMS/Mock.php
new file mode 100644
index 00000000..8a29890b
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS/Mock.php
@@ -0,0 +1,51 @@
+request(
+ method: 'POST',
+ url: 'http://request-catcher:5000/mock-sms',
+ headers: [
+ "content-type: application/json",
+ "x-username: {$this->user}",
+ "x-key: {$this->secret}",
+ ],
+ body: \json_encode([
+ 'message' => $message->getContent(),
+ 'from' => $message->getFrom(),
+ 'to' => \implode(',', $message->getTo()),
+ ]),
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS/Msg91.php b/src/Utopia/Messaging/Adapters/SMS/Msg91.php
new file mode 100644
index 00000000..a83e143d
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS/Msg91.php
@@ -0,0 +1,60 @@
+ ['mobiles' => \ltrim($to, '+')],
+ $message->getTo()
+ );
+
+ return $this->request(
+ method: 'POST',
+ url: 'https://api.msg91.com/api/v5/flow/',
+ headers: [
+ "content-type: application/json",
+ "authkey: {$this->authKey}",
+ ],
+ body: \json_encode([
+ 'sender' => $this->senderId,
+ 'otp' => $message->getContent(),
+ 'flow_id' => $message->getFrom(),
+ 'recipients' => [$to]
+ ]),
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS/Telesign.php b/src/Utopia/Messaging/Adapters/SMS/Telesign.php
new file mode 100644
index 00000000..7cef0f45
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS/Telesign.php
@@ -0,0 +1,56 @@
+ \ltrim($to, '+'),
+ $message->getTo()
+ );
+
+ return $this->request(
+ method: 'POST',
+ url: 'https://rest-ww.telesign.com/v1/verify/bulk_sms',
+ headers: [
+ 'Authorization: Basic ' . base64_encode("{$this->username}:{$this->password}")
+ ],
+ body: [
+ 'template' => $message->getContent(),
+ 'recipients' => \implode(',', $to)
+ ],
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS/TextMagic.php b/src/Utopia/Messaging/Adapters/SMS/TextMagic.php
new file mode 100644
index 00000000..a78a97dc
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS/TextMagic.php
@@ -0,0 +1,58 @@
+ \ltrim($to, '+'),
+ $message->getTo()
+ );
+
+ return $this->request(
+ method: 'POST',
+ url: 'https://rest.textmagic.com/api/v2/messages',
+ headers: [
+ "X-TM-Username: {$this->username}",
+ "X-TM-Key: {$this->apiKey}",
+ ],
+ body: [
+ 'text' => $message->getContent(),
+ 'from' => \ltrim($message->getFrom(), '+'),
+ 'phones' => \implode(',', $to),
+ ],
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS/Twilio.php b/src/Utopia/Messaging/Adapters/SMS/Twilio.php
new file mode 100644
index 00000000..9a65225a
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS/Twilio.php
@@ -0,0 +1,49 @@
+request(
+ method: 'POST',
+ url: "https://api.twilio.com/2010-04-01/Accounts/{$this->accountSid}/Messages.json",
+ headers: [
+ 'Authorization: Basic ' . base64_encode("{$this->accountSid}:{$this->authToken}")
+ ],
+ body: \http_build_query([
+ 'Body' => $message->getContent(),
+ 'From' => $message->getFrom(),
+ 'To' => $message->getTo()[0]
+ ]),
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS/TwilioNotify.php b/src/Utopia/Messaging/Adapters/SMS/TwilioNotify.php
new file mode 100644
index 00000000..64e07d8c
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS/TwilioNotify.php
@@ -0,0 +1,52 @@
+request(
+ method: 'POST',
+ url: "https://notify.twilio.com/v1/Services/{$this->serviceSid}/Notifications",
+ headers: [
+ 'Authorization: Basic ' . base64_encode("{$this->accountSid}:{$this->authToken}")
+ ],
+ body: \http_build_query([
+ 'Body' => $message->getContent(),
+ 'ToBinding' => \json_encode(\array_map(
+ fn($to) => ['binding_type' => 'sms', 'address' => $to],
+ $message->getTo()
+ )),
+ ]),
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Adapters/SMS/Vonage.php b/src/Utopia/Messaging/Adapters/SMS/Vonage.php
new file mode 100644
index 00000000..598029ea
--- /dev/null
+++ b/src/Utopia/Messaging/Adapters/SMS/Vonage.php
@@ -0,0 +1,56 @@
+ \ltrim($to, '+'),
+ $message->getTo()
+ );
+
+ return $this->request(
+ method: 'POST',
+ url: 'https://rest.nexmo.com/sms/json',
+ body: [
+ 'text' => $message->getContent(),
+ 'from' => $message->getFrom(),
+ 'to' => \implode(',', $to),
+ 'api_key' => $this->apiKey,
+ 'api_secret' => $this->apiSecret
+ ]
+ );
+ }
+}
diff --git a/src/Utopia/Messaging/Message.php b/src/Utopia/Messaging/Message.php
new file mode 100644
index 00000000..3b5e21ae
--- /dev/null
+++ b/src/Utopia/Messaging/Message.php
@@ -0,0 +1,10 @@
+to;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSubject(): string
+ {
+ return $this->subject;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContent(): string
+ {
+ return $this->content;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getFrom(): ?string
+ {
+ return $this->from;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getAttachments(): ?array
+ {
+ return $this->attachments;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isHtml(): bool
+ {
+ return $this->html;
+ }
+}
diff --git a/src/Utopia/Messaging/Messages/Push.php b/src/Utopia/Messaging/Messages/Push.php
new file mode 100644
index 00000000..4a86a05e
--- /dev/null
+++ b/src/Utopia/Messaging/Messages/Push.php
@@ -0,0 +1,114 @@
+
On Apple platforms, if the message is sent via APNs, it represents the custom data fields. If it is sent via FCM, it would be represented as key value dictionary in AppDelegate application:didReceiveRemoteNotification:.
On Android, this would result in an intent extra named score with the string value 3x1.
The key should not be a reserved word ("from", "message_type", or any word starting with "google" or "gcm"). Do not use any of the words defined in this table (such as collapse_key).
Values in string types are recommended. You have to convert values in objects or other non-string data types (e.g., integers or booleans) to string.
+ * @param string|null $sound The sound to play when the device receives the notification.
On Android, sound files must reside in /res/raw/.
On iOS, sounds files must reside in the main bundle of the client app or in the Library/Sounds folder of the app's data container.
+ * @param string|null $action The action associated with a user click on the notification.
On Android, this is the activity to launch.
On iOS, this is the category to launch.
+ * @param string|null $icon Android only. The icon of the push notification. Sets the notification icon to myicon for drawable resource myicon. If you don't send this key in the request, FCM displays the launcher icon specified in your app manifest.
+ * @param string|null $color Android only. The icon color of the push notification, expressed in #rrggbb format.
+ * @param string|null $tag Android only. Identifier used to replace existing notifications in the notification drawer.
If not specified, each request creates a new notification.
If specified and a notification with the same tag is already being shown, the new notification replaces the existing one in the notification drawer.
+ * @param string|null $badge iOS only. The value of the badge on the home screen app icon. If not specified, the badge is not changed. If set to 0, the badge is removed.
+ */
+ public function __construct(
+ private array $to,
+ private string $title,
+ private string $body,
+ private ?array $data = null,
+ private ?string $action = null,
+ private ?string $sound = null,
+ private ?string $icon = null,
+ private ?string $color = null,
+ private ?string $tag = null,
+ private ?string $badge = null,
+ ) {
+ }
+
+ /**
+ * @return array
+ */
+ public function getTo(): array
+ {
+ return $this->to;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBody(): string
+ {
+ return $this->body;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getData(): ?array
+ {
+ return $this->data;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getAction(): ?string
+ {
+ return $this->action;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getSound(): ?string
+ {
+ return $this->sound;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getIcon(): ?string
+ {
+ return $this->icon;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getColor(): ?string
+ {
+ return $this->color;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getTag(): ?string
+ {
+ return $this->tag;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getBadge(): ?string
+ {
+ return $this->badge;
+ }
+}
diff --git a/src/Utopia/Messaging/Messages/SMS.php b/src/Utopia/Messaging/Messages/SMS.php
new file mode 100644
index 00000000..bcb913c0
--- /dev/null
+++ b/src/Utopia/Messaging/Messages/SMS.php
@@ -0,0 +1,48 @@
+to;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContent(): string
+ {
+ return $this->content;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getFrom(): ?string
+ {
+ return $this->from;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getAttachments(): ?array
+ {
+ return $this->attachments;
+ }
+}
diff --git a/tests/e2e/Base.php b/tests/e2e/Base.php
new file mode 100644
index 00000000..14970e51
--- /dev/null
+++ b/tests/e2e/Base.php
@@ -0,0 +1,31 @@
+send($message);
+
+ $lastEmail = $this->getLastEmail();
+
+ $this->assertEquals($to, $lastEmail['to'][0]['address']);
+ $this->assertEquals($from, $lastEmail['from'][0]['address']);
+ $this->assertEquals($subject, $lastEmail['subject']);
+ $this->assertEquals($content, \trim($lastEmail['text']));
+ }
+}
diff --git a/tests/e2e/SMS/SMSTest.php b/tests/e2e/SMS/SMSTest.php
new file mode 100644
index 00000000..b7cdb8be
--- /dev/null
+++ b/tests/e2e/SMS/SMSTest.php
@@ -0,0 +1,35 @@
+send($message);
+
+ $smsRequest = $this->getLastRequest();
+
+ $this->assertEquals('http://request-catcher:5000/mock-sms', $smsRequest['url']);
+ $this->assertEquals('Appwrite Mock Message Sender', $smsRequest['headers']['User-Agent']);
+ $this->assertEquals('username', $smsRequest['headers']['X-Username']);
+ $this->assertEquals('password', $smsRequest['headers']['X-Key']);
+ $this->assertEquals('POST', $smsRequest['method']);
+ $this->assertEquals('+987654321', $smsRequest['data']['from']);
+ $this->assertEquals('+123456789', $smsRequest['data']['to']);
+ }
+}
diff --git a/tests/unit/.gitkeep b/tests/unit/.gitkeep
new file mode 100644
index 00000000..e69de29b