From 143db29e6b66a535865c4a1724143e5e91819524 Mon Sep 17 00:00:00 2001
From: Mateusz Charytoniuk <mateusz.charytoniuk@protonmail.com>
Date: Tue, 6 Feb 2024 11:21:51 +0100
Subject: [PATCH] chore: cache constraints

---
 src/InputValidator/FrontMatterValidator.php         |  3 ++-
 src/InputValidator/RPCMessageValidator.php          |  3 ++-
 src/InputValidatorController.php                    | 13 ++++++++++++-
 .../InputValidatorCollectionProvider.php            | 10 ++++++++++
 .../WebSocketRPCResponderAggregateProvider.php      |  4 ++++
 src/WebSocketRPCConnectionHandle.php                |  6 ++++--
 src/WebSocketRPCResponderAggregate.php              |  6 ++++++
 7 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/src/InputValidator/FrontMatterValidator.php b/src/InputValidator/FrontMatterValidator.php
index eb1aebeb..0b1a8bc0 100644
--- a/src/InputValidator/FrontMatterValidator.php
+++ b/src/InputValidator/FrontMatterValidator.php
@@ -15,6 +15,7 @@ use Distantmagic\Resonance\Constraint\StringConstraint;
 use Distantmagic\Resonance\FrontMatterCollectionReference;
 use Distantmagic\Resonance\InputValidatedData\FrontMatter;
 use Distantmagic\Resonance\InputValidator;
+use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\StaticPageContentType;
 use Generator;
 use RuntimeException;
@@ -32,7 +33,7 @@ use RuntimeException;
  *     title: non-empty-string,
  * }>
  */
-#[Singleton]
+#[Singleton(collection: SingletonCollection::InputValidator)]
 readonly class FrontMatterValidator extends InputValidator
 {
     public function castValidatedData(mixed $data): FrontMatter
diff --git a/src/InputValidator/RPCMessageValidator.php b/src/InputValidator/RPCMessageValidator.php
index 954d1344..68cfd78c 100644
--- a/src/InputValidator/RPCMessageValidator.php
+++ b/src/InputValidator/RPCMessageValidator.php
@@ -16,6 +16,7 @@ use Distantmagic\Resonance\Feature;
 use Distantmagic\Resonance\InputValidatedData\RPCMessage;
 use Distantmagic\Resonance\InputValidator;
 use Distantmagic\Resonance\RPCMethodValidatorInterface;
+use Distantmagic\Resonance\SingletonCollection;
 
 /**
  * @extends InputValidator<RPCMessage, array{
@@ -25,7 +26,7 @@ use Distantmagic\Resonance\RPCMethodValidatorInterface;
  * }>
  */
 #[GrantsFeature(Feature::WebSocket)]
-#[Singleton]
+#[Singleton(collection: SingletonCollection::InputValidator)]
 readonly class RPCMessageValidator extends InputValidator
 {
     public function __construct(private RPCMethodValidatorInterface $rpcMethodValidator) {}
diff --git a/src/InputValidatorController.php b/src/InputValidatorController.php
index d02f6d09..a8e33f33 100644
--- a/src/InputValidatorController.php
+++ b/src/InputValidatorController.php
@@ -5,10 +5,21 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use Ds\Map;
 
 #[Singleton]
 readonly class InputValidatorController
 {
+    /**
+     * @var Map<InputValidator,Constraint>
+     */
+    public Map $cachedConstraints;
+
+    public function __construct()
+    {
+        $this->cachedConstraints = new Map();
+    }
+
     /**
      * @template TCastedData of InputValidatedData
      * @template TValidatedData
@@ -19,7 +30,7 @@ readonly class InputValidatorController
      */
     public function validateData(InputValidator $inputValidator, mixed $data): InputValidationResult
     {
-        $constraintResult = $inputValidator->getConstraint()->validate($data);
+        $constraintResult = $this->cachedConstraints->get($inputValidator)->validate($data);
 
         return $this->castJsonSchemaValidationResult($constraintResult, $inputValidator);
     }
diff --git a/src/SingletonProvider/InputValidatorCollectionProvider.php b/src/SingletonProvider/InputValidatorCollectionProvider.php
index 9b2e34d1..7cb802cf 100644
--- a/src/SingletonProvider/InputValidatorCollectionProvider.php
+++ b/src/SingletonProvider/InputValidatorCollectionProvider.php
@@ -8,6 +8,7 @@ use Distantmagic\Resonance\Attribute\RequiresSingletonCollection;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\InputValidator;
 use Distantmagic\Resonance\InputValidatorCollection;
+use Distantmagic\Resonance\InputValidatorController;
 use Distantmagic\Resonance\PHPProjectFiles;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\SingletonContainer;
@@ -20,6 +21,10 @@ use Distantmagic\Resonance\SingletonProvider;
 #[Singleton(provides: InputValidatorCollection::class)]
 final readonly class InputValidatorCollectionProvider extends SingletonProvider
 {
+    public function __construct(
+        private InputValidatorController $inputValidatorController,
+    ) {}
+
     public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): InputValidatorCollection
     {
         $inputValidatorCollection = new InputValidatorCollection();
@@ -30,6 +35,11 @@ final readonly class InputValidatorCollectionProvider extends SingletonProvider
                     $singleton::class,
                     $singleton,
                 );
+                $this
+                    ->inputValidatorController
+                    ->cachedConstraints
+                    ->put($singleton, $singleton->getConstraint())
+                ;
             }
         }
 
diff --git a/src/SingletonProvider/WebSocketRPCResponderAggregateProvider.php b/src/SingletonProvider/WebSocketRPCResponderAggregateProvider.php
index 8210877b..611752ce 100644
--- a/src/SingletonProvider/WebSocketRPCResponderAggregateProvider.php
+++ b/src/SingletonProvider/WebSocketRPCResponderAggregateProvider.php
@@ -27,6 +27,10 @@ final readonly class WebSocketRPCResponderAggregateProvider extends SingletonPro
         $webSocketRPCResponderAggregate = new WebSocketRPCResponderAggregate();
 
         foreach ($this->collectWebSocketRPCResponders($singletons) as $rpcResponderAttribute) {
+            $webSocketRPCResponderAggregate->cachedConstraints->put(
+                $rpcResponderAttribute->singleton,
+                $rpcResponderAttribute->singleton->getConstraint(),
+            );
             $webSocketRPCResponderAggregate->rpcResponders->put(
                 $rpcResponderAttribute->attribute->method,
                 $rpcResponderAttribute->singleton,
diff --git a/src/WebSocketRPCConnectionHandle.php b/src/WebSocketRPCConnectionHandle.php
index 022c632e..0f8918c1 100644
--- a/src/WebSocketRPCConnectionHandle.php
+++ b/src/WebSocketRPCConnectionHandle.php
@@ -43,8 +43,10 @@ readonly class WebSocketRPCConnectionHandle
             ->selectResponder($rpcMessage)
         ;
 
-        $constraintResult = $responder
-            ->getConstraint()
+        $constraintResult = $this
+            ->webSocketRPCResponderAggregate
+            ->cachedConstraints
+            ->get($responder)
             ->validate($rpcMessage->payload)
         ;
 
diff --git a/src/WebSocketRPCResponderAggregate.php b/src/WebSocketRPCResponderAggregate.php
index 7457d265..c0ac18d7 100644
--- a/src/WebSocketRPCResponderAggregate.php
+++ b/src/WebSocketRPCResponderAggregate.php
@@ -10,6 +10,11 @@ use Ds\Map;
 
 readonly class WebSocketRPCResponderAggregate
 {
+    /**
+     * @var Map<RPCMethodInterface,Constraint>
+     */
+    public Map $cachedConstraints;
+
     /**
      * @var Map<RPCMethodInterface,WebSocketRPCResponderInterface> $rpcResponders
      */
@@ -17,6 +22,7 @@ readonly class WebSocketRPCResponderAggregate
 
     public function __construct()
     {
+        $this->cachedConstraints = new Map();
         $this->rpcResponders = new Map();
     }
 
-- 
GitLab