East Foundation
Créer des applications PHP en implémentant la philosophie #East programming.
Pour intégrer votre projet suivant le #East programming avec votre framework favori tout en gardant ce paradigme.
La bibliothèque implémente le pattern "Middleware" pour vous permettre de compléter son comportement, tel qu'ajouter des fonctionnalités, le support des sessions ou des traductions.
Cette bibliothèque est construite sur la bibliothèque "Recipe" et surchage uniquement certaines interfaces afin de correspondre au contexte HTTP. Elle peut être néamoins utilisée pour des workers.
- Les Middlewares sont des actions, mais ils implémentent une interface spécifique.
- Le workflow HTTP est définie via une "Recipe", capable d'être complétée.
- Le Chef devient un "Manager" et exécute le workflow définie pour les requêtes HTTP.
- Gestion des tâches asynchrones (grâce à pcntl) via des timers.
- Mécanisme permettant de mettre en place un healthcheck des workers.
- Fournit une fonction sleep non bloquante.
- Utilisable avec n'importe quel framework PSR-11 ou avec Symfony 6.4+
Pour illustrer la philosophie "East", pensez à une boussole : comme le nord, le sud, l'est, l'ouest ; l'idée est que la direction "ouest" est avec état (vous appelez des méthodes et travaillez sur les valeurs de retour), tandis que la direction "est" est sans état (vous transmettez des lambdas ou des implémentations d'interface).
http://www.draconianoverlord.com/2013/04/12/east-oriented-programming.html
Fonctionnalités
East
Utiliser le #East programming avec votre framework préféré.
PSR-7
Interopérable avec tout code exploitant la recommandation PSR-7.
PSR-11
Interopérable avec tout framework utilisant la recommandation PSR-11.
Extensible
Grâce à Recipe, peut-être complété uniquement via la configuration de la DI.
Cloner le projet sur GitHub
Projet sous licence open source! Hébergé, développé et maintenu sur GitHub.
Voir le projet sur GitHub
Supporter le projet sur Patreon
Ce projet est logiciel libre et le restera, mais son développement a un coût. Si vous l'appréciez et si vous souhaitez nous aider à le maintenir et à le faire évoluer. N'hésitez pas à nous supporter sur Patreon.
Supporter le p[rojet
Exemple
<?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