@export(About.Scanner)
Scanner
trait helps find (non-standard) magic methods in your classes, in order to automatically register events (and probably routes, views, global functions, and more).
See Actions
documentation for more information.
@export(TODO.Scanner)
- allow configuration of the prefix regex
- Class property to auto-merge with built-in prefixes
- As an alternative to actionPrefixes() method
- Document how to use prefixFor()
@export(Usage.Scanner.ScanFor)
Call $scannerClass->scanFor($prefix) to get an array of all methods using that prefix.
Your functions can bedfined as:
-
prefix_methodName()
- prefix followed by_
then upper or lower case - prefixMethodName - prefix followed by upper case
Example:
class Scannable {
use \Liaison\Scanner;
public function on_abigthing(){}
public function onSmallThing
}
$get = \Scannable::scanFor('on');
print_r($get);
/ will print, in no particular order:
[ 'abigthing' => 'on_abigthing',
'SmallThing' => 'onSmallThing'
];
@export(Usage.Scanner.ActionFor)
Get the action for a given prefix. This lets prefixes be automatically scanned for & mapped to global functions through Actions
. See Actions
documentation for more information.
To add more actions than what's built-in, override actionFor
in your scannable class, then to fallback to the built-ins call $this->Scanner_actionFor($prefix)
@export(Usage.Scanner.ActionMap.Override)
Call $this->actionPrefixes()
to get a map of prefix=>action
s that will be used for automatic scanning and for actionFor()
method. Override it to provide your own map of prefixes to actions
pass liaison to actionPrefixes() as the only paramater, or by declaring on your class a property $liaison
, or property $lia
, or exclude it if you don't need global prefixes via Liaison
Return an array like ['prefix1'=>'action1', 'prefix2'=>'action2']
.
To merge custom prefixes with built-in prefixes, call $this->Scanner_actionPrefixes()
.
Example: (in your class that uses the Scanner
trait)
protected function actionPrefixes(){
$builtIn = $this->Scanner_actionPrefixes();
$custom = ['prefix1'=>'action1', 'prefix2'=>'action2'];
$all = array_merge($builtIn, $custom);
return $all;
}
@export(Usage.Scanner.AutoRegister)
Call $this->autoRegisterScannedPrefixes($liaison)
to automatically scan for prefixes defined in actionPrefixes()
and register them to actions.
@export(About.Globals.Actions)
Actions tie string keys to global functions. This allows global function ($lia->sendEvent('EventName')
) names to change ($lia->emit('EventName')
) while maintaining the same central key identifier (event
).
This is primarily to make the Scanner
work in a robust & reliable way. Components declare actions to map to instead of global functions, and hopefully this is good future-proofing.
@export(TODO.Globals.Actions)
- Improve documentation
- Improve performance of addGlobal() function...
- in the derive method, $deriver is a string representing the global function. Need to check if this global function has been set before trying to call it.
- in the invoke method, $invoker is a string representing the global function. Need to check if this global function has been set before trying to call it.
- Might not care for this, since my exceptions are nice now... (Though this doesn't throw an exception, does it? )
- Maybe I just need to check to make sure it is a string?
@export(Usage.Globals.addGlobal)
Adding a global action / prefix / function is basically the core of Liaison. It's what makes everything work. This is not very performant right now, due to many if statements
This is the example usage with all the options.
$lia->addGlobal([
'action'=>'route',
'prefix'=>'rte', // (optional) declare functions as rte_RouteName(){} or rteRouteName(){} in your components
// a `rte_Blog(){}` method in a `$component` will ultimately call:
// $routerObject->setupRoute('Blog', [$component,'rte_Blog'])
//'strict'=>false, // to disable most errors. Don't use this (normally). Strict is 'true' by default.
\Liaison::DERIVER=>[ // $liaison->derive('route',$RouteName, $callback) will call the callable
'name'=>'addRoute', // $liaison->addRoute() will call the 'callable'.
'callable'=>[$routerObject,'setupRoute'],
//'strict'=>false //If 'addRoute' has already been set globally, strict=false will overwrite
],
\Liaison::INVOKER=>[ //$liaison->invoke('route', $url) will call $liaison->deliverRoute($url);
'name'=>'deliverRoute', //$liaison->deliverRoute() will call $routerObject->deliver()
'callable'=>[$routerObject,'deliver'],
//'strict'=>true //the default, will throw if global function 'deliverRoute' already exists
]
\Liaison::ADDER=>[
THIS NEEDS TO BE FILLED OUT & DOCUMENTED
]
]);
@export(Usage.Globals.mapActionToFunction)
Call $lia->mapActionToFunction($actionKey, 'register' or 'invoke', $globalFunctionName)
so that scannable components can automatically register methods to your global function.
Generally, you will call this immediately after register
ing a function.
Simple events
example:
$action = 'event';
$liaison->addGlobalFunction('schedule',
//we'd reference this class method like: [$eventClass, 'schedule']
public function schedule($eventName, $callback) use (&$events){
$this->events[$eventName] = $callback;
}
);
$liaison->mapActionToFunction('event', 'register', 'schedule');
Using it:
/now, calling:
$liaison->addGlobalFunction('event', 'Request.Started', $setupUserFunction);
/will call $eventClass->schedule('Request.Started', $setupUserFunction);
Without the action mapping, you could only call it through liaison with:
$liaison->schedule('EventName', $setupUserFunction);
which would be fine normally, but what if $eventClass
decides to change it's global function to scheduleEvent
instead of schedule
? The action mapping allows that to happen, without breaking anything.
@export(Usage.Globals.derive)
Call $lia->derive($actionName, ...$args)
to derive to a global action.
Usually, this will be done by the component class & Scanner trait automatically. Thus it is unlikely you will ever need to call derive()
To utilize with a global Scanner prefix, the global function must accept a function($key, $callback)
... (Sorry, I know this isn't clear...)
@export(Usage.Globals.invoke)
Call $lia->invoke($actionName, ...$arguments)
to call a global function via it's action name.
Internal Liaison components will likely use invoke
, as these need to be robust & survive changes caused by extensions & future changes.
If you're developing a Liaison extension, it might be good to use invoke
as well, for more robust...ness
If you're using Liaison for a website & you're in control of the extensions, it's probably easier (& less confusing) to just call the global function directly.
@export(Usage.Globals.add)
Call $lia->add($actionName, ...$arguments)
to... essentially load ...$arguments
into the $actionName
which will likely be used by the invoker of that action. (Sorry, I know this is poorly documented)
@export(TODO.Debug)
- Add
$lia->enableDebugging()
if I think it's a good idea. - make
debug
a read-only public property - consider just returning when debug is disabled, instead of throwing.
- Document buffering the stack
- Document examples of debugging
- Document using the debugger inside an extensions, with examples
- Review the documentation for accuracy
@export(Usage.Debug.subStack)
Substacks can help make a debug entry easier to understand. This needs better documentation
@export(Usage.Debug.debug)
To enable debugging: $lia = new \Liaison(['debug'=>true]);
Call debug($debugGroup)
to get the stack of items in the debug group. Call inspect(\Liaison::STACK)
to get a list of debugable groups currently in the stack.
So far, it's \Liaison::FUNCTIONS & \Liaison::MEDIATORS, but this could change. The code will be consistent, these docs may not be.
@export(Usage.Debug.add)
To add a debug item to the stack, if $lia->debug
is true, call $lia->addDebug(string $debugGroup, string $action, array $extraArgs)
.
Example:
if ($this->debug){
$this->addDebug(\Liaison::FUNCTIONS, 'register', ['function'=>$functionName]);
}
@export(Usage.Debug.inspect)
To find out what all has been registered to liaison, call $lia->inspect()
. With no paramaters (or an invalid paramater), you'll receive a list of items that can be inspect.
Then call $lia->inspect(\Liaison::THE_ITEM)
to inspect the specific thing you're interested in, such as FUNCTIONS, MEDIATORS, CONFIG, etc.
You can also pass THE_ITEM as a string.
Returned is an array w/ 'message' (string), and 'items' (array)
@export(TODO.Filters)
- declare and document more filters (Should I document them here or in the place where they're declared? Yes.)
- Document examples
@export(Usage.Filters.Execute)
Remove unwanted array items with custom filter functions.
To execute the filter do $filteredItems = $lia->filter($filterName, array $itemsToFilter, ...$extraParamaters);
See the Filters Test for a thorough example.
@export(Usage.Filters.Add)
Routes might frequently need some filtering that can't be reliably provided by liaison, such as if you declare dynamic routes that sometimes conflict with static routes.
Your filter function can accept bound arguments prior to the filterable array of items. See the Filters Test for a full example.
Example:
$priorityBaseUrl = '/blog/';
$lia->addFilter(Router.routes, [$yourObject, 'removeDynamicRoutes'], $priorityBaseUrl);
/any args after the callable will be passed to your callback before the filterable array of items and subsequent paramaters that are passed when filter() is called
Then removeDynamicRoutes
would be:
public function removeDynamicRoutes($priorityBaseUrl, array $routes, $url){
if ($url does-not-start-with $priorityBaseUrl)return $routes; //We will only filter for '/blog/' urls
$ret = [];
foreach ($routes as $r){
if ($r is-a-dynamic-route)continue;
$ret[] = $r;
}
if (count($ret)>0)return $ret;
else return $routes;
}
@export(Usage.Filters.Sort)
In case you have added multiple filters for the same name, you can sort the order in which they execute.
Your callback will be like function(...$boundArgs, array $filterObjects)
- The $filterObjects will be an array of
\Lia\Utility\FancyClosure
objects
And you add the filter with$lia->sortFilter($name, $callback, ...$boundArgs)
You don't have to pass bound args.
See the Filters Test for an example.
@export(About.Functions)
Functions can be registered to Liaison, to share functionality across a set of packages. The core components like Router
and Events
do this to make functions like $lia->addRoute()
and $lia->emit()
available everywhere.
@export(TODO.Functions)
- Consider removing action-function mapping when unRegistering global function
- Consider removing global prefix as well
- Add conflict resolution for duplicate global prefixes
- Add error handling or conflict resolution when there is a duplicate mediator
@export(Usage.Functions.mediate)
If register
is called for the same name twice, you can either modify your code to use reRegister/unRegister or you can add a mediator function.
You can either mediate by the exact function name or use *
(wildcard) as the function name to handle all function registrations that don't have a named mediator.
You call $lia->mediate($functionName, function($functionName,$oldCallback, $newCallback){return oneCallback;}
Example:
$helloMediator =
function($functionName, $oldCallback, $newCallback){
if ($newCallback[1]=='hola')return $newCallback;
return $oldCallback;
}
$lia->mediate('*', $functionThatAlwaysReturnsTheNewCallback); //this only gets called if there is not a named mediator, so it won't be used in this example.
$lia->mediate('hello',$someRandomFunction); //this is replaced by the next call, with no errors
$lia->mediate('hello',$helloMediator);
$lia->addGlobalFunction('hello',[$this,'hello']); // echo "hello {$name}"
$lia->addGlobalFunction('hello',[$this,'hola']);
$lia->addGlobalFunction('hello',[$this,'hallo']);
$lia->hello("Friend");
/echos `¡Hola Friend!`
calling mediate twice with the same function name will replace the existing mediate function. No errors.
@export(Usage.Functions.register)
Register a global function by calling $lia->addGlobalFunction($functionName, $callback, $options)
. Your function can then be called with $lia->$functionName().
- call addGlobalFunction('name') twice throws an error
- call removeGlobalFunction('name') to safely call addGlobalFunction() again
- call reRegister('name', $function) to safely register $function without exceptions
- Use a mediator to fine-tune duplicate register('name') calls
Example:
$lia->addGlobalFunction('schedule',
function($eventName, $callback){
echo "We scheduled {$eventName}!";
$this->events[$eventName][] = $callback;
},
//options array is optional
[ 'action'=>'event', //if action is added, 'type' is required
'type' =>\Liaison::DERIVER, //DERIVER, ADDER, or INVOKER. The invoker will handle an event being emitted
'prefix'=>'on' //Prefix is optional, but requires 'action' be set. components declaring onSetup() will automatically register to the 'Setup' event
]
)
$lia->schedule("Setup", $userSigninHandler); // when $lia->emit("Setup") is called, the user sign-in handler will be called
/ 'emit' would also be registered in the same way 'schedule' is above.
$lia->addGlobalFunction('schedule', function(){}) //throws a \Lia\Exception\Duplicate
You may pass a function(){}
, a string $functionName
, a $callable == ['className', 'staticFunctionName']
, a $callable == [$object, 'methodName']
, or any object that implements __invoke
.
See Utility documentation for FancyClosure
if you need to bind additional paramaters to your callable.
@export(Usage.Functions.reRegister)
Reregister simply calls $lia->removeGlobalFunction($functionName); $lia->addGlobalFunction($functionName, $callable)
.
Example:
$lia->addGlobalFunction('hello', function($name){echo "Hello {$name}!";});
$lia->replaceGlobalFunction('hello', function($name){echo "¡Hola {$name}!";});
$lia->hello('Reed');
/echos `¡Hola Reed!`
Don't worry, if you use reRegister
when nothing has been registered to that name, the unRegister
call won't hurt anything.
@export(Usage.Functions.unRegister)
Unregister a previously registered function with $lia->removeGlobalFunction($functionName)
. You can then call $lia->addGlobalFunction()
for the same function name.
@export(TODO.View)
- Config to set a namespace for all views of a given package
- perhaps $lia should be a required paramater, not hidden in an array
- Add an init + template approach like
view/Blog/template.php
andview/Blog/init.php
- Add automatic routing via the view system (which is more robust and neat than simple public-files), such as with a
public-view
dir- a `
might be a valid option, but that seems more like an extension feature than a core feature
- a `
- Document what paramaters are always available to views
- Implement a way to pass certain paramaters to every view of a package. $lia & $package are already passed, always, but this should be extensible to allow views to automatically have access to paramaters
- Document an example of a view file's code (such as the example blog.php view)
- For sibling resource files (view/blog.php, view/blog.css, view/blog.js), resources() returns '.js' and '.css' for the resource files.
- Maybe they should have real names? But the current setup prevents conflicts between view/blog.css and view/blog/blog.css
- Document resources() function & returned array
@export(Usage.View.Packaged)
Views are defined in your package and can follow one of two different structures
public/ ... public files
view/
- Blog.php
- Blog.js
- Blog.css
- Theme.php
- Theme/
- main.js
- main.css
- extra/ ... contains a bunch more css & js files
- Calling
$view = $lia->view('Theme', $args)
will encompassTheme.php
and load every single.js
and.css
file in theTheme
directory - Calling
$view = $lia->view('Blog', $args)
will encompassBlog.php
for the view and loadBlog.js
andBlog.css
- @TODO implement these sibling-resource files (currnetly, only the sub-dir resource files work)
-
$args
areextract
ed so yourTheme.php
&Blog.php
files receive the array keys as paramaters. - Resource files are loaded when you call
$view->resources()
,$content = $view->content()
, or$content = "".$view;
(tostring) - Place your view in a subdirectory like
view/Theme/Light.php
for a view namedTheme/Light
@export(Utility.FancyClosure.TODO)
@TODO add check to see if a callable is actually callable
@TODO setup read-only properties
@export(Utility.FancyClosure)
Fancy closures enable binding of paramaters to a callable and inspection of callables, whether as ['\\StaticClass','functionName']
, [$object, 'methodName']
, or $anonymousFunction = function(){}
.
Example:
$cat = new \Funny\Cat();
$loudness = 7;
$closure = new \Lia\Utility\FancyClosure([$cat, 'purr'], [$loudness]);
$closure->funcName === 'purr';
$closure->object === $cat;
$closure->class === 'Funny\\Cat'; //it prints with a single slash, but backslashes are generally better off being escaped in my experience
$closure->isStatic === false; // would be true for ['Funny\Cat', 'purr']
$closure->isAnonymous === false; // would be true for function(){echo 'Kindness can be anonymous too.';}
$closure->function === null; // would be the anonymous function, if you had passed a function instead of a `[$obj, 'funcName']` callable
$closure->origCallable === [$cat, 'purr']; //just the first arg you pass to the constructor
$closure->bound === [$loudness]; // and this is the 2nd arg
@export(TODO.Package)
- document the public dir
- document the file-to-pattern conversion
- document url normalization
- Document changing the view class
- Enable configs
- autoloader is not necessarily available when package is costructed... Need to setup autoloading, though. Probably need package lifecycle methods or some kind of queuing system
- Document general package usage
- Document creating a custom package
@export(Usage.Router.publicDir)
Files in the public
dir of your package will be automatically routed to.
-
public/index.php
files will be delivered without the file name or extension -
public/dir/file.php
files will be delivered at/dir/file/
-
public/resource.js
and other non.php
files will be delivered at/resource.ext
There's much more to document and features to set up with routing, still.
@export_end(Usage.View.AddView)
$lia = $this->lia;
$dir = $this->dir('view');
$files = \Lia\Utility\Files::all($dir,$dir, '.php');
//set view conflict mode
$lia->setViewConflictMode($this->config['views']['conflict'] ?? $lia->get('views.conflict'));
foreach ($files as $f){
$viewName = pathinfo($f,PATHINFO_FILENAME);
$class = $this->class('view');
$dir = $dir;
$args =
[
'lia'=>$lia,
'package'=>$this
];
$lia->addView($class, $dir, $viewName, $args);
}
@export(TODO.Autoload)
- Add classmap-autoloading method (currently only PSR4 is supported, I think)
- Cache autoloader classmap to disk
- Document using autoloader component directly w/ addDir() & loadClass()
@export(Usage.Autoloader.Invoker)
Use the built-in autoloader to load classes with the following structure:
hierarchy
- Contact/
- Submitter.php: namespace Contact, class Submitter
- Customer/
- Order.php, namespace Customer, class Order
- Person.php, ns Customer, cls Person
flat
- Visa.php: ns Payment\Processor, cls Visa
- Bitcoin.php: ns Payment\Processor, cls Bitcoin
- Oauth.php: ns User\Auth, cls Oauth
Load all of the above with the following:
$dir = __DIR__;
$lia->autoload($dir.'/hierarchy');
$lia->autoload($dir.'/flat',['Payment\\Processor', 'User\\Auth'])
@export(Usage.Autoloader.Adder)
Call $lia->addAutoloader($callback)
to add a custom autoloader. Example:
$autoloader =
function($class) use ($customClassMap) {
$file = $customClassMap[$class] ?? null;
if ($file==null)return;
require_once($file);
};
$lia->addAutoloader($autoloader);
Added autoloaders will be called in no particulary order. You can also just use spl_autoload_register
, the built-in php function.
@export(Usage.Autoloader.Deriver)
Add an autoloader in your component by declaring autoloadFunctionName($class){}
. Example:
class Blog extends \Lia\Compo {
$classMap = ['Namespace\\ClassName'=>'/file/path'];
public function autoloadBlogClasses($class){
if (isset($this->classMap[$class]))require_once($classMap[$class]);
}
}
@export(TODO.Error)
- Figure out how to do error handling...
@export(TODO.Events)
- Figure out how events are going to work
- Implement an events directory, possibly
- Rename register & send to schedule & emit
- Should events offer return values? No, I don't think so
@export(TODO.Resources)
- set page meta data like title, og:image, description, etc
- Handle script & stylesheet URLs
- Handle script & stylesheet code
- Consider using custom routes instead of storing files in a cache-public dir
- jsFiles & cssFiles arrays have the full file path as their key & value. Add ability to name the paths. This will improve dev experience when sorting a list of scripts/stylesheets
- Improve sorting
- add sort preference to
addResourceFile($file)
. A Liaison extension that adds a JS framework would use this to ensure it's framework file is always the first thing.- Without this, the sort burden is on the developer who is integrating the Liaison extension.
- add sort preference to
- Document Resources...
- Consider other methods of routing that might improve performance...
- Remove duplication of code between CSS & JS compilation
- Add url prefix for compiled files as a config option
- move SEO code into it's own component
- Use config for default site title
- add all meta properties that are available...
- Auto-add charset=utf8 & viewport meta tags (with config to disable)
- Separate large files from the block of compiled code.
- If i'm using a, say, 50KB or larger JS file on 10 different pages, but each of those pages has a couple different small JS files for their individual components, it's probably better to cache the 50KB file on its own
- Minify js & css
@export(TODO.ViewComponent)
- document the use of a view that does NOT implement Lia\IView
- implement & document adding a view by simple file path
@export(TODO.Cache)
- Auto-delete expired cache-files
- Probably use a post-response hook (after content has been sent to the browser)
- And probably auto-delete if an expired file is requested
- Possibly add
lia.cacheDir.public
&lia.cacheDir.public.baseUrl
to assist in routing - Consider adding defaultExpiry as a config, rather than a default paramater in the cacheFile() function call
- Are these the same thing?
- Consider function to return file content, not just file path. Consider additional decoding like parsing json or yaml.
- Consider adding type-based processing (PHP, JSON, DOMDocument??)
- Consider extensible features (such as auto en/decoding additional types not supported by Liaison)
- cache response of getCacheFile() so files don't have to be loaded more than once
@export(Internals.Cache)
Cache files can be used for whatever you like and are stored by key without file extensions, unless you give one to them. Namespaces are recommended.
Two files are created when you cache a file: file-ns.thekey
and meta-ns.thekey
. The meta-
file stores the expiry and may hold more information in the future. The file-
stores the content.
@export(Usage.Cache.dir)
There is both a public and private cache dir. Both of which are in Liaison's directory by default.
- The cache is used by the Resources class to handle your compiled css and js files.
To change the cachedir do:
$lia->set('lia.cacheDir', $yourPackage->dir('cache'));
- You MAY use an explicit path instead of using your
$package->dir()
lookup -
lia.cacheDir
may be anywhere you like.
@export(Usage.Cache)
Call $lia->cacheFile($key, $content, $maxAgeInSeconds)
to store a file in the cache.
- You may leave off
$maxAgeInSeconds
to use the default, which is five days - Cache files cannot be loaded after they expire and will be automatically cleaned up at some point.
Call $lia->getCacheFile($key)
to get the contetns of a cached file
- returns
false
if the cache file does not exist or has expired - Files are returned as a string. JSON is not decoded. PHP files are not processed.
@export(TODO.Router)
-
Document the paramaters that are passed to a public file
-
add a globalDeriver for object-oriented routing
-
add a built-in filter-routes method that prefers the least-dynamic route (no vars, or least number of vars)
-
improve 'Pattern Rules' documentation
-
figure out sitemaps... Though that may not be part of router??
-
document examples of routing
-
caching of urls & routes
-
write up documentation into an MD file
-
auto-add
public
dir for each package -
add a function to convert a file path to a pattern (aka, remove extensions like '.md' and '.php', per a configuration)
-
add a normalizeUrl function (add trailing slash, remove .php / .html / .md, all lowercase?)
-
Option to return both perfect-match routes AND routes that match with the normalized url, aka: /some/url && /some/url/ would return the same route(s)
-
add... something to help convert pattern-based routes to sitemap representations
-
create 'prefer-static' feature that would only do regex-checks if a static match is NOT found.
-
add a mediator function to handle multiple global routers, possibly??
-
document how to use the Route object
@export(Usage.Router.addRoute)
Call $lia->addRoute($pattern, $callbackOrFile,$package=null)
to add a route to the built-in router
@param $pattern A pattern. See [the pattern matching rules](@link(Rules.Router.pattern))
@param $callbackOrFile a callback or a file path
@param $package (optional) A liaison package
@export(Usage.Router.getRoute)
Get a Route to work with as you please by calling $lia->route($url, $method='GET');
@export(Rules.Router.pattern)
rules:
.php will generally be removed & replaced with a trailing slash, but that is NOT part of parsePattern()
That will be a pattern-normalization step that happens prior to parsePattern() and is extensible/configurable
Methods: @POST, @GET, @PUT, @DELETE, @OPTIONS, @TRACE, @HEAD, @CONNECT
- We do not currently check the name of the method, just @ABCDEF for length 3-7
- These must appear after a `/` or after another '@METHOD.' or they will be taken literally
- lower case is not valid
- Each method MUST be followed by a period (.)
- example: /@POST.dir/sub/@GET.file/ is valid for both POST /dir/sub/file/ and GET /dir/sub/file
Paramaters:
- {under_scoreCamel} specifies a named, dynamic paramater
- {param} must be surrounded by path delimiters (/) OR periods (.) which will be literal characters in the url
- {param} MAY be at the end of a pattern with no trailing delimiter
- {paramName:regex} would specify a dynamic portion of a url that MUST match the given regex.
- Not currently implemented
examples:
/blog/{category}/{post} is valid for url /blog/black-lives/matter
/blog/{category}.{post}/ is valid for url /blog/environment.zero-waste/
/blog/{category}{post}/ is valid for url /blog/{category}{post}/ and has NO dynamic paramaters
@export(TODO.Server)
- idk... finish building the class??
- Graceful fallback for when view() is not available
- Graceful fallback for when 'theme' view is not available
- When route is a callable, should the callable output content or should it return content that is echo'd by Server?
@export(Usage.Config.notes)
-
set($key, $val)
will override any previously set$key
-
default($key, $val)
will never overrideset()
or previousdefault()
calls -
get($key)
will throw an exception if the$key
has not been set
@export(TODO.Config)
- config file loading... set() should generally be able to override configs from the file
- per-component &/or per-package configs (unless those aren't handled by this compo)
- Do config file contents automatically get loaded into Liaison? Maybe, if they're namespaced, at least?
- Does a package have it's own configs that are NOT Liaised? YES, or it could anyway
- Directories are one example of per-package configs
- Should I limit access to set() & get(). NOT EARLY ON
- maybe in the future? I'm concerned about performance, the time to implement, and how much it's actually needed (assuming you only run code you trust)
- What happens if a config value is overridden? Error? Let it happen? Fail silently? Log?
- add conflict resolution for duplicate config keys
- consider a non-exception approach to a not-set config key
@export_end(Usage.Router.PatternCallback)
// Declare functions inside your component with the 'routePattern' prefix, followed by an uppercase letter or underscore
// I believe this implementation is going to change.
public function routePatternBlog($route) {
if ($route===false)return [
'/blog/{article}/',
'/blog/{article}',
'/about/{page}/'
];
$blogs = [
'black-lives-matter'=> 'Have you looked at traffic data in your home town? Is there a racial disparity?',
'toxic-pollution' => 'The US EPA, under Trump, has rolled back many protections for U.S. citizens',
'us-voter-suppression' => 'Why are mailboxes and mail-sorting machines being removed from cities? Why isn't the post office tax-payer funded?',
];
$abouts = [
'me'=>"Hi, I'm Reed. I'm an indie developer. I'm very opinionated and wish the world were a better place."
];
$var = $route->var(0);
if ($route->part(0)=='about'
&&isset($abouts[$var]))return $abouts[$var];
else if ($route->part(0)=='blog'
&&isset($blogs[$var]))return $blogs[$var];
else return "A blog was not found for '{$var}'";
}
@export_end(Usage.View.AddViewCallable)
// This approach is extremely rudimentary and not recommended.
$phrase = "Fight for your right to vote.";
$lia->addViewCallable('theme',
function($name, $args) use ($phrase){
return $phrase;
}
);
$content = $lia->view('theme').'';
@export_end(Usage.Seo)
$lia->seoTitle('Test Page');
$lia->seoDescription('Test description');
$lia->seoImage('/path/to/image.jpg', 'alt text for image');
$lia->seoUrl('/canonical/url/');
$lia->seoSiteName('Liaison test');
$html = $lia->getHeadHtml(); //includes script & stylesheet tags
//You can alternatively use $lia->getSeoHtml();
@export_end(Usage.Seo.Output)
@export_end(Example.View.display)
//'blog' is the name of the view. The array is paramaters to pass to the view
echo $lia->view('blog',
['title'=>'About Liaison',
'summary'=> 'Create full-GUI, packaged webapps that work with any PHP server',
'body'=> "Liaison is it's own framework. You can use it for full websites or for GUI libraries.\nIt's pre-alpha at time of writing this.\n\n-Sept 8, 2020"
]
);