Liaison

PHP Web Framework. Code is grouped into apps. Apps may contain addons, views, and public files. Other features can be created through addons, or more directly by adding shared methods to the liaison object. Addons can interact with eachother and share properties through the liaison instance.

WARNING! New readme. Not yet error checked. Some things might not work.

Install

composer require taeluf/liaison v0.6.x-dev   

or in your composer.json

{"require":{ "taeluf/liaison": "v0.6.x-dev"}}  

Basic Usage

class Lia has properties for shared properties, methods, packages (apps), and addons. There are methods already on Lia to add methods, set properties, and set & retrieve addons. There is also a convenience dump($anything) method for debugging and a system for scanning classes & automatically adding methods to Liaison (this feature may be removed).

We'll start with a simple example of properties & methods.

<?php  
  
$lia = new \Lia(); // Liaison is a lot to type, so it's just `Lia`  
$lia->set('style.brand_color', 'purple');   
$lia->addMethod('sum',  
    function (int $a,int $b): int {  
        return $a + $b;  
    }  
);  
  
$sum = $lia->sum(3, 4);   
// $sum == 7  
echo "brand_color:" . $lia->get('style.brand_color');  
// prints brand_color: purple  

Setup webserver

Liaison comes with it's own package and suite of addons. The built-in addons provide routing, seo meta tag generation, views, script & style compilation, caching, hooks, and page redirects (there are a couple others).

You'll have an app directory at __DIR__.'/site/' (or wherever you prefer) containing any or all of the folders named addon, public, and view. (We cover this in the next section)

I put this in deliver.php and setup an .htaccess file to point all requests to deliver.php

<?php  
  
$lia = new \Lia();  
$server_app = new \Lia\Package\Server($lia, 'lia:server'); // second arg is fully qualified name. third arg is optional app dir. fourth is optional base url  
  
$site_app = new \Lia\Package\Server($lia, 'myname:site', __DIR__.'/site/');  
  
// delivers files in `site/public/*`   
$lia->deliver();  
  • home page (/) delivers site/public/index.php
  • /cats/ delivers /site/public/cats.php
  • Non-php files are delivered at their url

Simple Website

Setup the webserver, as above. We'll create the below file structure (We'll walk you through it, one file at a time.)

File Structure

The public dir is setup by \Lia\Addon\Server (NOT Package\Server), view by Lia\Addon\View, and addon by the \Lia\Package\Server (NOT Addon\Server).

config.json  
public/  
    index.php  
    legal/  
        terms.php  
view/  
    theme.php  
    theme.css  
    footer.php  
addon/  
    DevMode.php  

Completing this, we'll have a basic website with a home page, a terms & conditions page, and custom addon that adds a (very insecure) developer mode.

Start your php development server: php -S localhost:3000 deliver.php (3000 can be changed)

1. config.json

A simple configuration, giving a namespace & base url for liaison, and a config for the DevMode addon.

{  
    "namespace":"site",  
    "base_url": "/",  
    "dev_mode.key": "RandomKey83"  
}  
  • / is the default base_url, so base_url doesn't have to be supplied.

2. public/index.php

This is our home page. Make a simple html layout.

<h1>Civic Engagement Blog</h1>  
<p>Check out my tutorials about engaging with local government, civic groups, and community groups.</p>  
  
<!-- @TODO code to display a list of my blog posts -->  

Now visit http://localhost:3000 in your browser. You should see your header & paragraph.

3. addon/DevMode.php

We'll create a very simple, very NOT secure addon.

<?php  
  
class DevMode extends \Lia\Addon {  
      
    public function init_lia(){  
        $lia = $this->lia;  
        if (@$_GET['dev_mode'] === $lia->get('dev_mode.key')){   
            $lia->set('dev_mode', true);   
            // NOTE: Any other addon could change this setting  
        }  
    }  
    /** called automatically by Server package, before onPackageReady() */  
    public function init_lia(){}  
    public function onPackageReady($package){}  
}  

4. edit public/index.php (add debugging)

Let's add some debugging in developer mode.

<?php  
  
if ($lia->get('dev_mode')===true){  
    echo '<pre>';  
    $lia->dump();  
    echo '</pre>';  
    return;  
}  
  
?>  
<h1>...</h1>  
<p>...</p>  

Go to http://localhost:3000/?dev_mode=RandomKey83 & you'll see a debug dump of Liaison.

Tip: $lia->devmode->some_property could be used instead of set() & get().

5. view/theme.php

Liaison has a barebones built-in theme. Let's make our own. $content and $lia are passed to it automatically. getHeadHtml() will print seo meta tags, scripts, and stylesheets (most of which are compiled together).

<!DOCTYPE html>  
<html>  
<head>  
    <?=$lia->getHeadHtml();?>  
</head>  
<body>  
    <header>  
        <strong>My Civic Engagement Website</strong>  
    </header>  
    <main>  
        <?=$content;?>  
    </main>  
    <footer>  
        <?=$lia->view('footer.php', ['site_name'=> 'My Civic Engagement Website']);?>  
    </footer>  
</body>  
</html>  

Go to http://localhost:3000/ & you'll see your new theme, but unstyled.

6. view/theme.css

Simple styling to make a vertical page.

body {  
    display:flex;  
    flex-direction:column;  
}  
  
header { background: blue; }  
body > main { flex: 1; }  

7. view/footer.php

We'll make the footer view & handle the passed in param site_name, which is made automatically available to the view as $site_name. Let's document it too.

<?php  
/**  
* A very simple, unstyled footer.  
* @param $site_name The official name of the website  
*/  
<h3><?=$site_name?></h3>  
<a href="/legal/terms/">Terms & Conditions</a>  

You can also make a footer.css & footer.js which will be automatically included - plus css & js files in footer/* if you need it.

8. Tips & Tricks

You have the bones of an incredibly oversimplified Liaison app.

Some other important features to know:

  • $lia->addRoute('/some/route/', function(\Lia\Obj\Route $route,\Lia\Obj\Response $response){echo "something";});
    • Also accepts pattern routes like /blog/{slug}/'. Get the slug with one of: $route->param('slug'), $route->part(1), or $route->var(0)
    • Prefix routes with @POST. or @POST.@GET. to enable POST on a route, or allow both POST & GET.
  • echo lia->view('some/view', ['arg'=>'Some thing']); - view() returns a string.
  • All the built in features come from addons. See code/addon/.
  • $lia->setTheme('view/name') - change the view name that is used as a theme.
    • $lia->themeName = 'json' - alternate to setTheme. json will return json array with keys content, scripts, and stylesheets. content is a string. The other two are arrays of urls.
  • $lia->useTheme = false - disable the theme
  • TODO: (to document)
    • dump()
    • hooks
    • redirects
    • sorting resources
    • seo features
    • caching
    • autoloading
    • error addon
    • server addon
    • package / Server package information
    • Lia\Simple
    • (also, this document should be split into multiple docs. I want a very brief intro here, then a getting started doc, an architecture doc, maybe, link to apis & other useful docs (view list, routes list? idk))
    • fast delivery

Lia\Simple

Lia\Simple extends \Lia and simplifies a lot of server setup. Simple is also pretty messy & confusing.

Much more documentation is needed

This is basically all thoe code you need to setup a Liaison server.

<?php  
require(__DIR__.'/vendor/autoload.php');  
  
$site = __DIR__.'/site/';  
$apps = __DIR__.'/apps/';  
$lia = new \Lia\Simple();  
$lia->debug = !$lia->is_production();  
$lia->generic_error_page = __DIR__.'/cache/generic-error-page.html';  
$lia->root_dir = __DIR__;  
// very simple path-based delivery of static files  
$lia->deliver_files("$site/public-archive/");  
// by default it will auto-load `$lia->root_dir.'/RSettings.json'`  
R()->load("$site/RSettings.json");  
// load Liaison apps.  
$lia->load_apps("$site", "$apps/Files", "$apps/Blog", "$apps/Forms");  
// initialize the apps. See Lifecycle below  
$lia->setup();  
  
// retrieve phad instance from a liaison app & configure it.  
$files_phad = $lia->phad_from_dir("$apps/Files/");  
$files_phad->dir_upload = __DIR__.'/backup/files-upload/';  
  
// sends a response, redirect, or whatever  
$lia->respond();  

Lifecycle of Lia\Simple

Lia\Simple::__construct initializes the default \Lia\Package\Server instance, which loads the built-in package & addons like the Router, Seo, View, and others.

Lia\Simple::load_apps($dir1, $dir2, ...) will initialize the given app directories, creating instances of \Lia\Package\Server for each. Simple does not allow a custom Package class when using load_apps() or load_app

Lia\Package\Server::__construct - called for each app when load_apps() is called.

  1. Set configs from "$app_dir/config.json", if file exists.
  2. Set up routes, views, cache dir, default settings like $lia->base_url & cache dir
  3. Initialize addons found within the app dir
  4. Call $addon->init_lia($package) on each addon
  5. Call $package->ready(), which calls $addon->onPackageReady() for each addon in the package.

Lia\Simple::setup() - This is bad feature because Lia\Package\Server already calls $package->ready() during its __construct

  1. Loop over all packages (each app has a Lia\Package class instance)
  2. Call each $package->ready()
  3. $package loops over each of its $package->addons and calls $addon->onPackageReady($package)
  4. Override onPackageReady() in your Lia\Addon for any app to do further setup. The base Addon class doesn't do anything with onPackageReady().

Lia\Simple::respond() - calls $lia->deliver(), but wraps it in error handling.
Notes:

  • Will try to deliver a generic error page. if an exception is thrown. Worst case scenario, just prints that there was an error & a generic link to the home page.
  • If $_SERVER['DO_NOT_RESPOND'] == true, then simply returns and does nothing.
  • If $lia->debug == true AND the request is to /debug/, call $lia->debug() and exit;.
  • If $lia->debug == true, will throw errors instead of showing generic error page. respond() DOES NOT change php error display/reporting settings.
  • Generic error page must be absolute path set at $lia->cache->dir.'/generic-error-page.html' OR $lia->generic_error_page.

Lifecycle Lia::deliver() - called by Simple::respond(), and actually calls Lia\Addon\Server::deliver() (Apr 13, 2023)

  1. Call hooks ServerStart, PreAllPackagesReady, and AllPackagesReady with no params
  2. Initialize Lia\Obj\Request and Lia\Obj\Response
  3. Call hook RequestStarted with params $request, $response.
  4. Call $lia->route($request), which calls Lia\Addon\Router::route($request);. Gets a list of matching routes from the routeMap. ALTERNATIVELY, if $router->routers contains an array of callables & one of those callables returns something truthy (supposed to be an array of routes), then return that.
  5. Call hook RoutesFound with param $routeList
  6. Foreach route, call hook FilterRoute with param $route. If ANY of the hook handlers returns strict (===) false, remove $route from the list.
  7. Filter remaining $routeList down to one route using an oversimplified and bad algorithm.
  8. if route is null, then try_redirect_to_corrected_url(), which is a url normalizer & redirecter that exits. If it does not exit, throw an excpetion that no routes were found.
  9. Call RoutesFiltered hook with param \Lia\Obj\Route $route
  10. Set $response->useTheme = true
  11. Call $server->process_route($route, $response); - executes callable, requires php file, or loads static file. Considers cache for static file. Theme is disabled for static file. Enabled for callable or php file. Sets $response->content instead of outputting content.
  12. If $_GET['theme']=='json', then sets $server->theme = 'json' and compiles resource files (js & css files)
  13. Call hook RouteResolved with params $route, $response.
  14. Apply Theme. if EITHER $response->useTheme OR $server->useTheme are falsy, do nothing. if $server->theme=='json', build array with keys content, scripts, and stylesheets, json_encode that array, and set $response->content to that json string. Otherwise, load the view with name $server->themeName and pass keys response and content to that view. Then call hook ThemeLoaded with param \Lia\Obj\View $themeView. Then set $response->content to stringified $themeView.
  15. Call hook ResponseReady with param $response
  16. send $response->headers, then $response->content
  17. Call hooks ResponseSent then RequestFinished, both with one param $response

Old Documentation (prior to Apr 13, 2023)

These docs below are probably accurate for the most part ... but they are not in reference to Lia\Simple, and they need reviewed.

Next Version (release TBD)

This version is planned for the future & as of Apr 12, 2022 development has not begun. Just notes. See Status.md

v0.6 will be the next version & will come with major internal changes but likely will not have any significant changes to the API. The current version has Package & Addon as sub-classes of Lia. Also, there are MANY properties by-reference. These complexities are slow & confusing. In the new version, Packages & Addons will likely not extend from Liaison any further. And by-reference properties will be removed.

Beta Version (April 5, 2021)

v0.5 marks the official beta. Software-wise, it's basically ready to go. Documentation wise, it's pretty poor. There are some weird things in here that will make it hard for you to use to it's full ability until I finish writing proper documentation. There's also some minor code issues in Liaison that I do need to fix.

My advice: Admire what it could be, keep an eye on it, and use it when it is a little more mature.

Quick Start

  1. Write a deliver script, such as deliver.php. Comment out the require add-route.php line
<?php  
  
require_once(dirname(__DIR__,2).'/vendor/autoload.php');  
  
$lia = new \Lia();  
  
$main = \Lia\Package\Server::main($lia);  
$site = new \Lia\Package\Server($lia, 'site', __DIR__);  
  
  
//comment this line out in step 1  
require(__DIR__.'/add-route.php');  
  
$lia->deliver();  
  1. Write a home page.
    Create a file public/index.php
<h1>Index Page</h1>  
<p>Any file in the `public` dir will be routed to automatically. Any public/*.php files are routed with NO file extension.</p>  
  1. Start the server: php -S localhost:3000 deliver.php. Visit http://localhost:3000/ in your browser

Ideally, write tests to ensure your site works as expected. See test/run/ServerMinimal.php for simple examples. I use php/tester, but Php Unit is the popular go-to for php testing

  1. Write a view file at view/ArticlePreview.php
<div class="ArticlePreview" >  
    <h1><?=$title?></h1>  
    <p><?=$description?></p>  
</div>  
  1. Write a stylesheet at view/ArticlePreview.css
    You can also write view/ArticlePreview.js and view/ArticlePreview/*.css|*.js files to add more styling and scripting
.ArticlePreview {  
    border:1px solid black;  
}  
  1. Write a route in your deliver.php file. Alternatively, make a public file public/{slug}.php and echo $view instead of $response->content = $view;
<?php  
// in production, you might use a database & have some error handling  
$articles = [  
    'cat'=>[  
        'title'=>'Cats are great',  
        'description'=>'I\'ve always loved cats. I had two when I was a little kid. As a teen I had a dog & a cat. Loved them both dearly. I love dogs too.'  
    ],  
    'dog'=>[  
        'title'=>'fill me in',  
        'description'=>'fill me in'  
    ],  
];  
  
$lia->addRoute('/{article}/',  
    function($route, $response) use ($lia, $articles){  
        $slug = $route->param('article');  
        $view = $lia->view('ArticlePreview', $articles[$slug]);  
        $response->content = $view;  
    }  
);  
  1. Write a theme at view/theme.php:
    Call $lia->setTheme('theme/name') to change the theme.
<!DOCTYPE html>  
<html>  
<head>  
<?=$this->getHeadHtml()?>  
</head>  
<body>  
<?=$content?>  
</body>  
</html>  

Lia\Simple

A class for more easily setting up a Liaison instance with several integrations.
Notes:

  • Sets user_has_role handler on phad to a function that returns $this->user->has_role($role) or true if $this->user is not set.

Older Documentation

I believe the remaining docs are still accurate, but I have not reviewed them recently for accuracy or clarity.

Structure

  • Lia manages methods, addons, and properties
  • Lia\Package is a base class for packaging addons together
  • Lia\Addon is a base class for writing addons
  • Lia\Package\Server helps deliver websites by tying together main addons (public files, views, cache, autoloading, and more)
  • dir code/class/Object/* are objects used by built-in components
  • dir code/class/Utility/* are Utility classes
  • dir view/theme provides a default theme
  • dir file/mime_type_map.php is just that

Components

Addons create all the features. Lia\Package calls upon several of these components

  • Autoloader: loads classes within given directories
  • Cache: cache key/value pairs & files
  • Error: Report errors to users
  • Hook: register & call hooks (very generic form of extensibility)
  • Redirect: Redirect requests
  • Resources:
    • Add css & js files, urls, and/or code blocks to a request
    • Sorting api to set order of resource files in compiled output
    • Routes to compiled resource files
    • Manages seo information (this should go in a new component)
    • Outputs headHtml (must be called)
    • Passes compiled files to cache component
  • ResourceSorter: A wrapper around the Resources sorting api, to make it easier
  • Router: Set pattern & static routes. Parse paramaters from urls. Process url for route target (package, callable, file)
  • Server: Handles request delivery
    • Helps all the addons work together to respond to a url
    • delivers static non-php files
  • View: Display re-usable scripts/templates

Directory Structure

The Package decides the structure, so this can be changed. Default is:

App/  
    - config.json <- Package settings  
    - public/ <- Public files to be routed to. Ex: `public/contact.php` routes to `/contact/`  
    - view/ <- Views  
    - addon/ <- Addons (generally extending from \Lia\Addon)  
    - class/ <- Classes to autoload, PSR4 style. Will be converted to classmap style later  
    - cache/ <- Dir to store cache files. Only one cache dir is used (each app does not get its own)  

Other Stuff

  • Environment-dependent features are not built-in, but I recommend my php/env
  • set $_SERVER['DO_NOT_RESPOND'] = true; to stop Simple->respond() from doing anything.
  • \Lia\Simple->get_all_sitemap_routes() is a useful method to know
  • send $_GET['theme'] = 'json' to return a response as json with keys content, scripts, and stylesheets.