Biblioteca Web Push para PHP
WebPush pode ser usado para enviar mensagens push para endpoints conforme descrito no protocolo Web Push.
Essa mensagem push é então recebida pelo navegador, que pode então criar uma notificação usando o service worker e a API de Notificações.
PHP 8.1+ e as seguintes extensões:
Não há suporte e manutenção para versões mais antigas do PHP, porém você está livre para usar as seguintes versões compatíveis:
v1.x
v2.x
v3.x-v5.x
v6.x
v7.x
v8.x
Este README é compatível apenas com a versão mais recente. Cada versão da biblioteca possui uma tag git onde o README correspondente pode ser lido.
Use o compositor para baixar e instalar a biblioteca e suas dependências.
composer require minishlink/web-push
Um exemplo completo com frontend html+JS e backend php usando web-push-php
pode ser encontrado aqui: Minishlink/web-push-php-example
<?php
use Minishlink WebPush WebPush ;
use Minishlink WebPush Subscription ;
// store the client - side `PushSubscription` object ( calling ` . toJSON` on it) as - is and then create a WebPush Subscription from it
$ subscription = Subscription:: create ( json_decode ( $ clientSidePushSubscriptionJSON , true ));
// array of notifications
$ notifications = [
[
' subscription ' => $ subscription ,
' payload ' => ' {"message":"Hello World!"} ' ,
], [
// current PushSubscription format ( browsers might change this in the future)
' subscription ' => Subscription:: create ([
" endpoint " => " https://example.com/other/endpoint/of/another/vendor/abcdef... " ,
" keys " => [
' p256dh ' => ' (stringOf88Chars) ' ,
' auth ' => ' (stringOf24Chars) '
],
]),
' payload ' => ' {"message":"Hello World!"} ' ,
], [
// old Firefox PushSubscription format
' subscription ' => Subscription:: create ([
' endpoint ' => ' https://updates.push.services.mozilla.com/push/abc... ' , // Firefox 43 + ,
' publicKey ' => ' BPcMbnWQL5GOYX/5LKZXT6sLmHiMsJSiEvIFvfcDvX7IZ9qqtq68onpTPEYmyxSQNiH7UD/98AUcQ12kBoxz/0s= ' , // base 64 encoded , should be 88 chars
' authToken ' => ' CxVX6QsVToEGEcjfYPqXQw== ' , // base 64 encoded , should be 24 chars
]),
' payload ' => ' hello ! ' ,
], [
// old Chrome PushSubscription format
' subscription ' => Subscription:: create ([
' endpoint ' => ' https://fcm.googleapis.com/fcm/send/abcdef... ' ,
]),
' payload ' => null ,
], [
// old PushSubscription format
' subscription ' => Subscription:: create ([
' endpoint ' => ' https://example.com/other/endpoint/of/another/vendor/abcdef... ' ,
' publicKey ' => ' (stringOf88Chars) ' ,
' authToken ' => ' (stringOf24Chars) ' ,
' contentEncoding ' => ' aesgcm ' , // one of PushManager . supportedContentEncodings
]),
' payload ' => ' {"message":"test"} ' ,
]
];
$ webPush = new WebPush ();
// send multiple notifications with payload
foreach ( $ notifications as $ notification ) {
$ webPush -> queueNotification (
$ notification [ ' subscription ' ],
$ notification [ ' payload ' ] // optional ( defaults null)
);
}
/**
* Check sent results
* @ var MessageSentReport $ report
* /
foreach ( $ webPush -> flush () as $ report ) {
$ endpoint = $ report -> getRequest ()-> getUri ()-> __toString ();
if ( $ report -> isSuccess ()) {
echo " [v] Message sent successfully for subscription { $ endpoint } . " ;
} else {
echo " [x] Message failed to sent for subscription { $ endpoint } : { $ report -> getReason ()}" ;
}
}
/**
* send one notification and flush directly
* @ var MessageSentReport $ report
* /
$ report = $ webPush -> sendOneNotification (
$ notifications [ 0 ][ ' subscription ' ],
$ notifications [ 0 ][ ' payload ' ], // optional ( defaults null)
);
Os navegadores precisam verificar sua identidade. Um padrão chamado VAPID pode autenticar você em todos os navegadores. Você precisará criar e fornecer uma chave pública e privada para o seu servidor. Essas chaves devem ser armazenadas com segurança e não devem ser alteradas.
Você pode especificar seus detalhes de autenticação ao instanciar o WebPush. As chaves podem ser passadas diretamente (recomendado) ou você pode carregar um arquivo PEM ou seu conteúdo:
<?php
use Minishlink WebPush WebPush ;
$ endpoint = ' https://fcm.googleapis.com/fcm/send/abcdef... ' ; // Chrome
$ auth = [
' VAPID ' => [
' subject ' => ' mailto:[email protected] ' , // can be a mailto : or your website address
' publicKey ' => ' ~88 chars ' , // (recommended) uncompressed public key P - 256 encoded in Base64 - URL
' privateKey ' => ' ~44 chars ' , // (recommended) in fact the secret multiplier of the private key encoded in Base64 - URL
' pemFile ' => ' path/to/pem ' , // if you have a PEM file and can link to it on your filesystem
' pem ' => ' pemFileContent ' , // if you have a PEM file and want to hardcode its content
],
];
$ webPush = new WebPush ( $ auth );
$ webPush -> queueNotification (...);
Para gerar a chave pública e secreta descompactada, codificada em Base64, digite o seguinte no bash do Linux:
$ openssl ecparam -genkey -name prime256v1 -out private_key.pem
$ openssl ec -in private_key.pem -pubout -outform DER | tail -c 65 | base64 | tr -d ' = ' | tr ' /+ ' ' _- ' >> public_key.txt
$ openssl ec -in private_key.pem -outform DER | tail -c +8 | head -c 32 | base64 | tr -d ' = ' | tr ' /+ ' ' _- ' >> private_key.txt
Se você não conseguir acessar um bash do Linux, poderá imprimir a saída da função createVapidKeys
:
var_dump ( VAPID :: createVapidKeys ()); // store the keys afterwards
No lado do cliente, não se esqueça de se inscrever com a chave pública VAPID como applicationServerKey
: ( fonte urlBase64ToUint8Array
aqui)
serviceWorkerRegistration . pushManager . subscribe ( {
userVisibleOnly : true ,
applicationServerKey : urlBase64ToUint8Array ( vapidPublicKey )
} )
Os cabeçalhos VAPID usam um JSON Web Token (JWT) para verificar sua identidade. Essa carga útil do token inclui o protocolo e o nome do host do endpoint incluído na assinatura e um carimbo de data/hora de expiração (geralmente entre 12 e 24 horas) e é assinado usando sua chave pública e privada. Dado isso, duas notificações enviadas para o mesmo serviço push usarão o mesmo token, então você pode reutilizá-las para a mesma sessão de liberação para aumentar o desempenho usando:
$ webPush -> setReuseVAPIDHeaders ( true );
Cada notificação pode ter um tempo de vida, urgência e tópico específicos. O padrão WebPush afirma que urgency
é opcional, mas alguns usuários relatam que o Safari gera erros quando não é especificado. Isso pode ser corrigido no futuro. Você pode alterar as opções padrão com setDefaultOptions()
ou no construtor:
<?php
use Minishlink WebPush WebPush ;
$ defaultOptions = [
' TTL ' => 300 , // defaults to 4 weeks
' urgency ' => ' normal ' , // protocol defaults to "normal" . ( very - low , low , normal , or high)
' topic ' => ' newEvent ' , // not defined by default . Max . 32 characters from the URL or filename - safe Base64 characters sets
' batchSize ' => 200 , // defaults to 1000
];
// for every notification
$ webPush = new WebPush ([], $ defaultOptions );
$ webPush -> setDefaultOptions ( $ defaultOptions );
// or for one notification
$ webPush -> sendOneNotification ( $ subscription , $ payload , [ ' TTL ' => 5000 ]);
Time To Live (TTL, em segundos) é quanto tempo uma mensagem push é retida pelo serviço push (por exemplo, Mozilla) caso o navegador do usuário ainda não esteja acessível (por exemplo, não esteja conectado). Você pode querer usar muito tempo para notificações importantes. O TTL padrão é de 4 semanas. No entanto, se você enviar várias notificações não essenciais, defina um TTL como 0: a notificação push será entregue somente se o usuário estiver conectado no momento. Para outros casos, você deve usar no mínimo um dia se seus usuários tiverem vários fusos horários e, caso contrário, várias horas serão suficientes.
A urgência pode ser “muito baixa”, “baixa”, “normal” ou “alta”. Se o fornecedor do navegador tiver implementado esse recurso, ele economizará bateria em dispositivos móveis (cf. protocolo).
Esta string fará com que o fornecedor mostre ao usuário apenas a última notificação deste tópico (cf. protocolo).
Se você enviar dezenas de milhares de notificações por vez, poderá ocorrer estouro de memória devido à forma como os endpoints são chamados no Guzzle. Para corrigir isso, o WebPush envia notificações em lotes. O tamanho padrão é 1000. Dependendo da configuração do servidor (memória), você pode diminuir esse número. Faça isso ao instanciar WebPush ou chamar setDefaultOptions
. Ou, se você quiser personalizar isso para uma liberação específica, forneça-o como parâmetro: $webPush->flush($batchSize)
.
Você pode ver o que o servidor do fornecedor do navegador envia de volta caso encontre um erro (expiração da assinatura push, parâmetros errados...).
sendOneNotification()
retorna um MessageSentReport
flush()
retorna um Generator
com objetos MessageSentReport
. Para percorrer os resultados, basta passá-los para foreach
. Você também pode usar iterator_to_array
para verificar o conteúdo durante a depuração. <?php
/** @var MinishlinkWebPush MessageSentReport $ report * /
foreach ( $ webPush -> flush () as $ report ) {
$ endpoint = $ report -> getEndpoint ();
if ( $ report -> isSuccess ()) {
echo " [v] Message sent successfully for subscription { $ endpoint } . " ;
} else {
echo " [x] Message failed to sent for subscription { $ endpoint } : { $ report -> getReason ()}" ;
// also available ( to get more info )
/** @var Psr Http Message RequestInterface $ requestToPushService * /
$ requestToPushService = $ report -> getRequest ();
/** @var Psr Http Message ResponseInterface $ responseOfPushService * /
$ responseOfPushService = $ report -> getResponse ();
/** @var string $ failReason */
$ failReason = $ report -> getReason ();
/** @var bool $ isTheEndpointWrongOrExpired */
$ isTheEndpointWrongOrExpired = $ report -> isSubscriptionExpired ();
}
}
ATENÇÃO: Você só pode iterar uma vez no objeto Generator
.
Os erros do Firefox estão listados na documentação do autopush.
As cargas úteis são criptografadas pela biblioteca. O comprimento máximo da carga útil é teoricamente 4.078 bytes (ou caracteres ASCII). Porém, por motivos de compatibilidade (arquivado), sua carga útil deve ter menos de 3.052 bytes.
A biblioteca preenche a carga por padrão. Isso é mais seguro, mas diminui o desempenho do servidor e do dispositivo do usuário.
Quando você criptografa uma string de determinado comprimento, a string resultante sempre terá o mesmo comprimento, não importa quantas vezes você criptografe a string inicial. Isso pode fazer com que os invasores adivinhem o conteúdo da carga. Para contornar isso, esta biblioteca adiciona algum preenchimento nulo à carga inicial, para que todas as entradas do processo de criptografia tenham o mesmo comprimento. Dessa forma, toda a saída do processo de criptografia também terá o mesmo comprimento e os invasores não conseguirão adivinhar o conteúdo da sua carga útil.
Criptografar mais bytes exige mais tempo de execução do servidor e também torna o dispositivo do usuário mais lento com a descriptografia. Além disso, o envio e o recebimento do pacote levarão mais tempo. Também não é muito amigável com usuários que possuem planos de dados limitados.
Você pode personalizar o preenchimento automático para melhor atender às suas necessidades.
Aqui estão algumas idéias de configurações:
Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH
(2820 bytes) para fins de compatibilidade com Firefox para Android (Veja #108)Encryption::MAX_PAYLOAD_LENGTH
(4078 bytes) para segurança máximafalse
para desempenho máximoX
bytes, defina-as como X
para obter o melhor equilíbrio entre segurança e desempenho. <?php
use Minishlink WebPush WebPush ;
$ webPush = new WebPush ();
$ webPush -> setAutomaticPadding ( false ); // disable automatic padding
$ webPush -> setAutomaticPadding ( 512 ); // enable automatic padding to 512 bytes ( you should make sure that your payload is less than 512 bytes , or else an attacker could guess the content )
$ webPush -> setAutomaticPadding ( true ); // enable automatic padding to default maximum compatibility length
WebPush usa Guzzle. Ele utilizará o cliente mais adequado que encontrar, e na maioria das vezes será MultiCurl
, que permite enviar múltiplas notificações em paralelo.
Você pode personalizar as opções de solicitação padrão e o tempo limite ao instanciar o WebPush:
<?php
use Minishlink WebPush WebPush ;
$ timeout = 20 ; // seconds
$ clientOptions = [
GuzzleHttp RequestOptions:: ALLOW_REDIRECTS => false ,
]; // see GuzzleHttp RequestOptions
$ webPush = new WebPush ([], [], $ timeout , $ clientOptions );
Os seguintes estão disponíveis:
Sinta-se à vontade para adicionar o seu próprio!
A carga útil é criptografada de acordo com o padrão Message Encryption for Web Push, usando a chave pública do usuário e o segredo de autenticação que você pode obter seguindo a especificação da API Web Push.
Internamente, o WebPush usa a estrutura WebToken e OpenSSL para lidar com a geração e criptografia de chaves de criptografia.
Aqui estão algumas ideias:
defaultOptions
ou como parâmetro para flush()
)flushPooled()
em vez de flush()
. O primeiro utiliza solicitações simultâneas, acelerando o processo e muitas vezes dobrando a velocidade das solicitações.Sua instalação não possui alguns certificados.
php.ini
: depois de [curl]
, digite curl.cainfo = /path/to/cacert.pem
.Você também pode forçar o uso de um cliente sem verificação por pares.
Certifique-se de exigir o autoloader do Composer.
require __DIR__ . ' /path/to/vendor/autoload.php ' ;
Certifique-se de ter campos de banco de dados grandes o suficiente para o comprimento dos dados que você está armazenando (#233). Para o endpoint, os usuários relataram que o comprimento do URL não excede 500 caracteres, mas isso pode evoluir para que você possa configurá-lo para o limite de 2.048 caracteres da maioria dos navegadores.
Veja a edição nº 58.
Este serviço não existe mais. Ele foi substituído pelo Firebase Cloud Messaging (FCM) do Google em 29 de maio de 2019.
Esta biblioteca não oferece suporte ao Firebase Cloud Messaging (FCM). As assinaturas antigas do Chrome (anteriores a 2018 e VAPID) usam o protocolo HTTP legado do Firebase Cloud Messaging (FCM), que está obsoleto desde 2023 e deixará de funcionar em junho de 2024. O suporte para esta assinatura desatualizada foi removido.
Não se confunda, pois o protocolo HTTP legado e o Web Push com VAPID usam o URL de endpoint idêntico:
https://fcm.googleapis.com/fcm/send
Web Push com VAPID permanecerá disponível neste URL. Nenhuma ação adicional é necessária atualmente.
Os fornecedores de navegadores não permitem o envio de dados usando a API Push sem criar uma notificação. Use algumas APIs alternativas como WebSocket/WebTransport ou sincronização em segundo plano.
WebPush é para aplicativos da web. Você precisa de algo como RMSPushNotificationsBundle (Symfony).
Esta biblioteca foi inspirada na biblioteca Node.js web-push-libs/web-push.
server.js
: ele deve ser substituído pelo código do seu servidor PHP com esta biblioteca)MIT