cevantime/sherpa-core

0.0.1 2018-10-30 22:09 UTC

This package is auto-updated.

Last update: 2024-04-29 04:06:06 UTC


README

A basic core class to build psr compliant applicationswith ease. The code is intentionnally minimal. For a more integrated experience, see Sherpa Framework.

Installation

Sherpa Core is installable via composer

composer require cevantime/sherpa

Getting Started

Sherpa use psr-7 for HTTP messages. You can use Zend Diactoros implementation to build your Request in your index file.

// index.php
use Zend\Diactoros\ServerRequestFactory;
 
$request = ServerRequestFactory::fromGlobals();

Now, you can init the Sherpa Kernel

use Sherpa\Kernel\Kernel;
// ...
$app = new Kernel();

Now, you can add a little middleware to display a simple 'Hello Sherpa' :

use Zend\Diactoros\Response\HtmlResponse;
// ...
$app->pipe(function(){
    return new HtmlResponse("Hello Sherpa !");
});

The Kernel will handle the response for the request

$response = $app->handle($request);

When the response is ready, it needs to be emitted !

use Zend\Diactoros\Response\SapiEmitter;
// ...
(new SapiEmitter())->emit($response);

That's it ! Now visit your index file. You should see "Hello Sherpa !"

Middlewares

Sherpa Core do his best to be compliant with psr7 and psr-15 recommandations. Those recommendations introduces the usage of Message Interface and Middlewares. Piping middlewares in the Kernel is pretty simple :

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

$kernel->pipe(function(ServerRequestInterface $request, RequestHandlerInterface $handler) {
	// ...
});

Accordingly to the specifications, middlewares must return an instance of Psr\Http\Message\ResponseInterface. It can do so by :

  • Delegating the Response creation to the next middleware by calling $handler->handle($request) and eventually modify the response.
  • Generate his own Response and bypass the next Middlewares.

Here is a (imaginary) examplen that check if we are visiting an admin uri and prevent user that are not admin to enter !

$kernel->pipe(function(ServerRequestInterface $request, RequestHandlerInterface $handler) {
	// check if user tries to access an admin page and check if he is admin
	if(preg_match($request->getUri()->getPath(), '~^/admin~') 
		&& ! $request->getAttribute('user')->isAdmin()) {
		// if not, send an error
		return (new Response('php://memory'))
			->withStatus(403, 'You are not allowed to enter !';
	}
	// otherwise, let the process continue and the next middleware proceed
	return $handler->handle($request);
});

Optionnally (but it is worth noting it !) an integer can be provided as second param in order to set a priority for the middleware (higher comes before).

$kernel->pipe(function(){ return new Response(...); }, 50);

Container and Autowiring

Sherpa Core use PHP DI Container as a Dependency Container and Dependency Injector. This package lets you inject any class of your project (even your vendor) with minimal configuration. Every type hinted parameter can be injected. You only need to configure parameters than can't be guessed by type hinting. Classes that are defined using class/interface names as keys will become injectable :

use function DI\get;  
use function DI\create;
use Psr\Log\LoggerInterface;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Psr\Container\ContainerInterface;
//... 
$builder = $kernel->getContainerBuilder();
$builder->addDefinitions([
        // create definition directly...
	'log.folder' => 'var/log',
	// ... or using a callback function...
	StreamHandler::class => function(ContainerInterface $container) {
		return new StreamHandler($container->get('log.folder');
	},
	// ... or using configuration methods that come with php di
	LoggerInterface::class => create(Logger::class)
		 ->constructor('mylogger')
		 ->method('pushHandler', get(StreamHandler::class))
]);

Now a logger is injectable anywhere, including a class middleware :

use App\UserRepository;  
use Psr\Log\LoggerInterface;  
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Psr\Http\Message\ResponseInterface as Response;
  
class UserMiddleware implements MiddlewareInterface
{
	protected $userRepository;
	protected $logger;
	
	/**
	 * @param UserRepository $userRepository is auto-injected !
	 * @param LoggerInterface $logger hase been configure to inject an instance of Logger
	 * /
	public function __construct(UserRepository $userRepository, LoggerInterface $logger)
	{
		$this->userRepository = $userRepository;
		$this->logger = $logger;
	}
	public function process(Request $request, Handler $handler): Response 
	{
		$user = $this->userRepository->findBy(['id' => $_SESSION['user_id'] ?? 0]);
		if($user) {
			$request->setAttribute('user', $user);
		} else {
			$this->logger->log('info', 'No user found in session');
		}
		return $handler->handle($request);
	}
}

The Kernel will use di container to inject the middleware in the middleware stack. Thus you can pipe middlewares by only providing their class names.

$kernel->pipe(UserMiddleware::class);

Conclusion

This kernel is fairly minimal but it is a good starting point to build great applications with full psr compliance. Sherpa Framework is an example of how this kernel can be used.