![Coverage Status][ico-coveralls]
Turbine is a advanced derivate of Proton and is a PSR-7, StackPHP, Zend Stratigility compatible micro framework.
Turbine uses latest versions of League\Route for routing, League\Container for dependency injection, and League\Event for event dispatching.
Turbine is available on Packagist and can be installed using Composer. This can be done by running the following command or by updating your composer.json
file.
composer require blast/turbine
composer.json
{
"require": {
"blast/turbine": "~1.0"
}
}
Be sure to also include your Composer autoload file in your project:
<?php
require __DIR__ . '/vendor/autoload.php';
This project is also available for download as a .zip
file on GitHub. Visit the releases page, select the version you want, and click the "Source code (zip)" download button.
The following versions of PHP are supported by this version.
- PHP 5.5
- PHP 5.6
- PHP 7.0
- HHVM
Create a new app
<?php
require __DIR__.'/../vendor/autoload.php';
$app = new \Turbine\Application();
Create a new app with configuration
<?php
$config = [
'key' => 'value'
];
$app = new \Turbine\Application($config);
Add routes
<?php
$app->get('/', function ($request, $response) {
$response->getBody()->write('<h1>It works!</h1>');
return $response;
});
$app->get('/hello/{name}', function ($request, $response, $args) {
$response->getBody()->write(
sprintf('<h1>Hello, %s!</h1>', $args['name'])
);
return $response;
});
Run application
<?php
$app->run();
Extend configuration of an existing instance
<?php
//add many values
$app->setConfig([
'database' => 'mysql://root:root@localhost/acmedb',
'services' => [
'Acme\Services\ViewProvider',
]
]);
//add a single value
$app->setConfig('baseurl' => 'localhost/');
Access configuration
<?php
//access all configuration
$app->getConfig();
//get one configuration item
$app->getConfig('database');
Turbine is using routing integration of league/route
and allows access to route collection methods directly.
Basic usage with anonymous functions:
<?php
// index.php
<?php
$app->get('/', function ($request, $response) {
$response->getBody()->write('<h1>It works!</h1>');
return $response;
});
$app->get('/hello/{name}', function ($request, $response, $args) {
$response->getBody()->write(
sprintf('<h1>Hello, %s!</h1>', $args['name'])
);
return $response;
});
$app->run();
Turbine allows to access Turbine\Application
from anonymous function through closure binding.
<?php
$app->get('/hello/{name}', function ($request, $response, $args) {
// access Turbine\Application
$app = $this
$response->getBody()->write(
sprintf('<h1>Hello, %s!</h1>', $args['name'])
);
return $response;
});
Basic usage with controllers:
<?php
require __DIR__.'/../vendor/autoload.php';
$app = new Turbine\Application();
$app->get('/', 'HomeController::index'); // calls index method on HomeController class
$app->run();
<?php
// HomeController.php
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class HomeController
{
public function index(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$response->getBody()->write('<h1>It works!</h1>');
return $response;
}
}
Automatic constructor injection of controllers:
<?php
// index.php
<?php
require __DIR__.'/../vendor/autoload.php';
$app = new Turbine\Application();
$app->share('App\CustomService', new App\CustomService)
$app->get('/', 'HomeController::index'); // calls index method on HomeController class
$app->run();
<?php
// HomeController.php
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class HomeController
{
/**
* @var App\CustomService
*/
private $service;
/**
* @param App\CustomService $application
*/
public function __construct(App\CustomService $service = null)
{
$this->service = $service;
}
/**
* @return App\CustomService
*/
public function getService()
{
return $this->service;
}
public function index(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
//do somehing with service
$service = $this->getService();
$response->getBody()->write();
return $response;
}
}
For more information about routes read this guide
Turbine add support for route groups.
<?php
$app->group('/admin', function ($route) {
//access app container (or any other method!)
$app = $this;
$route->map('GET', '/acme/route1', 'AcmeController::actionOne');
$route->map('GET', '/acme/route2', 'AcmeController::actionTwo');
$route->map('GET', '/acme/route3', 'AcmeController::actionThree');
});
$route
-\League\Route\RouteGroup
$this
-\Turbine\Application
Basic usage with StackPHP (using Stack\Builder
and Stack\Run
):
<?php
// index.php
<?php
require __DIR__.'/../vendor/autoload.php';
$app = new Turbine\Application();
$app->get('/', function ($request, $response) {
$response->setContent('<h1>Hello World</h1>');
return $response;
});
$httpKernel = new Turbine\Symfony\HttpKernelAdapter($app);
$stack = (new Stack\Builder())
->push('Some/MiddleWare') // This will execute first
->push('Some/MiddleWare') // This will execute second
->push('Some/MiddleWare'); // This will execute third
$app = $stack->resolve($httpKernel);
Stack\run($httpKernel); // The app will run after all the middlewares have run
Basic usage with Stratigility (using Zend\Stratigility\MiddlewarePipe
):
<?php
$application = new Application();
$application->get('/', function($request, ResponseInterface $response){
$response->getBody()->write('Hello World');
});
$middleware = new MiddlewarePipeAdapter($application);
//wrap html heading
$middleware->pipe('/', function($request, ResponseInterface $response, $next){
$response->getBody()->write('<h1>');
$response = $next($request, $response);
$response->getBody()->write('</h1>');
});
$response = $middleware(ServerRequestFactory::fromGlobals(), $application->getResponse());
echo $response->getBody(); //prints <h1>Hello World</h1>
Turbine is using Whoops error handling framework and determines the error handler by request content type.
Set your own handler:
<?php
$app->getErrorHandler()->push(new Acme\ErrorResponseHandler);
By default Turbine runs with error options disabled. To enable debugging add
<?php
$app->setConfig('error', true);
By default Turbine is catching all errors. To disable error catching add
<?php
$app->setConfig('error.catch', false);
Turbine has built in support for Monolog. To access a channel call:
<?php
$app->getLogger('channel name');
For more information about channels read this guide - https://github.com/Seldaek/monolog/blob/master/doc/usage.md#leveraging-channels.
You can intercept requests and responses at three points during the lifecycle:
<?php
$app->subscribe($app::EVENT_REQUEST_RECEIVED, function ($event, $request) {
// manipulate request
});
This event is fired when a request is received but before it has been processed by the router.
<?php
$app->subscribe($app::EVENT_RESPONSE_CREATED, function ($event, $request, $response) {
//manipulate request or response
});
This event is fired when a response has been created but before it has been output.
<?php
$app->subscribe($app::EVENT_RESPONSE_SENT, function ($event, $request, $response) {
//manipulate request and response
});
This event is fired when a response has been output and before the application lifecycle is completed.
<?php
$app->subscribe($app::EVENT_RUNTIME_ERROR, function ($event, $exception) use ($app) {
//process exception
});
This event is always fired when an error occurs.
$errorResponse
is used as default response
<?php
$app->subscribe($app::EVENT_LIFECYCLE_ERROR, function ($event, $exception, $errorResponse, $request, $response) {
//manipulate $errorResponse and process exception
});
This event is fired only when an error occurs while handling request/response lifecycle. This event is fired after runtime.error
<?php
$app->subscribe($app::EVENT_LIFECYCLE_COMPLETE, function ($event, $request, $response) {
// access the request using $event->getRequest()
// access the response using $event->getResponse()
})
This event is fired when a response has been output and before the application lifecycle is completed.
You can fire custom events using the event emitter directly:
<?php
// Subscribe
$app->subscribe('custom.event', function ($event, $time) {
return 'the time is '.$time;
});
// Publish
$app->getEventEmitter()->emit('custom.event', time());
Turbine uses League/Container
as its dependency injection container.
You can bind singleton objects into the container from the main application object using ArrayAccess:
<?php
$app['db'] = function () {
$manager = new Illuminate\Database\Capsule\Manager;
$manager->addConnection([
'driver' => 'mysql',
'host' => $config['db_host'],
'database' => $config['db_name'],
'username' => $config['db_user'],
'password' => $config['db_pass'],
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci'
], 'default');
$manager->setAsGlobal();
return $manager;
};
or by accessing the container directly:
<?php
$app->getContainer()->share('db', function () {
$manager = new Illuminate\Database\Capsule\Manager;
$manager->addConnection([
'driver' => 'mysql',
'host' => $config['db_host'],
'database' => $config['db_name'],
'username' => $config['db_user'],
'password' => $config['db_pass'],
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci'
], 'default');
$manager->setAsGlobal();
return $manager;
});
Multitons can be added using the add
method on the container:
<?php
//callback
$app->getContainer()->add('foo', function () {
return new Foo();
});
Service providers can be registered using the register
method on the Turbine app or addServiceProvider
on the container:
<?php
$app->register('\My\Service\Provider');
$app->getContainer()->addServiceProvider('\My\Service\Provider');
For more information about service providers check out this page - http://container.thephpleague.com/service-providers/.
For easy testing down the road it is recommended you embrace constructor injection:
<?php
$app->getContainer()->add('Bar', function () {
return new Bar();
});
$app->getContainer()->add('Foo', function () use ($app) {
return new Foo(
$app->getContainer()->get('Bar')
);
});
Set your own container needs an instance of \League\Container\ContainerInterface
<?php
$app->setContainer($container);
Get container
<?php
$app->getContainer($container);
Turbine uses dependecy injection container to access it's services. Following integrations can be exchanged.
Uses in Application::setConfig()
,Application::getConfig()
and Application::hasConfig()
<?php
$app->getConfigurator();
<?php
$app->getContainer()->share(\ArrayAccess::class, \ArrayObject::class)
<?php
$app->getContainer()->share(\Whoops\Run::class, new \Whoops\Run());
<?php
$app->getErrorHandler()
<?php
$app->getContainer()->share(\Whoops\Handler\HandlerInterface::class, Acme\ErrorResponseHandler::class);
<?php
$app->getErrorResponseHandler()
Get a new logger instance by channel name
<?php
$app->getContainer()->add(\Psr\Log\LoggerInterface::class, \Monolog\Logger::class);
<?php
$app->getLogger('channel name');
<?php
$app->getContainer()->share(\Psr\Http\Message\ServerRequestInterface::class, \Zend\Diactoros\ServerRequestFactory::fromGlobals());
<?php
$app->getRequest()
<?php
$app->getContainer()->add(\Psr\Http\Message\ResponseInterface::class, \Zend\Diactoros\Response::class);
<?php
$app->getRequest()
<?php
$app->getContainer()->share(\Zend\Diactoros\Response\EmitterInterface::class, \Zend\Diactoros\Response\SapiEmitter::class);
<?php
$app->getResponseEmitter()
Please see CHANGELOG for more information what has changed recently.
$ composer test
Please see CONTRIBUTING for details.
If you discover any security related issues, please email mjls@web.de instead of using the issue tracker.
- Marco Bunge
- Alex Bilbie (Proton)
- All contributors
The MIT License (MIT). Please see License File for more information.