MVC API for STDERR flow in PHP applications
Table of contents:, (*1)
This API is a skeleton (requires binding by developers) created to efficiently handle errors or uncaught exceptions in a web application using a dialect of MVC paradigm where:, (*2)
, (*3)
Just as MVC API for STDOUT handles http requests into responses, so does this API for STDERR handle errors or uncaught exceptions that happen during previous handling process. It does so in a manner that is both efficient and modular, without being bundled to any framework:, (*4)
Furthermore, this whole process is done in a manner that is made flexible through a combination of:, (*5)
API is fully PSR-4 compliant, only requiring Abstract MVC API for basic MVC logic, PHP7.1+ interpreter and SimpleXML extension. To quickly see how it works, check:, (*6)
All classes inside belong to Lucinda\STDERR namespace!, (*7)
To configure this API you must have a XML with following tags inside:, (*8)
Tag documentation is completely covered by inherited Abstract MVC API specification! Since STDIN for this API is made of handled throwables but there is no generic throwable value of default_route attribute must be default., (*9)
Maximal syntax of this tag is:, (*10)
<display_errors> <{ENVIRONMENT}>{VALUE}</{ENVIRONMENT}> ... </display_errors>
Most of tag logic is already covered by Abstract MVC API specification. Following extra sub-tags/attributes are defined:, (*11)
If no display_errors is defined or no {ENVIRONMENT} subtag is found matching current development environment, value 0 is assumed (\Throwable details won't be exposed)!, (*12)
Tag example:, (*13)
<display_errors> <local>1</local> <live>0</live> </display_errors>
Maximal syntax of this tag is:, (*14)
<reporters> <{ENVIRONMENT}> <reporter class="..." {OPTIONS}/> ... </{ENVIRONMENT}> ... </reporters>
Where:, (*15)
Tag example:, (*16)
<reporters> <local> <reporter class="Lucinda\Project\Reporters\File" path="errors" format="%d %f %l %m"/> </local> <live> <reporter class="Lucinda\Project\Reporters\SysLog" application="unittest" format="%v %f %l %m"/> </live> </reporters>
Tag documentation is completely covered by inherited Abstract MVC API specification!, (*17)
Maximal syntax of this tag is:, (*18)
<routes> <route id="..." controller="..." view="..." error_type="..." http_status="..."/> ... </routes>
Most of tag logic is already covered by Abstract MVC API specification. Following extra observations need to be made:, (*19)
Tag example:, (*20)
<routes> <route id="default" http_status="500" error_type="LOGICAL" view="500"/> <route id="Lucinda\MVC\STDOUT\PathNotFoundException" controller="Lucinda\Project\Controllers\PathNotFound" http_status="404" error_type="CLIENT" view="404"/> </routes>
If handled \Throwable matches no route, default route is used!, (*21)
In order to remain flexible and achieve highest performance, API takes no more assumptions than those absolutely required! It offers developers instead an ability to bind to its prototypes in order to gain certain functionality., (*22)
It offers developers an ability to bind declaratively to its prototype classes/interfaces via XML:, (*23)
XML Attribute @ Tag | Class Prototype | Ability Gained |
---|---|---|
controller @ route | Controller | MVC controller for any \Throwable |
class @ resolver | \Lucinda\MVC\ViewResolver | Resolving response in a particular format (eg: html) |
class @ reporter | Reporter | Reporting \Throwable to a storage medium |
It offers developers an ability to bind programmatically to its prototypes via FrontController constructor:, (*24)
Class Prototype | Ability Gained |
---|---|
ErrorHandler | (mandatory) Handler to use if a \Throwable while API handles request into response |
Now that developers have finished setting up XML that configures the API, they are finally able to initialize it by instantiating FrontController., (*25)
As a handler of \Throwable instances, above needs to implement ErrorHandler. Apart of method run required by interface above, FrontController comes with following public methods, all related to initialization process:, (*26)
Method | Arguments | Returns | Description |
---|---|---|---|
__construct | string $documentDescriptor, string $developmentEnvironment, string $includePath, ErrorHandler $emergencyHandler | void | API registers itself as sole \Throwable handler then starts the wait process |
setDisplayFormat | string $displayFormat | void | Sets future response to use a different display format from that defined by default_format @ application tag |
Where:, (*27)
Very important to notice that once handlers are registered, API employs Aspect Oriented Programming concepts to listen asynchronously for error events then triggering handler automatically. So once API is initialized, you can immediately start your preferred framework that handles http requests to responses!, (*28)
Once a \Throwable event has occurred inside STDOUT request-response phase, handle method of FrontController is called. This:, (*29)
All components that are in developers' responsibility (Controller, \Lucinda\MVC\ViewResolver, Reporter) implement \Lucinda\MVC\Runnable interface., (*30)
First choose a folder, then write this command there using console:, (*31)
composer require lucinda/errors-api
Then create a configuration.xml file holding configuration settings (see configuration above) and a index.php file (see initialization above) in project root with following code:, (*32)
// detects current development environment from ENVIRONMENT environment variable (eg: set in .htaccess via "SetEnv ENVIRONMENT local"); define("ENVIRONMENT", getenv("ENVIRONMENT")); // starts API as error handler to listen for errors/exceptions thrown in STDOUT phase below new FrontController("configuration.xml", getenv("ENVIRONMENT"), __DIR__, new EmergencyHandler()); // runs preferred STDOUT framework (eg: STDOUT MVC API) to handle requests into responses
Example of emergency handler:, (*33)
class EmergencyHandler implements \ErrorHandler { public function handle($exception): void { var_dump($exception); die(); } }
For tests and examples, check following files/folders in API sources:, (*34)
These classes are fully implemented by API:, (*35)
These abstract classes require to be extended by developers in order to gain an ability:, (*36)
Class Application extends Lucinda\MVC\Application and adds one method relevant to developers:, (*37)
Method | Arguments | Returns | Description |
---|---|---|---|
getDisplayErrors | void | bool | Gets whether or not errors should be displayed in current development environment |
Class Application\Route extends Lucinda\MVC\Application\Route and adds following public methods:, (*38)
Method | Arguments | Returns | Description |
---|---|---|---|
getErrorType | void | ErrorType | Gets error type based on error_type attribute of matching route XML tag |
getHttpStatus | void | \Lucinda\MVC\Response\HttpStatus | Gets response http status code based on http_status attribute of matching route XML tag |
Class Request encapsulates handled \Throwable and matching Application\Route. It defines following public methods relevant to developers:, (*39)
Method | Arguments | Returns | Description |
---|---|---|---|
getException | void | \Throwable | Gets throwable that is being handled |
getRoute | void | Application\Route | Gets route information detected from XML based on throwable |
Interface ErrorHandler contains blueprint for handling \Throwable via method:, (*40)
Method | Arguments | Returns | Description |
---|---|---|---|
handle | \Throwable | void | Handles error by delegating to reporting and rendering |
Usage example:, (*41)
https://github.com/aherne/lucinda-framework/blob/master/src/EmergencyHandler.php, (*42)
Abstract class Reporter implements \Lucinda\MVC\Runnable and encapsulates a single \Throwable reporter. It defines following public method relevant to developers:, (*43)
Method | Arguments | Returns | Description |
---|---|---|---|
run | void | void | Inherited prototype to be implemented by developers to report \Throwable based on information saved by constructor |
Developers need to implement run method for each reporter, where they are able to access following protected fields injected by API via constructor:, (*44)
Field | Type | Description |
---|---|---|
$request | Request | Gets request information encapsulating handled throwable and matching route information detected from XML. |
$xml | \SimpleXMLElement | Gets a pointer to matching reporter XML tag, to read attributes from. |
Usage example:, (*45)
https://github.com/aherne/lucinda-framework-engine/blob/master/src/AbstractReporter.php https://github.com/aherne/lucinda-framework/blob/master/src/Reporters/File.php, (*46)
For more info how reporters are detected, check How Are Reporters Located section below!, (*47)
Abstract class Controller implements \Lucinda\MVC\Runnable) to set up response (views in particular) based on information detected beforehand. It defines following public method relevant to developers:, (*48)
Method | Arguments | Returns | Description |
---|---|---|---|
run | void | void | Inherited prototype to be implemented by developers to set up response based on information saved by constructor |
Developers need to implement run method for each controller, where they are able to access following protected fields injected by API via constructor:, (*49)
Field | Type | Description |
---|---|---|
$application | Application | Gets application information detected from XML. |
$request | Request | Gets request information encapsulating handled throwable and matching route information detected from XML. |
$response | \Lucinda\MVC\Response | Gets access to object based on which response can be manipulated. |
Usage example:, (*50)
https://github.com/aherne/lucinda-framework/blob/master/src/Controllers/SecurityPacket.php, (*51)
For more info how controllers are detected, check How Are Controllers Located section below!, (*52)
Since this API works on top of Abstract MVC API specifications it follows their requirements and adds extra ones as well:, (*53)
This follows parent API specifications only that routes are detected based on \Throwable handled. One difference is that detected value can be overridden using setDisplayFormat method (see Initialization)., (*54)
This follows parent API specifications in its entirety., (*55)
This follows parent API specifications only that routes are detected based on \Throwable handled. Let's take this XML for example:, (*56)
<application default_route="default" ...> ... </application> <routes> <route id="default" .../> <route id="\Bar\Exception" .../> ... </routes>
There will be following situations for above:, (*57)
If Throwable Is | Then Route ID Detected | Description |
---|---|---|
\Foo\Exception | default | Because no matching route was found, that identified by default_route is used |
\Bar\Exception | \Bar\Exception | Because throwable is matched to a route, specific route is used |
This follows parent API specifications only that class defined as controller attribute in route tag must extend Controller., (*58)
To better understand how class attributes @ reporter tags matching development environment, let's take this XML for example:, (*59)
<reporters> <ENVIRONMENT1> <reporter class="Lucinda\Project\Reporters\File" .../> <reporter class="Lucinda\Project\Reporters\SysLog" .../> </ENVIRONMENT1> <ENVIRONMENT2> <reporter class="Lucinda\Project\Reporters\SysLog" .../> </ENVIRONMENT2> ... </reporters>
In that case if "psr-4" attribute in composer.json associates "Lucinda\Project\" with "src/" folder then:, (*60)
ENVIRONMENT | Files Loaded | Classes Instanced |
---|---|---|
ENVIRONMENT1 | src/Reporters/File.phpbr/src/Reporters/SysLog.php | Lucinda\Project\Reporters\Filebr/Lucinda\Project\Reporters\SysLog |
ENVIRONMENT2 | src/Reporters/SysLog.php | Lucinda\Project\Reporters\SysLog |
All classes referenced above must be instance of Reporter!, (*61)
This follows parent API specifications in its entirety. Extension is yet to be decided, since it depends on type of view resolved!, (*62)