Code Monkey home page Code Monkey logo

hawkbit's Introduction

Hawkbit\Application

Latest Version on Packagist Software License Build Status Total Downloads Coverage Status

Hawkbit\Application micro framework is a high customizable, event driven and compatible with PSR-7, StackPHP and Zend Stratigility.

Hawkbit\Application uses latest versions of League\Route for routing, League\Container for dependency injection, League\Event for event dispatching, Zend Config for configuration.

Hawkbit\Application is an advanced derivate of Proton and part of Hawkbit\Application Component collection by Marco Bunge. Hawkbit\Application 1.x is also known as Blast Hawkbit\Application.

Quick start

Please see public/ for example usage and read documentation.

Integrations

Hawkbit\Application delivers also optional packages:

Motivation

My vision is to provide a micro framework which is able to handle HTTP and CLI in the same fashion. The developer should be able to reuse it's code, design it's business layer by his needs. Hawkbit should be a supporting tool instead of predefined framework. And yes it is under active development.

I love PSR, phpleague und a minimal set of dependecies and want to create a micro framework which is used the best packages out there and bundeld in a nice application layer. I'm also love the style of component-based development.

Hawkbit is built on top phpleague packages and keep PSR in mind. Hawkbit is designed to co-exist whith your code instead of replace code base. Hawkbit does has a small dependency footprint. Last but not least Hawkbit does not force the developer how to design the application bussiness logic, since we prefer to use POPO's for Controllers / Commands (the accessor to bussiness logic).

At the moment I design and develop all Hawkbit packages and manage the whole codebase. I would be appreciate for support or even better: for contributors!

Please refer to Issue #33 for details.

Special thanks

Thank you for PR, identifing Bus, or any other Improvements!

Install

Using Composer

Hawkbit\Application 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 hawkbit/hawkbit

composer.json

{
    "require": {
        "hawkbit/hawkbit": "~2.0"
    }
}

Be sure to also include your Composer autoload file in your project:

<?php

require __DIR__ . '/vendor/autoload.php';

Downloading .zip file

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.

Requirements

The following versions of PHP are supported by this version.

  • PHP 5.5
  • PHP 5.6
  • PHP 7.0
  • PHP 7.1
  • HHVM

Setup

Create a new app

<?php

require __DIR__.'/../vendor/autoload.php';

$app = new \Hawkbit\Application();

Create a new app with configuration

<?php

$config = [
    'key' => 'value'
];
$app = new \Hawkbit\Application($config);

Add routes

<?php

/** @var Hawkbit\Application $app */
$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();

See also our example at /public/index.php.

Configuration

Add additional configuration to application

Hawkbit\Application Configuration is managed by zend-config.

<?php

//add many values
$app->setConfig([
    'database' => [
        'default' => 'mysql://root:root@localhost/acmedb',
    ],
    'services' => [
        'Acme\Services\ViewProvider',
    ]
]);

//add a single value
$app->setConfig('baseurl', 'localhost/');

$app->getConfig()->baseurl = 'localhost/';
$app->getConfig()['baseurl'] = 'localhost/';

Access configuration

<?php

//access all configuration
$app->getConfig();

//get configuration item
$default = $app->getConfig('database')->default; // returns 'mysql://root:root@localhost/acmedb
$default = $app->getConfig()->database->default; // returns 'mysql://root:root@localhost/acmedb
$default = $app->getConfig('database')['default']; // returns 'mysql://root:root@localhost/acmedb
$default = $app->getConfig()['database']['default']; // returns 'mysql://root:root@localhost/acmedb

Middlewares

Hawkbit\Application middlewares allows advanced control of lifecycle execution.

<?php

$app->addMiddleware(new Acme\SomeMiddleware);

Hawkbit\Application uses it's own runner Hawkbit\Application\MiddelwareRunner

Routing

Hawkbit\Application uses routing integration of league/route and allows access to route collection methods directly.

Basic usage with anonymous functions:

<?php
// index.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();

Access app from anonymous function

Hawkbit\Application allows to access itself from anonymous function through closure binding.

<?php

$app->get('/hello/{name}', function ($request, $response, $args) {
    
    // access Hawkbit\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 Hawkbit\Application();

$app->get('/', 'HomeController::index'); // calls index method on HomeController class

$app->run();
<?php

// HomeController.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

require __DIR__.'/../vendor/autoload.php';

$app = new Hawkbit\Application();

$app->getContainer()->add('CustomService', new CustomService);
$app->get('/', 'HomeController::index'); // calls index method on HomeController class

$app->run();

Please use boot method in Service Providers for correct injection of services into controller!

<?php

// HomeController.php

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class HomeController
{
    /**
     * @var CustomService
     */
    private $service;

    /**
     * @param CustomService $application
     */
    public function __construct(CustomService $service = null)
    {
        $this->service = $service;
    }

    /**
     * @return CustomService
     */
    public function getService()
    {
        return $this->service;
    }
    
    public function index(ServerRequestInterface $request, ResponseInterface $response, array $args)
    {
        //do somehing with service
        $service = $this->getService();
        return $response;
    }
}

For more information about routes read this guide

Route groups

Hawkbit\Application add support for route groups.

<?php

$app->group('/admin', function (\League\Route\RouteGroup $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');
});

Available vars

  • $route - \League\Route\RouteGroup
  • $this - \Hawkbit\Application

Middleware integrations

StackPHP

Basic usage with StackPHP (using Stack\Builder and Stack\Run):

<?php

// index.php
require __DIR__.'/../vendor/autoload.php';

$app = new Hawkbit\Application();

$app->get('/', function ($request, $response) {
    $response->setContent('<h1>Hello World</h1>');
    return $response;
});

$httpKernel = new Hawkbit\Application\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

Zend Stratigility

Basic usage with Stratigility (using Zend\Stratigility\MiddlewarePipe):

<?php

use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\ServerRequestFactory;
use Hawkbit\Application;
use Hawkbit\Application\Stratigility\MiddlewarePipeAdapter;

$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>');

    /** @var ResponseInterface $response */
    $response = $next($request, $response);

    $response->getBody()->write('</h1>');
});

/** @var ResponseInterface $response */
$response = $middleware(ServerRequestFactory::fromGlobals(), $application->getResponse());

echo $response->getBody(); //prints <h1>Hello World</h1>

Error handling

Hawkbit\Application uses 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 Hawkbit\Application runs with error options disabled. To enable debugging add

<?php

$app->setConfig('error', true);

By default Hawkbit\Application is catching all errors. To disable error catching add

<?php

$app->setConfig('error.catch', false);

Console

The console application inherit all methods from Http Application except routing and PSR-7 handling and capturing. In addition to http application, the console application does not support all events (Refer to events for more information!)

Logging

Hawkbit\Application 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.

Events

You can intercept requests and responses at seven points during the lifecycle. You can manipulate Request, Response and ErrorResponse via Hawkbit\ApplicationEvent.

Application event

<?php

/** @var \Hawkbit\Application\ApplicationEvent $event */

// custom params
$event->getParamCollection(); // returns a mutable \ArrayObject

// access application
$event->getApplication();

request.received

<?php

$app->addListener($app::EVENT_REQUEST_RECEIVED, function (\Hawkbit\Application\ApplicationEvent $event) {
    $request = $event->getRequest();
    
    // manipulate $request
    
    $event->setRequest($request);
});

This event is fired when a request is received but before it has been processed by the router.

response.created

Not available for Console applications!

<?php

$app->addListener($app::EVENT_RESPONSE_CREATED, function (\Hawkbit\Application\ApplicationEvent $event) {
    $request = $event->getRequest();
    $response = $event->getResponse();
        
    // manipulate request or response
    
    $event->setRequest($request);
    $event->setResponse($response);
});

This event is fired when a response has been created but before it has been output.

response.sent

Not available for Console applications! Please use shutdown

<?php

$app->addListener($app::EVENT_RESPONSE_SENT, function (\Hawkbit\Application\ApplicationEvent $event) {
    $request = $event->getRequest();
    $response = $event->getResponse();
    
    // manipulate request or response
    
    $event->setRequest($request);
    $event->setResponse($response);
});

This event is fired when a response has been output and before the application lifecycle is completed. Not available for Console Applications!

runtime.error

<?php

$app->addListener($app::EVENT_RUNTIME_ERROR, function (\Hawkbit\Application\ApplicationEvent $event, $exception) use ($app) {
    //process exception
});

This event is always fired when an error occurs.

lifecycle.error

Not available for Console applications! Please use runtime.error

$errorResponse is used as default response

<?php

$app->addListener($app::EVENT_LIFECYCLE_ERROR, function (\Hawkbit\Application\ApplicationEvent $event, \Exception $exception) {
    $errorResponse = $event->getErrorResponse();
 
    //manipulate error response and process exception
        
    $event->setErrorResponse($errorResponse);
});

This event is fired only when an error occurs while handling request/response lifecycle. This event is fired after runtime.error

lifecycle.complete

Not available for Console applications! Please use shutdown

<?php

$app->addListener($app::EVENT_LIFECYCLE_COMPLETE, function (\Hawkbit\Application\ApplicationEvent $event) {
    // 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.

shutdown

<?php

$app->addListener($app::EVENT_SHUTDOWN, function (\Hawkbit\Application\ApplicationEvent $event, $response, $terminatedOutputBuffers = []) {
    // access the response using $event->getResponse()
    // access terminated output buffer contents
    // or force application exit()
});

This event is always fired after each operation is completed or failed.

Custom Events

You can fire custom events using the event emitter directly:

<?php

// addListener
$app->addListener('custom.event', function ($event, $time) {
    return 'the time is '.$time;
});

// or with class addListener
$app->addListener(Acme\Event::class, function (Acme\Event $event, $time) {
    return 'the time is '.$time;
});

// Publish
$app->getEventEmitter()->emit('custom.event', time());

Dependency Injection Container

Hawkbit\Application uses League/Container as its dependency injection container.

You can bind singleton objects into the container from the main application object using ArrayAccess:

<?php
/** @var Hawkbit\Application $app */
$app['db'] = function () use($app) {
    $config = $app->getConfig('database');
    $manager = new Illuminate\Database\Capsule\Manager;

    $manager->addConnection([
        'driver'    => 'mysql',
        'host'      => $config['host'],
        'database'  => $config['name'],
        'username'  => $config['user'],
        'password'  => $config['pass'],
        'charset'   => 'utf8',
        'collation' => 'utf8_unicode_ci'
    ], 'default');

    $manager->setAsGlobal();

    return $manager;
};

or by container access:

<?php
/** @var Hawkbit\Application $app */
$app->getContainer()->share('db', function () use($app) {
    $config = $app->getConfig('database');
    $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 Hawkbit\Application 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')
        );
});

Container

Set your own container needs an instance of \League\Container\ContainerInterface

<?php

$app->setContainer($container);

Get container

<?php

$app->getContainer();

Services

Hawkbit\Application uses dependency injection container to access services. Following integrations can be exchanged.

Configurator

Uses in Application::setConfig(),Application::getConfig() and Application::hasConfig()

<?php

$app->getConfigurator();
<?php

$app->getContainer()->share(\Zend\Config\Config::class, new \Zend\Config\Config([], true));

error handler

<?php

$app->getContainer()->share(\Whoops\Run::class, new \Whoops\Run());
<?php

$app->getErrorHandler();

error response handler

<?php

$app->getContainer()->share(\Whoops\Handler\HandlerInterface::class, Acme\ErrorResponseHandler::class);
<?php

$app->getErrorResponseHandler();

psr logger

Get a new logger instance by channel name

<?php

$app->getContainer()->add(\Psr\Log\LoggerInterface::class, \Monolog\Logger::class);
<?php

$app->getLogger('channel name');

Get a list of available logger channels

<?php

$app->getLoggerChannels();

psr server request

<?php

$app->getContainer()->share(\Psr\Http\Message\ServerRequestInterface::class, \Zend\Diactoros\ServerRequestFactory::fromGlobals());
<?php

$app->getRequest();

psr response

<?php

$app->getContainer()->add(\Psr\Http\Message\ResponseInterface::class, \Zend\Diactoros\Response::class);
<?php

$app->getRequest();

response emitter

<?php

$app->getContainer()->share(\Zend\Diactoros\Response\EmitterInterface::class, \Zend\Diactoros\Response\SapiEmitter::class);
<?php

$app->getResponseEmitter();

Change log

Please see CHANGELOG for more information what has changed recently.

Testing

$ composer test

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

hawkbit's People

Contributors

adammbalogh avatar alexbilbie avatar avenirer avatar blackscorp avatar grahamcampbell avatar hannesvdvreken avatar mbunge avatar sagikazarmark avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

hawkbit's Issues

Json is always forces on XHR!

While try to response with html, hawkbit wants to create response with json. A XHR is not a ajax request basically.

The future of hawkbit

Based on Questions by @designcise:

@mbunge Since there isn't a platform where we can discuss this, I'm going to ask this here, I hope you find the time to reply:

So, the Hawkbit framework is very good, but I want to know:

  1. Is the Hawkbit framework being actively developed? Are there any specific future goals, vision, etc. laid out?
  2. What made you develop Hawkbit? Why you felt there was a need for it?
  3. Was Hawkbit intended to compete, innovate, lead, or merely co-exist?
  4. How many developers are working on it? What kind of funding is required and how much time they are able to apply to the project?

Sorry, I'm just asking to assess what the future holds for the framework before implementing it into my projects. I would love to contribute to it in the future, but it's important to know how things are presently.

Thank you for your time! I really appreciate it!

Routes Not Working

Nothing besides the / root route gets captured. For example:

$app->get('/hello/{name}', function ($request, $response, $args) {
    $response->getBody()->write(
        sprintf('<h1>Hello, %s!</h1>', $args['name'])
    );
    return $response;
});

doesn't work! Neither does anything else besides /. Could it be because of @BlackScorp 's PR?

HomeControlller Not Found

I have put the HomeController.php on src folder and thrown a error Whoops\Exception\ErrorException: Class 'HomeController' not found in file.

Where i should put this file ?

my index.php like this:
`<?php
require DIR.'/../vendor/autoload.php';

$config = [
'key' => '1234'
];

$app = new Hawkbit\Application($config);

$app->get('/', 'HomeController::index'); // calls index method on HomeController class

$app->run();
`

Getting Started Issue

I'm following the docs:

<?php

require __DIR__.'/../vendor/autoload.php';

$app = new Hawkbit\Application();

$app->get('/', function ($request, $response) {
    $response->getBody()->write('<h1>It works!</h1>');
    return $response;
});

$app->run();

?>

But that code returns:

Fatal error: Class 'Hawkbit\Application\Application' not found in D:\Work\web\index.php on line 5

Removing "\Application" works (so it must mean there's a typo in the docs), but this presents the following issue:

League\Route\Http\Exception\NotFoundException: Not Found in file D:\Work\framework\vendor\league\route\src\Dispatcher.php on line 76 Stack trace: 1. League\Route\Http\Exception\NotFoundException->() D:\Work\framework\vendor\league\route\src\Dispatcher.php:76 2. League\Route\Dispatcher->handleNotFound() D:\Work\framework\vendor\league\route\src\Dispatcher.php:35 3. League\Route\Dispatcher->handle() D:\Work\framework\vendor\league\route\src\RouteCollection.php:118 4. League\Route\RouteCollection->dispatch() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application.php:920 5. Hawkbit\Application->handleResponse() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application.php:875 6. Hawkbit\Application->Hawkbit\{closure}() [internal]:0 7. Closure->__invoke() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application\MiddlewareRunner.php:97 8. call_user_func_array() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application\MiddlewareRunner.php:97 9. Hawkbit\Application\MiddlewareRunner->Hawkbit\Application\{closure}() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application.php:868 10. Hawkbit\Application->Hawkbit\{closure}() [internal]:0 11. Closure->__invoke() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application\MiddlewareRunner.php:97 12. call_user_func_array() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application\MiddlewareRunner.php:97 13. Hawkbit\Application\MiddlewareRunner->Hawkbit\Application\{closure}() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application\MiddlewareRunner.php:101 14. Hawkbit\Application\MiddlewareRunner->run() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application.php:884 15. Hawkbit\Application->handle() D:\Work\framework\vendor\hawkbit\hawkbit\src\Application.php:985 16. Hawkbit\Application->run() D:\Work\framework\web\index.php:12

Any help would be greatly appreciated! Here's my composer.json:

{
    "require": {
		"hawkbit/hawkbit": "~2.0",
		"hawkbit/Presentation": "~1.0"
	}
}

[Insight] Error silenced by the at sign (@) - in src/Application.php, line 899

in src/Application.php, line 899

Adding "@" before gc_enable() prevents warning and errors during this function execution from being displayed.
If you need to do that, you should probably implement a better way to qualify and recover from errors, using Exceptions.

     */
    public function cleanUp()
    {
        // try to enable garbage collection
        if (!gc_enabled()) {
            @gc_enable();
        }

        // collect garbage only if garbage
        // collection is enabled
        if(gc_enabled()){

Posted from SensioLabsInsight

Undefined variable: this

Issue: Inside an application closure function, $this is undefined.

To Reproduce:

/// ...
$app->get('/', function (Request $request, Response $response) {
    echo nl2br(print_r($this, true)); // or try $temp = $this;
    return $response;
});

Expected Result: Should return an instance of the Hawkbit\Application object.

The same thing happens inside $app->group()


Note: Also on an unrelated note, this link in the docs leads to a 404 "http://container.thephpleague.com/service-providers/"

Inccorect error handling

  • Whoops handler is not registered
  • Added events in ApplicationSystemFacade are incorrect. Wrap handler function and emit error event

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.