dev-master
9999999-devServlets API for PHP
MIT
The Requires
- php >=5.4
The Development Requires
api servlets
Servlets API for PHP
Table of contents:, (*1)
This API is a skeleton (requires binding by developers) created to efficiently handle web requests into server responses using a MVC version where views and models are expected to be independent while controllers mediate between the two based on user request. Designed with modularity, efficiency and simplicity at its foundation, API is both object and event oriented: similar to JavaScript, it allows developers to bind logic that will be executed when predefined events are reached while handling., (*2)
, (*3)
API does nothing more than standard MVC logic, so in real life it expects a web framework to be built on top to add further features (eg: DB connectivity). In order to use it, following steps are required from developers:, (*4)
API is fully PSR-4 compliant, only requiring Abstract MVC API for basic MVC logic, PHP8.1+ interpreter and SimpleXML extension. To quickly see how it works, check:, (*5)
All classes inside belong to Lucinda\STDOUT namespace!, (*6)
To configure this API you must have a XML with following tags inside:, (*7)
Tag documentation is completely covered by inherited Abstract MVC API specification! Since STDIN for this API is made of HTTP(s) requests, value of default_route attribute must point to index (homepage) for requests that come with no route., (*8)
Tag documentation is completely covered by inherited Abstract MVC API specification!, (*9)
Maximal syntax of this tag is:, (*10)
<routes> <route id="..." controller="..." view="..." format="..." method="..."> <parameter name="..." validator="..." mandatory="..."/> ... </route> ... </routes>
Most of tag logic is already covered by Abstract MVC API specification. Following extra observations need to be made:, (*11)
Tag example:, (*12)
<routes> <route id="index" controller="Lucinda\Project\Controllers\Homepage" view="index"/> <route id="user/(id)" controller="Lucinda\Project\Controllers\UserInfo" view="user-info"> </routes>
^ It is mandatory to define a route for that defined by default_route attribute @ application XML tag!, (*13)
If request came without route, default route is used. If, however, request came with a route that matches no id, a PathNotFoundException is thrown!, (*14)
Each route tag can hold one or more rules to validate values of request and path parameters that came along with request. Each parameter corresponds to a parameter tag, where validation is configurable based on attributes:, (*15)
^ If parameter names collide, path parameters take precedence over request parameters!, (*16)
Tag example:, (*17)
<routes> <route id="index" controller="Lucinda\Project\Controllers\Homepage" view="index"/> <route id="user/(id)" controller="Lucinda\Project\Controllers\UserInfo" view="user-info" method="GET"> <parameter name="id" validator="Lucinda\Project\ParameterValidators\UserNameValidator"/> </route> </routes>
Maximal syntax of this tag is:, (*18)
<session save_path="..." name="..." expired_time="..." expired_on_close="..." https_only="..." headers_only="..." referrer_check="..." handler="..." auto_start="...">
Where:, (*19)
Tag example:, (*20)
<session save_path="/tmp/sessions/" name="SESSID" expired_time="60" expired_on_close="120" https_only="1" headers_only="1" referrer_check="Chrome" handler="application/models/RedisHandler" auto_start="1">
Maximal syntax of this tag is:, (*21)
<cookies path="..." domain="..." https_only="..." headers_only="...">
Where:, (*22)
Tag example:, (*23)
<cookies path="/" domain="example.com" https_only="1" headers_only="1">
To understand how to properly set path and domain when needed, check specification!, (*24)
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., (*25)
It offers developers an ability to bind declaratively to its prototype classes/interfaces via XML:, (*26)
XML Attribute @ Tag | Class Prototype | Ability Gained |
---|---|---|
controller @ route | Controller | MVC controller for any request URI |
validator @ parameter | EventListeners\Validators\ParameterValidator | Validates value of request/path parameter |
class @ resolver | \Lucinda\MVC\ViewResolver | Resolving response in a particular format (eg: html) |
handler @ session | \SessionHandlerInterface | Handling session to a storage medium |
It offers developers an ability to bind programmatically to its prototypes via FrontController constructor:, (*27)
Class Prototype | Ability Gained |
---|---|
Attributes | (mandatory) Collects data (via setters and getters) to be made available throughout request-response cycle |
and addEventListener method (see: Binding Events section)!, (*28)
Now that developers have finished setting up XML that configures the API, they are finally able to initialize it by instantiating FrontController., (*29)
Apart of method run required by Runnable interface it implements, class comes with following public methods:, (*30)
Method | Arguments | Returns | Description |
---|---|---|---|
__construct | string $documentDescriptor, Attributes $attributes | void | Records user defined XML and attributes for later handling |
addEventListener | EventType $type, string $className | void | Binds a listener to an event type |
Where:, (*31)
Example:, (*32)
$handler = new FrontController("configuration.xml", new MyCustomAttributes("application/event_listeners"); $handler->run();
As mentioned above, API allows developers to bind listeners to handle lifecycle events via addEventListener method. Each EventType corresponds to an abstract Runnable class:, (*33)
Type | Class | Description |
---|---|---|
EventType::START | EventListeners\Start | Ran before configuration XML is read |
EventType::APPLICATION | EventListeners\Application | Ran after configuration XML is read into Application |
EventType::REQUEST | EventListeners\Request | Ran after user request is read into Request, Session and Cookies objects |
EventType::RESPONSE | EventListeners\Response | Ran after Lucinda\MVC\Response body is compiled but before it's rendered |
EventType::END | EventListeners\End | Ran after Lucinda\MVC\Response was rendered back to caller |
Listeners must extend matching event class and implement required run method holding the logic that will execute when event is triggered. It is required for them to be registered BEFORE run method is ran:, (*34)
$handler = new FrontController("stdout.xml", new FrameworkAttributes("application/listeners"); $handler->addEventListener(EventType::APPLICATION, Lucinda\Project\EventListeners\Logging::class); $handler->addEventListener(EventType::REQUEST, Lucinda\Project\EventListeners\Security::class); $handler->run();
To understand how event listeners are located, check specifications!, (*35)
API allows event listeners to set variables that are going to be made available to subsequent event listeners and controllers. For each variable there is a:, (*36)
API comes with Attributes, which holds the foundation every site must extend in order to set up its own variables. Unless your site is extremely simple, it will require developers to extend this class and add further variables, for whom setters and getters must be defined!, (*37)
Once above steps are done, developers are finally able to handle requests into responses via run method of FrontController, which:, (*38)
All components that are in developers' responsibility (Controller, Lucinda\MVC\ViewResolver, along with event listeners themselves, implement Runnable interface., (*39)
First choose a folder, then write this command there using console:, (*40)
composer require lucinda/mvc
Rename folder above to DESTINATION_FOLDER then create an .htaccess file there with following content:, (*41)
RewriteEngine on Options -Indexes ErrorDocument 404 default RewriteCond %{REQUEST_URI} !^/public RewriteRule ^(.*)$ index.php
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:, (*42)
$controller = new Lucinda\STDOUT\FrontController("configuration.xml", new Attributes("application/events")); // TODO: add event listeners here $controller->run();
For tests and examples, check following files/folders in API sources:, (*43)
These classes are fully implemented by API:, (*44)
Apart of classes mentioned in binding events, following abstract classes require to be extended by developers in order to gain an ability:, (*45)
Class Application encapsulates information detected from XML and defines following public methods relevant to developers:, (*46)
Method | Arguments | Returns | Description |
---|---|---|---|
getVersion | void | string | Gets application version based on version attribute @ application XML tag |
getTag | string $name | \SimpleXMLElement | Gets a pointer to a custom tag in XML root |
Class Request encapsulates information detected about user request based on superglobals ($_SERVER, $_GET, $_POST, $_FILES) and defines following public methods relevant to developers:, (*47)
Method | Arguments | Returns | Description |
---|---|---|---|
getClient | void | Request\Client | Gets client information detected from request. |
getInputStream | void | string | Gets access to input stream for binary requests. |
getMethod | void | Request\Method | Gets request HTTP method (REQUEST_METHOD @ $_SERVER). |
getProtocol | void | Request\Protocol | Gets request protocol (HTTPS @ $_SERVER) |
getServer | void | Request\Server | Gets server information detected from request. | |
getUri | void | Request\URI | Gets path information detected from request. |
headers | void | array | Gets all request headers received from client by standard ISO name |
headers | string $name | string | Gets value of request header by name or NULL if not found. |
parameters | void | array | Gets all request parameters received from client matching current request method ($_GET, $_POST, etc). |
parameters | string $name | mixed | Gets value of request parameter by name or NULL if not found. |
uploadedFiles | void | array | Gets all uploaded files received from client, each encapsulated as Request\UploadedFiles\File based on $_FILES |
uploadedFiles | string $name | mixed | Gets Request\UploadedFiles\File received by name or NULL if not found. |
Class Request\Client encapsulates client information detected from request based on $_SERVER superglobal and defines following public methods relevant to developers:, (*48)
Method | Arguments | Returns | Description |
---|---|---|---|
getName | void | string | Gets client server name (REMOTE_HOST @ $_SERVER) |
getIP | void | string | Gets client ip (REMOTE_ADDR @ $_SERVER) |
getPort | void | int | Gets client port (REMOTE_PORT @ $_SERVER) |
Class Request\Server encapsulates web server information detected from request based on $_SERVER superglobal and defines following public methods relevant to developers:, (*49)
Method | Arguments | Returns | Description |
---|---|---|---|
getName | void | string | Gets server name (SERVER_NAME @ $_SERVER) |
getIP | void | string | Gets server ip (SERVER_ADDR @ $_SERVER) |
getPort | void | int | Gets server port (SERVER_PORT @ $_SERVER) |
getEmail | void | int | Gets server admin email (SERVER_ADMIN @ $_SERVER) |
getSoftware | void | int | Gets server software info (SERVER_SOFTWARE @ $_SERVER) |
Class Request\URI encapsulates path information detected from request based on $_SERVER superglobal and defines following public methods relevant to developers:, (*50)
Method | Arguments | Returns | Description |
---|---|---|---|
getContextPath | void | string | Gets context path based on requested URI (DOCUMENT_ROOT & SCRIPT_FILENAME @ $_SERVER) |
getPage | void | string | Gets resource (page) requested based on requested URI (REQUEST_URI @ $_SERVER) |
getQueryString | void | string | Gets query string that came with URI (QUERY_STRING @ $_SERVER) |
parameters | void | array | Gets query string parameters that came with URI ($_GET) |
parameters | string $name | mixed | Gets value of query string parameter by name or NULL if not found. |
To understand how requested URI is processed by this class, check specifications!, (*51)
Class Request\UploadedFiles\File encapsulates information about a single file uploaded based on $_FILES superglobal and defines following public methods relevant to developers:, (*52)
Method | Arguments | Returns | Description |
---|---|---|---|
getName | void | string | Gets name of file uploaded. |
getLocation | void | string | Gets temporary location of file uploaded. |
getContentType | void | string | Gets content type of file uploaded. |
getSize | void | int | Gets size of file uploaded. |
To process file uploaded, two methods were added for developers:, (*53)
Method | Arguments | Returns | Description |
---|---|---|---|
move | string $destination | boolean | Moves file uploaded to a final location and returns whether or not operation was successful |
delete | void | boolean | Deletes file uploaded and returns whether or not operation was successful |
To understand how uploaded files are processed into this class, check specifications!, (*54)
Class Session encapsulates operations to perform with a http session via $_SESSION superglobal and defines following public methods, all relevant to developers:, (*55)
Method | Arguments | Returns | Description |
---|---|---|---|
start | void | void | Starts session, using settings defined in session XML tag |
isStarted | void | bool | Checks if session was started |
set | string $key, mixed $value | void | Sets session parameter by key and value |
get | string $key | mixed | Gets value of session parameter by key |
contains | string $key | bool | Checks if session contains parameter by key |
remove | string $key | void | Deletes session parameter by key, if any |
destroy | void | bool | Destroys session, clearing of all parameters. |
abort | void | bool | Terminates current session and discards all changes. |
commit | void | bool | Terminates current session and saves all changes. |
cookie | void | Session\Cookie | Gets access to session cookie operations. |
Class Session\Cookie encapsulates operations to perform with a http session cookie and defines following public methods, all relevant to developers:, (*56)
Method | Arguments | Returns | Description |
---|---|---|---|
getName | void | string | Get name of session id cookie |
getID | void | string | Get value of session id cookie |
regenerateID | void | bool | Regenerate session id, keeping old session info |
createNewID | void | bool | Create new session id disregarding session info |
Class Cookies encapsulates operations to perform with a http cookie via $_COOKIE superglobal and defines following public methods, all relevant to developers:, (*57)
Method | Arguments | Returns | Description |
---|---|---|---|
set | string $name, mixed $value, int $expiration | void | Sets cookie parameter by name and value, lasting for expiration seconds from now, using settings defined in cookies XML tag |
get | string $name | mixed | Gets value of cookie by name |
contains | string $name | bool | Checks if a cookie exists by name |
remove | string $name | void | Deletes cookie by name |
Interface EventListeners\Validators\ParameterValidator implements blueprint for a single request parameter value validation via method:, (*58)
Method | Arguments | Returns | Description |
---|---|---|---|
validate | mixed $value | mixed | Validates value and returns result on success (eg: matching DB id) or NULL on failure |
Example of a class that validates user name received as parameter (eg: /user/(name) route):, (*59)
class UserNameValidator implements Lucinda\STDOUT\EventListeners\Validators\ParameterValidator { public function validate($value) { $result = DB("SELECT id FROM users WHERE name=:name", [":name"=>$value])->toValue(); return ($result?$result:null); } }
To understand more how parameters work, check specifications and tag documentation!, (*60)
Abstract class EventListeners\Start implements Runnable) and listens to events that execute BEFORE configuration XML is read., (*61)
Developers need to implement a run method, where they are able to access following protected fields injected by API via constructor:, (*62)
Field | Type | Description |
---|---|---|
$attributes | Attributes | Gets access to object encapsulating data where custom attributes should be set. |
A common example of a START listener is the need to set start time, in order to benchmark duration of handling later on:, (*63)
class StartBenchmark extends Lucinda\STDOUT\EventListeners\Start { public function run(): void { // you will first need to extend Application and add: setStartTime, getStartTime $this->attributes->setStartTime(microtime(true)); } }
Abstract class EventListeners\Application implements Runnable) and listens to events that execute AFTER configuration XML is read., (*64)
Developers need to implement a run method, where they are able to access following protected fields injected by API via constructor:, (*65)
Field | Type | Description |
---|---|---|
$application | Application | Gets application information detected from XML. |
$attributes | Attributes | Gets access to object encapsulating data where custom attributes should be set. |
Usage example:, (*66)
https://github.com/aherne/lucinda-framework/blob/master/src/EventListeners/SQLDataSource.php, (*67)
Abstract class EventListeners\Request implements Runnable) and listens to events that execute AFTER Request, Session and Cookies objects are created., (*68)
Developers need to implement a run method, where they are able to access following protected fields injected by API via constructor:, (*69)
Field | Type | Description |
---|---|---|
$application | Application | Gets application information detected from XML. |
$request | Request | Gets request information detected from superglobals. |
$session | Session | Gets pointer to class encapsulating operations on http session. |
$cookies | Cookies | Gets pointer to class encapsulating operations on http cookies. |
$attributes | Attributes | Gets access to object encapsulating data where custom attributes should be set. |
Usage example:, (*70)
https://github.com/aherne/lucinda-framework/blob/master/src/EventListeners/Security.php, (*71)
Abstract class EventListeners\Response implements Runnable) and listens to events that execute AFTER Lucinda\MVC\Response body was set but before it's committed back to caller., (*72)
Developers need to implement a run method, where they are able to access following protected fields injected by API via constructor:, (*73)
Field | Type | Description |
---|---|---|
$application | Application | Gets application information detected from XML. |
$request | Request | Gets request information detected from superglobals. |
$session | Session | Gets pointer to class encapsulating operations on http session. |
$cookies | Cookies | Gets pointer to class encapsulating operations on http cookies. |
$response | Lucinda\MVC\Response | Gets access to object based on which response can be manipulated. |
$attributes | Attributes | Gets access to object encapsulating data where custom attributes should be set. |
Usage example:, (*74)
https://github.com/aherne/lucinda-framework/blob/master/src/EventListeners/HttpCaching.php, (*75)
Abstract class EventListeners\End implements Runnable) and listens to events that execute AFTER Lucinda\MVC\Response was rendered back to caller., (*76)
Developers need to implement a run method, where they are able to access following protected fields injected by API via constructor:, (*77)
Field | Type | Description |
---|---|---|
$application | Application | Gets application information detected from XML. |
$request | Request | Gets request information detected from superglobals. |
$session | Session | Gets pointer to class encapsulating operations on http session. |
$cookies | Cookies | Gets pointer to class encapsulating operations on http cookies. |
$response | Lucinda\MVC\Response | Gets access to object based on which response can be manipulated. |
$attributes | Attributes | Gets access to object encapsulating data where custom attributes should be set. |
A common example of a START listener is the need to set end time, in order to benchmark duration of handling:, (*78)
class EndBenchmark extends Lucinda\STDOUT\EventListeners\End { public function run(): void { $benchmark = new Benchmark(); $benchmark->save($this->attributes->getStartTime(), microtime(true)); } }
Abstract class Controller implements Runnable) to set up response (views in particular) by binding information detected beforehand to models. It defines following public method relevant to developers:, (*79)
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:, (*80)
Field | Type | Description |
---|---|---|
$application | Application | Gets application information detected from XML. |
$request | Request | Gets request information detected from superglobals. |
$session | Session | Gets pointer to class encapsulating operations on http session. |
$cookies | Cookies | Gets pointer to class encapsulating operations on http cookies. |
$response | Lucinda\MVC\Response | Gets access to object based on which response can be manipulated. |
$attributes | Attributes | Gets access to object encapsulating data set by event listeners beforehand. |
Usage example:, (*81)
https://github.com/aherne/lucinda-framework-configurer/blob/master/files/controllers/no_rest/IndexController.php, (*82)
To understand more about how controllers are detected, check specifications!, (*83)
Class Attributes encapsulates data collected throughout request-response cycle, each corresponding to a getter and a setter, and made available to subsequent event listeners or controllers. API already comes with following:, (*84)
Method | Arguments | Returns | Description |
---|---|---|---|
__construct | string $folder | void | Sets folder in which event listeners will be searched for |
getEventsFolder | void | string | Gets folder in which event listeners will be searched for |
getPathParameters | string $name | string | Gets value of path parameter by its name or NULL if not found |
getPathParameters | void | array | Gets all path parameters received in request |
getValidFormat | void | string | Gets final response format to use |
getValidPage | void | string | Gets final route requested |
getValidParameters | string $name | mixed | Gets result of request/path parameter validation by its name or NULL if validation failed |
getValidParameters | void | array | Gets results of request/path parameters validation |
Most of the data collected will need to be set by developers themselves to fit their project demands so in 99% of cases class will need to be extended for each project! Usage example:, (*85)
https://github.com/aherne/lucinda-framework/blob/master/src/Attributes.php, (*86)
Since this API works on top of Abstract MVC API specifications it follows their requirements and adds extra ones as well:, (*87)
This section follows parent API specifications only that routes are detected based on value of $_SERVER["REQUEST_URI"]., (*88)
This section follows parent API specifications in its entirety., (*89)
This section follows parent API specifications only that routes are detected based on value of $_SERVER["REQUEST_URI"]. Let's take this XML for example:, (*90)
<application default_route="index" ...> ... </application> <routes> <route id="index" .../> <route id="users" .../> <route id="user/(id)" .../> </routes>
There will be following situations for above:, (*91)
If Page Requested | Then Route ID Detected | Description |
---|---|---|
/ | index | Because requested page came empty, that identified by default_route is used |
/users | users | Because requested page is matched to a route, specific route is used |
/hello | - | Because no route is found matching the one requested a PathNotFoundException is thrown |
/user/12 | user/(id) | Because requested page matched one with a route parameter, specific route is used and id=12 path parameter is detected |
This section follows parent API specifications only that class defined as controller attribute in route tag must extend Controller., (*92)
To better understand how validators attribute in application XML tag plays together with parameter sub-tags in routes tag in order to locate validators to run based on incoming request, let's take this XML for example:, (*93)
<routes> <route id="users/(uname)" method="GET" ...> <parameter name="uname" class="Lucinda\Project\ParameterValidators\UserName"/> </route> <route id="user/info" method="POST" ...> <parameter name="id" class="Lucinda\Project\ParameterValidators\UserId"/> <parameter name="name" class="Lucinda\Project\ParameterValidators\UserName" mandatory="0"/> </route> ... </routes>
When a request to /users/aherne is received, API will:, (*94)
When a request to /users/info is received, API will:, (*95)
All parameter validators need to be PSR-4 autoload compliant and implement EventListeners\Validators\ParameterValidator!, (*96)
Table below shows the effects of path attribute @ cookies XML tag:, (*97)
Value | Effect |
---|---|
the cookie will be available in the current directory that the cookie is being set in (default) | |
/ | the cookie will be available within the entire domain (recommended) |
/foo/ | the cookie will only be available within the /foo/ directory and all sub-directories such as /foo/bar/ of domain |
Table below shows the effects of domain attribute @ cookies XML tag:, (*98)
Value | Effect |
---|---|
makes cookie available to current subdomain | |
www.example.com | makes cookie available to that subdomain and all other sub-domains of it (i.e. w2.www.example.com) |
example.com | makes cookie available to the whole domain (including all subdomains of it) |
Unlike $_FILES superglobal, like parameters sent by $_GET or $_POST, API preserves structure sent in form, so:, (*99)
<input type="file" name="asd[fgh]"/>
Once posted, uploadedFiles method will return:, (*100)
[ "asd"=>["fgh"=>object] ]
Where object is a Request\UploadedFiles\File. To retrieve uploaded files, use uploadedFiles method @ Request!, (*101)
API breaks down requested URI (value of REQUEST_URI param @ $_SERVER) into relevant components based on following algorithm:, (*102)
Examples:, (*103)
DOCUMENT_ROOT | SCRIPT_FILENAME | REQUEST_URI | context path | route | query string |
---|---|---|---|---|---|
/aaa/bbb | /aaa/bbb/ccc/index.php | /ddd?a=b | ccc | ddd | ?a=b |
/aaa/bbb | /aaa/bbb/index.php | /ddd/fff | ddd/fff |
This section follows parent API specifications in its entirety. Extension is yet to be decided, since it depends on type of view resolved!, (*104)
Servlets API for PHP
MIT
api servlets