web-push-libs / web-push-php

Web Push library for PHP
MIT License
1.69k stars 295 forks source link

Use protected properties and methods in WebPush class #324

Closed Kingdutch closed 3 years ago

Kingdutch commented 3 years ago

This allows other projects to extend the WebPush class and alter the way it works. An example use-case is to replace Guzzle with a ReactPHP based HTTP client for better integration in long-running asynchronous processes.

Kingdutch commented 3 years ago

As an example the following class allows sending push notifications using ReactPHP. I've intentionally positioned this next to the existing methods because creating new methods rather than using the same function signatures was easier.

<?php

use Minishlink\WebPush\MessageSentReport;
use Minishlink\WebPush\WebPush;
use Psr\Http\Message\ResponseInterface;
use React\Http\Browser;
use React\Promise\Promise;
use function React\Promise\all as promise_all;
use function React\Promise\resolve as promise_resolve;

/**
 * WebPush implementation that uses the React HTTP client instead of Guzzle.
 */
class ReactWebPush extends WebPush {

  /**
   * A ReactPHP HTTP client connected to the application's event loop.
   */
  protected Browser $reactClient;

  /**
   * Set the ReactPHP Browser to use.
   *
   * This is set in a separate method rather than an overwritten __construct so
   * that the calling of the WebPush library remains the same.
   *
   * @param \React\Http\Browser $client
   *   A ReactPHP HTTP client connected to the application's event loop.
   *
   * @return $this
   *   This WebPush instance.
   */
  public function setClient(Browser $client) : self {
    $this->reactClient = $client;
    return $this;
  }

  /**
   * Flush notifications. Triggers the requests.
   *
   * Uses ReactPHP as HTTP client instead of Guzzle for better integration with
   * the event loop.
   *
   * @return \React\Promise\Promise
   *   A promise that resolves to an array of MessageSentReport instances.
   */
  public function flushAsync() : Promise {
    if (empty($this->notifications)) {
      return promise_resolve([]);
    }

    $promises = [];
    $requests = $this->prepare($this->notifications);
    $this->notifications = [];

    /** @var \GuzzleHttp\Psr7\Request $request */
    foreach ($requests as $request) {
      // Convert a Guzzle request to our ReactPHP client's method call.
      $promises[] = $this->reactClient
        ->request(
          $request->getMethod(),
          (string) $request->getUri(),
          $request->getHeaders(),
          $request->getBody()->getContents()
        )
        ->then(
          fn (ResponseInterface $response) => new MessageSentReport($request, $response),
          fn (\Exception $e) => new MessageSentReport($request, method_exists($e, "getResponse") ? $e->getResponse() : NULL, FALSE, $e->getMessage())
        );
    }

    return promise_all($promises);
  }

}