diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index d293ae9f487a5..b4ac2e8649946 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2185,9 +2185,9 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder ->setFactory([new Reference('messenger.transport_factory'), 'createTransport']) ->setArguments([$transport['dsn'], $transport['options'] + ['transport_name' => $name], new Reference($serializerId)]) ->addTag('messenger.receiver', [ - 'alias' => $name, - 'is_failure_transport' => \in_array($name, $failureTransports, true), - ] + 'alias' => $name, + 'is_failure_transport' => \in_array($name, $failureTransports, true), + ] ) ; $container->setDefinition($transportId = 'messenger.transport.'.$name, $transportDefinition); @@ -2814,6 +2814,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\Yunpian\YunpianTransportFactory::class => 'notifier.transport_factory.yunpian', NotifierBridge\Zendesk\ZendeskTransportFactory::class => 'notifier.transport_factory.zendesk', NotifierBridge\Zulip\ZulipTransportFactory::class => 'notifier.transport_factory.zulip', + NotifierBridge\Primotexto\PrimotextoTransportFactory::class => 'notifier.transport_factory.primotexto', ]; $parentPackages = ['symfony/framework-bundle', 'symfony/notifier']; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index df9be94ed5e32..648a481bc1786 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -112,6 +112,7 @@ 'unifonic' => Bridge\Unifonic\UnifonicTransportFactory::class, 'vonage' => Bridge\Vonage\VonageTransportFactory::class, 'yunpian' => Bridge\Yunpian\YunpianTransportFactory::class, + 'primotexto' => Bridge\Primotexto\PrimotextoTransportFactory::class, ]; foreach ($texterFactories as $name => $class) { diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/.gitattributes b/src/Symfony/Component/Notifier/Bridge/Primotexto/.gitattributes new file mode 100644 index 0000000000000..14c3c35940427 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/.gitattributes @@ -0,0 +1,3 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.git* export-ignore diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/.gitignore b/src/Symfony/Component/Notifier/Bridge/Primotexto/.gitignore new file mode 100644 index 0000000000000..c49a5d8df5c65 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/Primotexto/CHANGELOG.md new file mode 100644 index 0000000000000..243eee0da2e03 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +7.1 +--- + +* Add the bridge diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/LICENSE b/src/Symfony/Component/Notifier/Bridge/Primotexto/LICENSE new file mode 100644 index 0000000000000..99c6bdf356ee7 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoErrorCode.php b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoErrorCode.php new file mode 100644 index 0000000000000..13e32caf43282 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoErrorCode.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Primotexto; + +enum PrimotextoErrorCode: int +{ + case NO_PHONE_NUMBER_PROVIDED = 10; + case INVALID_PHONE_NUMBER_SYNTAX = 11; + case NUMBER_BLACKLISTED_UNSUBSCRIBED = 12; + case NUMBER_BLACKLISTED_USER_BOUNCE = 13; + case NUMBER_BLACKLISTED_GLOBAL_BOUNCE = 14; + case NUMBER_ALREADY_EXISTS = 15; + case NO_MESSAGE_FOR_THIS_QUERY = 16; + case TOO_MANY_FIELDS_SELECTED = 17; + case FILE_TOO_LARGE = 18; + case PHONE_NUMBER_IS_FIXED_LINE = 19; + + case INVALID_CHARACTERS_IN_SENDER = 20; + case SENDER_TOO_SHORT = 21; + case SENDER_TOO_LONG = 22; + case FULL_NUMERIC_SENDER_NOT_ALLOWED = 23; + + case NO_MESSAGE_CONTENT_PROVIDED = 30; + case INVALID_CHARACTERS_IN_MESSAGE = 31; + case MESSAGE_CONTENT_TOO_LONG = 32; + + case INVALID_CAMPAIGN_NAME = 40; + case INVALID_CAMPAIGN_PROGRAMMING_TIME = 41; + case CAMPAIGN_CANNOT_BE_DELETED = 42; + case CAMPAIGN_CANNOT_BE_CANCELLED = 43; + case FREE_MODE_CAMPAIGN_LIMIT_REACHED = 44; + case INVALID_CAMPAIGN_TAG = 45; + case TAG_ALREADY_EXISTS = 46; + case CAMPAIGN_NOT_FOUND = 47; + case INVALID_CAMPAIGN_SEND_LIST = 48; + + case LIST_NOT_FOUND = 60; + case INVALID_DATE_FORMAT = 61; + case INVALID_SCHEDULED_DATE = 62; + + case API_ACCESS_DISABLED = 70; + case INSUFFICIENT_CREDITS = 71; + case AUTHENTICATION_FAILED = 72; + case INVALID_JSON = 73; + case CONTACTS_POST_LIMIT_REACHED = 74; + case IDENTIFIERS_COLUMN_NOT_FOUND = 75; + case QUOTA_EXCEEDED = 76; + + case COUNTRY_NOT_SUPPORTED = 90; + case INTERNATIONAL_MODE_NEEDED = 91; + case BLOCKED_ACCOUNT = 92; + case USER_NOT_FOUND = 93; + case USER_INFO_NOT_RETRIEVABLE = 94; + case UNABLE_TO_CREATE_ACCOUNT = 95; + case AUTO_INVALID_CAMPAIGN = 120; + case AUTO_INVALID_CONTACT = 121; + + case UNKNOWN_ERROR = 1000; +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoOptions.php b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoOptions.php new file mode 100644 index 0000000000000..da907d6d62994 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoOptions.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Primotexto; + +use Symfony\Component\Notifier\Message\MessageOptionsInterface; + +/** + * @author Samaël tomas + */ +final class PrimotextoOptions implements MessageOptionsInterface +{ + private array $options; + + public function __construct(array $options = []) + { + $this->options = $options; + } + + public function getRecipientId(): ?string + { + return null; + } + + /** + * @return $this + */ + public function campaignName(string $campaignName): static + { + $this->options['campaignName'] = $campaignName; + + return $this; + } + + /** + * @return $this + */ + public function category(string $category): static + { + $this->options['category'] = $category; + + return $this; + } + + /** + * @return $this + */ + public function date(int $timestamp): static + { + $this->options['date'] = $timestamp; + + return $this; + } + + public function toArray(): array + { + return $this->options; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoTransport.php b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoTransport.php new file mode 100644 index 0000000000000..039df9c148fe7 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoTransport.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Primotexto; + +use Symfony\Component\Notifier\Exception\TransportException; +use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SentMessage; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\Transport\AbstractTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * @author Samaël tomas + */ +final class PrimotextoTransport extends AbstractTransport +{ + protected const HOST = 'api.primotexto.com'; + + private string $apiKey; + private ?string $from; + + public function __construct( + #[\SensitiveParameter] string $apiKey, + ?string $from = null, + ?HttpClientInterface $client = null, + ?EventDispatcherInterface $dispatcher = null, + ) { + $this->apiKey = $apiKey; + $this->from = $from; + + parent::__construct($client, $dispatcher); + } + + protected function doSend(MessageInterface $message): SentMessage + { + if (!$message instanceof SmsMessage) { + throw new UnsupportedMessageTypeException(__CLASS__, SmsMessage::class, $message); + } + + $options = $message->getOptions()?->toArray() ?? []; + $options['from'] = $message->getFrom() ?: $this->from; + $options['number'] = $message->getPhone(); + $options['message'] = $message->getSubject(); + + $endpoint = sprintf('https://%s/v2/notification/messages/send', $this->getEndpoint()); + $response = $this->client->request('POST', $endpoint, [ + 'headers' => [ + 'X-Primotexto-ApiKey' => $this->apiKey, + 'Content-Type' => 'application/json', + ], + 'json' => array_filter($options), + ]); + + try { + $statusCode = $response->getStatusCode(); + } catch (TransportExceptionInterface $e) { + throw new TransportException('Could not reach the remote Primotexto server.', $response, 0, $e); + } + + if (200 !== $statusCode) { + $error = $response->toArray(false); + + $errorCodeValue = PrimotextoErrorCode::tryFrom((int) $error['code']) ?? PrimotextoErrorCode::UNKNOWN_ERROR; + + throw new TransportException(sprintf('Unable to send the SMS, error %s : "%s".', $error['code'], $errorCodeValue->name), $response); + } + + $success = $response->toArray(false); + + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['snapshotId']); + + return $sentMessage; + } + + public function __toString(): string + { + return sprintf('primotexto://%s%s', $this->getEndpoint(), null !== $this->from ? '?from='.$this->from : ''); + } + + public function supports(MessageInterface $message): bool + { + return $message instanceof SmsMessage && (null === $message->getOptions() || $message->getOptions() instanceof PrimotextoOptions); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoTransportFactory.php new file mode 100644 index 0000000000000..8a0140f938a50 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/PrimotextoTransportFactory.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Primotexto; + +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\AbstractTransportFactory; +use Symfony\Component\Notifier\Transport\Dsn; + +/** + * @author Samaël tomas + */ +final class PrimotextoTransportFactory extends AbstractTransportFactory +{ + public function create(Dsn $dsn): PrimotextoTransport + { + $scheme = $dsn->getScheme(); + + if ('primotexto' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'primotexto', $this->getSupportedSchemes()); + } + + $apiKey = $this->getUser($dsn); + $from = $dsn->getOption('from'); + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new PrimotextoTransport($apiKey, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + } + + protected function getSupportedSchemes(): array + { + return ['primotexto']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/README.md b/src/Symfony/Component/Notifier/Bridge/Primotexto/README.md new file mode 100644 index 0000000000000..1d3deb2e4a1ae --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/README.md @@ -0,0 +1,50 @@ +Primotexto Notifier +================= + +Provides [Primotexto](https://www.primotexto.com/) integration for Symfony Notifier. + +DSN example +----------- + +``` +PRIMOTEXTO_DSN=primotexto://APIKEY@default?from=FROM +``` + +where: + - `APIKEY` is your Primotexto API key + - `FROM` is your sender name + +Adding Options to a Message +--------------------------- + +With a Primotexto Message, you can use the `PrimotextoOptions` class to add +[message options](https://www.primotexto.com/api/sms/notification.asp). + +```php +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\Bridge\Primotexto\options; + +$sms = new SmsMessage('+1411111111', 'My message'); + +$options = (new PrimotextoOptions()) + ->campaignName('Code de confirmation') + ->category('codeConfirmation') + ->date(1398177000000) + // ... + ; + +// Add the custom options to the sms message and send the message +$sms->options($options); + +$texter->send($sms); +``` + +Resources +--------- + +* [Primotexto error codes](https://www.primotexto.com/api/plus/code_erreurs) + + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoOptionsTest.php b/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoOptionsTest.php new file mode 100644 index 0000000000000..45cecbc4e7b7a --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoOptionsTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Primotexto\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Primotexto\PrimotextoOptions; + +class PrimotextoOptionsTest extends TestCase +{ + public function testPrimotextoOptions() + { + $primotextoOptions = (new PrimotextoOptions()) + ->date(1714121739) + ->category('test_category') + ->campaignName('test_campaign_name'); + + self::assertSame([ + 'date' => 1714121739, + 'category' => 'test_category', + 'campaignName' => 'test_campaign_name', + ], $primotextoOptions->toArray()); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoTransportFactoryTest.php new file mode 100644 index 0000000000000..7f98451881de9 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoTransportFactoryTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Primotexto\Tests; + +use Symfony\Component\Notifier\Bridge\Primotexto\PrimotextoTransportFactory; +use Symfony\Component\Notifier\Test\TransportFactoryTestCase; + +final class PrimotextoTransportFactoryTest extends TransportFactoryTestCase +{ + public function createFactory(): PrimotextoTransportFactory + { + return new PrimotextoTransportFactory(); + } + + public static function createProvider(): iterable + { + yield [ + 'primotexto://host.test', + 'primotexto://apiKey@host.test', + ]; + + yield [ + 'primotexto://host.test?from=TEST', + 'primotexto://apiKey@host.test?from=TEST', + ]; + } + + public static function supportsProvider(): iterable + { + yield [true, 'primotexto://apiKey@default']; + yield [false, 'somethingElse://apiKey@default']; + } + + public static function unsupportedSchemeProvider(): iterable + { + yield ['somethingElse://apiKey@default']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoTransportTest.php new file mode 100644 index 0000000000000..9a1b6af426636 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/Tests/PrimotextoTransportTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Primotexto\Tests; + +use Symfony\Component\Notifier\Bridge\Primotexto\PrimotextoTransportFactory; +use Symfony\Component\Notifier\Test\TransportFactoryTestCase; + +final class PrimotextoTransportTest extends TransportFactoryTestCase +{ + public function createFactory(): PrimotextoTransportFactory + { + return new PrimotextoTransportFactory(); + } + + public static function createProvider(): iterable + { + yield [ + 'primotexto://host.test', + 'primotexto://apiKey@host.test', + ]; + + yield [ + 'primotexto://host.test?from=TEST', + 'primotexto://apiKey@host.test?from=TEST', + ]; + } + + public static function supportsProvider(): iterable + { + yield [true, 'primotexto://apiKey@default']; + yield [false, 'somethingElse://apiKey@default']; + } + + public static function unsupportedSchemeProvider(): iterable + { + yield ['somethingElse://apiKey@default']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/composer.json b/src/Symfony/Component/Notifier/Bridge/Primotexto/composer.json new file mode 100644 index 0000000000000..01828a1413a86 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/primotexto-notifier", + "type": "symfony-notifier-bridge", + "description": "Symfony Primotexto Notifier Bridge", + "keywords": ["sms", "primotexto", "notifier"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Samaël Tomas", + "email": "samael.tomas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.2", + "symfony/http-client": "^6.4|^7.0", + "symfony/notifier": "^6.4|^7.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Primotexto\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/Primotexto/phpunit.xml.dist new file mode 100644 index 0000000000000..53b0186de4e49 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + + ./Resources + ./Tests + ./vendor + + + diff --git a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php index a9930d452a9e2..d974dd68d39df 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -328,6 +328,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\Zulip\ZulipTransportFactory::class, 'package' => 'symfony/zulip-notifier', ], + 'primotexto' => [ + 'class' => Bridge\Primotexto\PrimotextoTransportFactory::class, + 'package' => 'symfony/primotexto-notifier', + ], ]; /** diff --git a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php index 8b11e62c41d21..24e730de4ba99 100644 --- a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php +++ b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php @@ -103,6 +103,7 @@ public static function setUpBeforeClass(): void Bridge\Yunpian\YunpianTransportFactory::class => false, Bridge\Zendesk\ZendeskTransportFactory::class => false, Bridge\Zulip\ZulipTransportFactory::class => false, + Bridge\Primotexto\PrimotextoTransportFactory::class => false, ]); } @@ -184,6 +185,7 @@ public static function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \ yield ['zendesk', 'symfony/zendesk-notifier']; yield ['zulip', 'symfony/zulip-notifier']; yield ['goip', 'symfony/go-ip-notifier']; + yield ['primotexto', 'symfony/primotexto-notifier']; } /** diff --git a/src/Symfony/Component/Notifier/Transport.php b/src/Symfony/Component/Notifier/Transport.php index 0ed0c465747f6..b0e5759a9d59f 100644 --- a/src/Symfony/Component/Notifier/Transport.php +++ b/src/Symfony/Component/Notifier/Transport.php @@ -105,6 +105,7 @@ final class Transport Bridge\Yunpian\YunpianTransportFactory::class, Bridge\Zendesk\ZendeskTransportFactory::class, Bridge\Zulip\ZulipTransportFactory::class, + Bridge\Primotexto\PrimotextoTransportFactory::class, ]; public static function fromDsn(#[\SensitiveParameter] string $dsn, ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null): TransportInterface