Skip to content
Snippets Groups Projects
Commit aade3c7d authored by Mateusz Charytoniuk's avatar Mateusz Charytoniuk
Browse files

feat: use context to fetch current request

parent c7a34f47
No related branches found
No related tags found
No related merge requests found
Showing
with 94 additions and 141 deletions
...@@ -89,15 +89,10 @@ advantage of the asynchronous environment. ...@@ -89,15 +89,10 @@ advantage of the asynchronous environment.
#[RespondsToHttp( #[RespondsToHttp(
method: RequestMethod::GET, method: RequestMethod::GET,
pattern: '/', pattern: '/',
routeSymbol: HttpRouteSymbol::Homepage,
)] )]
#[Singleton(collection: SingletonCollection::HttpResponder)] function Homepage(ServerRequestInterface $request, ResponseInterface $response): TwigTemplate
readonly class Homepage implements HttpResponderInterface
{ {
public function respond(ServerRequestInterface $request, ResponseInterface $response): TwigTemplate return new TwigTemplate('website/homepage.twig');
{
return new TwigTemplate($request, $response, 'website/homepage.twig');
}
} }
``` ```
......
...@@ -67,8 +67,6 @@ use Psr\Http\Message\ServerRequestInterface; ...@@ -67,8 +67,6 @@ use Psr\Http\Message\ServerRequestInterface;
final readonly class BlogPostShow extends HttpController final readonly class BlogPostShow extends HttpController
{ {
public function createResponse( public function createResponse(
ServerRequestInterface $request,
ResponseInterface $response,
#[DoctrineEntityRouteParameter( #[DoctrineEntityRouteParameter(
from: 'blog_post_slug', from: 'blog_post_slug',
intent: CrudAction::Read, intent: CrudAction::Read,
...@@ -76,14 +74,9 @@ final readonly class BlogPostShow extends HttpController ...@@ -76,14 +74,9 @@ final readonly class BlogPostShow extends HttpController
)] )]
BlogPost $blogPost, BlogPost $blogPost,
): HttpInterceptableInterface { ): HttpInterceptableInterface {
return new TwigTemplate( return new TwigTemplate('turbo/website/blog_post.twig', [
$request, 'blog_post' => $blogPost,
$response, ]);
'turbo/website/blog_post.twig',
[
'blog_post' => $blogPost,
],
);
} }
} }
``` ```
......
...@@ -114,14 +114,9 @@ final readonly class Blog extends HttpResponder ...@@ -114,14 +114,9 @@ final readonly class Blog extends HttpResponder
$blogPostsRepository = $entityManager->getRepository(BlogPost::class); $blogPostsRepository = $entityManager->getRepository(BlogPost::class);
return new TwigTemplate( return new TwigTemplate('turbo/website/blog.twig', [
$request, 'blog_posts' => $blogPostsRepository->findAll(),
$response, ]);
'turbo/website/blog.twig',
[
'blog_posts' => $blogPostsRepository->findAll(),
]
);
} }
} }
``` ```
...@@ -143,7 +138,6 @@ namespace App\HttpResponder; ...@@ -143,7 +138,6 @@ namespace App\HttpResponder;
use App\DoctrineEntity\BlogPost; use App\DoctrineEntity\BlogPost;
use App\HttpRouteSymbol; use App\HttpRouteSymbol;
use Distantmagic\Resonance\Attribute\DoctrineEntityManager;
use Distantmagic\Resonance\Attribute\DoctrineEntityRepository; use Distantmagic\Resonance\Attribute\DoctrineEntityRepository;
use Distantmagic\Resonance\Attribute\RespondsToHttp; use Distantmagic\Resonance\Attribute\RespondsToHttp;
use Distantmagic\Resonance\Attribute\Singleton; use Distantmagic\Resonance\Attribute\Singleton;
...@@ -153,10 +147,7 @@ use Distantmagic\Resonance\HttpResponder\HttpController; ...@@ -153,10 +147,7 @@ use Distantmagic\Resonance\HttpResponder\HttpController;
use Distantmagic\Resonance\RequestMethod; use Distantmagic\Resonance\RequestMethod;
use Distantmagic\Resonance\SingletonCollection; use Distantmagic\Resonance\SingletonCollection;
use Distantmagic\Resonance\TwigTemplate; use Distantmagic\Resonance\TwigTemplate;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
#[RespondsToHttp( #[RespondsToHttp(
method: RequestMethod::GET, method: RequestMethod::GET,
...@@ -167,21 +158,12 @@ use Psr\Http\Message\ServerRequestInterface; ...@@ -167,21 +158,12 @@ use Psr\Http\Message\ServerRequestInterface;
final readonly class Blog extends HttpController final readonly class Blog extends HttpController
{ {
public function createResponse( public function createResponse(
ServerRequestInterface $request,
ResponseInterface $response,
#[DoctrineEntityManager]
EntityManager $entityManager,
#[DoctrineEntityRepository(BlogPost::class)] #[DoctrineEntityRepository(BlogPost::class)]
EntityRepository $blogPosts, EntityRepository $blogPosts,
): HttpInterceptableInterface { ): HttpInterceptableInterface {
return new TwigTemplate( return new TwigTemplate('website/blog.twig', [
$request, 'blog_posts' => $blogPosts->findAll(),
$response, ]);
'website/blog.twig',
[
'blog_posts' => $blogPosts->findAll(),
]
);
} }
} }
``` ```
...@@ -100,7 +100,7 @@ final readonly class BlogPostDestroy extends HttpController ...@@ -100,7 +100,7 @@ final readonly class BlogPostDestroy extends HttpController
): HttpInterceptableInterface { ): HttpInterceptableInterface {
// ... // ...
return new InternalRedirect($request, $response, HttpRouteSymbol::Blog); return new InternalRedirect(HttpRouteSymbol::Blog);
} }
} }
......
...@@ -91,7 +91,7 @@ final readonly class ObservableTasksDashboard extends HttpResponder ...@@ -91,7 +91,7 @@ final readonly class ObservableTasksDashboard extends HttpResponder
public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
{ {
return new TwigTemplate($request, $response, 'observable_task_table.twig',[ return new TwigTemplate('observable_task_table.twig',[
'observableTaskTable' => $this->observableTaskTable, 'observableTaskTable' => $this->observableTaskTable,
]); ]);
} }
......
...@@ -65,7 +65,7 @@ final readonly class LoginValidation extends HttpController ...@@ -65,7 +65,7 @@ final readonly class LoginValidation extends HttpController
$user, $user,
); );
return new InternalRedirect($request, $response, HttpRouteSymbol::Homepage); return new InternalRedirect(HttpRouteSymbol::Homepage);
} }
} }
``` ```
......
...@@ -40,7 +40,7 @@ final readonly class Twig extends HttpResponder ...@@ -40,7 +40,7 @@ final readonly class Twig extends HttpResponder
{ {
public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
{ {
return new TwigTemplate($request, $response, 'test.twig'); return new TwigTemplate('test.twig');
} }
} }
``` ```
......
...@@ -36,20 +36,20 @@ description: > ...@@ -36,20 +36,20 @@ description: >
<ul class="homepage__examples"> <ul class="homepage__examples">
<li class="formatted-content homepage__example"> <li class="formatted-content homepage__example">
<h2 class="homepage__example__title"> <h2 class="homepage__example__title">
Chat with Open-Source LLMs Simple Things Remain Simple
</h2> </h2>
<div class="homepage__example__description"> <div class="homepage__example__description">
<p> <p>
Create prompt controllers to directly answer user's Writing HTTP controllers is similar to how it's done in
prompts. the synchronous code.
</p> </p>
<p> <p>
LLM takes care of determining user's intention, you Controllers have new exciting features that take
can focus on taking an appropriate action. advantage of the asynchronous environment.
</p> </p>
<a <a
class="homepage__cta homepage__cta--example" class="homepage__cta homepage__cta--example"
href="/docs/features/ai/" href="/docs/features/http/controllers.html"
> >
Learn More Learn More
</a> </a>
...@@ -58,40 +58,27 @@ description: > ...@@ -58,40 +58,27 @@ description: >
class="language-php" class="language-php"
data-controller="hljs" data-controller="hljs"
data-hljs-language-value="php" data-hljs-language-value="php"
>#[RespondsToPromptSubject( >#[RespondsToHttp(
action: 'adopt', method: RequestMethod::GET,
subject: 'cat', pattern: '/',
)] )]
#[Singleton(collection: SingletonCollection::PromptSubjectResponder)] function Homepage(ServerRequestInterface $request, ResponseInterface $response): TwigTemplate
readonly class CatAdopt implements PromptSubjectResponderInterface
{ {
public function respondToPromptSubject(PromptSubjectRequest $request, PromptSubjectResponse $response): void return new TwigTemplate('website/homepage.twig');
{
// Pipes message through WebSocket...
$response->write("Here you go:\n\n");
$response->write(" |\_._/|\n");
$response->write(" | o o |\n");
$response->write(" ( T )\n");
$response->write(" .^`-^-`^.\n");
$response->write(" `. ; .`\n");
$response->write(" | | | | |\n");
$response->write(" ((_((|))_))\n");
$response->end();
}
}</code></pre> }</code></pre>
</li> </li>
<li class="formatted-content homepage__example"> <li class="formatted-content homepage__example">
<h2 class="homepage__example__title"> <h2 class="homepage__example__title">
Artificial Intelligence Chat with Open-Source LLMs
</h2> </h2>
<div class="homepage__example__description"> <div class="homepage__example__description">
<p> <p>
Integrate your application with self-hosted open-source Create prompt controllers to directly answer user's
LLMs. prompts.
</p> </p>
<p> <p>
Use your own Machine Learning models in production. LLM takes care of determining user's intention, you
can focus on taking an appropriate action.
</p> </p>
<a <a
class="homepage__cta homepage__cta--example" class="homepage__cta homepage__cta--example"
...@@ -104,21 +91,26 @@ readonly class CatAdopt implements PromptSubjectResponderInterface ...@@ -104,21 +91,26 @@ readonly class CatAdopt implements PromptSubjectResponderInterface
class="language-php" class="language-php"
data-controller="hljs" data-controller="hljs"
data-hljs-language-value="php" data-hljs-language-value="php"
>class LlamaCppGenerate >#[RespondsToPromptSubject(
action: 'adopt',
subject: 'cat',
)]
#[Singleton(collection: SingletonCollection::PromptSubjectResponder)]
readonly class CatAdopt implements PromptSubjectResponderInterface
{ {
public function __construct(protected LlamaCppClientInterface $llamaCppClient) public function respondToPromptSubject(PromptSubjectRequest $request, PromptSubjectResponse $response): void
{
}
public function doSomething(): void
{ {
$request = new LlamaCppCompletionRequest('How to make a cat happy?'); // Pipes message through WebSocket...
$completion = $this->llamaCppClient->generateCompletion($request);
foreach ($completion as $token) { $response->write("Here you go:\n\n");
// ... $response->write(" |\_._/|\n");
} $response->write(" | o o |\n");
$response->write(" ( T )\n");
$response->write(" .^`-^-`^.\n");
$response->write(" `. ; .`\n");
$response->write(" | | | | |\n");
$response->write(" ((_((|))_))\n");
$response->end();
} }
}</code></pre> }</code></pre>
</li> </li>
...@@ -165,47 +157,6 @@ final readonly class EchoResponder extends WebSocketJsonRPCResponder ...@@ -165,47 +157,6 @@ final readonly class EchoResponder extends WebSocketJsonRPCResponder
$rpcRequest->payload, $rpcRequest->payload,
)); ));
} }
}</code></pre>
</li>
<li class="formatted-content homepage__example">
<h2 class="homepage__example__title">
Simple Things Remain Simple
</h2>
<div class="homepage__example__description">
<p>
Writing HTTP controllers is similar to how it's done in
the synchronous code.
</p>
<p>
Controllers have new exciting features that take
advantage of the asynchronous environment.
</p>
<a
class="homepage__cta homepage__cta--example"
href="/docs/features/http/controllers.html"
>
Learn More
</a>
</div>
<pre class="homepage__example__code fenced-code"><code
class="language-php"
data-controller="hljs"
data-hljs-language-value="php"
>#[RespondsToHttp(
method: RequestMethod::GET,
pattern: '/',
routeSymbol: HttpRouteSymbol::Homepage,
)]
#[Singleton(collection: SingletonCollection::HttpResponder)]
readonly class Homepage implements HttpResponderInterface
{
public function respond(
ServerRequestInterface $request,
ResponseInterface $response
): TwigTemplate
{
return new TwigTemplate($request, $response, 'website/homepage.twig');
}
}</code></pre> }</code></pre>
</li> </li>
<li class="formatted-content homepage__example"> <li class="formatted-content homepage__example">
......
...@@ -115,7 +115,7 @@ final readonly class Homepage extends HttpResponder ...@@ -115,7 +115,7 @@ final readonly class Homepage extends HttpResponder
{ {
public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
{ {
return new TwigTemplate($request, $response, 'homepage.twig'); return new TwigTemplate('homepage.twig');
} }
} }
``` ```
......
...@@ -113,7 +113,7 @@ final readonly class LlmChat extends HttpResponder ...@@ -113,7 +113,7 @@ final readonly class LlmChat extends HttpResponder
{ {
public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
{ {
return new TwigTemplate($request, $response, 'turbo/llmchat/index.twig'); return new TwigTemplate('turbo/llmchat/index.twig');
} }
} }
``` ```
......
...@@ -255,7 +255,7 @@ final readonly class LoginForm extends HttpResponder ...@@ -255,7 +255,7 @@ final readonly class LoginForm extends HttpResponder
{ {
public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
{ {
return new TwigTemplate($request, $response, 'auth/login_form.twig'); return new TwigTemplate('auth/login_form.twig');
} }
} }
``` ```
...@@ -473,7 +473,7 @@ final readonly class LoginValidation extends HttpController ...@@ -473,7 +473,7 @@ final readonly class LoginValidation extends HttpController
$user->user, $user->user,
); );
return new InternalRedirect($request, $response, HttpRouteSymbol::Homepage); return new InternalRedirect(HttpRouteSymbol::Homepage);
} }
} }
``` ```
...@@ -512,7 +512,7 @@ final readonly class LogoutForm extends HttpResponder ...@@ -512,7 +512,7 @@ final readonly class LogoutForm extends HttpResponder
{ {
public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
{ {
return new TwigTemplate($request, $response, 'auth/logout_form.twig'); return new TwigTemplate('auth/logout_form.twig');
} }
} }
``` ```
......
...@@ -64,6 +64,11 @@ readonly class HttpResponderAggregate implements RequestHandlerInterface ...@@ -64,6 +64,11 @@ readonly class HttpResponderAggregate implements RequestHandlerInterface
$responder = $this->selectResponder($request); $responder = $this->selectResponder($request);
try { try {
$context = SwooleCoroutineHelper::mustGetContext();
$context['psr_http_request'] = $request;
$context['psr_http_response'] = $response;
return $this->recursiveResponder->respondRecursive($request, $response, $responder); return $this->recursiveResponder->respondRecursive($request, $response, $responder);
} catch (Throwable $throwable) { } catch (Throwable $throwable) {
$this->eventDispatcher->dispatch(new UnhandledException($throwable)); $this->eventDispatcher->dispatch(new UnhandledException($throwable));
......
...@@ -9,15 +9,22 @@ use Psr\Http\Message\ServerRequestInterface; ...@@ -9,15 +9,22 @@ use Psr\Http\Message\ServerRequestInterface;
readonly class InternalRedirect implements HttpInterceptableInterface readonly class InternalRedirect implements HttpInterceptableInterface
{ {
private ServerRequestInterface $request;
private ResponseInterface $response;
/** /**
* @param array<string,string> $params * @param array<string,string> $params
*/ */
public function __construct( public function __construct(
private ServerRequestInterface $request,
private ResponseInterface $response,
public HttpRouteSymbolInterface $routeSymbol, public HttpRouteSymbolInterface $routeSymbol,
public array $params = [], public array $params = [],
) {} ?ResponseInterface $response = null,
) {
$context = SwooleCoroutineHelper::mustGetContext();
$this->request = $context['psr_http_request'];
$this->response = $response ?? $context['psr_http_response'];
}
public function getResponse(): ResponseInterface public function getResponse(): ResponseInterface
{ {
......
...@@ -95,7 +95,7 @@ readonly class OAuth2AuthorizationCodeFlowController implements OAuth2Authorizat ...@@ -95,7 +95,7 @@ readonly class OAuth2AuthorizationCodeFlowController implements OAuth2Authorizat
->getHttpRouteSymbolForEndpoint(OAuth2Endpoint::AuthenticatedPage) ->getHttpRouteSymbolForEndpoint(OAuth2Endpoint::AuthenticatedPage)
; ;
return new InternalRedirect($request, $response, $routeSymbol); return new InternalRedirect($routeSymbol);
} }
public function redirectToClientScopeConsentPage( public function redirectToClientScopeConsentPage(
...@@ -107,7 +107,7 @@ readonly class OAuth2AuthorizationCodeFlowController implements OAuth2Authorizat ...@@ -107,7 +107,7 @@ readonly class OAuth2AuthorizationCodeFlowController implements OAuth2Authorizat
->getHttpRouteSymbolForEndpoint(OAuth2Endpoint::ClientScopeConsentForm) ->getHttpRouteSymbolForEndpoint(OAuth2Endpoint::ClientScopeConsentForm)
; ;
return new InternalRedirect($request, $response, $routeSymbol); return new InternalRedirect($routeSymbol);
} }
public function redirectToLoginPage( public function redirectToLoginPage(
...@@ -119,6 +119,6 @@ readonly class OAuth2AuthorizationCodeFlowController implements OAuth2Authorizat ...@@ -119,6 +119,6 @@ readonly class OAuth2AuthorizationCodeFlowController implements OAuth2Authorizat
->getHttpRouteSymbolForEndpoint(OAuth2Endpoint::LoginForm) ->getHttpRouteSymbolForEndpoint(OAuth2Endpoint::LoginForm)
; ;
return new InternalRedirect($request, $response, $routeSymbol); return new InternalRedirect($routeSymbol);
} }
} }
...@@ -5,6 +5,8 @@ declare(strict_types=1); ...@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace Distantmagic\Resonance; namespace Distantmagic\Resonance;
use RuntimeException; use RuntimeException;
use Swoole\Coroutine;
use Swoole\Coroutine\Context;
use Throwable; use Throwable;
use function Swoole\Coroutine\go; use function Swoole\Coroutine\go;
...@@ -12,6 +14,17 @@ use function Swoole\Coroutine\run; ...@@ -12,6 +14,17 @@ use function Swoole\Coroutine\run;
final readonly class SwooleCoroutineHelper final readonly class SwooleCoroutineHelper
{ {
public static function mustGetContext(): Context
{
$context = Coroutine::getContext();
if (is_null($context)) {
throw new RuntimeException('Unable to get coroutine context');
}
return $context;
}
/** /**
* @param callable() $callback * @param callable() $callback
*/ */
......
...@@ -9,15 +9,22 @@ use Psr\Http\Message\ServerRequestInterface; ...@@ -9,15 +9,22 @@ use Psr\Http\Message\ServerRequestInterface;
final readonly class TwigTemplate implements HttpInterceptableInterface final readonly class TwigTemplate implements HttpInterceptableInterface
{ {
private ServerRequestInterface $request;
private ResponseInterface $response;
/** /**
* @psalm-taint-source file $templatePath * @psalm-taint-source file $templatePath
*/ */
public function __construct( public function __construct(
private ServerRequestInterface $request,
private ResponseInterface $response,
private string $templatePath, private string $templatePath,
private array $templateData = [], private array $templateData = [],
) {} ?ResponseInterface $response = null,
) {
$context = SwooleCoroutineHelper::mustGetContext();
$this->request = $context['psr_http_request'];
$this->response = $response ?? $context['psr_http_response'];
}
public function getResponse(): ResponseInterface public function getResponse(): ResponseInterface
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment