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 7e4abf5

Browse filesBrowse files
committed
feature #33968 [Notifier] Add Firebase bridge (Jeroeny)
This PR was squashed before being merged into the 5.1-dev branch (closes #33968). Discussion ---------- [Notifier] Add Firebase bridge | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | See #33687 | License | MIT This would add [Firebase](https://firebase.google.com) integration for the Notifier component. With Firebase you can send push notifications to the users of you Android and iOS app and website (formerly known as Google Cloud messaging). I'm not sure if it's possible to have this merged, like the other bridges. Or if I should create a stand-alone repository? That'd be fine too. Also it's now using the `ChatMessage` as implementation of `Symfony\Component\Notifier\Message\MessageInterface`, but I feel like this component could use a `PushMessage` or something similar. Although I'm not sure if it would contain more than `subject` that the `ChatMessage` does. Commits ------- 2776d2f [Notifier] Add Firebase bridge
2 parents 1d472a6 + 2776d2f commit 7e4abf5
Copy full SHA for 7e4abf5

16 files changed

+529
-0
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
use Symfony\Component\Messenger\Transport\TransportInterface;
9191
use Symfony\Component\Mime\MimeTypeGuesserInterface;
9292
use Symfony\Component\Mime\MimeTypes;
93+
use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory;
9394
use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory;
9495
use Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory;
9596
use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory;
@@ -2003,6 +2004,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $
20032004
NexmoTransportFactory::class => 'notifier.transport_factory.nexmo',
20042005
RocketChatTransportFactory::class => 'notifier.transport_factory.rocketchat',
20052006
TwilioTransportFactory::class => 'notifier.transport_factory.twilio',
2007+
FirebaseTransportFactory::class => 'notifier.transport_factory.firebase',
20062008
];
20072009

20082010
foreach ($classToServices as $class => $service) {

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.xml
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
<tag name="texter.transport_factory" />
3535
</service>
3636

37+
<service id="notifier.transport_factory.firebase" class="Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory" parent="notifier.transport_factory.abstract">
38+
<tag name="texter.transport_factory" />
39+
</service>
40+
3741
<service id="notifier.transport_factory.null" class="Symfony\Component\Notifier\Transport\NullTransportFactory" parent="notifier.transport_factory.abstract">
3842
<tag name="notifier.transport_factory" />
3943
</service>
+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
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
5.1.0
5+
-----
6+
7+
* Created the bridge
+67Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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\Component\Notifier\Bridge\Firebase;
13+
14+
use Symfony\Component\Notifier\Message\MessageOptionsInterface;
15+
16+
/**
17+
* @author Jeroen Spee <https://github.com/Jeroeny>
18+
*
19+
* @see https://firebase.google.com/docs/cloud-messaging/xmpp-server-ref.html
20+
*
21+
* @experimental in 5.1
22+
*/
23+
abstract class FirebaseOptions implements MessageOptionsInterface
24+
{
25+
/** @var string the recipient */
26+
private $to;
27+
28+
/**
29+
* @var array
30+
*
31+
* @see https://firebase.google.com/docs/cloud-messaging/xmpp-server-ref.html#notification-payload-support
32+
*/
33+
protected $options;
34+
35+
public function __construct(string $to, array $options)
36+
{
37+
$this->to = $to;
38+
$this->options = $options;
39+
}
40+
41+
public function toArray(): array
42+
{
43+
return [
44+
'to' => $this->to,
45+
'notification' => $this->options,
46+
];
47+
}
48+
49+
public function getRecipientId(): ?string
50+
{
51+
return $this->to;
52+
}
53+
54+
public function title(string $title): self
55+
{
56+
$this->options['title'] = $title;
57+
58+
return $this;
59+
}
60+
61+
public function body(string $body): self
62+
{
63+
$this->options['body'] = $body;
64+
65+
return $this;
66+
}
67+
}
+88Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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\Component\Notifier\Bridge\Firebase;
13+
14+
use Symfony\Component\Notifier\Exception\InvalidArgumentException;
15+
use Symfony\Component\Notifier\Exception\LogicException;
16+
use Symfony\Component\Notifier\Exception\TransportException;
17+
use Symfony\Component\Notifier\Message\ChatMessage;
18+
use Symfony\Component\Notifier\Message\MessageInterface;
19+
use Symfony\Component\Notifier\Transport\AbstractTransport;
20+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
21+
use Symfony\Contracts\HttpClient\HttpClientInterface;
22+
23+
/**
24+
* @author Jeroen Spee <https://github.com/Jeroeny>
25+
*
26+
* @experimental in 5.1
27+
*/
28+
final class FirebaseTransport extends AbstractTransport
29+
{
30+
protected const HOST = 'fcm.googleapis.com/fcm/send';
31+
32+
/** @var string */
33+
private $token;
34+
35+
public function __construct(string $token, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null)
36+
{
37+
$this->token = $token;
38+
$this->client = $client;
39+
40+
parent::__construct($client, $dispatcher);
41+
}
42+
43+
public function __toString(): string
44+
{
45+
return sprintf('firebase://%s', $this->getEndpoint());
46+
}
47+
48+
public function supports(MessageInterface $message): bool
49+
{
50+
return $message instanceof ChatMessage;
51+
}
52+
53+
protected function doSend(MessageInterface $message): void
54+
{
55+
if (!$message instanceof ChatMessage) {
56+
throw new LogicException(sprintf('The "%s" transport only supports instances of "%s" (instance of "%s" given).', __CLASS__, ChatMessage::class, \get_class($message)));
57+
}
58+
59+
$endpoint = sprintf('https://%s', $this->getEndpoint());
60+
$options = ($opts = $message->getOptions()) ? $opts->toArray() : [];
61+
if (!isset($options['to'])) {
62+
$options['to'] = $message->getRecipientId();
63+
}
64+
if (null === $options['to']) {
65+
throw new InvalidArgumentException(sprintf('The "%s" transport required the "to" option to be set', __CLASS__));
66+
}
67+
$options['notification'] = $options['notification'] ?? [];
68+
$options['notification']['body'] = $message->getSubject();
69+
$response = $this->client->request('POST', $endpoint, [
70+
'headers' => [
71+
'Authorization' => sprintf('key=%s', $this->token),
72+
],
73+
'json' => array_filter($options),
74+
]);
75+
76+
$contentType = $response->getHeaders(false)['Content-Type'] ?? '';
77+
$jsonContents = 0 === strpos($contentType, 'application/json') ? $response->toArray(false) : null;
78+
79+
if (200 !== $response->getStatusCode()) {
80+
$errorMessage = $jsonContents ? $jsonContents['results']['error'] : $response->getContent(false);
81+
82+
throw new TransportException(sprintf('Unable to post the Firebase message: %s.', $errorMessage), $response);
83+
}
84+
if ($jsonContents && isset($jsonContents['results']['error'])) {
85+
throw new TransportException(sprintf('Unable to post the Firebase message: %s.', $jsonContents['error']), $response);
86+
}
87+
}
88+
}
+44Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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\Component\Notifier\Bridge\Firebase;
13+
14+
use Symfony\Component\Notifier\Exception\UnsupportedSchemeException;
15+
use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
16+
use Symfony\Component\Notifier\Transport\Dsn;
17+
use Symfony\Component\Notifier\Transport\TransportInterface;
18+
19+
/**
20+
* @author Jeroen Spee <https://github.com/Jeroeny>
21+
*
22+
* @experimental in 5.1
23+
*/
24+
final class FirebaseTransportFactory extends AbstractTransportFactory
25+
{
26+
public function create(Dsn $dsn): TransportInterface
27+
{
28+
$scheme = $dsn->getScheme();
29+
$token = sprintf('%s:%s', $this->getUser($dsn), $this->getPassword($dsn));
30+
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
31+
$port = $dsn->getPort();
32+
33+
if ('firebase' === $scheme) {
34+
return (new FirebaseTransport($token, $this->client, $this->dispatcher))->setHost($host)->setPort($port);
35+
}
36+
37+
throw new UnsupportedSchemeException($dsn, 'firebase', $this->getSupportedSchemes());
38+
}
39+
40+
protected function getSupportedSchemes(): array
41+
{
42+
return ['firebase'];
43+
}
44+
}
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2019 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.
+96Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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\Component\Notifier\Bridge\Firebase\Notification;
13+
14+
use Symfony\Component\Notifier\Bridge\Firebase\FirebaseOptions;
15+
16+
/**
17+
* @experimental in 5.1
18+
*/
19+
final class AndroidNotification extends FirebaseOptions
20+
{
21+
public function channelId(string $channelId): self
22+
{
23+
$this->options['android_channel_id'] = $channelId;
24+
25+
return $this;
26+
}
27+
28+
public function icon(string $icon): self
29+
{
30+
$this->options['icon'] = $icon;
31+
32+
return $this;
33+
}
34+
35+
public function sound(string $sound): self
36+
{
37+
$this->options['sound'] = $sound;
38+
39+
return $this;
40+
}
41+
42+
public function tag(string $tag): self
43+
{
44+
$this->options['tag'] = $tag;
45+
46+
return $this;
47+
}
48+
49+
public function color(string $color): self
50+
{
51+
$this->options['color'] = $color;
52+
53+
return $this;
54+
}
55+
56+
public function clickAction(string $clickAction): self
57+
{
58+
$this->options['click_action'] = $clickAction;
59+
60+
return $this;
61+
}
62+
63+
public function bodyLocKey(string $bodyLocKey): self
64+
{
65+
$this->options['body_loc_key'] = $bodyLocKey;
66+
67+
return $this;
68+
}
69+
70+
/**
71+
* @param string[] $bodyLocArgs
72+
*/
73+
public function bodyLocArgs(array $bodyLocArgs): self
74+
{
75+
$this->options['body_loc_args'] = $bodyLocArgs;
76+
77+
return $this;
78+
}
79+
80+
public function titleLocKey(string $titleLocKey): self
81+
{
82+
$this->options['title_loc_key'] = $titleLocKey;
83+
84+
return $this;
85+
}
86+
87+
/**
88+
* @param string[] $titleLocArgs
89+
*/
90+
public function titleLocArgs(array $titleLocArgs): self
91+
{
92+
$this->options['title_loc_args'] = $titleLocArgs;
93+
94+
return $this;
95+
}
96+
}

0 commit comments

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