Adopt Schema component in server handlers
This commit is contained in:
parent
6025f890f9
commit
09707b4798
92 changed files with 1516 additions and 2424 deletions
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\Capability\Prompt;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class PromptGet
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $arguments
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $id,
|
||||
public readonly string $name,
|
||||
public readonly array $arguments = [],
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\Capability\Prompt;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class PromptGetResult
|
||||
{
|
||||
/**
|
||||
* @param list<PromptGetResultMessages> $messages
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $description,
|
||||
public readonly array $messages = [],
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\Capability\Prompt;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class PromptGetResultMessages
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $role,
|
||||
public readonly string $result,
|
||||
/**
|
||||
* @var "text"|"image"|"audio"|"resource"|non-empty-string
|
||||
*/
|
||||
public readonly string $type = 'text',
|
||||
public readonly string $mimeType = 'text/plan',
|
||||
public readonly ?string $uri = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ namespace Mcp\Capability\Prompt;
|
|||
|
||||
use Mcp\Exception\PromptGetException;
|
||||
use Mcp\Exception\PromptNotFoundException;
|
||||
use Mcp\Schema\Request\GetPromptRequest;
|
||||
use Mcp\Schema\Result\GetPromptResult;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -23,5 +25,5 @@ interface PromptGetterInterface
|
|||
* @throws PromptGetException if the prompt execution fails
|
||||
* @throws PromptNotFoundException if the prompt is not found
|
||||
*/
|
||||
public function get(PromptGet $input): PromptGetResult;
|
||||
public function get(GetPromptRequest $request): GetPromptResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ namespace Mcp\Capability;
|
|||
use Mcp\Capability\Prompt\CollectionInterface;
|
||||
use Mcp\Capability\Prompt\IdentifierInterface;
|
||||
use Mcp\Capability\Prompt\MetadataInterface;
|
||||
use Mcp\Capability\Prompt\PromptGet;
|
||||
use Mcp\Capability\Prompt\PromptGetResult;
|
||||
use Mcp\Capability\Prompt\PromptGetterInterface;
|
||||
use Mcp\Exception\InvalidCursorException;
|
||||
use Mcp\Exception\PromptGetException;
|
||||
use Mcp\Exception\PromptNotFoundException;
|
||||
use Mcp\Schema\Request\GetPromptRequest;
|
||||
use Mcp\Schema\Result\GetPromptResult;
|
||||
|
||||
/**
|
||||
* A collection of prompts. All prompts need to implement IdentifierInterface.
|
||||
|
|
@ -60,18 +60,18 @@ class PromptChain implements PromptGetterInterface, CollectionInterface
|
|||
}
|
||||
}
|
||||
|
||||
public function get(PromptGet $input): PromptGetResult
|
||||
public function get(GetPromptRequest $request): GetPromptResult
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
if ($item instanceof PromptGetterInterface && $input->name === $item->getName()) {
|
||||
if ($item instanceof PromptGetterInterface && $request->name === $item->getName()) {
|
||||
try {
|
||||
return $item->get($input);
|
||||
return $item->get($request);
|
||||
} catch (\Throwable $e) {
|
||||
throw new PromptGetException($input, $e);
|
||||
throw new PromptGetException($request, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new PromptNotFoundException($input);
|
||||
throw new PromptNotFoundException($request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace Mcp\Capability\Resource;
|
|||
|
||||
use Mcp\Exception\ResourceNotFoundException;
|
||||
use Mcp\Exception\ResourceReadException;
|
||||
use Mcp\Schema\Request\ReadResourceRequest;
|
||||
use Mcp\Schema\Result\ReadResourceResult;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -23,5 +25,5 @@ interface ResourceReaderInterface
|
|||
* @throws ResourceReadException if the resource execution fails
|
||||
* @throws ResourceNotFoundException if the resource is not found
|
||||
*/
|
||||
public function read(ResourceRead $input): ResourceReadResult;
|
||||
public function read(ReadResourceRequest $request): ReadResourceResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ namespace Mcp\Capability;
|
|||
use Mcp\Capability\Resource\CollectionInterface;
|
||||
use Mcp\Capability\Resource\IdentifierInterface;
|
||||
use Mcp\Capability\Resource\MetadataInterface;
|
||||
use Mcp\Capability\Resource\ResourceRead;
|
||||
use Mcp\Capability\Resource\ResourceReaderInterface;
|
||||
use Mcp\Capability\Resource\ResourceReadResult;
|
||||
use Mcp\Exception\InvalidCursorException;
|
||||
use Mcp\Exception\ResourceNotFoundException;
|
||||
use Mcp\Exception\ResourceReadException;
|
||||
use Mcp\Schema\Request\ReadResourceRequest;
|
||||
use Mcp\Schema\Result\ReadResourceResult;
|
||||
|
||||
/**
|
||||
* A collection of resources. All resources need to implement IdentifierInterface.
|
||||
|
|
@ -60,18 +60,18 @@ class ResourceChain implements CollectionInterface, ResourceReaderInterface
|
|||
}
|
||||
}
|
||||
|
||||
public function read(ResourceRead $input): ResourceReadResult
|
||||
public function read(ReadResourceRequest $request): ReadResourceResult
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
if ($item instanceof ResourceReaderInterface && $input->uri === $item->getUri()) {
|
||||
if ($item instanceof ResourceReaderInterface && $request->uri === $item->getUri()) {
|
||||
try {
|
||||
return $item->read($input);
|
||||
return $item->read($request);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ResourceReadException($input, $e);
|
||||
throw new ResourceReadException($request, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResourceNotFoundException($input);
|
||||
throw new ResourceNotFoundException($request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\Capability\Tool;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class ToolCall
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $arguments
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $id,
|
||||
public readonly string $name,
|
||||
public readonly array $arguments = [],
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\Capability\Tool;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class ToolCallResult
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $result,
|
||||
/**
|
||||
* @var "text"|"image"|"audio"|"resource"|non-empty-string
|
||||
*/
|
||||
public readonly string $type = 'text',
|
||||
public readonly string $mimeType = 'text/plan',
|
||||
public readonly bool $isError = false,
|
||||
public readonly ?string $uri = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ namespace Mcp\Capability\Tool;
|
|||
|
||||
use Mcp\Exception\ToolExecutionException;
|
||||
use Mcp\Exception\ToolNotFoundException;
|
||||
use Mcp\Schema\Request\CallToolRequest;
|
||||
use Mcp\Schema\Result\CallToolResult;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -23,5 +25,5 @@ interface ToolExecutorInterface
|
|||
* @throws ToolExecutionException if the tool execution fails
|
||||
* @throws ToolNotFoundException if the tool is not found
|
||||
*/
|
||||
public function call(ToolCall $input): ToolCallResult;
|
||||
public function call(CallToolRequest $request): CallToolResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ namespace Mcp\Capability;
|
|||
use Mcp\Capability\Tool\CollectionInterface;
|
||||
use Mcp\Capability\Tool\IdentifierInterface;
|
||||
use Mcp\Capability\Tool\MetadataInterface;
|
||||
use Mcp\Capability\Tool\ToolCall;
|
||||
use Mcp\Capability\Tool\ToolCallResult;
|
||||
use Mcp\Capability\Tool\ToolExecutorInterface;
|
||||
use Mcp\Exception\InvalidCursorException;
|
||||
use Mcp\Exception\ToolExecutionException;
|
||||
use Mcp\Exception\ToolNotFoundException;
|
||||
use Mcp\Schema\Request\CallToolRequest;
|
||||
use Mcp\Schema\Result\CallToolResult;
|
||||
|
||||
/**
|
||||
* A collection of tools. All tools need to implement IdentifierInterface.
|
||||
|
|
@ -60,18 +60,18 @@ class ToolChain implements ToolExecutorInterface, CollectionInterface
|
|||
}
|
||||
}
|
||||
|
||||
public function call(ToolCall $input): ToolCallResult
|
||||
public function call(CallToolRequest $request): CallToolResult
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
if ($item instanceof ToolExecutorInterface && $input->name === $item->getName()) {
|
||||
if ($item instanceof ToolExecutorInterface && $request->name === $item->getName()) {
|
||||
try {
|
||||
return $item->call($input);
|
||||
return $item->call($request);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ToolExecutionException($input, $e);
|
||||
throw new ToolExecutionException($request, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ToolNotFoundException($input);
|
||||
throw new ToolNotFoundException($request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
src/Exception/LogicException.php
Normal file
19
src/Exception/LogicException.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\Exception;
|
||||
|
||||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*/
|
||||
final class LogicException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace Mcp\Exception;
|
||||
|
||||
use Mcp\Capability\Prompt\PromptGet;
|
||||
use Mcp\Schema\Request\GetPromptRequest;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -19,9 +19,9 @@ use Mcp\Capability\Prompt\PromptGet;
|
|||
final class PromptGetException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
public readonly PromptGet $promptGet,
|
||||
public readonly GetPromptRequest $request,
|
||||
?\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct(\sprintf('Handling prompt "%s" failed with error: %s', $promptGet->name, $previous->getMessage()), previous: $previous);
|
||||
parent::__construct(\sprintf('Handling prompt "%s" failed with error: "%s".', $request->name, $previous->getMessage()), previous: $previous);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace Mcp\Exception;
|
||||
|
||||
use Mcp\Capability\Prompt\PromptGet;
|
||||
use Mcp\Schema\Request\GetPromptRequest;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -19,8 +19,8 @@ use Mcp\Capability\Prompt\PromptGet;
|
|||
final class PromptNotFoundException extends \RuntimeException implements NotFoundExceptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
public readonly PromptGet $promptGet,
|
||||
public readonly GetPromptRequest $request,
|
||||
) {
|
||||
parent::__construct(\sprintf('Prompt not found for name: "%s"', $promptGet->name));
|
||||
parent::__construct(\sprintf('Prompt not found for name: "%s".', $request->name));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace Mcp\Exception;
|
||||
|
||||
use Mcp\Capability\Resource\ResourceRead;
|
||||
use Mcp\Schema\Request\ReadResourceRequest;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -19,8 +19,8 @@ use Mcp\Capability\Resource\ResourceRead;
|
|||
final class ResourceNotFoundException extends \RuntimeException implements NotFoundExceptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ResourceRead $readRequest,
|
||||
public readonly ReadResourceRequest $request,
|
||||
) {
|
||||
parent::__construct(\sprintf('Resource not found for uri: "%s"', $readRequest->uri));
|
||||
parent::__construct(\sprintf('Resource not found for uri: "%s".', $request->uri));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace Mcp\Exception;
|
||||
|
||||
use Mcp\Capability\Resource\ResourceRead;
|
||||
use Mcp\Schema\Request\ReadResourceRequest;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -19,9 +19,9 @@ use Mcp\Capability\Resource\ResourceRead;
|
|||
final class ResourceReadException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ResourceRead $readRequest,
|
||||
public readonly ReadResourceRequest $request,
|
||||
?\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct(\sprintf('Reading resource "%s" failed with error: %s', $readRequest->uri, $previous?->getMessage() ?? ''), previous: $previous);
|
||||
parent::__construct(\sprintf('Reading resource "%s" failed with error: "%s".', $request->uri, $previous?->getMessage() ?? ''), previous: $previous);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,6 @@ namespace Mcp\Exception;
|
|||
/**
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*/
|
||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
final class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace Mcp\Exception;
|
||||
|
||||
use Mcp\Capability\Tool\ToolCall;
|
||||
use Mcp\Schema\Request\CallToolRequest;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -19,9 +19,9 @@ use Mcp\Capability\Tool\ToolCall;
|
|||
final class ToolExecutionException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ToolCall $toolCall,
|
||||
public readonly CallToolRequest $request,
|
||||
?\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct(\sprintf('Execution of tool "%s" failed with error: %s', $toolCall->name, $previous?->getMessage() ?? ''), previous: $previous);
|
||||
parent::__construct(\sprintf('Execution of tool "%s" failed with error: "%s".', $request->name, $previous?->getMessage() ?? ''), previous: $previous);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace Mcp\Exception;
|
||||
|
||||
use Mcp\Capability\Tool\ToolCall;
|
||||
use Mcp\Schema\Request\CallToolRequest;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
|
|
@ -19,8 +19,8 @@ use Mcp\Capability\Tool\ToolCall;
|
|||
final class ToolNotFoundException extends \RuntimeException implements NotFoundExceptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ToolCall $toolCall,
|
||||
public readonly CallToolRequest $request,
|
||||
) {
|
||||
parent::__construct(\sprintf('Tool not found for call: "%s"', $toolCall->name));
|
||||
parent::__construct(\sprintf('Tool not found for call: "%s".', $request->name));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
143
src/JsonRpc/Handler.php
Normal file
143
src/JsonRpc/Handler.php
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\JsonRpc;
|
||||
|
||||
use Mcp\Exception\ExceptionInterface;
|
||||
use Mcp\Exception\HandlerNotFoundException;
|
||||
use Mcp\Exception\InvalidInputMessageException;
|
||||
use Mcp\Exception\NotFoundExceptionInterface;
|
||||
use Mcp\Schema\JsonRpc\Error;
|
||||
use Mcp\Schema\JsonRpc\HasMethodInterface;
|
||||
use Mcp\Schema\JsonRpc\Response;
|
||||
use Mcp\Server\MethodHandlerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*
|
||||
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||
*/
|
||||
class Handler
|
||||
{
|
||||
/**
|
||||
* @var array<int, MethodHandlerInterface>
|
||||
*/
|
||||
private readonly array $methodHandlers;
|
||||
|
||||
/**
|
||||
* @param iterable<int, MethodHandlerInterface> $methodHandlers
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly MessageFactory $messageFactory,
|
||||
iterable $methodHandlers,
|
||||
private readonly LoggerInterface $logger = new NullLogger(),
|
||||
) {
|
||||
$this->methodHandlers = $methodHandlers instanceof \Traversable ? iterator_to_array($methodHandlers) : $methodHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string|null>
|
||||
*
|
||||
* @throws ExceptionInterface When a handler throws an exception during message processing
|
||||
* @throws \JsonException When JSON encoding of the response fails
|
||||
*/
|
||||
public function process(string $input): iterable
|
||||
{
|
||||
$this->logger->info('Received message to process', ['message' => $input]);
|
||||
|
||||
try {
|
||||
$messages = $this->messageFactory->create($input);
|
||||
} catch (\JsonException $e) {
|
||||
$this->logger->warning('Failed to decode json message', ['exception' => $e]);
|
||||
|
||||
yield $this->encodeResponse(Error::forParseError($e->getMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($messages as $message) {
|
||||
if ($message instanceof InvalidInputMessageException) {
|
||||
$this->logger->warning('Failed to create message', ['exception' => $message]);
|
||||
yield $this->encodeResponse(Error::forInvalidRequest($message->getMessage(), 0));
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->logger->info('Decoded incoming message', ['message' => $message]);
|
||||
|
||||
try {
|
||||
yield $this->encodeResponse($this->handle($message));
|
||||
} catch (\DomainException) {
|
||||
yield null;
|
||||
} catch (NotFoundExceptionInterface $e) {
|
||||
$this->logger->warning(\sprintf('Failed to create response: %s', $e->getMessage()), ['exception' => $e]);
|
||||
|
||||
yield $this->encodeResponse(Error::forMethodNotFound($e->getMessage()));
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->logger->warning(\sprintf('Invalid argument: %s', $e->getMessage()), ['exception' => $e]);
|
||||
|
||||
yield $this->encodeResponse(Error::forInvalidParams($e->getMessage()));
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->critical(\sprintf('Uncaught exception: %s', $e->getMessage()), ['exception' => $e]);
|
||||
|
||||
yield $this->encodeResponse(Error::forInternalError($e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \JsonException When JSON encoding fails
|
||||
*/
|
||||
private function encodeResponse(Response|Error|null $response): ?string
|
||||
{
|
||||
if (null === $response) {
|
||||
$this->logger->info('The handler created an empty response.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->logger->info('Encoding response', ['response' => $response]);
|
||||
|
||||
if ($response instanceof Response && [] === $response->result) {
|
||||
return json_encode($response, \JSON_THROW_ON_ERROR | \JSON_FORCE_OBJECT);
|
||||
}
|
||||
|
||||
return json_encode($response, \JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the handler does support the message, but does not create a response, other handlers will be tried.
|
||||
*
|
||||
* @throws NotFoundExceptionInterface When no handler is found for the request method
|
||||
* @throws ExceptionInterface When a request handler throws an exception
|
||||
*/
|
||||
private function handle(HasMethodInterface $message): Response|Error|null
|
||||
{
|
||||
$handled = false;
|
||||
foreach ($this->methodHandlers as $handler) {
|
||||
if ($handler->supports($message)) {
|
||||
$return = $handler->handle($message);
|
||||
$handled = true;
|
||||
|
||||
if (null !== $return) {
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($handled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new HandlerNotFoundException(\sprintf('No handler found for method "%s".', $message::getMethod()));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue