tombrown86 / WorkFrame

Super lightweight, simple, unintrusive PHP framework that just works

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Thanks for looking at WorkFrame!

.... 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:

  1. Pull the github project (https://github.com/tombrown86/WorkFrame/) or include it in your composer.json
  2. 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

  3. 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>

  4. Update the definitions in init script for your application (like in ./ExampleApp/init.php) that are hopefully self explanatory.
  5. 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:

Standard constructs

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.

Model layer

These consist of 3 classes of thing

Services
You write your own by extending \WorkFrame\Service
These contain the bulk of the business logic
They are responsible for (probably all) manipulation of Domain_object's and also handle data persistance (through Data_mappers)
They would usually end up returning outcomes or output related data (presentation data) but wouldn't be expected to return HTML. (Instead your Request_handlers would take this outcome info and map them into the view layer.)

Domain_objects
You write your own by extending \WorkFrame\Domain_object
They can represent any kind of entity you like, including web forms &/or data for persistance (sometimes 1:1 with DB tables)
They can optionally exhibit scenarios, which you define as the programmer to describe the different roles &/or rules of the object
Attribute lists can be defined for each scenario to dictate which attributes can be "mass assigned" (E.g. from a _POST or data mapper)
There are some convenience methods like from_assoc and to_assoc which you can use to help work with the objects data
You can use the \WorkFrame\Magic_get_set_trait to automatically implement get/set behaviour for calls to nonexisting $this->get_privatevarname() methods AS WELL AS referencing private attributes externally with $this->$varname (Use this sesnsibly!)

Data_mapper
You write your own by extending \WorkFrame\Data_mapper
... or more likely one of it's children like \WorkFrame\Database_data_mapper
This is the persistance end of the model layer
They should accept and return Domain_objects (individually or as lists where appropriate)
This is where you should put DB queries for DB CRUD.
These should not contain (hardly any!) logic.. keep them simple. Their most complex feature may be to apply a condition to a data select
You may want to implement this instead with an ORM library, or perhaps have it just implement a DB access helper library (ActiveRecord, data table gateway etc)

Loader / instance sharing

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

  • (or just omit the 2nd param to get an individual instance)
  • You can also do this with domain objects and data mappers with $this->DOMAIN_OBJECT(...) and $this->DATA_MAPPER(...) respectively

To unload an existing instance call:
$this->UNLOAD($component_type, $component_name)

The Renderer_trait

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(...)

The Processor_trait

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).

\WorkFrame\Html\Form_tools

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.

\WorkFrame\Libraries\Session

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.

Application class (\App\App), hooks, etc

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).

Logging

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.

Conf

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).


Security

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)

  1. User type based auth rules / config
  2. A built in DB accessor system (for use in your Data_mapper's)
  3. Simple RESTful API support
  4. A cookie library
  5. .. anything else fundamental missing? Please let me know

Shortfalls

  1. Currently, built in DB access is limited to mysqli
  2. Some of the front end features rely on the availability of JavaScript
  3. Will not work on windows! (basically due to directory separators!)
  4. No support for internationalisation
  5. No built in benchmarking
  6. 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).
  7. No caching
  8. ... it could go on for a while

This framework provides the fundamentals and can easily be extending with any additional desired functionality :)

About

Super lightweight, simple, unintrusive PHP framework that just works

License:Apache License 2.0


Languages

Language:PHP 83.3%Language:HTML 13.1%Language:JavaScript 2.2%Language:CSS 1.1%Language:Hack 0.3%