East Foundation
Create PHP applications following the #East programming philosophy.
To integrate your code, following the #east programming philosophy, to your framework, while keeping this paradigm.
This library implement a middleware pattern to allow additional behavior or feature, like session or translation.
This library is built on the Recipe library, and redefine only some interfaces to be more comprehensive with HTTP context. It can be also used for workers.
- Middleware are actions, but must implement a specific interface.
- The HTTP workflow is defined into a Recipe, able to be extended.
- Chef became a manager, to execute the workflow when a request is accepted.
- Triggering asynchronous tasks (thanks to pcntl) for timers.
- Setting up a worker health check
- Provides non blocking sleep method
- Usable with any PSR-11 Framework, Symfony implementation is also provided.
To understand “east”, think of a map, like north, south, east, west; the idea, as far as I understand, is that west-oriented is stateful (you call methods and work on the return values), while east-oriented is stateless (you pass lambdas or interface implementations).
http://www.draconianoverlord.com/2013/04/12/east-oriented-programming.html
Features
East
Can implement your #East package in your framework while keeping this philosophy.
PSR-7
Interoperable with any code following the PSR-7 recommendation.
PSR-11
Interoperable with any framework implementing the PSR-11 recommendation.
Extendable
Thanks to Recipe, can be extendable only via the DI's configuration.
Fork the project on GitHub
It is open source! Hosted, developed, and maintained on GitHub.
View GitHub Project
Support this project on Patreon
This project is free and will remain free, but its development is not. If you like it and help us maintain it and evolve it, don't hesitate to support us on Patreon.
Support it
Example
<?php
declare(strict_types=1);
use Psr\Http\Message\MessageInterface;
use Teknoo\East\Foundation\Client\ResponseInterface;
use Teknoo\East\Foundation\Router\ResultInterface;
use DI\ContainerBuilder;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\Response\TextResponse;
use Teknoo\East\Foundation\Client\ClientInterface;
use Teknoo\East\Foundation\Manager\ManagerInterface;
use Teknoo\East\Foundation\Middleware\MiddlewareInterface;
use Teknoo\East\Foundation\Recipe\RecipeInterface;
use Teknoo\East\Foundation\Router\Result;
use Teknoo\East\Foundation\Router\RouterInterface;
use function DI\decorate;
require_once 'vendor/autoload.php';
//Simulate client, accepts responses from controller and pass them to the "framework" or lower layer to send them to
//the browser.
$client = new class implements ClientInterface {
private ResponseInterface | MessageInterface | null $response = null;
private bool $inSilentlyMode = false;
public function updateResponse(callable $modifier): ClientInterface
{
$modifier($this, $this->response);
return $this;
}
public function acceptResponse(ResponseInterface | MessageInterface $response): ClientInterface
{
$this->response = $response;
return $this;
}
public function sendResponse(
ResponseInterface | MessageInterface | null $response = null,
bool $silently = false
): ClientInterface
{
$silently = $silently || $this->inSilentlyMode;
if (null !== $response) {
$this->acceptResponse($response);
}
if (true === $silently && null === $this->response) {
return $this;
}
if ($this->response instanceof MessageInterface) {
print $this->response->getBody() . PHP_EOL;
} else {
print $this->response . PHP_EOL;
}
return $this;
}
public function errorInRequest(Throwable $throwable, bool $silently = false): ClientInterface
{
print $throwable->getMessage() . PHP_EOL;
return $this;
}
public function mustSendAResponse(): ClientInterface
{
$this->inSilentlyMode = false;
return $this;
}
public function sendAResponseIsOptional(): ClientInterface
{
$this->inSilentlyMode = true;
return $this;
}
};
//First controller / endpoint, dedicated for the request /foo
$endPoint1 = static function (MessageInterface $message, ClientInterface $client): void {
$client->sendResponse(
new TextResponse('request /bar, endpoint 1, value : ' . $message->getQueryParams()['value'])
);
};
//Second controller / endpoint, dedicated for the request /bar
$endPoint2 = static function (ClientInterface $client, string $value) {
$client->sendResponse(
new class ($value) implements ResponseInterface {
public function __construct(
private string $value,
) {
}
public function __toString(): string
{
return "request /bar, endpoint 2, value : {$this->value}";
}
}
);
};
/**
* Simulate router
*/
$router = new class($endPoint1, $endPoint2) implements RouterInterface {
/**
* @var callable
*/
private $endPoint1;
/**
* @var callable
*/
private $endPoint2;
public function __construct(callable $endPoint1 , callable $endPoint2)
{
$this->endPoint1 = $endPoint1;
$this->endPoint2 = $endPoint2;
}
public function execute(
ClientInterface $client ,
MessageInterface $message,
ManagerInterface $manager
): MiddlewareInterface
{
$uri = (string) $message->getUri();
$manager->updateWorkPlan([
ResultInterface::class => match ($uri) {
'/foo' => new Result($this->endPoint1),
'/bar' => new Result($this->endPoint2),
},
]);
$manager->continueExecution($client, $message);
return $this;
}
};
$builder = new ContainerBuilder();
$builder->addDefinitions('src/di.php');
$builder->addDefinitions([
RouterInterface::class => $router,
RecipeInterface::class => decorate(function ($previous) use ($router) {
if ($previous instanceof RecipeInterface) {
$previous = $previous->registerMiddleware(
$router,
RouterInterface::MIDDLEWARE_PRIORITY
);
}
return $previous;
})
]);
$container = $builder->build();
//Simulate Server request reception
$request1 = new ServerRequest([], [], '/foo', 'GET');
$request1 = $request1->withQueryParams(['value' => 'bar']);
$request2 = new ServerRequest([], [], '/bar', 'GET');
$request2 = $request2->withQueryParams(['value' => 'foo']);
$manager = $container->get(ManagerInterface::class);
$manager->receiveRequest($client, $request1);
//Print: request /bar, endpoint 1, value : bar
$manager->receiveRequest($client, $request2);
//Print: request /bar, endpoint 2, value : foo