<div style="width:8.5in; padding:0.3in 0.5in; box-sizing:border-box;"><!-- DO NOT EDIT. This file generated from template by Code Scrawl https://tluf.me/php/code-scrawl/ -->
<h1>Liaison</h1>
<p>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 methods to the liaison object. Addons can interact with eachother through the liaison instance.</p>
<h2>Install</h2>
<pre><code class="language-bash">composer require taeluf/liaison v0.6.x-dev
</code></pre>
<p>or in your <code>composer.json</code></p>
<pre><code class="language-json">{"require":{ "taeluf/liaison": "v0.6.x-dev"}}
</code></pre>
<h2>Basic Usage</h2>
<p><code>class Lia</code> has properties for properties, methods, packages (apps), and addons. There are methods already on <code>Lia</code> to add methods, set properties, and set & retrieve addons. There is also a convenience <code>dump($anything)</code> method for debugging. There is also a system for scanning classes & automatically adding methods to Liaison (<em>this feature may be removed</em>).</p>
<pre><code class="language-php"><?php
$lia = new \Lia(); // Liaison is a lot to type, so it's just `Lia`
$lia->set('style.brand_color', 'purple'); // $lia->get('style.brand_color') to revrieve 'purple'.
$lia->addMethod('sum',
function (int $a,int $b): int {
return $a + $b;
}
);
$sum = $lia->sum(3, 4);
// $sum == 7
</code></pre>
<h2>Use as webserver (with your own app)</h2>
<p>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 (<em>there are a couple others</em>).</p>
<p>You'll have an app directory at <code>__DIR__.'/site/'</code> (<em>or wherever you prefer</em>) containing any or all of the folders named <code>addon</code>, <code>public</code>, and <code>view</code>. (<em>We'll cover this more later</em>)</p>
<pre><code class="language-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();
</code></pre>
<ul>
<li>home page (<code>/</code>) delivers <code>site/public/index.php</code>
</li>
<li>
<code>/cats/</code> delivers <code>/site/public/cats.php</code>
</li>
<li>Non-php files are delivered at their url</li>
</ul>
<h2>Manually</h2>
<h1>Lia\Simple</h1>
<p>Lia\Simple extends \Lia and simplifies a lot of server setup. Simple is also pretty messy & confusing.</p>
<p>Much more documentation is needed</p>
<h2>Server Setup</h2>
<p>This is basically all thoe code you need to setup a Liaison server.</p>
<pre><code class="language-php"><?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();
</code></pre>
<h2>Lifecycle of Lia\Simple</h2>
<p><code>Lia\Simple::__construct</code> initializes the default <code>\Lia\Package\Server</code> instance, which loads the built-in package & addons like the Router, Seo, View, and others.</p>
<p><code>Lia\Simple::load_apps($dir1, $dir2, ...)</code> will initialize the given app directories, creating instances of <code>\Lia\Package\Server</code> for each. Simple does not allow a custom Package class when using <code>load_apps()</code> or <code>load_app</code></p>
<p><code>Lia\Package\Server::__construct</code> - called for each app when <code>load_apps()</code> is called.</p>
<ol>
<li>Set configs from <code>"$app_dir/config.json"</code>, if file exists.</li>
<li>Set up routes, views, cache dir, default settings like <code>$lia->base_url</code> & cache dir</li>
<li>Initialize addons found within the app dir</li>
<li>Call <code>$addon->init_lia($package)</code> on each addon</li>
<li>Call <code>$package->ready()</code>, which calls <code>$addon->onPackageReady()</code> for each addon in the package.</li>
</ol>
<p><code>Lia\Simple::setup()</code> - This is bad feature because <code>Lia\Package\Server</code> already calls <code>$package->ready()</code> during its <code>__construct</code></p>
<ol>
<li>Loop over all packages (each app has a <code>Lia\Package</code> class instance)</li>
<li>Call each <code>$package->ready()</code>
</li>
<li>
<code>$package</code> loops over each of its <code>$package->addons</code> and calls <code>$addon->onPackageReady($package)</code>
</li>
<li>Override <code>onPackageReady()</code> in your <code>Lia\Addon</code> for any app to do further setup. The base Addon class doesn't do anything with <code>onPackageReady()</code>.</li>
</ol>
<p><code>Lia\Simple::respond()</code> - calls <code>$lia->deliver()</code>, but wraps it in error handling.<br />
Notes:</p>
<ul>
<li>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.</li>
<li>If <code>$_SERVER['DO_NOT_RESPOND'] == true</code>, then simply returns and does nothing.</li>
<li>If <code>$lia->debug == true</code> AND the request is to <code>/debug/</code>, call <code>$lia->debug()</code> and <code>exit;</code>.</li>
<li>If <code>$lia->debug == true</code>, will throw errors instead of showing generic error page. <code>respond()</code> DOES NOT change php error display/reporting settings.</li>
<li>Generic error page must be absolute path set at <code>$lia->cache->dir.'/generic-error-page.html'</code> OR <code>$lia->generic_error_page</code>.</li>
</ul>
<p>Lifecycle <code>Lia::deliver()</code> - called by <code>Simple::respond()</code>, and actually calls <code>Lia\Addon\Server::deliver()</code> (<em>Apr 13, 2023</em>)</p>
<ol>
<li>Call hooks <code>ServerStart</code>, <code>PreAllPackagesReady</code>, and <code>AllPackagesReady</code> with no params</li>
<li>Initialize <code>Lia\Obj\Request</code> and <code>Lia\Obj\Response</code>
</li>
<li>Call hook <code>RequestStarted</code> with params <code>$request, $response</code>.</li>
<li>Call <code>$lia->route($request)</code>, which calls <code>Lia\Addon\Router::route($request);</code>. Gets a list of matching routes from the routeMap. ALTERNATIVELY, if <code>$router->routers</code> contains an array of callables & one of those callables returns something truthy (supposed to be an array of routes), then return that.</li>
<li>Call hook <code>RoutesFound</code> with param <code>$routeList</code>
</li>
<li>Foreach route, call hook <code>FilterRoute</code> with param <code>$route</code>. If ANY of the hook handlers returns strict (<code>===</code>) <code>false</code>, remove <code>$route</code> from the list.</li>
<li>Filter remaining <code>$routeList</code> down to one route using an oversimplified and bad algorithm.</li>
<li>if route is null, then <code>try_redirect_to_corrected_url()</code>, which is a url normalizer & redirecter that <code>exit</code>s. If it does not <code>exit</code>, throw an excpetion that no routes were found.</li>
<li>Call <code>RoutesFiltered</code> hook with param <code>\Lia\Obj\Route $route</code>
</li>
<li>Set <code>$response->useTheme = true</code>
</li>
<li>Call <code>$server->process_route($route, $response);</code> - executes callable, <code>require</code>s php file, or loads static file. Considers cache for static file. Theme is disabled for static file. Enabled for callable or php file. Sets <code>$response->content</code> instead of outputting content.</li>
<li>If <code>$_GET['theme']=='json'</code>, then sets <code>$server->theme = 'json'</code> and compiles resource files (js & css files)</li>
<li>Call hook <code>RouteResolved</code> with params <code>$route, $response</code>.</li>
<li>Apply Theme. if EITHER <code>$response->useTheme</code> OR <code>$server->useTheme</code> are falsy, do nothing. if <code>$server->theme=='json'</code>, build array with keys <code>content</code>, <code>scripts</code>, and <code>stylesheets</code>, json_encode that array, and set <code>$response->content</code> to that json string. Otherwise, load the view with name <code>$server->themeName</code> and pass keys <code>response</code> and <code>content</code> to that view. Then call hook <code>ThemeLoaded</code> with param <code>\Lia\Obj\View $themeView</code>. Then set <code>$response->content</code> to stringified <code>$themeView</code>.</li>
<li>Call hook <code>ResponseReady</code> with param <code>$response</code>
</li>
<li>send <code>$response->headers</code>, then <code>$response->content</code>
</li>
<li>Call hooks <code>ResponseSent</code> then <code>RequestFinished</code>, both with one param <code>$response</code>
</li>
</ol>
<h1>Old Documentation (prior to Apr 13, 2023)</h1>
<p>These docs below are probably accurate for the most part ... but they are not in reference to Lia\Simple, and they need reviewed.</p>
<h2>Next Version (release TBD)</h2>
<p>This version is planned for the future & as of Apr 12, 2022 development has not begun. Just notes. See Status.md</p>
<p><code>v0.6</code> 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.</p>
<h2>Beta Version (April 5, 2021)</h2>
<p><code>v0.5</code> 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.</p>
<p><strong>My advice:</strong> Admire what it could be, keep an eye on it, and use it when it is a little more mature.</p>
<h2>Quick Start</h2>
<ol>
<li>Write a deliver script, such as <code>deliver.php</code>. Comment out the <code>require add-route.php</code> line</li>
</ol>
<pre><code class="language-php"><?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();
</code></pre>
<ol start="2">
<li>Write a home page.<br />
Create a file <code>public/index.php</code>
</li>
</ol>
<pre><code class="language-html"><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>
</code></pre>
<ol start="3">
<li>Start the server: <code>php -S localhost:3000 deliver.php</code>. Visit <code>http://localhost:3000/</code> in your browser</li>
</ol>
<p>Ideally, write tests to ensure your site works as expected. See <a href="/test/run/ServerMinimal.php">test/run/ServerMinimal.php</a> for simple examples. I use <a href="https://tluf.me/php/tester">php/tester</a>, but <a href="https://phpunit.de/">Php Unit</a> is the popular go-to for php testing</p>
<ol start="4">
<li>Write a view file at <code>view/ArticlePreview.php</code>
</li>
</ol>
<pre><code class="language-php"><div class="ArticlePreview" >
<h1><?=$title?></h1>
<p><?=$description?></p>
</div>
</code></pre>
<ol start="5">
<li>Write a stylesheet at <code>view/ArticlePreview.css</code><br />
You can also write <code>view/ArticlePreview.js</code> and <code>view/ArticlePreview/*.css|*.js</code> files to add more styling and scripting</li>
</ol>
<pre><code class="language-php">.ArticlePreview {
border:1px solid black;
}
</code></pre>
<ol start="5">
<li>Write a route in your <code>deliver.php</code> file. Alternatively, make a public file <code>public/{slug}.php</code> and <code>echo $view</code> instead of <code>$response->content = $view</code>;</li>
</ol>
<pre><code class="language-php"><?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;
}
);
</code></pre>
<ol start="6">
<li>Write a theme at <code>view/theme.php</code>:<br />
Call <code>$lia->setTheme('theme/name')</code> to change the theme.</li>
</ol>
<pre><code class="language-php"><!DOCTYPE html>
<html>
<head>
<?=$this->getHeadHtml()?>
</head>
<body>
<?=$content?>
</body>
</html>
</code></pre>
<h2>Lia\Simple</h2>
<p>A class for more easily setting up a Liaison instance with several integrations.<br />
Notes:</p>
<ul>
<li>Sets <code>user_has_role</code> handler on phad to a function that returns <code>$this->user->has_role($role)</code> or <code>true</code> if <code>$this->user</code> is not set.</li>
</ul>
<h2>Older Documentation</h2>
<p>I believe the remaining docs are still accurate, but I have not reviewed them recently for accuracy or clarity.</p>
<h3>Structure</h3>
<ul>
<li>
<code>Lia</code> manages methods, addons, and properties</li>
<li>
<code>Lia\Package</code> is a base class for packaging addons together</li>
<li>
<code>Lia\Addon</code> is a base class for writing addons</li>
<li>
<code>Lia\Package\Server</code> helps deliver websites by tying together main addons (public files, views, cache, autoloading, and more)</li>
<li>dir <code>code/class/Object/*</code> are objects used by built-in components</li>
<li>dir <code>code/class/Utility/*</code> are Utility classes</li>
<li>dir <code>view/theme</code> provides a default theme</li>
<li>dir <code>file/mime_type_map.php</code> is just that</li>
</ul>
<h3>Components</h3>
<p>Addons create all the features. <code>Lia\Package</code> calls upon several of these components</p>
<ul>
<li>Autoloader: loads classes within given directories</li>
<li>Cache: cache key/value pairs & files</li>
<li>Error: Report errors to users</li>
<li>Hook: register & call hooks (very generic form of extensibility)</li>
<li>Redirect: Redirect requests</li>
<li>Resources:
<ul>
<li>Add css & js files, urls, and/or code blocks to a request</li>
<li>Sorting api to set order of resource files in compiled output</li>
<li>Routes to compiled resource files</li>
<li>Manages seo information (this should go in a new component)</li>
<li>Outputs headHtml (must be called)</li>
<li>Passes compiled files to cache component</li>
</ul>
</li>
<li>ResourceSorter: A wrapper around the <code>Resources</code> sorting api, to make it easier</li>
<li>Router: Set pattern & static routes. Parse paramaters from urls. Process url for route target (package, callable, file)</li>
<li>Server: Handles request delivery
<ul>
<li>Helps all the addons work together to respond to a url</li>
<li>delivers static non-php files</li>
</ul>
</li>
<li>View: Display re-usable scripts/templates</li>
</ul>
<h3>Directory Structure</h3>
<p>The <code>Package</code> decides the structure, so this can be changed. Default is:</p>
<pre><code>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)
</code></pre>
<h3>Other Stuff</h3>
<ul>
<li>Environment-dependent features are not built-in, but I recommend my <a href="https://tluf.me/php/env">php/env</a>
</li>
<li>set <code>$_SERVER['DO_NOT_RESPOND'] = true;</code> to stop <code>Simple->respond()</code> from doing anything.</li>
<li>
<code>\Lia\Simple->get_all_sitemap_routes()</code> is a useful method to know</li>
<li>send <code>$_GET['theme'] = 'json'</code> to return a response as json with keys <code>content</code>, <code>scripts</code>, and <code>stylesheets</code>.</li>
</ul>
</div>