Thin PHP application framework
This is a very thin framework for PSR-15 PHP application using MVC., (*1)
Currently, it offers the following functionality only:
* Application shell
* View
* basic exception handling, (*2)
It requires the following additional modules to work:
* PSR-7 request / response implementation
* PSR-17 factory implementation, (*3)
Moreover, you are suggested to add various libraries to improve the development experience.
* Dependency injector
* Session handling
* Cookie handling, (*4)
Basic usage
In order to create a PHP application using the framework, you need to do the following:, (*5)
-
Subclass the provided
Application
class to handle your application logic for each entry point. The dependencies
should be injected to the constructor of your concrete Application, and it receives a ServerRequestInterface
and returns a ResponseInterface
when run.
-
Create views by writing templates and subclassing the provided abstract View
(for non-template views),
Template
(for templates in any language) , PhpTemplate
(for PHP templates) or StaticTemplate
(for static templates) class.
The view data for PHP templates should be declared as protected members in your concrete view, which is accessible in the
template by $this->member
., (*6)
Helper functions are provided for easy output escaping:, (*7)
If you are providing JSON API only, you (obviously) don't need to write any views., (*8)
-
Create the entry point to start the application, which is accessible under the document root. The entry point should:, (*9)
-
require
the composer autoloader.
- Set up error and exception handlers (optional).
- Create the
Application
and ServerRequestInterface
objects.
- Run the application.
- Output the response.
Please refer to miklcct/thin_php_app_demo
repository for how this framework works., (*10)
Controller
In this framework, application and controller refer to the same thing - something that accepts a request and
returns a response., (*11)
Just implement your controller code in the provided Application
class and you are good to go., (*12)
Routing
No routing functionality is built-in ecause I don't want to make everything go through a single entry point.
This would make the entry controller bloated because all dependencies used in every route have to be injected into the
main controller. I don't want to make a container mandatory and injecting container is also a bad idea., (*13)
Instead, create a PHP file on each entry point of the application, subclass the provided Application
class and do the
work specific to that route., (*14)
If you need to use SEO friendly URL, please use path info and web server rewrite. For example, if you want a specific
blog page accessible under http://example.com/article/2018/01/24/hello-world
, please create article.php
under your
document root, set up your web server to rewrite /article to /article.php, (*15)
Alternatively, you can install a router as a middleware in the application., (*16)
Middleware
It is very easy to install PSR-15 middlewares in the framework., (*17)
Controller middleware
If you are using the provided Application
class, just register it in getMiddlewares()
method.
Remember to call parent::getMiddlewares()
inside the overridden method to inherit the previous middlewares., (*18)
Global middleware
It is also easy to install global middlewares. Just extend the provided Application
class, register your global
middlewares, and implement your controllers on top of it., (*19)
Installing middlewares on existing application
It is very easy to install middlewares on top of an existing PSR-15 application from another framework
even if you don't use the provided Application
class. MiddlewareApplication
class is provided for such purpose., (*20)
View
Views are defined as content with a content type. You need to implement __toString()
(for string), render()
(for StreamInterface
) and getContentType()
. If you implement __toString
you can use the provided StringToStream
trait to fill in render()
, conversely, if you implement render()
you can use the provided StreamToString
to fill in
__toString()
., (*21)
Static and PHP templates
You can use the built-in StaticTemplate
(for static page) or PhpTemplate
(for PHP templates). Just
implement getPathToTemplate()
method., (*22)
Other template languages
You need to install the template engine (e.g. Smarty, Twig, etc.), extend the provided Template
class
and implement render()
and __toString()
methods to call the template engine., (*23)
View data
PHP templates are run in the context of your View object. You are recommend to access view data using protected
fields and members (private
will not work because it is rendered in a base class method). and add a docblock
/* @var YourConcreteView $this */
near the beginning of your PHP templates such that IDE analysis will work., (*24)
Other template engines may have their own method to pass data into templates. You are recommended to declare exactly
what data is expected in the view constructor, or use a factory object to produce views with correct data., (*25)
Error and Exception
There are two helper classes provided: ExceptionErrorHandler
which will convert PHP error into ErrorException
depending on your error_reporting
setting, and ResponseFactoryExceptionHandler
which will output responses from a
factory when an exception is uncaught. Objects of these classes can be passed directly to set_error_handler()
and set_exception_handler()
respectively., (*26)
You can also register a middleware to handle exceptions from your application., (*27)
Code style and structure
All library codes are organised into the appropriate subfolder under src
folder, and properly namespaced using PSR-4, even including constants and functions., (*28)
You MUST adhere to the following rules when making pull requests:
* Follow the existing code style for existing files, but feel free to use any style for new files.
* Place all code into a subfolder under src
folder and namespace them. No global code, including global constants and functions are allowed.
* All PHP code MUST have strict type checking (declare(strict_types = 1);
) enabled.
* No eval
is allowed. This prohibition includes functions with eval
-behaviour e.g. preg_replace()
with e
modifier, create_function()
and the backtick operator.
* No goto
is allowed.
* No undeclared properties or methods are allowed. This prohibition includes the use of magic, i.e. the following magic methods are not allowed:
* __call()
* __callStatic()
* __get()
* __set()
* __isset()
* __unset()
, (*29)
The use of undeclared fields in `stdClass` object to represent a generic data object is allowed as an exception.
* Use of a string for indirect variable, property and method access is not allowed. For example, the use of following syntaxes are not allowed, where $foo
is a string:
* $$foo
(indirect variable)
* $object->$foo
(indirect property)
* $object->$foo()
(indirect method), (*30)
However, the use of a string for indirect *function* call is allowed, provided that the string comes from an expression declared, annotated or deduced as a `callable`, for example:
````
function test(string $foo, callable $bar) {
$foo(); // not allowed
$bar(); // allowed
}
````
Only proven callables are allowed to be used in `callable` expression, which include:
* anonymous functions
* string literal (representing a free standing function or static class method)
* array containing an object and a string literal (representing a method inside an object)
* objects with `__invoke()` method
- Alternative control syntax is only allowed in
.phtml
templates and not allowed in regular .php
files containing application logic.
If existing code are found to violate the above rules, please open a bug report., (*31)