andrewbreksa / unicorn
A tiny single class RAD PSR-7 web application "framework"
Requires
- league/container: ^2.2.0
- league/event: ^2.1.2
- league/route: ^2.0.1
- zendframework/zend-diactoros: ^1.3
Requires (Dev)
- phpunit/phpunit: ^5.7
- squizlabs/php_codesniffer: 2.*
This package is not auto-updated.
Last update: 2022-02-01 13:03:58 UTC
README
A tiny single class RAD PSR-7 web application "framework"
Unicorn is essentially a wrapper around zendframework/zend-diactoros and a couple of The PHP League packages: league/event, league/container, and league/route that provides support for the entire application lifecycle.
Still in it's infancy, Unicorn was born from my frustrations of wanting a framework to handle the plumbing for me, but not force a specific architecture or style, as applications tend to get very domain specific (as they should be).
- Want to build a closure application? Easy.
- Want to build a PSR-7 middleware app? Use your own pipeline implementation. (I recommend
league/pipeline or zend-framework/stratigility. Remember, the
Application
object adds itself to the service container so your class constructor dependencies are auto-wired if created via the container. I would include support for middleware by default, but I feel this is not always required by a PSR-7 application, and therefore outside of the Unicorn mission statement) - Want to use a "full", controller-based MVC framework? Just specify the class and method via routing.
- Want to use all of the above in the same app? Go ahead.
Unicorn attempts to do only the minimum, providing a service container, a router, a few event hooks for the various stages of the application lifecycle, and a PSR-7 implementation. And if you'd like, consider it an anti-framework framework. You might have to write some code... ;)
Installation
composer require andrewbreksa/unicorn
Usage
Check out the example index.php to see how to setup a basic app. (And I mean basic).
Application lifecycle/events
As defined in \Unicorn\App\Application
const EVENT_BOOTSTRAP = 'app.bootstrap';
const EVENT_DISPATCH = 'app.dispatch';
const EVENT_ROUTE_EXCEPTION = 'app.route.exception';
const EVENT_DISPATCH_EXCEPTION = 'app.dispatch.exception';
const EVENT_RENDER = 'app.render';
const EVENT_EMIT_EXCEPTION = 'app.emit.exception';
const EVENT_FINISH = 'app.finish';
Each event is emitted before the action takes place (if any, Application::EVENT_RENDER
doesn't do anything), and the event manager is accessible via Application::getInstance()->getEventEmitter()
. Fairly simple, see the league/event documentation.
Routing
You can access the router via Application::getInstance()->getRouteCollection()
. From there check out the league/route docs for more info.
A note on the return values of methods/closures/etc called on dispatch: If NULL
is returned (or nothing at all, implied) then Application->$response
is not updated, but the emitting process is carried out. However, if you return FALSE
, the emitting process is not carried out and Application::EVENT_RENDER
(needless to say, Application::EVENT_EMIT_EXCEPTION
) is not emitted either. This can be used to run other, "non-Unicorn", code or frameworks on specific routes.
PSR-7
Really? Ok, well checkout php-fig.org for more info.
Configuration
Configuration is (can be) stored in ./config/autoload
. Any *.php
or *.json
file here will either be include
d or parsed and added to Application::getInstance()->getConfig()
. And as always, feel free to completely ignore this convention and do whatever you please.
Regardless of how the configuration is set, on Application->bootstrap()
(after EVENT_BOOTSTRAP
), if there is any of services
, routes
, or eventListeners
in Application->getConfig()
, the values of these keys are passed to Application->bootstrapServices()
, Application->bootstrapRoutes()
and Application->bootstrapEventListeners()
respectively.
Dependency container
Unicorn uses league/container, which follows the container-interop standard. All of the Application::getInstance()->get*()
objects are also available via the container (Application::getInstance()->getContainer()
) as Unicorn registers itself as a delegate (except the container itself, baseDir
, and data
. The dependency container is also set to use the League\Container\ReflectionContainer
as a delegate, so constructor dependencies should be automatically set if available more on auto-wiring.
Notes
As the architecture and use cases of Unicorn are still being fleshed out, I've yet to put together thorough documentation. It's more of a pet project really that I'm using for RAD on a couple of personal projects.
Event listeners attached to the various Application::EVENT_*
events are passed a \League\Event\Event
and the Application instance by default. EVENT_*_EXCEPTION
events are passed an additional \Exception
parameter.
Conclusion
Unicorn is supposed to do just about nothing, or in short, everything you should need for any PHP web application. If you're tired of fitting your domain requirements to a framework or needing to write hacky workarounds to problems caused by lack of control, you just might enjoy working with Unicorn.
Feel free to fork and open pull requests against this repo. I don't claim to know what I'm doing, and I feel Unicorn would best serve the community evolving as a project defined by common project requirements instead of a specific set of design patterns and 'full-featured' mentality.