Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e25fd0f

Browse filesBrowse files
committed
[Notifier] added the component
1 parent a0bbae7 commit e25fd0f
Copy full SHA for e25fd0f

File tree

107 files changed

+4438
-0
lines changed
Filter options

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner

107 files changed

+4438
-0
lines changed

‎composer.json

Copy file name to clipboardExpand all lines: composer.json
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"symfony/messenger": "self.version",
7070
"symfony/mime": "self.version",
7171
"symfony/monolog-bridge": "self.version",
72+
"symfony/notifier": "self.version",
7273
"symfony/options-resolver": "self.version",
7374
"symfony/postmark-mailer": "self.version",
7475
"symfony/process": "self.version",
+75Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Monolog\Handler;
13+
14+
use Monolog\Handler\AbstractHandler;
15+
use Monolog\Logger;
16+
use Symfony\Component\Notifier\Notification\Notification;
17+
use Symfony\Component\Notifier\Notifier;
18+
use Symfony\Component\Notifier\NotifierInterface;
19+
20+
/**
21+
* Uses Notifier as a log handler.
22+
*
23+
* @author Fabien Potencier <fabien@symfony.com>
24+
*/
25+
class NotifierHandler extends AbstractHandler
26+
{
27+
private $notifier;
28+
29+
public function __construct(NotifierInterface $notifier, int $level = Logger::ERROR, bool $bubble = true)
30+
{
31+
$this->notifier = $notifier;
32+
33+
parent::__construct($level < Logger::ERROR ? Logger::ERROR : $level, $bubble);
34+
}
35+
36+
public function handle(array $record): bool
37+
{
38+
if (!$this->isHandling($record)) {
39+
return false;
40+
}
41+
42+
$this->notify([$record]);
43+
44+
return !$this->bubble;
45+
}
46+
47+
public function handleBatch(array $records): void
48+
{
49+
if ($records = array_filter($records, [$this, 'isHandling'])) {
50+
$this->notify($records);
51+
}
52+
}
53+
54+
private function notify(array $records): void
55+
{
56+
$record = max(array_column($records, 'level'));
57+
if ($record['context']['exception'] instanceof \Throwable) {
58+
$notification = Notification::fromThrowable($record['context']['exception']);
59+
} else {
60+
$notification = new Notification($record['message']);
61+
}
62+
63+
$notification->importanceFromLogLevel(strtolower(Logger::getLevelName($record['level'])));
64+
65+
try {
66+
$this->notifier->send($notification, ...Notifier::getAdminReceivers());
67+
} catch (\Throwable $e) {
68+
$message = $e->getMessage();
69+
while ($e = $e->getPrevious()) {
70+
$message .= ' > '.$e->getMessage();
71+
}
72+
trigger_error($message, E_USER_ERROR);
73+
}
74+
}
75+
}

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+48Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Symfony\Component\Lock\Store\SemaphoreStore;
2828
use Symfony\Component\Mailer\Mailer;
2929
use Symfony\Component\Messenger\MessageBusInterface;
30+
use Symfony\Component\Notifier\Notifier;
3031
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
3132
use Symfony\Component\Serializer\Serializer;
3233
use Symfony\Component\Translation\Translator;
@@ -115,6 +116,7 @@ public function getConfigTreeBuilder()
115116
$this->addRobotsIndexSection($rootNode);
116117
$this->addHttpClientSection($rootNode);
117118
$this->addMailerSection($rootNode);
119+
$this->addNotifierSection($rootNode);
118120

119121
return $treeBuilder;
120122
}
@@ -1590,4 +1592,50 @@ private function addMailerSection(ArrayNodeDefinition $rootNode)
15901592
->end()
15911593
;
15921594
}
1595+
1596+
private function addNotifierSection(ArrayNodeDefinition $rootNode)
1597+
{
1598+
$rootNode
1599+
->children()
1600+
->arrayNode('notifier')
1601+
->info('Notifier configuration')
1602+
->{!class_exists(FullStack::class) && class_exists(Notifier::class) ? 'canBeDisabled' : 'canBeEnabled'}()
1603+
->fixXmlConfig('chatter_transport')
1604+
->children()
1605+
->arrayNode('chatter_transports')
1606+
->useAttributeAsKey('name')
1607+
->prototype('scalar')->end()
1608+
->end()
1609+
->end()
1610+
->fixXmlConfig('texter_transport')
1611+
->children()
1612+
->arrayNode('texter_transports')
1613+
->useAttributeAsKey('name')
1614+
->prototype('scalar')->end()
1615+
->end()
1616+
->end()
1617+
->children()
1618+
->arrayNode('channel_policy')
1619+
->useAttributeAsKey('name')
1620+
->prototype('array')
1621+
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
1622+
->prototype('scalar')->end()
1623+
->end()
1624+
->end()
1625+
->end()
1626+
->fixXmlConfig('admin_receiver')
1627+
->children()
1628+
->arrayNode('admin_receivers')
1629+
->prototype('array')
1630+
->children()
1631+
->scalarNode('email')->cannotBeEmpty()->end()
1632+
->scalarNode('phone')->defaultNull()->end()
1633+
->end()
1634+
->end()
1635+
->end()
1636+
->end()
1637+
->end()
1638+
->end()
1639+
;
1640+
}
15931641
}

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+68Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@
9292
use Symfony\Component\Messenger\Transport\TransportInterface;
9393
use Symfony\Component\Mime\MimeTypeGuesserInterface;
9494
use Symfony\Component\Mime\MimeTypes;
95+
use Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory;
96+
use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory;
97+
use Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory;
98+
use Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory;
99+
use Symfony\Component\Notifier\Notifier;
100+
use Symfony\Component\Notifier\Receiver\AdminReceiver;
95101
use Symfony\Component\PropertyAccess\PropertyAccessor;
96102
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
97103
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
@@ -151,6 +157,7 @@ class FrameworkExtension extends Extension
151157
private $messengerConfigEnabled = false;
152158
private $mailerConfigEnabled = false;
153159
private $httpClientConfigEnabled = false;
160+
private $notifierConfigEnabled = false;
154161

155162
/**
156163
* Responds to the app.config configuration parameter.
@@ -321,6 +328,10 @@ public function load(array $configs, ContainerBuilder $container)
321328
$this->registerMailerConfiguration($config['mailer'], $container, $loader);
322329
}
323330

331+
if ($this->notifierConfigEnabled = $this->isConfigEnabled($container, $config['notifier'])) {
332+
$this->registerNotifierConfiguration($config['notifier'], $container, $loader);
333+
}
334+
324335
$propertyInfoEnabled = $this->isConfigEnabled($container, $config['property_info']);
325336
$this->registerValidationConfiguration($config['validation'], $container, $loader, $propertyInfoEnabled);
326337
$this->registerEsiConfiguration($config['esi'], $container, $loader);
@@ -2008,6 +2019,63 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co
20082019
$envelopeListener->setArgument(1, $recipients);
20092020
}
20102021

2022+
private function registerNotifierConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
2023+
{
2024+
if (!class_exists(Notifier::class)) {
2025+
throw new LogicException('Notifier support cannot be enabled as the component is not installed. Try running "composer require symfony/notifier".');
2026+
}
2027+
2028+
$loader->load('notifier.xml');
2029+
$loader->load('notifier_transports.xml');
2030+
2031+
if ($config['chatter_transports']) {
2032+
$container->getDefinition('chatter.transports')->setArgument(0, $config['chatter_transports']);
2033+
} else {
2034+
$container->removeDefinition('chatter');
2035+
}
2036+
if ($config['texter_transports']) {
2037+
$container->getDefinition('texter.transports')->setArgument(0, $config['texter_transports']);
2038+
} else {
2039+
$container->removeDefinition('texter');
2040+
}
2041+
2042+
if ($this->mailerConfigEnabled) {
2043+
$sender = $container->getDefinition('mailer.envelope_listener')->getArgument(0);
2044+
$container->getDefinition('notifier.email_channel')->setArgument(1, $sender);
2045+
} else {
2046+
$container->removeDefinition('notifier.email_channel');
2047+
}
2048+
2049+
if (!$this->messengerConfigEnabled) {
2050+
$container->removeDefinition('notifier.failed_message_listener');
2051+
}
2052+
2053+
$container->getDefinition('notifier.channel_policy')->setArgument(0, $config['channel_policy']);
2054+
2055+
$classToServices = [
2056+
SlackTransportFactory::class => 'notifier.transport_factory.slack',
2057+
TelegramTransportFactory::class => 'notifier.transport_factory.telegram',
2058+
NexmoTransportFactory::class => 'notifier.transport_factory.nexmo',
2059+
TwilioTransportFactory::class => 'notifier.transport_factory.twilio',
2060+
];
2061+
2062+
foreach ($classToServices as $class => $service) {
2063+
if (!class_exists($class)) {
2064+
$container->removeDefinition($service);
2065+
}
2066+
}
2067+
2068+
if (isset($config['admin_receivers'])) {
2069+
$notifier = $container->getDefinition('notifier');
2070+
foreach ($config['admin_receivers'] as $i => $receiver) {
2071+
$id = 'notifier.admin_receiver.'.$i;
2072+
$def = $container->setDefinition($id, new Definition(AdminReceiver::class, [$receiver['email'], $receiver['phone']]));
2073+
$def->setPublic(false);
2074+
$notifier->addMethodCall('addAdminReceiver', [new Reference($id)]);
2075+
}
2076+
}
2077+
}
2078+
20112079
/**
20122080
* {@inheritdoc}
20132081
*/
+92Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="notifier" class="Symfony\Component\Notifier\Notifier">
9+
<argument type="tagged_locator" tag="notifier.channel" index-by="key" />
10+
<argument type="service" id="notifier.channel_policy" on-invalid="ignore" />
11+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
12+
</service>
13+
<service id="Symfony\Component\Notifier\NotifierInterface" alias="notifier" />
14+
15+
<service id="notifier.channel_policy" class="Symfony\Component\Notifier\Channel\ChannelPolicy">
16+
<argument type="collection" /> <!-- policy -->
17+
</service>
18+
19+
<service id="notifier.browser_channel" class="Symfony\Component\Notifier\Channel\BrowserChannel">
20+
<argument type="service" id="request_stack" />
21+
<tag name="notifier.channel" key="browser" />
22+
</service>
23+
24+
<service id="notifier.chat_channel" class="Symfony\Component\Notifier\Channel\ChatChannel">
25+
<argument type="service" id="chatter.transports" />
26+
<tag name="notifier.channel" key="chat" />
27+
</service>
28+
29+
<service id="notifier.sms_channel" class="Symfony\Component\Notifier\Channel\SmsChannel">
30+
<argument type="service" id="texter.transports" />
31+
<tag name="notifier.channel" key="sms" />
32+
</service>
33+
34+
<service id="notifier.email_channel" class="Symfony\Component\Notifier\Channel\EmailChannel">
35+
<argument type="service" id="mailer.transports" />
36+
<tag name="notifier.channel" key="email" />
37+
</service>
38+
39+
<service id="notifier.monolog_handler" class="Symfony\Bridge\Monolog\Handler\NotifierHandler">
40+
<argument type="service" id="notifier" />
41+
</service>
42+
43+
<service id="notifier.failed_message_listener" class="Symfony\Component\Notifier\EventListener\SendFailedMessageToNotifierListener">
44+
<argument type="service" id="notifier" />
45+
<tag name="kernel.event_subscriber" />
46+
</service>
47+
48+
<!-- chatter -->
49+
<service id="chatter" class="Symfony\Component\Notifier\Chatter">
50+
<argument type="service" id="chatter.transports" />
51+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
52+
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
53+
</service>
54+
<service id="Symfony\Component\Notifier\ChatterInterface" alias="chatter" />
55+
56+
<service id="chatter.transports" class="Symfony\Component\Notifier\Transport\Transports">
57+
<factory service="chatter.transport_factory" method="fromStrings" />
58+
<argument type="collection" /> <!-- transports -->
59+
</service>
60+
61+
<service id="chatter.transport_factory" class="Symfony\Component\Notifier\Transport">
62+
<argument type="tagged_iterator" tag="chatter.transport_factory" />
63+
</service>
64+
65+
<service id="chatter.messenger.chat_handler" class="Symfony\Component\Notifier\Messenger\MessageHandler">
66+
<argument type="service" id="chatter.transports" />
67+
<tag name="messenger.message_handler" handles="Symfony\Component\Notifier\Message\ChatMessage" />
68+
</service>
69+
70+
<!-- texter -->
71+
<service id="texter" class="Symfony\Component\Notifier\Texter">
72+
<argument type="service" id="texter.transports" />
73+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
74+
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
75+
</service>
76+
<service id="Symfony\Component\Notifier\TexterInterface" alias="texter" />
77+
78+
<service id="texter.transports" class="Symfony\Component\Notifier\Transport\Transports">
79+
<factory service="texter.transport_factory" method="fromStrings" />
80+
<argument type="collection" /> <!-- transports -->
81+
</service>
82+
83+
<service id="texter.transport_factory" class="Symfony\Component\Notifier\Transport">
84+
<argument type="tagged_iterator" tag="texter.transport_factory" />
85+
</service>
86+
87+
<service id="texter.messenger.sms_handler" class="Symfony\Component\Notifier\Messenger\MessageHandler">
88+
<argument type="service" id="texter.transports" />
89+
<tag name="messenger.message_handler" handles="Symfony\Component\Notifier\Message\SmsMessage" />
90+
</service>
91+
</services>
92+
</container>
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="notifier.transport_factory.abstract" class="Symfony\Component\Notifier\Transport\AbstractTransportFactory" abstract="true">
9+
<argument type="service" id="event_dispatcher" />
10+
<argument type="service" id="http_client" on-invalid="ignore" />
11+
</service>
12+
13+
<service id="notifier.transport_factory.slack" class="Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory" parent="notifier.transport_factory.abstract">
14+
<tag name="chatter.transport_factory" />
15+
</service>
16+
17+
<service id="notifier.transport_factory.telegram" class="Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory" parent="notifier.transport_factory.abstract">
18+
<tag name="chatter.transport_factory" />
19+
</service>
20+
21+
<service id="notifier.transport_factory.nexmo" class="Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory" parent="notifier.transport_factory.abstract">
22+
<tag name="texter.transport_factory" />
23+
</service>
24+
25+
<service id="notifier.transport_factory.twilio" class="Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory" parent="notifier.transport_factory.abstract">
26+
<tag name="texter.transport_factory" />
27+
</service>
28+
29+
<service id="notifier.transport_factory.null" class="Symfony\Component\Notifier\Transport\NullTransportFactory" parent="notifier.transport_factory.abstract">
30+
<tag name="notifier.transport_factory" />
31+
</service>
32+
</services>
33+
</container>

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\Lock\Store\SemaphoreStore;
2222
use Symfony\Component\Mailer\Mailer;
2323
use Symfony\Component\Messenger\MessageBusInterface;
24+
use Symfony\Component\Notifier\Notifier;
2425

2526
class ConfigurationTest extends TestCase
2627
{
@@ -373,6 +374,13 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
373374
'transports' => [],
374375
'enabled' => !class_exists(FullStack::class) && class_exists(Mailer::class),
375376
],
377+
'notifier' => [
378+
'enabled' => !class_exists(FullStack::class) && class_exists(Notifier::class),
379+
'chatter_transports' => [],
380+
'texter_transports' => [],
381+
'channel_policy' => [],
382+
'admin_receivers' => [],
383+
],
376384
'error_controller' => 'error_controller',
377385
];
378386
}
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.