diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index db3bfd0c101e3..3fff07c8f03d6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -159,6 +159,7 @@ use Symfony\Component\Notifier\Bridge\OneSignal\OneSignalTransportFactory; use Symfony\Component\Notifier\Bridge\OrangeSms\OrangeSmsTransportFactory; use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransportFactory; +use Symfony\Component\Notifier\Bridge\Plivo\PlivoTransportFactory; use Symfony\Component\Notifier\Bridge\RingCentral\RingCentralTransportFactory; use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory; use Symfony\Component\Notifier\Bridge\Sendberry\SendberryTransportFactory; @@ -2584,6 +2585,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ OneSignalTransportFactory::class => 'notifier.transport_factory.one-signal', OrangeSmsTransportFactory::class => 'notifier.transport_factory.orange-sms', OvhCloudTransportFactory::class => 'notifier.transport_factory.ovh-cloud', + PlivoTransportFactory::class => 'notifier.transport_factory.plivo', RingCentralTransportFactory::class => 'notifier.transport_factory.ring-central', RocketChatTransportFactory::class => 'notifier.transport_factory.rocket-chat', SendberryTransportFactory::class => 'notifier.transport_factory.sendberry', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 2040d6dbc6dac..643e6cf4e58fb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -45,6 +45,7 @@ use Symfony\Component\Notifier\Bridge\OneSignal\OneSignalTransportFactory; use Symfony\Component\Notifier\Bridge\OrangeSms\OrangeSmsTransportFactory; use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransportFactory; +use Symfony\Component\Notifier\Bridge\Plivo\PlivoTransportFactory; use Symfony\Component\Notifier\Bridge\RingCentral\RingCentralTransportFactory; use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory; use Symfony\Component\Notifier\Bridge\Sendberry\SendberryTransportFactory; @@ -307,5 +308,9 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') + ->set('notifier.transport_factory.plivo', PlivoTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('texter.transport_factory') + ; }; diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/.gitattributes b/src/Symfony/Component/Notifier/Bridge/Plivo/.gitattributes new file mode 100644 index 0000000000000..84c7add058fb5 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/.gitattributes @@ -0,0 +1,4 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/.gitignore b/src/Symfony/Component/Notifier/Bridge/Plivo/.gitignore new file mode 100644 index 0000000000000..c49a5d8df5c65 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/Plivo/CHANGELOG.md new file mode 100644 index 0000000000000..75d6a403c1334 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +6.3 +--- + +* Add the bridge diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/LICENSE b/src/Symfony/Component/Notifier/Bridge/Plivo/LICENSE new file mode 100644 index 0000000000000..0ece8964f767d --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 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/Plivo/PlivoOptions.php b/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoOptions.php new file mode 100644 index 0000000000000..50318a91928c0 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoOptions.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Plivo; + +use Symfony\Component\Notifier\Message\MessageOptionsInterface; + +/** + * @author gnito-org + */ +final class PlivoOptions implements MessageOptionsInterface +{ + private array $options; + + public function __construct(array $options = []) + { + $this->options = $options; + } + + public function getLog(): ?bool + { + return $this->options['log'] ?? null; + } + + public function getMediaUrls(): ?string + { + return $this->options['media_urls'] ?? null; + } + + public function getMethod(): ?string + { + return $this->options['method'] ?? null; + } + + public function getPowerpackUuid(): ?string + { + return $this->options['powerpack_uuid'] ?? null; + } + + public function getRecipientId(): ?string + { + return $this->options['recipient_id'] ?? null; + } + + public function getSrc(): ?string + { + return $this->options['src'] ?? null; + } + + public function getTrackable(): ?bool + { + return $this->options['trackable'] ?? null; + } + + public function getType(): ?string + { + return $this->options['type'] ?? null; + } + + public function getUrl(): ?string + { + return $this->options['url'] ?? null; + } + + public function setLog(bool $log): self + { + $this->options['log'] = $log; + + return $this; + } + + public function setMediaUrls(string $mediaUrls): self + { + $this->options['media_urls'] = $mediaUrls; + + return $this; + } + + public function setMethod(string $method): self + { + $this->options['method'] = $method; + + return $this; + } + + public function setPowerpackUuid(string $powerpackUuid): self + { + $this->options['powerpack_uuid'] = $powerpackUuid; + + return $this; + } + + public function setRecipientId(string $id): self + { + $this->options['recipient_id'] = $id; + + return $this; + } + + public function setSrc(string $src): self + { + $this->options['src'] = $src; + + return $this; + } + + public function setTrackable(bool $trackable): self + { + $this->options['trackable'] = $trackable; + + return $this; + } + + public function setType(string $type): self + { + $this->options['type'] = $type; + + return $this; + } + + public function setUrl(string $url): self + { + $this->options['url'] = $url; + + return $this; + } + + public function toArray(): array + { + return $this->options; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoTransport.php b/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoTransport.php new file mode 100644 index 0000000000000..b5984cabfc377 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoTransport.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Plivo; + +use Symfony\Component\HttpClient\Exception\JsonException; +use Symfony\Component\Notifier\Exception\InvalidArgumentException; +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 gnito-org + */ +final class PlivoTransport extends AbstractTransport +{ + protected const HOST = 'api.plivo.com'; + + public function __construct( + private readonly string $authId, + #[\SensitiveParameter] private readonly string $authToken, + private readonly string $from, + HttpClientInterface $client = null, + EventDispatcherInterface $dispatcher = null + ) { + parent::__construct($client, $dispatcher); + } + + public function __toString(): string + { + return sprintf('plivo://%s?from=%s', $this->getEndpoint(), $this->from); + } + + public function supports(MessageInterface $message): bool + { + return $message instanceof SmsMessage; + } + + protected function doSend(MessageInterface $message): SentMessage + { + if (!$message instanceof SmsMessage) { + throw new UnsupportedMessageTypeException(__CLASS__, SmsMessage::class, $message); + } + + $opts = $message->getOptions(); + $options = $opts ? $opts->toArray() : []; + $options['text'] = $message->getSubject(); + $options['src'] = $options['src'] ?? $this->from; + $options['dst'] = $options['dst'] ?? $message->getPhone(); + + if (!preg_match('/^[a-zA-Z0-9\s]{2,11}$/', $options['src']) && !preg_match('/^\+?[1-9]\d{1,14}$/', $options['src'])) { + throw new InvalidArgumentException(sprintf('The "From" number "%s" is not a valid phone number, shortcode, or alphanumeric sender ID. Phone number must contain only numbers and optional + character.', $this->from)); + } + + $endpoint = sprintf('https://%s/v1/Account/%s/Message/', $this->getEndpoint(), $this->authId); + $response = $this->client->request('POST', $endpoint, ['auth_basic' => $this->authId.':'.$this->authToken, 'json' => array_filter($options)]); + + try { + $statusCode = $response->getStatusCode(); + } catch (TransportExceptionInterface $e) { + throw new TransportException('Could not reach the remote Plivo server.', $response, 0, $e); + } + + if (202 !== $statusCode) { + try { + $error = $response->toArray(false); + } catch (JsonException) { + $error['error'] = $response->getContent(false); + } + throw new TransportException(sprintf('Unable to send the SMS - status code: "%s": "%s".', $statusCode, $error['error'] ?? 'unknown error'), $response); + } + + $success = $response->toArray(false); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['message_uuid'][0]); + + return $sentMessage; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoTransportFactory.php new file mode 100644 index 0000000000000..7e389b0672867 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/PlivoTransportFactory.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Plivo; + +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\AbstractTransportFactory; +use Symfony\Component\Notifier\Transport\Dsn; + +/** + * @author gnito-org + */ +final class PlivoTransportFactory extends AbstractTransportFactory +{ + private const TRANSPORT_SCHEME = 'plivo'; + + public function create(Dsn $dsn): PlivoTransport + { + $scheme = $dsn->getScheme(); + + if (self::TRANSPORT_SCHEME !== $scheme) { + throw new UnsupportedSchemeException($dsn, self::TRANSPORT_SCHEME, $this->getSupportedSchemes()); + } + + $authId = $this->getUser($dsn); + $authToken = $this->getPassword($dsn); + $from = $dsn->getRequiredOption('from'); + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new PlivoTransport($authId, $authToken, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + } + + protected function getSupportedSchemes(): array + { + return [self::TRANSPORT_SCHEME]; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/README.md b/src/Symfony/Component/Notifier/Bridge/Plivo/README.md new file mode 100644 index 0000000000000..ec6a5ee23f584 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/README.md @@ -0,0 +1,25 @@ +Plivo Notifier +============== + +Provides [Plivo](https://www.plivo.com) integration for Symfony Notifier. + +DSN example +----------- + +``` +PLIVO_DSN=plivo://AUTH_ID:AUTH_TOKEN@default?from=FROM +``` + +where: + +- `AUTH_ID` is your Plivo Auth ID +- `AUTH_TOKEN` is your Plivo Auth Token +- `FROM` is your sender + +Resources +--------- + +* [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/Plivo/Tests/PlivoTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Plivo/Tests/PlivoTransportFactoryTest.php new file mode 100644 index 0000000000000..fa5d29f5e3f83 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/Tests/PlivoTransportFactoryTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Plivo\Tests; + +use Symfony\Component\Notifier\Bridge\Plivo\PlivoTransportFactory; +use Symfony\Component\Notifier\Test\TransportFactoryTestCase; + +final class PlivoTransportFactoryTest extends TransportFactoryTestCase +{ + public function createFactory(): PlivoTransportFactory + { + return new PlivoTransportFactory(); + } + + public function createProvider(): iterable + { + yield ['plivo://host.test?from=0611223344', 'plivo://authId:authToken@host.test?from=0611223344']; + } + + public function incompleteDsnProvider(): iterable + { + yield 'missing auth token' => ['plivo://authId@default?from=FROM']; + } + + public function missingRequiredOptionProvider(): iterable + { + yield 'missing option: from' => ['plivo://authId:authToken@default']; + } + + public function supportsProvider(): iterable + { + yield [true, 'plivo://authId:authToken@default?from=0611223344']; + yield [false, 'somethingElse://authId:authToken@default?from=0611223344']; + } + + public function unsupportedSchemeProvider(): iterable + { + yield ['somethingElse://authId:authToken@default?from=0611223344']; + yield ['somethingElse://authId:authToken@default']; // missing "from" option + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/Tests/PlivoTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Plivo/Tests/PlivoTransportTest.php new file mode 100644 index 0000000000000..9dec4768842de --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/Tests/PlivoTransportTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Plivo\Tests; + +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\Notifier\Bridge\Plivo\PlivoTransport; +use Symfony\Component\Notifier\Exception\InvalidArgumentException; +use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\Test\TransportTestCase; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; + +final class PlivoTransportTest extends TransportTestCase +{ + public function createTransport(HttpClientInterface $client = null, string $from = 'from'): PlivoTransport + { + return new PlivoTransport('authId', 'authToken', $from, $client ?? $this->createMock(HttpClientInterface::class)); + } + + public function invalidFromProvider(): iterable + { + yield 'too short' => ['a']; + yield 'too long' => ['abcdefghijkl']; + yield 'no zero at start if phone number' => ['+0']; + yield 'phone number too short' => ['+1']; + } + + public function supportedMessagesProvider(): iterable + { + yield [new SmsMessage('0611223344', 'Hello!')]; + } + + /** + * @dataProvider invalidFromProvider + */ + public function testInvalidArgumentExceptionIsThrownIfFromIsInvalid(string $from) + { + $transport = $this->createTransport(null, $from); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage(sprintf('The "From" number "%s" is not a valid phone number, shortcode, or alphanumeric sender ID.', $from)); + + $transport->send(new SmsMessage('+33612345678', 'Hello!')); + } + + /** + * @dataProvider validFromProvider + */ + public function testNoInvalidArgumentExceptionIsThrownIfFromIsValid(string $from) + { + $message = new SmsMessage('+33612345678', 'Hello!'); + $response = $this->createMock(ResponseInterface::class); + $response->expects(self::exactly(2))->method('getStatusCode')->willReturn(202); + $response->expects(self::once())->method('getContent')->willReturn(json_encode(['message' => 'message(s) queued', 'message_uuid' => ['foo'], 'api_id' => 'bar'])); + $client = new MockHttpClient(function (string $method, string $url) use ($response): ResponseInterface { + self::assertSame('POST', $method); + self::assertSame('https://api.plivo.com/v1/Account/authId/Message/', $url); + + return $response; + } + ); + $transport = $this->createTransport($client, $from); + $sentMessage = $transport->send($message); + + self::assertSame('foo', $sentMessage->getMessageId()); + } + + public function toStringProvider(): iterable + { + yield ['plivo://api.plivo.com?from=from', $this->createTransport()]; + } + + public function unsupportedMessagesProvider(): iterable + { + yield [new ChatMessage('Hello!')]; + yield [$this->createMock(MessageInterface::class)]; + } + + public function validFromProvider(): iterable + { + yield ['ab']; + yield ['abc']; + yield ['abcd']; + yield ['abcde']; + yield ['abcdef']; + yield ['abcdefg']; + yield ['abcdefgh']; + yield ['abcdefghi']; + yield ['abcdefghij']; + yield ['abcdefghijk']; + yield ['abcdef ghij']; + yield [' abcdefghij']; + yield ['abcdefghij ']; + yield ['+11']; + yield ['+112']; + yield ['+1123']; + yield ['+11234']; + yield ['+112345']; + yield ['+1123456']; + yield ['+11234567']; + yield ['+112345678']; + yield ['+1123456789']; + yield ['+11234567891']; + yield ['+112345678912']; + yield ['+1123456789123']; + yield ['+11234567891234']; + yield ['+112345678912345']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/composer.json b/src/Symfony/Component/Notifier/Bridge/Plivo/composer.json new file mode 100644 index 0000000000000..d2b921a98f544 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony/plivo-notifier", + "type": "symfony-notifier-bridge", + "description": "Symfony Plivo Notifier Bridge", + "keywords": [ + "plivo", + "notifier" + ], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "gnito-org", + "homepage": "https://github.com/gnito-org" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.1", + "symfony/http-client": "^5.4|^6.0", + "symfony/notifier": "^6.3" + }, + "require-dev": { + "symfony/event-dispatcher": "^5.4|^6.0" + }, + "autoload": { + "psr-4": {"Symfony\\Component\\Notifier\\Bridge\\Plivo\\": ""}, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/Plivo/phpunit.xml.dist new file mode 100644 index 0000000000000..cc84e156ea321 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/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 a0fc0540ae937..b44a43f751172 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -144,6 +144,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\OvhCloud\OvhCloudTransportFactory::class, 'package' => 'symfony/ovh-cloud-notifier', ], + 'plivo' => [ + 'class' => Bridge\Plivo\PlivoTransportFactory::class, + 'package' => 'symfony/plivo-notifier', + ], 'ringcentral' => [ 'class' => Bridge\RingCentral\RingCentralTransportFactory::class, 'package' => 'symfony/ring-central-notifier', diff --git a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php index c4d89e096e794..91e47a967a1e0 100644 --- a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php +++ b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php @@ -169,6 +169,7 @@ public function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \Generat yield ['octopush', 'symfony/octopush-notifier']; yield ['onesignal', 'symfony/one-signal-notifier']; yield ['ovhcloud', 'symfony/ovh-cloud-notifier']; + yield ['plivo', 'symfony/plivo-notifier']; yield ['ringcentral', 'symfony/ring-central-notifier']; yield ['rocketchat', 'symfony/rocket-chat-notifier']; yield ['sendberry', 'symfony/sendberry-notifier']; diff --git a/src/Symfony/Component/Notifier/Transport.php b/src/Symfony/Component/Notifier/Transport.php index ca4201ef026ab..f4e9bc61a4bcc 100644 --- a/src/Symfony/Component/Notifier/Transport.php +++ b/src/Symfony/Component/Notifier/Transport.php @@ -38,6 +38,7 @@ use Symfony\Component\Notifier\Bridge\Octopush\OctopushTransportFactory; use Symfony\Component\Notifier\Bridge\OrangeSms\OrangeSmsTransportFactory; use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransportFactory; +use Symfony\Component\Notifier\Bridge\Plivo\PlivoTransportFactory; use Symfony\Component\Notifier\Bridge\RingCentral\RingCentralTransportFactory; use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory; use Symfony\Component\Notifier\Bridge\Sendberry\SendberryTransportFactory; @@ -103,6 +104,7 @@ final class Transport OctopushTransportFactory::class, OrangeSmsTransportFactory::class, OvhCloudTransportFactory::class, + PlivoTransportFactory::class, RingCentralTransportFactory::class, RocketChatTransportFactory::class, SendberryTransportFactory::class,