diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2020-12-14 15:56:07 +0100 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2020-12-16 13:13:05 +0100 |
commit | 6995223b1ed202c7f8e920e83cb5b53efd7ce761 (patch) | |
tree | 76e839b9c3de3b4751a993f24f35f0cb93c0dbbd /lib | |
parent | d37034f1612279b07c78284771ac73fbd5a2a407 (diff) | |
download | nextcloud-server-6995223b1ed202c7f8e920e83cb5b53efd7ce761.tar.gz nextcloud-server-6995223b1ed202c7f8e920e83cb5b53efd7ce761.zip |
Add well known handlers API
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 7 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 7 | ||||
-rw-r--r-- | lib/private/AppFramework/Bootstrap/RegistrationContext.php | 24 | ||||
-rw-r--r-- | lib/private/Http/WellKnown/RequestManager.php | 124 | ||||
-rw-r--r-- | lib/public/AppFramework/Bootstrap/IRegistrationContext.php | 14 | ||||
-rw-r--r-- | lib/public/Http/WellKnown/GenericResponse.php | 51 | ||||
-rw-r--r-- | lib/public/Http/WellKnown/IHandler.php | 48 | ||||
-rw-r--r-- | lib/public/Http/WellKnown/IRequestContext.php | 46 | ||||
-rw-r--r-- | lib/public/Http/WellKnown/IResponse.php | 39 | ||||
-rw-r--r-- | lib/public/Http/WellKnown/JrdResponse.php | 170 |
10 files changed, 530 insertions, 0 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index c033a237ca4..282d8f402a9 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -359,6 +359,11 @@ return array( 'OCP\\Http\\Client\\IClientService' => $baseDir . '/lib/public/Http/Client/IClientService.php', 'OCP\\Http\\Client\\IResponse' => $baseDir . '/lib/public/Http/Client/IResponse.php', 'OCP\\Http\\Client\\LocalServerException' => $baseDir . '/lib/public/Http/Client/LocalServerException.php', + 'OCP\\Http\\WellKnown\\GenericResponse' => $baseDir . '/lib/public/Http/WellKnown/GenericResponse.php', + 'OCP\\Http\\WellKnown\\IHandler' => $baseDir . '/lib/public/Http/WellKnown/IHandler.php', + 'OCP\\Http\\WellKnown\\IRequestContext' => $baseDir . '/lib/public/Http/WellKnown/IRequestContext.php', + 'OCP\\Http\\WellKnown\\IResponse' => $baseDir . '/lib/public/Http/WellKnown/IResponse.php', + 'OCP\\Http\\WellKnown\\JrdResponse' => $baseDir . '/lib/public/Http/WellKnown/JrdResponse.php', 'OCP\\IAddressBook' => $baseDir . '/lib/public/IAddressBook.php', 'OCP\\IAppConfig' => $baseDir . '/lib/public/IAppConfig.php', 'OCP\\IAvatar' => $baseDir . '/lib/public/IAvatar.php', @@ -892,6 +897,7 @@ return array( 'OC\\Core\\Controller\\UserController' => $baseDir . '/core/Controller/UserController.php', 'OC\\Core\\Controller\\WalledGardenController' => $baseDir . '/core/Controller/WalledGardenController.php', 'OC\\Core\\Controller\\WebAuthnController' => $baseDir . '/core/Controller/WebAuthnController.php', + 'OC\\Core\\Controller\\WellKnownController' => $baseDir . '/core/Controller/WellKnownController.php', 'OC\\Core\\Controller\\WhatsNewController' => $baseDir . '/core/Controller/WhatsNewController.php', 'OC\\Core\\Controller\\WipeController' => $baseDir . '/core/Controller/WipeController.php', 'OC\\Core\\Data\\LoginFlowV2Credentials' => $baseDir . '/core/Data/LoginFlowV2Credentials.php', @@ -1136,6 +1142,7 @@ return array( 'OC\\Http\\Client\\ClientService' => $baseDir . '/lib/private/Http/Client/ClientService.php', 'OC\\Http\\Client\\Response' => $baseDir . '/lib/private/Http/Client/Response.php', 'OC\\Http\\CookieHelper' => $baseDir . '/lib/private/Http/CookieHelper.php', + 'OC\\Http\\WellKnown\\RequestManager' => $baseDir . '/lib/private/Http/WellKnown/RequestManager.php', 'OC\\InitialStateService' => $baseDir . '/lib/private/InitialStateService.php', 'OC\\Installer' => $baseDir . '/lib/private/Installer.php', 'OC\\IntegrityCheck\\Checker' => $baseDir . '/lib/private/IntegrityCheck/Checker.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 9afd8d98377..bb11975d089 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -388,6 +388,11 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Http\\Client\\IClientService' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IClientService.php', 'OCP\\Http\\Client\\IResponse' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IResponse.php', 'OCP\\Http\\Client\\LocalServerException' => __DIR__ . '/../../..' . '/lib/public/Http/Client/LocalServerException.php', + 'OCP\\Http\\WellKnown\\GenericResponse' => __DIR__ . '/../../..' . '/lib/public/Http/WellKnown/GenericResponse.php', + 'OCP\\Http\\WellKnown\\IHandler' => __DIR__ . '/../../..' . '/lib/public/Http/WellKnown/IHandler.php', + 'OCP\\Http\\WellKnown\\IRequestContext' => __DIR__ . '/../../..' . '/lib/public/Http/WellKnown/IRequestContext.php', + 'OCP\\Http\\WellKnown\\IResponse' => __DIR__ . '/../../..' . '/lib/public/Http/WellKnown/IResponse.php', + 'OCP\\Http\\WellKnown\\JrdResponse' => __DIR__ . '/../../..' . '/lib/public/Http/WellKnown/JrdResponse.php', 'OCP\\IAddressBook' => __DIR__ . '/../../..' . '/lib/public/IAddressBook.php', 'OCP\\IAppConfig' => __DIR__ . '/../../..' . '/lib/public/IAppConfig.php', 'OCP\\IAvatar' => __DIR__ . '/../../..' . '/lib/public/IAvatar.php', @@ -921,6 +926,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Controller\\UserController' => __DIR__ . '/../../..' . '/core/Controller/UserController.php', 'OC\\Core\\Controller\\WalledGardenController' => __DIR__ . '/../../..' . '/core/Controller/WalledGardenController.php', 'OC\\Core\\Controller\\WebAuthnController' => __DIR__ . '/../../..' . '/core/Controller/WebAuthnController.php', + 'OC\\Core\\Controller\\WellKnownController' => __DIR__ . '/../../..' . '/core/Controller/WellKnownController.php', 'OC\\Core\\Controller\\WhatsNewController' => __DIR__ . '/../../..' . '/core/Controller/WhatsNewController.php', 'OC\\Core\\Controller\\WipeController' => __DIR__ . '/../../..' . '/core/Controller/WipeController.php', 'OC\\Core\\Data\\LoginFlowV2Credentials' => __DIR__ . '/../../..' . '/core/Data/LoginFlowV2Credentials.php', @@ -1165,6 +1171,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Http\\Client\\ClientService' => __DIR__ . '/../../..' . '/lib/private/Http/Client/ClientService.php', 'OC\\Http\\Client\\Response' => __DIR__ . '/../../..' . '/lib/private/Http/Client/Response.php', 'OC\\Http\\CookieHelper' => __DIR__ . '/../../..' . '/lib/private/Http/CookieHelper.php', + 'OC\\Http\\WellKnown\\RequestManager' => __DIR__ . '/../../..' . '/lib/private/Http/WellKnown/RequestManager.php', 'OC\\InitialStateService' => __DIR__ . '/../../..' . '/lib/private/InitialStateService.php', 'OC\\Installer' => __DIR__ . '/../../..' . '/lib/private/Installer.php', 'OC\\IntegrityCheck\\Checker' => __DIR__ . '/../../..' . '/lib/private/IntegrityCheck/Checker.php', diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 12fca23c51f..3e5d70d6f1c 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -72,6 +72,9 @@ class RegistrationContext { /** @var array[] */ private $initialStates = []; + /** @var array[] */ + private $wellKnownHandlers = []; + /** @var ILogger */ private $logger; @@ -174,6 +177,13 @@ class RegistrationContext { $class ); } + + public function registerWellKnownHandler(string $class): void { + $this->context->registerWellKnown( + $this->appId, + $class + ); + } }; } @@ -260,6 +270,13 @@ class RegistrationContext { ]; } + public function registerWellKnown(string $appId, string $class): void { + $this->wellKnownHandlers[] = [ + 'appId' => $appId, + 'class' => $class, + ]; + } + /** * @param App[] $apps */ @@ -437,4 +454,11 @@ class RegistrationContext { public function getInitialStates(): array { return $this->initialStates; } + + /** + * @return array[] + */ + public function getWellKnownHandlers(): array { + return $this->wellKnownHandlers; + } } diff --git a/lib/private/Http/WellKnown/RequestManager.php b/lib/private/Http/WellKnown/RequestManager.php new file mode 100644 index 00000000000..d17ea5c671b --- /dev/null +++ b/lib/private/Http/WellKnown/RequestManager.php @@ -0,0 +1,124 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\Http\WellKnown; + +use OC\AppFramework\Bootstrap\Coordinator; +use OCP\AppFramework\QueryException; +use OCP\Http\WellKnown\IHandler; +use OCP\Http\WellKnown\IRequestContext; +use OCP\Http\WellKnown\IResponse; +use OCP\Http\WellKnown\JrdResponse; +use OCP\IRequest; +use OCP\IServerContainer; +use Psr\Log\LoggerInterface; +use RuntimeException; +use function array_reduce; + +class RequestManager { + + /** @var Coordinator */ + private $coordinator; + + /** @var IServerContainer */ + private $container; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(Coordinator $coordinator, + IServerContainer $container, + LoggerInterface $logger) { + $this->coordinator = $coordinator; + $this->container = $container; + $this->logger = $logger; + } + + public function process(string $service, IRequest $request): ?IResponse { + $handlers = $this->loadHandlers(); + $context = new class($request) implements IRequestContext { + /** @var IRequest */ + private $request; + + public function __construct(IRequest $request) { + $this->request = $request; + } + + public function getHttpRequest(): IRequest { + return $this->request; + } + }; + + $subject = $request->getParam('resource'); + $initialResponse = new JrdResponse($subject ?? ''); + $finalResponse = array_reduce($handlers, function (?IResponse $previousResponse, IHandler $handler) use ($context, $service) { + return $handler->handle($service, $context, $previousResponse); + }, $initialResponse); + + if ($finalResponse instanceof JrdResponse && $finalResponse->isEmpty()) { + return null; + } + + return $finalResponse; + } + + /** + * @return IHandler[] + */ + private function loadHandlers(): array { + $context = $this->coordinator->getRegistrationContext(); + + if ($context === null) { + throw new RuntimeException("Well known handlers requested before the apps had been fully registered"); + } + + $registrations = $context->getWellKnownHandlers(); + $this->logger->debug(count($registrations) . " well known handlers registered"); + + return array_filter( + array_map(function (array $registration) { + $class = $registration['class']; + + try { + $handler = $this->container->get($class); + + if (!($handler) instanceof IHandler) { + $this->logger->error("Well known handler $class is invalid"); + + return null; + } + + return $handler; + } catch (QueryException $e) { + $this->logger->error("Could not load well known handler $class", [ + 'exception' => $e, + ]); + + return null; + } + }, $registrations) + ); + } +} diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index aaf5ef00bfa..9966d19965a 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -180,4 +180,18 @@ interface IRegistrationContext { * @since 21.0.0 */ public function registerInitialStateProvider(string $class): void; + + /** + * Register a well known protocol handler + * + * It is allowed to register more than one handler per app. + * + * @param string $class + * @psalm-param class-string<\OCP\Http\WellKnown\IHandler> $class + * + * @return void + * + * @since 21.0.0 + */ + public function registerWellKnownHandler(string $class): void; } diff --git a/lib/public/Http/WellKnown/GenericResponse.php b/lib/public/Http/WellKnown/GenericResponse.php new file mode 100644 index 00000000000..6efacc41c00 --- /dev/null +++ b/lib/public/Http/WellKnown/GenericResponse.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCP\Http\WellKnown; + +use OCP\AppFramework\Http\Response; + +/** + * @since 21.0.0 + */ +final class GenericResponse implements IResponse { + + /** @var Response */ + private $response; + + /** + * @since 21.0.0 + */ + public function __construct(Response $response) { + $this->response = $response; + } + + /** + * @since 21.0.0 + */ + public function toHttpResponse(): Response { + return $this->response; + } +} diff --git a/lib/public/Http/WellKnown/IHandler.php b/lib/public/Http/WellKnown/IHandler.php new file mode 100644 index 00000000000..30fca0da5a2 --- /dev/null +++ b/lib/public/Http/WellKnown/IHandler.php @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCP\Http\WellKnown; + +/** + * Interface for an app handler that reacts to requests to Nextcloud's well + * known URLs, e.g. to a WebFinger + * + * @ref https://tools.ietf.org/html/rfc5785 + * + * @since 21.0.0 + */ +interface IHandler { + + /** + * @param string $service the name of the well known service, e.g. 'webfinger' + * @param IRequestContext $context + * @param IResponse|null $previousResponse the response of the previous handler, if any + * + * @return IResponse|null a response object if the request could be handled, null otherwise + * + * @since 21.0.0 + */ + public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse; +} diff --git a/lib/public/Http/WellKnown/IRequestContext.php b/lib/public/Http/WellKnown/IRequestContext.php new file mode 100644 index 00000000000..d064c467d68 --- /dev/null +++ b/lib/public/Http/WellKnown/IRequestContext.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCP\Http\WellKnown; + +use OCP\IRequest; + +/** + * The context object for \OCP\Http\IWellKnownHandler::handle + * + * Objects of this type will transport any optional information, e.g. the request + * object through which the app well known handler can obtain URL parameters + * + * @since 21.0.0 + */ +interface IRequestContext { + + /** + * @return IRequest + * + * @since 21.0.0 + */ + public function getHttpRequest(): IRequest; +} diff --git a/lib/public/Http/WellKnown/IResponse.php b/lib/public/Http/WellKnown/IResponse.php new file mode 100644 index 00000000000..8d7bc307252 --- /dev/null +++ b/lib/public/Http/WellKnown/IResponse.php @@ -0,0 +1,39 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCP\Http\WellKnown; + +use OCP\AppFramework\Http\Response; + +/** + * @since 21.0.0 + */ +interface IResponse { + + /** + * @since 21.0.0 + */ + public function toHttpResponse(): Response; +} diff --git a/lib/public/Http/WellKnown/JrdResponse.php b/lib/public/Http/WellKnown/JrdResponse.php new file mode 100644 index 00000000000..3a387107e8e --- /dev/null +++ b/lib/public/Http/WellKnown/JrdResponse.php @@ -0,0 +1,170 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCP\Http\WellKnown; + +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\Response; +use function array_filter; + +/** + * A JSON Document Format (JDF) response to a well-known request + * + * @ref https://tools.ietf.org/html/rfc6415#appendix-A + * @ref https://tools.ietf.org/html/rfc7033#section-4.4 + * + * @since 21.0.0 + */ +final class JrdResponse implements IResponse { + + /** @var string */ + private $subject; + + /** @var string|null */ + private $expires; + + /** @var string[] */ + private $aliases = []; + + /** @var (string|null)[] */ + private $properties = []; + + /** @var mixed[] */ + private $links; + + /** + * @param string $subject https://tools.ietf.org/html/rfc7033#section-4.4.1 + * + * @since 21.0.0 + */ + public function __construct(string $subject) { + $this->subject = $subject; + } + + /** + * @param string $expires + * + * @return $this + * + * @since 21.0.0 + */ + public function setExpires(string $expires): self { + $this->expires = $expires; + + return $this; + } + + /** + * Add an alias + * + * @ref https://tools.ietf.org/html/rfc7033#section-4.4.2 + * + * @param string $alias + * + * @return $this + * + * @since 21.0.0 + */ + public function addAlias(string $alias): self { + $this->aliases[] = $alias; + + return $this; + } + + /** + * Add a property + * + * @ref https://tools.ietf.org/html/rfc7033#section-4.4.3 + * + * @param string $property + * @param string|null $value + * + * @return $this + * + * @since 21.0.0 + */ + public function addProperty(string $property, ?string $value): self { + $this->properties[$property] = $value; + + return $this; + } + + /** + * Add a link + * + * @ref https://tools.ietf.org/html/rfc7033#section-8.4 + * + * @param string $rel https://tools.ietf.org/html/rfc7033#section-4.4.4.1 + * @param string|null $type https://tools.ietf.org/html/rfc7033#section-4.4.4.2 + * @param string|null $href https://tools.ietf.org/html/rfc7033#section-4.4.4.3 + * @param string[]|null $titles https://tools.ietf.org/html/rfc7033#section-4.4.4.4 + * @param string|null $properties https://tools.ietf.org/html/rfc7033#section-4.4.4.5 + * + * @psalm-param array<string,(string|null)>|null $properties https://tools.ietf.org/html/rfc7033#section-4.4.4.5 + * + * @return JrdResponse + * @since 21.0.0 + */ + public function addLink(string $rel, + ?string $type, + ?string $href, + ?array $titles = [], + ?array $properties = []): self { + $this->links[] = array_filter([ + 'rel' => $rel, + 'type' => $type, + 'href' => $href, + 'titles' => $titles, + 'properties' => $properties, + ]); + + return $this; + } + + /** + * @since 21.0.0 + */ + public function toHttpResponse(): Response { + return new JSONResponse(array_filter([ + 'subject' => $this->subject, + 'expires' => $this->expires, + 'aliases' => $this->aliases, + 'properties' => $this->properties, + 'links' => $this->links, + ])); + } + + /** + * Does this response have any data attached to it? + * + * @since 21.0.0 + */ + public function isEmpty(): bool { + return $this->expires === null + && empty($this->aliases) + && empty($this->properties) + && empty($this->links); + } +} |