<?php
declare(strict_types=1);
namespace Kreait\Firebase\Messaging;
use JsonSerializable;
use Kreait\Firebase\Exception\InvalidArgumentException;
use Stringable;
use function in_array;
use function mb_detect_encoding;
use function mb_detect_order;
use function mb_strtolower;
use function str_starts_with;
final class MessageData implements JsonSerializable
{
/**
* @param array<non-empty-string, string> $data
*/
private function __construct(private readonly array $data)
{
}
/**
* @param array<non-empty-string, Stringable|string> $data
*/
public static function fromArray(array $data): self
{
$validated = [];
foreach ($data as $key => $value) {
$value = (string) $value;
if (self::isBinary($value)) {
throw new InvalidArgumentException(
"The message data field '{$key}' seems to contain binary data. As this can lead to broken messages, "
.'please convert it to a string representation first, e.g. with bin2hex() or base64encode().',
);
}
$key = self::assertValidKey($key);
$validated[$key] = $value;
}
return new self($validated);
}
/**
* @return array<non-empty-string, string>
*/
public function toArray(): array
{
return $this->data;
}
public function jsonSerialize(): array
{
return $this->toArray();
}
private static function isBinary(string $value): bool
{
return mb_detect_encoding($value, (array) mb_detect_order(), true) === false;
}
/**
* @see https://firebase.google.com/docs/cloud-messaging/concept-options#data_messages
*
* @param non-empty-string $key
*
* @return non-empty-string
*/
private static function assertValidKey(string $key): string
{
$check = mb_strtolower($key);
// According to the docs, "notification" is reserved, but it's still accepted ¯\_(ツ)_/¯
$reservedWords = ['from', /* 'notification', */ 'message_type'];
$reservedPrefixes = ['google', 'gcm'];
if (in_array($check, $reservedWords, true)) {
throw new InvalidArgumentException("'{$key}' is a reserved word and can not be used as a key in FCM data payloads");
}
foreach ($reservedPrefixes as $prefix) {
if (str_starts_with($check, $prefix)) {
throw new InvalidArgumentException("'{$prefix}' is a reserved prefix and can not be used as a key in FCM data payloads");
}
}
return $key;
}
}
|