.... More will come
Introduction ¶
This is a first attempt at a super lightweight PHP7 - PHP8.2 compatible framework that exists to achieve the basic standard requirements of an MVC style framework with (hopefully) a couple of useful unexpected extras.
Motivation ¶
In my experience, frameworks are great for handling the bread and butter basics. However, they can become tiresom to work with when they have overbearing complexity.
Then they get upgraded, rewritten, changed beyond recognition which then causes everyone pain!
My advice is to avoid relying on the framework for detailed application logic.
Anything more complex is likely to have bespoke requirements that the framework does not attempt to provide.
This is a small flexible framework thats basically simple enough to upgrade to future PHP versions, yet powerful enough to provide the primary features we tend to require.
Quick start ¶
Do the following:
- Pull the github project (https://github.com/tombrown86/WorkFrame/) or include it in your composer.json
- Understand what is included (3 directories)
- The www directory - This is your web root (often called htdocs, public_html, etc)
- The ExampleApp directory - This contains the custom application code, store it outside your web root
- The WorkFrame directory - This is the framework, store it outside your web root
- Here is a typical vhost config (for apache), the Directory bit shows the essential ReWrite bit (routing all requests to the framework entry script).
<VirtualHost *:80> ServerName local.workframe DirectoryIndex index.php index.html DocumentRoot /var/www/site/www ErrorLog /var/log/apache2/workframe/local.workframe.error.log
<Directory /var/www/site/www/> Require all granted RewriteEngine on RewriteBase /var/www/site/www # Or If WorkFrame is running in a subdir: # RewriteBase /var/www/site/www/subdir RewriteCond $1 !^(index.php|public/|robots.txt) RewriteRule ^(.*)$ /index.php?$1 [L] </Directory>
</VirtualHost>
- Update the definitions in init script for your application (like in ./ExampleApp/init.php) that are hopefully self explanatory.
- Maybe check out the features
Basic features ¶
Your app code will essentially follow the pattern of an HMVC framework, but by no means needs to be used as such. Your app will probably at least contain the following:
- Request_handlers (a bit like a controllers)
- You write your own by extending \WorkFrame\Request_handler
- These purely exist as landing points for HTTP requests
- Requests are mapped to these handlers based on URL segments, with the last being the action name (method name) (E.g. /directoryname/subdirectoryname/request_handler_name/method_name?get_vars_here)
- If not provided, index is taken as the default values for both method names and Request_handler names
- Note, feel free to override pre_action_hook and post_action_hook, you can probably guess when they get called
- You can perform Exception based rerouting by throwing a \WorkFrame\Exceptions\Request_handler_rewrite_exception
- Model layer (see below)
- Handles "business logic" including data manipulation and data storage
- Request_handlers (usually) set these up and invoke procedures which retain data to be passed through to the view layer
- View layer
- Templates - page templates
- Partials - snippets to be included
An application with a large degree of complexity may benefit from having it's own class structure, nonconforming to this pattern.
These consist of 3 classes of thing
Anything extending/implementing any core WorkFrame components (most things) can easily label and share instances.
For example:
- Use $this->SERVICE('Service_name', 'service_label') to instantiate a service, and then reference it anywhere with $this->service_label
- You can also do this with domain objects and data mappers with $this->DOMAIN_OBJECT(...) and $this->DATA_MAPPER(...) respectively
(or just omit the 2nd param to get an individual instance)
To unload an existing instance call:
$this->UNLOAD($component_type, $component_name)
All your Request_handlers will exhibit this trait automatically.
Whilst not written in stone, you'll most likely invoke templates and partials from classes that have this trait
Things using this trait can make use of it's add_scripts() and add_stylesheets() methods to conveniently append JS/stylesheet tags to a template
(Note: There is a built in tool to minify such client side code by passing true as the 2nd parameter to add_script(...) based methods)
View data can be passed to the templates and partials with the add_view_var/add_view_vars() method.
By default, each Request handler and action will have a corresponding partial file path ($YOUR_APP/html/partials/request_handler_dir_path/request_handler_name/action_name (lowercased))
There is also a directory for templates: $YOUR_APP/html/templates/. You can automatically render your partials into templates by passing the template sub path into $this->render(...)
Processors can be attached to fields and can manipulate and/or validate data. The framework comes with some but you can add your own. There is a mechanism for them to work client side (with JavaScript), if the processor has been coded as such
Processors (/validation rules) can be attached to a subset to fields as well as a subset of scenarios on the entity (if the trait is used on a Domain_object (or infact anything with a scenario attribute)).
These can be used to validate/correct form fields inline - on the fly (using client, serverside or both).
To help you write your HTML. These can be instantiated and entities. They return HTML for common things (like form fields and validation errors).
They also (like most things) be extended if you want extra functionality.
A simple session library is provided. You may use it's following static methods.
- write($key, $value)
- read($key, $child=FALSE)
- delete($key)
- dump() [echos current session data array]
- regenerate_session_id()
- destroy()
- ... if you need more, it might have what you need - check it out.
Your app will probably want one of these sitting in it's route. It must have the same classname (and filename) as your application namespace. It must extend \WorkFrame\WorkFrame.
There are some standard hooks (like pre router, pre action etc) that can be defined in this class. You can also use this class to do anything application wide (like store current user, etc).
There is a very simple logging mechanism which is basically a single function: log_message($level, $message, $debug_silent=FALSE). It writes to a logs directory in your App.
You can use your own customer error levels as well if you like, but there are some predefined constants you may use: APP_LOG_LEVEL_WARNING, APP_LOG_LEVEL_ERROR and APP_LOG_LEVEL_INFO. Unless $debug_silent is passed as TRUE, any errors which aren't APP_LOG_LEVEL_INFO will get printed in the response if the app is in debug mode.
This is a simple mecahnism to define and retrieve config from files in the Conf directory.
Simple call conf($conf_filename) to call and return data from the conf with that $conf_filename.
If you want to use the existing mysql DB support, you must define (1 or more) DB connections in db.php (see example file).
A basic Security library has been added. So far all it can do is perform simple XSS filtering. If you enable this filtering (see security.php in conf). If enabled, you'll want to access the cleaned get/post/request variables with $this->GET(...), $this->POST(...) etc in the Request_handlers. With _GET as an example, calling $this->GET() will return the entire cleaned _GET array, passing in a $key as the 1st parameter will return the cleaned item with that key. You can still access the uncleaned original values by passing TRUE as the 2nd parameter.
Ideas / future ¶
Here is what I hope to work on (in chronological order)
- User type based auth rules / config
- A built in DB accessor system (for use in your Data_mapper's)
- Simple RESTful API support
- A cookie library
- .. anything else fundamental missing? Please let me know
Shortfalls ¶
- Currently, built in DB access is limited to mysqli
- Some of the front end features rely on the availability of JavaScript
- Will not work on windows! (basically due to directory separators!)
- No support for internationalisation
- No built in benchmarking
- No testing suite. Infact, the framework itself isn't unit tested..
Unit tests could probably be written for your application components without problem. There is potentially an issue with anything loaded through the WorkFrame Loader ($this->LOAD) since it loads components using some magic (and not using dependency injection). I think this issue can be worked around by overwriting the loader with your own fake test loader (you can inject it with set_loader, a func which should be available on all components). - No caching
- ... it could go on for a while
This framework provides the fundamentals and can easily be extending with any additional desired functionality :)