diff --git a/Dockerfile b/Dockerfile index 5bc1e5e3..f236eff1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN composer install \ --no-scripts \ --prefer-dist -FROM php:8.3.11-cli-alpine3.20 +FROM appwrite/utopia-base:php-8.5-2.1.0 WORKDIR /usr/local/src/ diff --git a/composer.json b/composer.json index 261c6cc1..5299e752 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "test": "./vendor/bin/phpunit", "lint": "./vendor/bin/pint --preset psr12 --test", "format": "./vendor/bin/pint --preset psr12", - "analyse": "./vendor/bin/phpstan analyse --memory-limit=2G --level=6 src tests" + "analyse": "./vendor/bin/phpstan analyse --memory-limit=2G" }, "autoload": { "psr-4": { @@ -22,21 +22,25 @@ } }, "require": { - "php": ">=8.1.0", + "php": ">=8.5.0", "ext-curl": "*", "ext-openssl": "*", - "phpmailer/phpmailer": "6.9.1", - "giggsey/libphonenumber-for-php-lite": "9.0.23", + "ext-swoole": "*", + "phpmailer/phpmailer": "^6.9", + "psr/http-client": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "utopia-php/client": "^0.2", "utopia-php/telemetry": "^0.4" }, "require-dev": { - "phpunit/phpunit": "11.*", - "laravel/pint": "1.*", - "phpstan/phpstan": "1.*" + "phpunit/phpunit": "^11.0", + "laravel/pint": "^1.0", + "phpstan/phpstan": "^2.0", + "swoole/ide-helper": "^6.0" }, "config": { "platform": { - "php": "8.3" + "php": "8.5" }, "allow-plugins": { "php-http/discovery": true, diff --git a/composer.lock b/composer.lock index ba1f9046..7ad8ac66 100644 --- a/composer.lock +++ b/composer.lock @@ -4,27 +4,26 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "94123f2a566774952009659b7e6f241f", + "content-hash": "bb61dfe720e1e98b65f2ccefdfc28d41", "packages": [ { "name": "brick/math", - "version": "0.14.8", + "version": "0.18.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" + "reference": "82944324d1c1bdb2c2618e89978d4e2ad78d69ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", + "url": "https://api.github.com/repos/brick/math/zipball/82944324d1c1bdb2c2618e89978d4e2ad78d69ad", + "reference": "82944324d1c1bdb2c2618e89978d4e2ad78d69ad", "shasum": "" }, "require": { "php": "^8.2" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.2", "phpstan/phpstan": "2.1.22", "phpunit/phpunit": "^11.5" }, @@ -56,7 +55,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.8" + "source": "https://github.com/brick/math/tree/0.18.0" }, "funding": [ { @@ -64,7 +63,7 @@ "type": "github" } ], - "time": "2026-02-10T14:33:43+00:00" + "time": "2026-06-14T18:21:03+00:00" }, { "name": "composer/semver", @@ -143,84 +142,6 @@ ], "time": "2025-08-20T19:15:30+00:00" }, - { - "name": "giggsey/libphonenumber-for-php-lite", - "version": "9.0.23", - "source": { - "type": "git", - "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git", - "reference": "e8f6f896c1bef398136849934a934904bd9bf072" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/e8f6f896c1bef398136849934a934904bd9bf072", - "reference": "e8f6f896c1bef398136849934a934904bd9bf072", - "shasum": "" - }, - "require": { - "php": "^8.1", - "symfony/polyfill-mbstring": "^1.17" - }, - "conflict": { - "giggsey/libphonenumber-for-php": "*" - }, - "require-dev": { - "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^3.71", - "infection/infection": "^0.29|^0.31.0", - "nette/php-generator": "^4.1", - "php-coveralls/php-coveralls": "^2.7", - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.7", - "phpstan/phpstan-deprecation-rules": "^2.0.1", - "phpstan/phpstan-phpunit": "^2.0.4", - "phpstan/phpstan-strict-rules": "^2.0.3", - "phpunit/phpunit": "^10.5.45", - "symfony/console": "^6.4", - "symfony/filesystem": "^6.4", - "symfony/process": "^6.4" - }, - "suggest": { - "giggsey/libphonenumber-for-php": "Use libphonenumber-for-php for geocoding, carriers, timezones and matching" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.x-dev" - } - }, - "autoload": { - "psr-4": { - "libphonenumber\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Joshua Gigg", - "email": "giggsey@gmail.com", - "homepage": "https://giggsey.com/" - } - ], - "description": "A lite version of giggsey/libphonenumber-for-php, which is a PHP Port of Google's libphonenumber", - "homepage": "https://github.com/giggsey/libphonenumber-for-php-lite", - "keywords": [ - "geocoding", - "geolocation", - "libphonenumber", - "mobile", - "phonenumber", - "validation" - ], - "support": { - "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues", - "source": "https://github.com/giggsey/libphonenumber-for-php-lite" - }, - "time": "2026-01-30T12:33:03+00:00" - }, { "name": "google/protobuf", "version": "v4.33.6", @@ -900,16 +821,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.9.1", + "version": "v6.12.0", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18" + "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/039de174cd9c17a8389754d3b877a2ed22743e18", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d1ac35d784bf9f5e61b424901d5a014967f15b12", + "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12", "shasum": "" }, "require": { @@ -969,7 +890,7 @@ "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.9.1" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.12.0" }, "funding": [ { @@ -977,7 +898,7 @@ "type": "github" } ], - "time": "2023-11-25T22:23:28+00:00" + "time": "2025-10-15T16:49:08+00:00" }, { "name": "psr/container", @@ -1320,20 +1241,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.2", + "version": "4.9.3", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "8429c78ca35a09f27565311b98101e2826affde0" + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", - "reference": "8429c78ca35a09f27565311b98101e2826affde0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/1df15849d00943a67d677dc9cfd80795f038c9f8", + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8", "shasum": "" }, "require": { - "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "brick/math": ">=0.8.16 <=0.18", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -1392,22 +1313,22 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.2" + "source": "https://github.com/ramsey/uuid/tree/4.9.3" }, - "time": "2025-12-14T04:43:48+00:00" + "time": "2026-06-18T03:57:49+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -1445,7 +1366,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -1465,20 +1386,20 @@ "type": "tidelift" } ], - "time": "2026-04-13T15:52:40+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/http-client", - "version": "v7.4.13", + "version": "v7.4.14", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "e8a112b8415707265a7e614278136a9d92989a6a" + "reference": "f6bc6b5a54ff5afac4725cacec9bf2f52eb15920" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/e8a112b8415707265a7e614278136a9d92989a6a", - "reference": "e8a112b8415707265a7e614278136a9d92989a6a", + "url": "https://api.github.com/repos/symfony/http-client/zipball/f6bc6b5a54ff5afac4725cacec9bf2f52eb15920", + "reference": "f6bc6b5a54ff5afac4725cacec9bf2f52eb15920", "shasum": "" }, "require": { @@ -1546,7 +1467,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.4.13" + "source": "https://github.com/symfony/http-client/tree/v7.4.14" }, "funding": [ { @@ -1566,20 +1487,20 @@ "type": "tidelift" } ], - "time": "2026-05-24T09:57:54+00:00" + "time": "2026-06-16T11:50:14+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d" + "reference": "41fc42d276aeff21192465331ebbab7d83a743c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d", - "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41fc42d276aeff21192465331ebbab7d83a743c0", + "reference": "41fc42d276aeff21192465331ebbab7d83a743c0", "shasum": "" }, "require": { @@ -1628,7 +1549,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.1" }, "funding": [ { @@ -1648,7 +1569,7 @@ "type": "tidelift" } ], - "time": "2026-03-06T13:17:50+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -1897,16 +1818,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { @@ -1960,7 +1881,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, "funding": [ { @@ -1980,7 +1901,7 @@ "type": "tidelift" } ], - "time": "2026-03-28T09:44:51+00:00" + "time": "2026-06-16T09:55:08+00:00" }, { "name": "tbachert/spi", @@ -2034,22 +1955,217 @@ }, "time": "2025-06-29T15:42:06+00:00" }, + { + "name": "utopia-php/client", + "version": "0.2.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/client.git", + "reference": "377dc1f79441ac1584f25dba57904ee1cf0f626b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/client/zipball/377dc1f79441ac1584f25dba57904ee1cf0f626b", + "reference": "377dc1f79441ac1584f25dba57904ee1cf0f626b", + "shasum": "" + }, + "require": { + "php": ">=8.5", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "utopia-php/pools": "^1.0", + "utopia-php/psr7": "^0.2", + "utopia-php/span": "^1.1 || ^3.0 || ^4.0" + }, + "require-dev": { + "swoole/ide-helper": "^6.0" + }, + "suggest": { + "ext-curl": "Required to use the cURL HTTP client adapter.", + "ext-simplexml": "Required to decode XML responses with Response::xml().", + "ext-swoole": "Required to use the Swoole coroutine HTTP client adapter." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A lightweight PSR-18 HTTP client with cURL and Swoole coroutine backends", + "keywords": [ + "client", + "curl", + "http", + "php", + "psr-18", + "swoole", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/client/issues", + "source": "https://github.com/utopia-php/client/tree/0.2.3" + }, + "time": "2026-07-13T15:29:09+00:00" + }, + { + "name": "utopia-php/pools", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "13346168ca7097d9b220872a017c9ad8c5dac62f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/13346168ca7097d9b220872a017c9ad8c5dac62f", + "reference": "13346168ca7097d9b220872a017c9ad8c5dac62f", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "utopia-php/telemetry": "^0.4" + }, + "require-dev": { + "swoole/ide-helper": "6.*" + }, + "suggest": { + "ext-swoole": "Required to use the Swoole pool adapter" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/1.0.9" + }, + "time": "2026-06-26T10:48:18+00:00" + }, + { + "name": "utopia-php/psr7", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/psr7.git", + "reference": "115753c36194d53abe5587d383810724ac3a782d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/psr7/zipball/115753c36194d53abe5587d383810724ac3a782d", + "reference": "115753c36194d53abe5587d383810724ac3a782d", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-simplexml": "Required to decode XML responses with Response::xml()." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Psr7\\": "src/Psr7/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PSR-7 HTTP message implementations and PSR-17 factories for Utopia", + "keywords": [ + "http", + "php", + "psr-17", + "psr-7", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/psr7/issues", + "source": "https://github.com/utopia-php/psr7/tree/0.2.0" + }, + "time": "2026-07-06T12:40:23+00:00" + }, + { + "name": "utopia-php/span", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/span.git", + "reference": "d11f2714324cb22b286b2afbf3ea9de32c68de83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/span/zipball/d11f2714324cb22b286b2afbf3ea9de32c68de83", + "reference": "d11f2714324cb22b286b2afbf3ea9de32c68de83", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "swoole/ide-helper": "^5.0" + }, + "suggest": { + "ext-swoole": "Required for coroutine-based storage" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Span\\": "src/Span/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Simple span tracing library for PHP with coroutine support", + "support": { + "issues": "https://github.com/utopia-php/span/issues", + "source": "https://github.com/utopia-php/span/tree/4.0.1" + }, + "time": "2026-06-20T09:45:06+00:00" + }, { "name": "utopia-php/telemetry", - "version": "0.4.1", + "version": "0.4.5", "source": { "type": "git", "url": "https://github.com/utopia-php/telemetry.git", - "reference": "028f3a85bb987b1e9357fb1632aa40f020d1b86e" + "reference": "139943bffcd4f6dd8fb9ed247f946a1d151b006a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/028f3a85bb987b1e9357fb1632aa40f020d1b86e", - "reference": "028f3a85bb987b1e9357fb1632aa40f020d1b86e", + "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/139943bffcd4f6dd8fb9ed247f946a1d151b006a", + "reference": "139943bffcd4f6dd8fb9ed247f946a1d151b006a", "shasum": "" }, "require": { - "ext-opentelemetry": "*", "ext-protobuf": "*", "nyholm/psr7": "1.*", "open-telemetry/exporter-otlp": "1.*", @@ -2058,7 +2174,6 @@ "symfony/http-client": "7.*" }, "require-dev": { - "phpbench/phpbench": "1.*", "swoole/ide-helper": "6.*" }, "suggest": { @@ -2083,24 +2198,24 @@ ], "support": { "issues": "https://github.com/utopia-php/telemetry/issues", - "source": "https://github.com/utopia-php/telemetry/tree/0.4.1" + "source": "https://github.com/utopia-php/telemetry/tree/0.4.5" }, - "time": "2026-06-10T15:48:26+00:00" + "time": "2026-07-08T11:07:25+00:00" } ], "packages-dev": [ { "name": "laravel/pint", - "version": "v1.29.0", + "version": "v1.29.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "bdec963f53172c5e36330f3a400604c69bf02d39" + "reference": "da1d1111a6aa2e082d2a388b194afe1ba0a05d14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/bdec963f53172c5e36330f3a400604c69bf02d39", - "reference": "bdec963f53172c5e36330f3a400604c69bf02d39", + "url": "https://api.github.com/repos/laravel/pint/zipball/da1d1111a6aa2e082d2a388b194afe1ba0a05d14", + "reference": "da1d1111a6aa2e082d2a388b194afe1ba0a05d14", "shasum": "" }, "require": { @@ -2111,14 +2226,14 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.94.2", - "illuminate/view": "^12.54.1", - "larastan/larastan": "^3.9.3", - "laravel-zero/framework": "^12.0.5", + "friendsofphp/php-cs-fixer": "^3.95.8", + "illuminate/view": "^12.62.0", + "larastan/larastan": "^3.10.0", + "laravel-zero/framework": "^12.1.0", + "laravel/agent-detector": "^2.0.2", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.4.0", - "pestphp/pest": "^3.8.6", - "shipfastlabs/agent-detector": "^1.1.0" + "pestphp/pest": "^3.8.6" }, "bin": [ "builds/pint" @@ -2155,7 +2270,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2026-03-12T15:51:39+00:00" + "time": "2026-06-16T15:34:04+00:00" }, { "name": "myclabs/deep-copy", @@ -2219,20 +2334,19 @@ }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v5.8.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-json": "*", "ext-tokenizer": "*", "php": ">=7.4" @@ -2271,9 +2385,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2026-07-04T14:30:18+00:00" }, { "name": "phar-io/manifest", @@ -2395,15 +2509,15 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.33", + "version": "2.2.5", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1", - "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/909c1e5fef7989ac0d0c1c5c42e32a5c4f6198a0", + "reference": "909c1e5fef7989ac0d0c1c5c42e32a5c4f6198a0", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -2422,6 +2536,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", @@ -2444,7 +2569,7 @@ "type": "github" } ], - "time": "2026-02-28T20:30:03+00:00" + "time": "2026-07-05T06:31:06+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2795,24 +2920,24 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.55", + "version": "11.5.56", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f83edffa6967c3db468d48a695ec7bcb02e9256", + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256", "shasum": "" }, "require": { "ext-dom": "*", + "ext-filter": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", - "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", @@ -2877,31 +3002,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.56" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "url": "https://phpunit.de/sponsoring.html", + "type": "other" } ], - "time": "2026-02-18T12:37:06+00:00" + "time": "2026-07-06T14:52:39+00:00" }, { "name": "sebastian/cli-parser", @@ -3941,6 +4050,38 @@ ], "time": "2024-10-20T05:08:20+00:00" }, + { + "name": "swoole/ide-helper", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "6f12243dce071714c5febe059578d909698f9a52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/6f12243dce071714c5febe059578d909698f9a52", + "reference": "6f12243dce071714c5febe059578d909698f9a52", + "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/6.0.2" + }, + "time": "2025-03-23T07:31:41+00:00" + }, { "name": "theseer/tokenizer", "version": "1.3.1", @@ -3998,13 +4139,14 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.1.0", + "php": ">=8.5.0", "ext-curl": "*", - "ext-openssl": "*" + "ext-openssl": "*", + "ext-swoole": "*" }, "platform-dev": {}, "platform-overrides": { - "php": "8.3" + "php": "8.5" }, "plugin-api-version": "2.9.0" } diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..954472e6 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,7 @@ +parameters: + level: 6 + paths: + - src + - tests + scanDirectories: + - vendor/swoole/ide-helper diff --git a/src/Utopia/Messaging/Adapter.php b/src/Utopia/Messaging/Adapter.php index 3cc63f3b..33c8f6e1 100644 --- a/src/Utopia/Messaging/Adapter.php +++ b/src/Utopia/Messaging/Adapter.php @@ -2,17 +2,53 @@ namespace Utopia\Messaging; +use Closure; use Exception; -use libphonenumber\PhoneNumberUtil; +use JsonException; +use Psr\Http\Client\ClientExceptionInterface; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Swoole\Coroutine; +use Swoole\Coroutine\WaitGroup; +use Utopia\Client; +use Utopia\Client\Adapter\Curl\Client as CurlAdapter; +use Utopia\Pools\Adapter\Swoole as SwoolePoolAdapter; +use Utopia\Pools\Pool as ConnectionPool; +use Utopia\Psr7\Request\Factory as RequestFactory; use Utopia\Telemetry\Adapter as Telemetry; use Utopia\Telemetry\Adapter\None as NoTelemetry; use Utopia\Telemetry\Counter; abstract class Adapter { + /** + * Upper bound on the connection pool size used by requestMulti(). + */ + private const int MAX_CONCURRENT_REQUESTS = 25; + + /** + * Name of the connection pool used by requestMulti(). + */ + private const string CONNECTION_POOL_NAME = 'messaging'; + + /** + * Counter tracking sent messages, labelled by result, type and provider. + */ private Counter $sendCounter; - public function __construct(?Telemetry $telemetry = null) + /** + * @param Telemetry|null $telemetry Telemetry adapter to record metrics with; defaults to a no-op adapter. + * @param (Closure(): ClientInterface)|null $clientFactory Factory producing the PSR-18 clients + * used for HTTP requests — called once per request() and once per pooled requestMulti() + * connection, so it must return a new (or safely shareable) client on each call. Defaults + * to utopia-php/client's cURL adapter configured for HTTP/2 with the request()/requestMulti() + * timeouts applied. A custom factory owns its own timeout configuration, and its clients + * must be able to negotiate HTTP/2 for push adapters — APNs rejects HTTP/1.1 connections, + * so a bare `new Client(new CurlAdapter())` will not work; configure the adapter with + * `new CurlAdapter(options: [CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0])`. + */ + public function __construct(?Telemetry $telemetry = null, private ?Closure $clientFactory = null) { $this->sendCounter = ($telemetry ?? new NoTelemetry())->createCounter('messaging.send'); } @@ -146,11 +182,11 @@ private function recordResponse(Message $message, array $response): void * statusCode: int, * response: array|string|null, * headers: array, - * error: string|null, + * error: string, * errorCode: int * } * - * @throws \Exception If the request fails. + * @throws Exception If the request fails. */ protected function request( string $method, @@ -160,67 +196,31 @@ protected function request( int $timeout = 30, int $connectTimeout = 10 ): array { - $ch = \curl_init(); - - foreach ($headers as $header) { - if (\str_contains($header, 'application/json')) { - $body = \json_encode($body); - break; - } - if (\str_contains($header, 'application/x-www-form-urlencoded')) { - $body = \http_build_query($body); - break; - } - } - - if (!\is_null($body)) { - \curl_setopt($ch, CURLOPT_POSTFIELDS, $body); - - if (\is_string($body)) { - $headers[] = 'Content-Length: '.\strlen($body); - } - } - - $responseHeaders = []; - - \curl_setopt_array($ch, [ - CURLOPT_CUSTOMREQUEST => $method, - CURLOPT_URL => $url, - CURLOPT_HTTPHEADER => $headers, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_USERAGENT => "Appwrite {$this->getName()} Message Sender", - CURLOPT_TIMEOUT => $timeout, - CURLOPT_CONNECTTIMEOUT => $connectTimeout, - CURLOPT_HEADERFUNCTION => function ($ch, string $header) use (&$responseHeaders): int { - $parts = \explode(':', $header, 2); - if (\count($parts) === 2) { - $responseHeaders[\strtolower(\trim($parts[0]))] = \trim($parts[1]); - } + $client = $this->clientFactory !== null + ? ($this->clientFactory)() + : $this->defaultClient($timeout, $connectTimeout); - return \strlen($header); - }, - ]); - - $response = \curl_exec($ch); + $request = $this->buildRequest($method, $url, $headers, $body); try { - $response = \json_decode($response, true, flags: JSON_THROW_ON_ERROR); - } catch (\JsonException) { - // Ignore + $response = $client->sendRequest($request); + } catch (ClientExceptionInterface $error) { + return [ + 'url' => $url, + 'statusCode' => 0, + 'response' => null, + 'headers' => [], + 'error' => $error->getMessage(), + 'errorCode' => $error->getCode(), + ]; } - return [ - 'url' => $url, - 'statusCode' => \curl_getinfo($ch, CURLINFO_RESPONSE_CODE), - 'response' => $response, - 'headers' => $responseHeaders, - 'error' => \curl_error($ch), - 'errorCode' => \curl_errno($ch), - ]; + return $this->buildResult($response, $url); } /** - * Send multiple concurrent HTTP requests using HTTP/2 multiplexing. + * Send multiple concurrent HTTP requests using Swoole coroutines over a + * bounded connection pool. * * @param array $urls * @param array $headers @@ -231,7 +231,7 @@ protected function request( * statusCode: int, * response: array|string|null, * headers: array, - * error: string|null, + * error: string, * errorCode: int * }> * @@ -249,40 +249,6 @@ protected function requestMulti( throw new \Exception('No URLs provided. Must provide at least one URL.'); } - foreach ($headers as $header) { - if (\str_contains($header, 'application/json')) { - foreach ($bodies as $i => $body) { - $bodies[$i] = \json_encode($body); - } - break; - } - if (\str_contains($header, 'application/x-www-form-urlencoded')) { - foreach ($bodies as $i => $body) { - $bodies[$i] = \http_build_query($body); - } - break; - } - } - - $sh = \curl_share_init(); - $mh = \curl_multi_init(); - $ch = \curl_init(); - - \curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); - \curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); - - \curl_setopt_array($ch, [ - CURLOPT_SHARE => $sh, - CURLOPT_CUSTOMREQUEST => $method, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0, - CURLOPT_HTTPHEADER => $headers, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FORBID_REUSE => false, - CURLOPT_FRESH_CONNECT => false, - CURLOPT_TIMEOUT => $timeout, - CURLOPT_CONNECTTIMEOUT => $connectTimeout, - ]); - $urlCount = \count($urls); $bodyCount = \count($bodies); @@ -296,85 +262,167 @@ protected function requestMulti( $urls = \array_pad($urls, $bodyCount, $urls[0]); } - for ($i = 0; $i < \count($urls); $i++) { - if (!empty($bodies[$i])) { - $headers[] = 'Content-Length: '.\strlen($bodies[$i]); - } - - \curl_setopt($ch, CURLOPT_URL, $urls[$i]); - \curl_setopt($ch, CURLOPT_POSTFIELDS, $bodies[$i]); - \curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - \curl_setopt($ch, CURLOPT_PRIVATE, $i); - \curl_multi_add_handle($mh, \curl_copy_handle($ch)); + $requests = []; + foreach ($urls as $i => $url) { + $requests[$i] = $this->buildRequest($method, $url, $headers, $bodies[$i] ?? null); } - $active = true; - do { - $status = \curl_multi_exec($mh, $active); - - if ($active) { - \curl_multi_select($mh); + $results = []; + + $run = function () use ($requests, $timeout, $connectTimeout, &$results): void { + $pool = new ConnectionPool( + pool: new SwoolePoolAdapter(), + name: self::CONNECTION_POOL_NAME, + size: \min(\count($requests), self::MAX_CONCURRENT_REQUESTS), + init: $this->clientFactory ?? $this->defaultClient($timeout, $connectTimeout)->withConnectionReuse(...), + ); + + $group = new WaitGroup(); + + foreach ($requests as $index => $request) { + $group->add(); + + Coroutine::create(function () use ($pool, $request, $index, &$results, $group): void { + try { + $results[$index] = $pool->use(fn (ClientInterface $client): array => $this->buildResult($client->sendRequest($request), (string)$request->getUri())); + } catch (\Throwable $error) { + // Throwable rather than the PSR client exception: pool + // acquisition and factory failures must also land in + // this slot's result — an uncaught throwable in a + // coroutine is fatal and would drop the slot entirely. + $results[$index] = [ + 'url' => (string)$request->getUri(), + 'statusCode' => 0, + 'response' => null, + 'headers' => [], + 'error' => $error->getMessage(), + 'errorCode' => (int)$error->getCode(), + ]; + } finally { + $group->done(); + } + }); } - } while ($active && $status == CURLM_OK); + + $group->wait(); + }; + + // Fan out directly when already inside a coroutine runtime (e.g. + // Swoole servers/workers); otherwise bootstrap a scheduler for the + // duration of the batch. + if (Coroutine::getCid() > 0) { + $run(); + } else { + \Swoole\Coroutine\run($run); + } $responses = []; + foreach ($results as $index => $result) { + $responses[] = ['index' => $index] + $result; + } - // Check each handle's result - while ($info = \curl_multi_info_read($mh)) { - $ch = $info['handle']; + return $responses; + } + + /** + * Build the default HTTP client used when none was injected. + * + * cURL rather than Swoole's HTTP client: APNs only accepts HTTP/2, which + * Coroutine\Http\Client cannot negotiate (curl falls back to HTTP/1.1 for + * servers without it). Swoole's native-curl hook keeps requestMulti() + * sends concurrent per coroutine. + */ + private function defaultClient(int $timeout, int $connectTimeout): Client + { + return new Client(new CurlAdapter(options: [CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0])) + ->withTimeout((float)$timeout) + ->withConnectTimeout((float)$connectTimeout); + } - $response = \curl_multi_getcontent($ch); + /** + * Build a PSR-7 request, encoding the body based on the request headers: + * JSON, form-urlencoded, or multipart/form-data (mirroring curl's + * handling of array CURLOPT_POSTFIELDS) in that order of precedence. + * + * @param array $headers Headers as "Name: value" strings. + * @param array|null $body + */ + private function buildRequest(string $method, string $url, array $headers, ?array $body): RequestInterface + { + $factory = new RequestFactory(); - try { - $response = \json_decode($response, true, flags: JSON_THROW_ON_ERROR); - } catch (\JsonException) { - // Ignore + $headerMap = []; + foreach ($headers as $header) { + $parts = \explode(':', $header, 2); + if (\count($parts) === 2) { + $headerMap[\trim($parts[0])] = \trim($parts[1]); } + } - $responses[] = [ - 'index' => (int)\curl_getinfo($ch, CURLINFO_PRIVATE), - 'url' => \curl_getinfo($ch, CURLINFO_EFFECTIVE_URL), - 'statusCode' => \curl_getinfo($ch, CURLINFO_RESPONSE_CODE), - 'response' => $response, - // Kept in sync with request()'s shape. Response headers are not - // captured here: this path copies a configured handle with - // curl_copy_handle(), and copying a handle that carries a - // CURLOPT_HEADERFUNCTION closure segfaults. Wire up per-handle - // capture (without copy_handle) if a multi-path adapter needs it. - 'headers' => [], - 'error' => \curl_error($ch), - 'errorCode' => $info['result'], - ]; + // On the request rather than the client so injected PSR-18 clients + // send the same identity. + if (!\array_any(\array_keys($headerMap), fn (string $name): bool => \strtolower($name) === 'user-agent')) { + $headerMap['User-Agent'] = "Appwrite {$this->getName()} Message Sender"; + } - \curl_multi_remove_handle($mh, $ch); + if ($body === null) { + return $factory->query($method, $url, [], $headerMap); } - \curl_share_close($sh); + foreach ($headers as $header) { + if (\str_contains($header, 'application/json')) { + return $factory->json($method, $url, $body, $headerMap); + } + if (\str_contains($header, 'application/x-www-form-urlencoded')) { + return $factory->form($method, $url, $body, $headerMap); + } + } - return $responses; - } + // Drop any bare multipart Content-Type so the factory can set one + // carrying the boundary. + foreach (\array_keys($headerMap) as $name) { + if (\strtolower($name) === 'content-type') { + unset($headerMap[$name]); + } + } + return $factory->multipart($method, $url, $body, $headerMap); + } /** - * @param string $phone - * @return int|null - * @throws Exception + * Map a PSR-7 response to the array shape adapters consume. + * + * @return array{ + * url: string, + * statusCode: int, + * response: array|string|null, + * headers: array, + * error: string, + * errorCode: int + * } */ - public function getCountryCode(string $phone): ?int + private function buildResult(ResponseInterface $response, string $url): array { - if (empty($phone)) { - throw new Exception('$phone cannot be empty.'); - } - - $helper = PhoneNumberUtil::getInstance(); + $body = (string)$response->getBody(); try { - return $helper - ->parse($phone) - ->getCountryCode(); + $body = \json_decode($body, true, flags: JSON_THROW_ON_ERROR); + } catch (JsonException) { + // Ignore + } - } catch (\Throwable $th) { - throw new Exception("Error parsing phone: " . $th->getMessage()); + $headers = []; + foreach (\array_keys($response->getHeaders()) as $name) { + $headers[\strtolower((string)$name)] = $response->getHeaderLine((string)$name); } + + return [ + 'url' => $url, + 'statusCode' => $response->getStatusCode(), + 'response' => $body, + 'headers' => $headers, + 'error' => '', + 'errorCode' => 0, + ]; } } diff --git a/src/Utopia/Messaging/Adapter/Email/Mailgun.php b/src/Utopia/Messaging/Adapter/Email/Mailgun.php index c166f7b1..77c6042e 100644 --- a/src/Utopia/Messaging/Adapter/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapter/Email/Mailgun.php @@ -5,6 +5,7 @@ use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Messages\Email as EmailMessage; use Utopia\Messaging\Response; +use Utopia\Psr7\Request\Multipart\Part; class Mailgun extends EmailAdapter { @@ -117,10 +118,11 @@ protected function process(EmailMessage $message): array foreach ($message->getAttachments() as $index => $attachment) { $isMultipart = true; - $body["attachment[$index]"] = \curl_file_create( + $body["attachment[$index]"] = Part::file( + "attachment[$index]", $attachment->getPath(), - $attachment->getType(), $attachment->getName(), + $attachment->getType(), ); } } diff --git a/src/Utopia/Messaging/Adapter/Push/APNS.php b/src/Utopia/Messaging/Adapter/Push/APNS.php index 2e6e4b45..f46f342c 100644 --- a/src/Utopia/Messaging/Adapter/Push/APNS.php +++ b/src/Utopia/Messaging/Adapter/Push/APNS.php @@ -135,7 +135,7 @@ public function process(PushMessage $message): array default: $error = ($result['response']['reason'] ?? null) === 'ExpiredToken' || ($result['response']['reason'] ?? null) === 'BadDeviceToken' ? $this->getExpiredErrorMessage() - : $result['response']['reason']; + : ($result['response']['reason'] ?? ($result['error'] ?: 'Unknown error')); $response->addResult($device, $error); break; diff --git a/src/Utopia/Messaging/Adapter/SMS/GEOSMS.php b/src/Utopia/Messaging/Adapter/SMS/GEOSMS.php index e88d8a57..f5e8ac0b 100644 --- a/src/Utopia/Messaging/Adapter/SMS/GEOSMS.php +++ b/src/Utopia/Messaging/Adapter/SMS/GEOSMS.php @@ -96,7 +96,6 @@ protected function process(SMS $message): array } while (count($recipients) > 0); foreach ($batches as $index => $batch) { - /** @var array|null $metadata */ $metadata = $message->getMetadata(); if (\count($batches) > 1 && $metadata !== null) { foreach ([MetadataParameter::CRQID, MetadataParameter::UUID] as $parameter) { diff --git a/src/Utopia/Messaging/Helpers/JWT.php b/src/Utopia/Messaging/Helpers/JWT.php index ba4f4b83..e6020ef2 100644 --- a/src/Utopia/Messaging/Helpers/JWT.php +++ b/src/Utopia/Messaging/Helpers/JWT.php @@ -23,7 +23,7 @@ class JWT * * @throws \Exception */ - public static function encode(array $payload, string $key, string $algorithm, string $keyId = null): string + public static function encode(array $payload, string $key, string $algorithm, ?string $keyId = null): string { $header = [ 'typ' => 'JWT', diff --git a/src/Utopia/Messaging/Messages/SMS.php b/src/Utopia/Messaging/Messages/SMS.php index 9ed1dfe5..bbab2aa7 100644 --- a/src/Utopia/Messaging/Messages/SMS.php +++ b/src/Utopia/Messaging/Messages/SMS.php @@ -11,7 +11,7 @@ class SMS implements Message /** * @param array $to * @param array|null $attachments - * @param array|null $metadata + * @param array|null $metadata */ public function __construct( private array $to, @@ -49,7 +49,7 @@ public function getAttachments(): ?array } /** - * @return array|null + * @return array|null */ public function getMetadata(): ?array { @@ -57,7 +57,7 @@ public function getMetadata(): ?array } /** - * @param array|null $metadata + * @param array|null $metadata */ public function setMetadata(?array $metadata): self { diff --git a/tests/Messaging/Adapter/Chat/DiscordTest.php b/tests/Messaging/Adapter/Chat/DiscordTest.php index 3147c34a..4680d456 100644 --- a/tests/Messaging/Adapter/Chat/DiscordTest.php +++ b/tests/Messaging/Adapter/Chat/DiscordTest.php @@ -64,7 +64,7 @@ public function testValidURLVariations(): void try { $discord = new Discord($url); // If we get here, the URL was accepted - $this->assertTrue(true, "Valid URL variant '{$label}' was accepted as expected"); + $this->addToAssertionCount(1); } catch (InvalidArgumentException $e) { $this->fail("Valid URL variant '{$label}' was rejected: " . $e->getMessage()); } diff --git a/tests/Messaging/Adapter/SMS/GEOSMSTest.php b/tests/Messaging/Adapter/SMS/GEOSMSTest.php index 893d87d6..03e7d76f 100644 --- a/tests/Messaging/Adapter/SMS/GEOSMSTest.php +++ b/tests/Messaging/Adapter/SMS/GEOSMSTest.php @@ -190,7 +190,6 @@ public function testSendSMSHandlesMetadata(): void $this->assertEquals('success', $result['local']['results'][0]['status']); $this->assertEquals('success', $result['default']['results'][0]['status']); - /** @var array $invalidMetadata */ $invalidMetadata = [ 'CRQID' => [], ]; diff --git a/tests/Messaging/Adapter/SMS/Msg91Test.php b/tests/Messaging/Adapter/SMS/Msg91Test.php index 9457f659..4548d8a9 100644 --- a/tests/Messaging/Adapter/SMS/Msg91Test.php +++ b/tests/Messaging/Adapter/SMS/Msg91Test.php @@ -70,7 +70,6 @@ public function testSendSMSHandlesMetadata(): void $this->assertStringContainsString('Msg91 CRQID metadata must be 80 characters or less', $e->getMessage()); } - /** @var array $metadata */ $metadata = [ 'CRQID' => null, ]; diff --git a/tests/Messaging/Adapter/SMS/SMSTest.php b/tests/Messaging/Adapter/SMS/SMSTest.php index 9ce9058e..848b034a 100644 --- a/tests/Messaging/Adapter/SMS/SMSTest.php +++ b/tests/Messaging/Adapter/SMS/SMSTest.php @@ -32,7 +32,5 @@ public function testSendSMS(): void $this->assertEquals('POST', $smsRequest['method']); $this->assertEquals('+987654321', $smsRequest['data']['from']); $this->assertEquals('+123456789', $smsRequest['data']['to']); - $this->assertEquals(98, $sender->getCountryCode($smsRequest['data']['from'])); - $this->assertEquals(1, $sender->getCountryCode($smsRequest['data']['to'])); } }