You are here

class Handler in JSON-RPC 2.x

Same name and namespace in other branches
  1. 8 src/Handler.php \Drupal\jsonrpc\Handler

Manages all the JSON-RPC business logic.

Hierarchy

Expanded class hierarchy of Handler

2 files declare their use of Handler
SecondMethod.php in tests/modules/jsonrpc_test/src/Plugin/jsonrpc/Method/SecondMethod.php
ThirdMethod.php in tests/modules/jsonrpc_test/src/Plugin/jsonrpc/Method/ThirdMethod.php
1 string reference to 'Handler'
jsonrpc.services.yml in ./jsonrpc.services.yml
jsonrpc.services.yml
1 service uses Handler
jsonrpc.handler in ./jsonrpc.services.yml
Drupal\jsonrpc\Handler

File

src/Handler.php, line 21

Namespace

Drupal\jsonrpc
View source
class Handler implements HandlerInterface {

  /**
   * The support JSON-RPC version.
   *
   * @var string
   */
  const SUPPORTED_VERSION = '2.0';

  /**
   * The JSON-RPC method plugin manager.
   *
   * @var \Drupal\Component\Plugin\PluginManagerInterface
   */
  protected $methodManager;

  /**
   * The renderer.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

  /**
   * Handler constructor.
   *
   * @param \Drupal\Component\Plugin\PluginManagerInterface $method_manager
   *   The plugin manager for the JSON RPC methods.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The Drupal renderer.
   */
  public function __construct(PluginManagerInterface $method_manager, RendererInterface $renderer) {
    $this->methodManager = $method_manager;
    $this->renderer = $renderer;
  }

  /**
   * {@inheritdoc}
   */
  public static function supportedVersion() {
    return static::SUPPORTED_VERSION;
  }

  /**
   * {@inheritdoc}
   */
  public function batch(array $requests) {
    return array_filter(array_map(function (Request $request) {
      return $this
        ->doRequest($request);
    }, $requests));
  }

  /**
   * {@inheritdoc}
   */
  public function supportedMethods() {
    return $this->methodManager
      ->getDefinitions();
  }

  /**
   * {@inheritdoc}
   */
  public function supportsMethod($name) {
    return !is_null($this
      ->getMethod($name));
  }

  /**
   * {@inheritdoc}
   */
  public function availableMethods(AccountInterface $account = NULL) {
    return array_filter($this
      ->supportedMethods(), function (MethodInterface $method) {
      return $method
        ->access('execute');
    });
  }

  /**
   * {@inheritdoc}
   */
  public function getMethod($name) {
    return $this->methodManager
      ->getDefinition($name, FALSE);
  }

  /**
   * Executes an RPC call and returns a JSON-RPC response.
   *
   * @param \Drupal\jsonrpc\Object\Request $request
   *   The JSON-RPC request.
   *
   * @return \Drupal\jsonrpc\Object\Response|null
   *   The JSON-RPC response.
   */
  protected function doRequest(Request $request) {

    // Helper closure to handle eventual exceptions.
    $handle_exception = function ($e, Request $request) {
      if (!$e instanceof JsonRpcException) {
        $id = $request
          ->isNotification() ? FALSE : $request
          ->id();
        $e = JsonRpcException::fromPrevious($e, $id);
      }
      return $e
        ->getResponse();
    };
    try {
      $context = new RenderContext();
      $result = $this->renderer
        ->executeInRenderContext($context, function () use ($request) {
        return $this
          ->doExecution($request);
      });
      if ($request
        ->isNotification()) {
        return NULL;
      }
      $rpc_response = $result instanceof Response ? $result : new Response(static::SUPPORTED_VERSION, $request
        ->id(), $result);
      $methodPluginClass = $this
        ->getMethod($request
        ->getMethod())
        ->getClass();
      $result_schema = call_user_func([
        $methodPluginClass,
        'outputSchema',
      ]);
      $rpc_response
        ->setResultSchema($result_schema);
      $response_headers = $this
        ->getMethod($request
        ->getMethod())->responseHeaders;
      $rpc_response
        ->getHeaders()
        ->add($response_headers);
      if (!$context
        ->isEmpty()) {

        /** @var \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata */
        $bubbleable_metadata = $context
          ->pop();
        $rpc_response
          ->addCacheableDependency($bubbleable_metadata);
        if ($rpc_response instanceof AttachmentsInterface) {
          $rpc_response
            ->addAttachments($bubbleable_metadata
            ->getAttachments());
        }
      }
      return $rpc_response;
    } catch (\Throwable $e) {
      return $handle_exception($e, $request);
    } catch (\Exception $e) {
      return $handle_exception($e, $request);
    }
  }

  /**
   * Gets an anonymous function which executes the RPC method.
   *
   * @param \Drupal\jsonrpc\Object\Request $request
   *   The JSON-RPC request.
   *
   * @return \Drupal\jsonrpc\Object\Response|null
   *   The JSON-RPC response.
   *
   * @throws \Drupal\jsonrpc\Exception\JsonRpcException
   */
  protected function doExecution(Request $request) {
    if ($method = $this
      ->getMethod($request
      ->getMethod())) {
      $this
        ->checkAccess($method);
      $configuration = [
        HandlerInterface::JSONRPC_REQUEST_KEY => $request,
      ];
      $executable = $this
        ->getExecutable($method, $configuration);
      return $request
        ->hasParams() ? $executable
        ->execute($request
        ->getParams()) : $executable
        ->execute(new ParameterBag([]));
    }
    else {
      throw JsonRpcException::fromError(Error::methodNotFound($method
        ->id()));
    }
  }

  /**
   * Gets an executable instance of an RPC method.
   *
   * @param \Drupal\jsonrpc\MethodInterface $method
   *   The method definition.
   * @param array $configuration
   *   Method configuration.
   *
   * @return object
   *   The executable method.
   *
   * @throws \Drupal\jsonrpc\Exception\JsonRpcException
   *   In case of error.
   */
  protected function getExecutable(MethodInterface $method, array $configuration) {
    try {
      return $this->methodManager
        ->createInstance($method
        ->id(), $configuration);
    } catch (PluginException $e) {
      throw JsonRpcException::fromError(Error::methodNotFound($method
        ->id()));
    }
  }

  /**
   * Check execution access.
   *
   * @param \Drupal\jsonrpc\MethodInterface $method
   *   The method for which to check access.
   *
   * @throws \Drupal\jsonrpc\Exception\JsonRpcException
   */
  protected function checkAccess(MethodInterface $method) {

    // TODO: Add cacheability metadata here.

    /* @var \Drupal\jsonrpc\MethodInterface $method_definition */
    $access_result = $method
      ->access('execute', NULL, TRUE);
    if (!$access_result
      ->isAllowed()) {
      $reason = 'Access Denied';
      if ($access_result instanceof AccessResultReasonInterface && ($detail = $access_result
        ->getReason())) {
        $reason .= ': ' . $detail;
      }
      throw JsonRpcException::fromError(Error::invalidRequest($reason, $access_result));
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Handler::$methodManager protected property The JSON-RPC method plugin manager.
Handler::$renderer protected property The renderer.
Handler::availableMethods public function The methods which are available to the given account. Overrides HandlerInterface::availableMethods
Handler::batch public function Executes a batch of remote procedure calls. Overrides HandlerInterface::batch
Handler::checkAccess protected function Check execution access.
Handler::doExecution protected function Gets an anonymous function which executes the RPC method.
Handler::doRequest protected function Executes an RPC call and returns a JSON-RPC response.
Handler::getExecutable protected function Gets an executable instance of an RPC method.
Handler::getMethod public function Gets a method definition by method name. Overrides HandlerInterface::getMethod
Handler::supportedMethods public function The methods supported by the handler. Overrides HandlerInterface::supportedMethods
Handler::supportedVersion public static function The supported JSON-RPC version. Overrides HandlerInterface::supportedVersion
Handler::SUPPORTED_VERSION constant The support JSON-RPC version.
Handler::supportsMethod public function Whether the given method is supported. Overrides HandlerInterface::supportsMethod
Handler::__construct public function Handler constructor.
HandlerInterface::JSONRPC_REQUEST_KEY constant The configuration array key for the JSON-RPC request object.