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 2554b5c

Browse filesBrowse files
committed
[Notifier] added the component
1 parent 5e48c39 commit 2554b5c
Copy full SHA for 2554b5c

File tree

107 files changed

+4548
-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

+4548
-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",
+79Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 = $this->getHighestRecord($records);
57+
if (($record['context']['exception'] ?? null) instanceof \Throwable) {
58+
$notification = Notification::fromThrowable($record['context']['exception']);
59+
} else {
60+
$notification = new Notification($record['message']);
61+
}
62+
63+
$notification->importanceFromLogLevelName(Logger::getLevelName($record['level']));
64+
65+
$this->notifier->send($notification, ...$this->notifier->getAdminRecipients());
66+
}
67+
68+
private function getHighestRecord(array $records)
69+
{
70+
$highestRecord = null;
71+
foreach ($records as $record) {
72+
if (null === $highestRecord || $highestRecord['level'] < $record['level']) {
73+
$highestRecord = $record;
74+
}
75+
}
76+
77+
return $highestRecord;
78+
}
79+
}

‎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;
@@ -114,6 +115,7 @@ public function getConfigTreeBuilder()
114115
$this->addRobotsIndexSection($rootNode);
115116
$this->addHttpClientSection($rootNode);
116117
$this->addMailerSection($rootNode);
118+
$this->addNotifierSection($rootNode);
117119

118120
return $treeBuilder;
119121
}
@@ -1475,4 +1477,50 @@ private function addMailerSection(ArrayNodeDefinition $rootNode)
14751477
->end()
14761478
;
14771479
}
1480+
1481+
private function addNotifierSection(ArrayNodeDefinition $rootNode)
1482+
{
1483+
$rootNode
1484+
->children()
1485+
->arrayNode('notifier')
1486+
->info('Notifier configuration')
1487+
->{!class_exists(FullStack::class) && class_exists(Notifier::class) ? 'canBeDisabled' : 'canBeEnabled'}()
1488+
->fixXmlConfig('chatter_transport')
1489+
->children()
1490+
->arrayNode('chatter_transports')
1491+
->useAttributeAsKey('name')
1492+
->prototype('scalar')->end()
1493+
->end()
1494+
->end()
1495+
->fixXmlConfig('texter_transport')
1496+
->children()
1497+
->arrayNode('texter_transports')
1498+
->useAttributeAsKey('name')
1499+
->prototype('scalar')->end()
1500+
->end()
1501+
->end()
1502+
->children()
1503+
->arrayNode('channel_policy')
1504+
->useAttributeAsKey('name')
1505+
->prototype('array')
1506+
->beforeNormalization()->ifString()->then(function (string $v) { return [$v]; })->end()
1507+
->prototype('scalar')->end()
1508+
->end()
1509+
->end()
1510+
->end()
1511+
->fixXmlConfig('admin_recipient')
1512+
->children()
1513+
->arrayNode('admin_recipients')
1514+
->prototype('array')
1515+
->children()
1516+
->scalarNode('email')->cannotBeEmpty()->end()
1517+
->scalarNode('phone')->defaultNull()->end()
1518+
->end()
1519+
->end()
1520+
->end()
1521+
->end()
1522+
->end()
1523+
->end()
1524+
;
1525+
}
14781526
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+71Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@
8989
use Symfony\Component\Messenger\Transport\TransportInterface;
9090
use Symfony\Component\Mime\MimeTypeGuesserInterface;
9191
use Symfony\Component\Mime\MimeTypes;
92+
use Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory;
93+
use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory;
94+
use Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory;
95+
use Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory;
96+
use Symfony\Component\Notifier\Notifier;
97+
use Symfony\Component\Notifier\Recipient\AdminRecipient;
9298
use Symfony\Component\PropertyAccess\PropertyAccessor;
9399
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
94100
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
@@ -297,6 +303,10 @@ public function load(array $configs, ContainerBuilder $container)
297303
$this->registerMailerConfiguration($config['mailer'], $container, $loader);
298304
}
299305

306+
if ($this->isConfigEnabled($container, $config['notifier'])) {
307+
$this->registerNotifierConfiguration($config['notifier'], $container, $loader);
308+
}
309+
300310
$propertyInfoEnabled = $this->isConfigEnabled($container, $config['property_info']);
301311
$this->registerValidationConfiguration($config['validation'], $container, $loader, $propertyInfoEnabled);
302312
$this->registerEsiConfiguration($config['esi'], $container, $loader);
@@ -1848,6 +1858,67 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co
18481858
$envelopeListener->setArgument(1, $recipients);
18491859
}
18501860

1861+
private function registerNotifierConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
1862+
{
1863+
if (!class_exists(Notifier::class)) {
1864+
throw new LogicException('Notifier support cannot be enabled as the component is not installed. Try running "composer require symfony/notifier".');
1865+
}
1866+
1867+
$loader->load('notifier.xml');
1868+
$loader->load('notifier_transports.xml');
1869+
1870+
if ($config['chatter_transports']) {
1871+
$container->getDefinition('chatter.transports')->setArgument(0, $config['chatter_transports']);
1872+
} else {
1873+
$container->removeDefinition('chatter');
1874+
}
1875+
if ($config['texter_transports']) {
1876+
$container->getDefinition('texter.transports')->setArgument(0, $config['texter_transports']);
1877+
} else {
1878+
$container->removeDefinition('texter');
1879+
}
1880+
1881+
if ($this->mailerConfigEnabled) {
1882+
$sender = $container->getDefinition('mailer.envelope_listener')->getArgument(0);
1883+
$container->getDefinition('notifier.email_channel')->setArgument(2, $sender);
1884+
} else {
1885+
$container->removeDefinition('notifier.email_channel');
1886+
}
1887+
1888+
if (!$this->messengerConfigEnabled) {
1889+
$container->removeDefinition('notifier.failed_message_listener');
1890+
} else {
1891+
// as we have a bus, the channels don't need the transports
1892+
$container->getDefinition('notifier.chat_channel')->setArgument(0, null);
1893+
$container->getDefinition('notifier.email_channel')->setArgument(0, null);
1894+
$container->getDefinition('notifier.sms_channel')->setArgument(0, null);
1895+
}
1896+
1897+
$container->getDefinition('notifier.channel_policy')->setArgument(0, $config['channel_policy']);
1898+
1899+
$classToServices = [
1900+
SlackTransportFactory::class => 'notifier.transport_factory.slack',
1901+
TelegramTransportFactory::class => 'notifier.transport_factory.telegram',
1902+
NexmoTransportFactory::class => 'notifier.transport_factory.nexmo',
1903+
TwilioTransportFactory::class => 'notifier.transport_factory.twilio',
1904+
];
1905+
1906+
foreach ($classToServices as $class => $service) {
1907+
if (!class_exists($class)) {
1908+
$container->removeDefinition($service);
1909+
}
1910+
}
1911+
1912+
if (isset($config['admin_recipients'])) {
1913+
$notifier = $container->getDefinition('notifier');
1914+
foreach ($config['admin_recipients'] as $i => $recipient) {
1915+
$id = 'notifier.admin_recipient.'.$i;
1916+
$container->setDefinition($id, new Definition(AdminRecipient::class, [$recipient['email'], $recipient['phone']]));
1917+
$notifier->addMethodCall('addAdminRecipient', [new Reference($id)]);
1918+
}
1919+
}
1920+
}
1921+
18511922
/**
18521923
* {@inheritdoc}
18531924
*/
+94Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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="channel" />
10+
<argument type="service" id="notifier.channel_policy" on-invalid="ignore" />
11+
</service>
12+
<service id="Symfony\Component\Notifier\NotifierInterface" alias="notifier" />
13+
14+
<service id="notifier.channel_policy" class="Symfony\Component\Notifier\Channel\ChannelPolicy">
15+
<argument type="collection" /> <!-- policy -->
16+
</service>
17+
18+
<service id="notifier.channel.browser" class="Symfony\Component\Notifier\Channel\BrowserChannel">
19+
<argument type="service" id="request_stack" />
20+
<tag name="notifier.channel" channel="browser" />
21+
</service>
22+
23+
<service id="notifier.channel.chat" class="Symfony\Component\Notifier\Channel\ChatChannel">
24+
<argument type="service" id="chatter.transports" />
25+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
26+
<tag name="notifier.channel" channel="chat" />
27+
</service>
28+
29+
<service id="notifier.channel.sms" class="Symfony\Component\Notifier\Channel\SmsChannel">
30+
<argument type="service" id="texter.transports" />
31+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
32+
<tag name="notifier.channel" channel="sms" />
33+
</service>
34+
35+
<service id="notifier.channel.email" class="Symfony\Component\Notifier\Channel\EmailChannel">
36+
<argument type="service" id="mailer.transports" />
37+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
38+
<tag name="notifier.channel" channel="email" />
39+
</service>
40+
41+
<service id="notifier.monolog_handler" class="Symfony\Bridge\Monolog\Handler\NotifierHandler">
42+
<argument type="service" id="notifier" />
43+
</service>
44+
45+
<service id="notifier.failed_message_listener" class="Symfony\Component\Notifier\EventListener\SendFailedMessageToNotifierListener">
46+
<argument type="service" id="notifier" />
47+
<tag name="kernel.event_subscriber" />
48+
</service>
49+
50+
<!-- chatter -->
51+
<service id="chatter" class="Symfony\Component\Notifier\Chatter">
52+
<argument type="service" id="chatter.transports" />
53+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
54+
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
55+
</service>
56+
<service id="Symfony\Component\Notifier\ChatterInterface" alias="chatter" />
57+
58+
<service id="chatter.transports" class="Symfony\Component\Notifier\Transport\Transports">
59+
<factory service="chatter.transport_factory" method="fromStrings" />
60+
<argument type="collection" /> <!-- transports -->
61+
</service>
62+
63+
<service id="chatter.transport_factory" class="Symfony\Component\Notifier\Transport">
64+
<argument type="tagged_iterator" tag="chatter.transport_factory" />
65+
</service>
66+
67+
<service id="chatter.messenger.chat_handler" class="Symfony\Component\Notifier\Messenger\MessageHandler">
68+
<argument type="service" id="chatter.transports" />
69+
<tag name="messenger.message_handler" handles="Symfony\Component\Notifier\Message\ChatMessage" />
70+
</service>
71+
72+
<!-- texter -->
73+
<service id="texter" class="Symfony\Component\Notifier\Texter">
74+
<argument type="service" id="texter.transports" />
75+
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
76+
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
77+
</service>
78+
<service id="Symfony\Component\Notifier\TexterInterface" alias="texter" />
79+
80+
<service id="texter.transports" class="Symfony\Component\Notifier\Transport\Transports">
81+
<factory service="texter.transport_factory" method="fromStrings" />
82+
<argument type="collection" /> <!-- transports -->
83+
</service>
84+
85+
<service id="texter.transport_factory" class="Symfony\Component\Notifier\Transport">
86+
<argument type="tagged_iterator" tag="texter.transport_factory" />
87+
</service>
88+
89+
<service id="texter.messenger.sms_handler" class="Symfony\Component\Notifier\Messenger\MessageHandler">
90+
<argument type="service" id="texter.transports" />
91+
<tag name="messenger.message_handler" handles="Symfony\Component\Notifier\Message\SmsMessage" />
92+
</service>
93+
</services>
94+
</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
{
@@ -411,6 +412,13 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
411412
'transports' => [],
412413
'enabled' => !class_exists(FullStack::class) && class_exists(Mailer::class),
413414
],
415+
'notifier' => [
416+
'enabled' => !class_exists(FullStack::class) && class_exists(Notifier::class),
417+
'chatter_transports' => [],
418+
'texter_transports' => [],
419+
'channel_policy' => [],
420+
'admin_recipients' => [],
421+
],
414422
'error_controller' => 'error_controller',
415423
];
416424
}

0 commit comments

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