FRED™ is a light-weight, component-based / dependecy injected, lazy loading framework for PHP 7+. Designed for flexibility and security, and optimized for speed.
It's an MVC framework with a front-controller / router. It has auto-view-format-detection based on the extension of the request (e.g. .json for async calls; defaults to HTML).
All configuration is done through PHP files / arrays. No exotic file formats that have to be parsed, cached, and cleared upon changes.
Bootstrap
We set up a simple autoloader to make sure FRED™ can find all the classes he needs for creating himself (and to make sure you can also use FRED™ constants in the config file). After that FRED™'s own autoloader will take over (gets prepended before this one). We do not use the autoloader generated by Composer since it is not efficient (among others it pre-loads all files in autoload_files.php
).
ini.php
spl_autoload_register(function($class_name){ //basic autoloader
if(file_exists($filename = __DIR__ . '/../vendor/rsi/fred/src/' . str_replace('\\','/',$class_name) . '.php'))
require($filename);
});
$fred = new \Rsi\Fred(__DIR__ . '/../config/config.php');
Configuration
The configuration can be passed directly as an array, or by referencing to a PHP file that returns an array.
Besides some general settings for FRED™ himself, there are seperate settings for every component. Component setting can also include a specific class name for that component. If this is omitted, the default component with the same name in the FRED™ namespace is taken. Component settings can also be a string pointing to another PHP file returning an array. This file will then only be loaded when the component is created.
By prefixing the configuration key with an @
its value can be determined dynamic (using the vars
component).
config.php
$root = dirname(__DIR__);
$temp = $root . '/temp';
$public = $root . '/www';
//combine both PSR-4 and PSR-0 namespaces
$namespaces = require($root . '/vendor/composer/autoload_psr4.php');
$namespaces['Project\\'] = [$root . '/project'];
foreach(require($root . '/vendor/composer/autoload_namespaces.php') as $prefix => $paths){
$suffix = '/' . str_replace('\\','/',rtrim($prefix,'\\'));
foreach($paths as &$path) $path .= $suffix;
unset($path);
$namespaces[$prefix] = $paths;
}
//local configuration that you do not want to include in your repo or production environment
$local = require('local.php');
return array_replace_recursive([
'autoloadNamespaces' => $namespaces,
'autoloadClasses' => require($root . '/vendor/composer/autoload_classmap.php'),
'autoloadFiles' => require($root . '/vendor/composer/autoload_files.php'),
'cache' => [
'type' => 'filesystem',
'params' => [$temp . '/cache','.tmp']
],
//...
],$local);
Local configuration that you do not want to include in your repo or production environment.
local.php
return [
'debug' => true,
'db' => [
'connection' => [
'dsn' => 'mysql:host=localhost;dbname=fred',
'username' => 'fred',
'password' => '********'
]
],
//...
];
Front-controller
Bootstraps the framework by means of the above created php.ini
, creates the front controller (simply by requesting it from FRED™), and executes it.
The request is directly taken from the global $_GET
, $_POST
, $_COOKIE
, and $_FILES
variables (although there is a request
component to make life easy - among others fixing this stupid $_FILES
structure on multi-upload; in short: no PSR-7). The router will add parameters embedded in the route to the $_GET
(route blog/[id:\d+]/[title]
for example will add id
(numeric only) and title
).
The response is written directly to the standard output. The output format depends on the extension of the requested URL (.json will return JSON, .xml will return XML, etc; default is HTML format).
index.php
require('ini.php');
$fred->front->execute();
$fred->halt(); //shutdown nicely (write logs, etc)
Objects
All objects use the same magic getter/setter strategy as Yii does. There is a little bonus: private properties can also be published. In that case you don't have to specify a getter and/or setter, but you can simply mark the property as read, write, or both.
There are also some convenience functions, most notably the configure()
function which will take an array and apply it using the most appropriate method (public property, setter, or private property).
Components
FRED™ comes with several handy components out of the box:
- Alive: Keeping track of a user's activity (still active or not; even if they are not activly engaging with the page anymore), and cleaning up afterwards.
- Cache: Cache is king — use it whenever you can. This component makes it easy to manage your caches.
- Client: Information about the client side (device type, user-agent, ...).
- Db: Database access layer. Work with your database(s) using higher level functions (but also raw SQL).
- Debug: Wraps an existing component, and logs all interactions with it. Handy for debugging.
- Def: Brings the database closer to your code: form field definitions matches the database, and raw data can be enriched automatically.
- Encrypt: Encryption (and decryption) made easy.
- Entity: Entity manager. Create entities, and keep track of them.
- Event: Register event listeners, and trigger events.
- External: Wrap calls to external services in an extra layer of cautiousness, and pause calls to a service when it is not responding anymore.
- File: Manage all kind of storage files, archiving them, and eventually purge them.
- Front: The front controller.
- Health: Perform a regular health check on key system properties (disk space, load, ...).
- Hint: Provide the user with hints when using a particular part of your website for the first time(s).
- Html: Takes the hassle out of creating standard HTML tags/components.
- Local: Convert values from and to their local representation.
- Location: Rewrite a files location based on rules (e.g. serve it from another static domain / CDN, or add aversion number).
- Lock: Lock objects for a specific user (and auto-unlock them when the user leaves).
- Log: Log the status of your program on different levels, to different handlers.
- Mail: Composing and sending of e-mails made easy.
- Message: Collect messages for the user along the way, and retrieve them when possible.
- Minify: Minify (and combine) resource files (script, style, ...).
- Parallel: Perform tasks in a different, parallel thread.
- Pdf: Create perfect PDF's.
- Proxy: Give access to selected files out of the public document root (e.g. client-side support files like JS and CSS for components/bundles).
- Request: Access to all request related data, and also the place to store the result.
- Router: Select the right controller for the request, retrieve and check parameters, and do the reverse to create new URLs.
- Security: Check incoming requests, and ban misbehaving users.
- Services: Make uses of selected services from Royal Shitware Inc. (RSI).
- Socket: Connect to the socket server.
- Stats: Increase your statistics.
- Storage: Flexible data storage (just store random data).
- Stream: Work with streams.
- Token: Generate and validate unique tokens.
- Trans: Retrieve language dependend texts, and format them using the provided data.
- User: Manage your user(s) status (authentication, authorization, ...).
- Vars: Higher level program configuration.
- Worker: Schedule (repeating) jobs, and execute them.
- Workflow: Handle workflow items, their status, and their jobs.
Coding style guide
The coding style is 100% PSR-1, and roughly PSR-2. Most notable differences are:
- Code MUST use 2 spaces for indenting, not tabs.
- Lines SHOULD be 127 characters or less.
- Opening braces for classes and methods MUST go on the same line.
- There MUST be one blank line before the closing braces of the class body.
- Control structure keywords MUST NOT have spaces after them.
- When present, there MUST be one blank line before the namespace declaration.
- For both the argument list of a function declaration, and the calling of a function there MUST NOT be a space after each comma.
- The body of each structure SHOULD NOT be enclosed by braces if it consists of only a single line.
- There always MUST be a line break after closing braces, EXCEPT for the closing brace of a class declaration.
Example
<?php
namespace Vendor\Package;
use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
class Foo extends Bar implements FooInterface{
public function sampleFunction($a,$b = null){
if($a === $b){
foo();
bar();
}
elseif($a > $b) $this->bar($a);
else BazClass::bar($a,$b);
switch($a){
case 0: return 'OK';
case 1: $b = $a; break;
case 2:
case 3:
$b = null;
break;
}
do $b += ++$a;
while($a < 10);
do{
$b *= 2;
$b += ++$a;
}
while($a < 20);
}
final public static function bar($a){
// method body
}
}