Liaison Development Status

Work in Progress (lia\Simple)

  • error page
    • DNOE displays /generic-error-page/
    • DONE need to cache the result to disk so it can be displayed on error
  • analytics
    • DONE analytics are enabled
    • DONE todo need a way to create the analytics table
    • DONE need to add the analytics phad view
  • other
    • add cli command to initialize the user database tables
    • add cli command to generate sitemaps
    • add cli command to regenerate all phad items
    • add cli command to clear the cache

themes

  • $lia->setTheme('name/of-theme');
  • $theme = new \Lia\Theme('name/of-theme');
  • $response->sendHeaders();
  • $theme->setContent($response->content);
  • $theme->display();
  • themes can be found in the view folder or theme folder
  • themes are available globally (if any app makes theme dir, those themes are shared)
  • theme is a subclass of view and has extra features (maybe)

How do I test this? Create a Lia\Simple instance initialize it? create an app with several themes in it set the theme add a route for / getResponse() for / check output of $response->content

WARNING

i did some work to cache routes, but decided not to finish the work but now i want to get rid of that code but maybe i want to keep it around and atm i don't care to work on this more so don't commit this in its current state Okay?

TODO

  • base url just doesn't work any more
  • re-enable config.json
  • testing with my user lib, v0.6 seems to be slower than v0.5 and i DONT UNDERSTAND
  • Improve varDelim. varDelim is an awful mechanism bc it applies globally to all routes
  • Compile list of views to reduce disk scanning

Changes required with this new version

  • addons & packages are no longer liaison objects
  • chained $package_or_addon->lia->method() must be used to call a global method, instead of calling it directly on the package or the addon.
  • addons MUST receive a \Lia\Package or null (instead of a liaison object)
  • addons MUST set public string $fqn = 'ns:package_name.addon_name'
  • remove all $lia->props[] uses
  • $lia->addons['key'] always points to an addon, never to a package. use $lia->packages['namespace:key'] for a package.
  • $lia->set('server.server.useTheme', false); now must be $lia->set('lia:server.server.useTheme') or $lia->set('server.useTheme') - it's either fully qualified name or JUST the addon name (no namespace)
  • new \Lia\Package($lia, 'ns:package_name', $dir), the ns:package_name. $dir is still optional, but $lia & the package name are now required.
    • though, name is optional for the server package... hmmm....
  • config.json inside a package dir is not currently loaded at all (this will be re-enabled as i figure out what i need from it)
  • $package->addon_name no longer works at all, must use $package->addons['addon_name'] or re-write the __get that will return the addon
  • $addon->init_lia() must be called to setup global methods & such. This is handled automatically by the Server package, though.
  • $addon->lia now points to a \Lia object and NOT it's parent/package object

Issues to fix with v0.6

  • I can't change the cache dir. calling $lia->set('cache.dir') can only be used after the Cache & Resource addons are initialized. But onPackageReady() calls scan() on the resource addon, seting up routes. onPackageReady() is called before i have access to the instantiated addon. This is a pervasive issue. Generally, i have no way to pass configurations to addons at time of their construction. I could use fqns, maybe... like lia->set('lia:server.cache.dir) then when lia:server.cache is added to $lia->addons() i dump all values from that fqn onto the cache addon. I was previously (in v0.5) doing this with references to $lia->props, which sucked. I could use $lia->addAddon() method (would require a change to addon class), then do this propagation during addAddon().

Notes

  • default name for server package is lia:server, and i really don't like that.
  • need to review & clean up some code & docs
  • i WILL need global properties on the liaison object itself.
  • i'm having issues with set() calls that didn't specify package, or didn't specify namespace, or were called BEFORE the addon is instantiated.

Apr 26, 2022

New Style:

  • Addons go in packages AND liaison
  • Packages go in liaison

Package no longer extends from Addon. I removed some things from Lia, Package, and Addon & made it all much simpler. I made addons require fqn, though name is still optional. I made $lia->get() & set() only work on addons, working both with fqn addon key & non-fqn addon keys

I also just ... removed some things that seemed complicated? idr what exactly.

  • removed $name from addon constructor, since it doesn't really make sense to make addon names configurable. (all addon usefulness is INSIDE the addon)

Updated Server package to remove complexities, ... commented out code that sets config.json to $package->props[] & i'll need to figure out what to do with those configs.

I updated types of $fqn and $name on server package & addons to string. All but 5 tests are failing now. I suspect the requirement of having an actual package instance will be a big cause of this.

In theory, many addons should be useable on their own. Like I don't think cache or view actually requires any features of package in order to function. But maybe they do? Idunno. I might remove the requirement that package be non-nullable.

I just made package (& lia) optional in addon. but the cache fails because it sets lia->methods ... so idunno what to do about that.

It might be nice to have separate initialization methods to setup liaison (that is separate from the constructor). Same goes for package? idk. That's a future problem.

Apr 21, 2022

There are some features I just want to remove from Liaison or otherwise significantly change. It's nice that $lia->get() & $lia->set() can reach into packages & into addons. It's also a waste of resources, generally speaking.

I used to have this really ... anything goes mindset about liaison, which just makes it hard to ... have the things I want go.

So we have:

  • Liaison/Lia: Central piece which gives you access to methods, properties, addons, and packages
  • Package: Initializes & holds a group of addons
  • Addon: actually does something

In Liaison:

  • addons
  • packages
  • props???
    • ... idk ...

Old Features

  • set (& get)
    • KEEP? set('package.addon.property', value)
    • DELETE set('array_key.property', value)
    • KEEP set('addon.property', value)
  • DELETE $lia->props['key']['subkey'] ...
  • DELETE addon is a lia instance
  • DELETE package is a lia instance
  • calling methods
    • DELETE $addon->global_method()
    • KEEP $lia->global_method()
    • DELETE $this->methods[key] = ... (on an addon) ... change to $this->lia->methods[key]

I'm having inconsistency issues ...

  • lia:package.addon.prop = value
  • package.addon.prop = value
  • addon.prop = value
  • package.prop = value

I'm gonna just have to drop some of the features.

I think the biggest issue I'm going to have with this refactor is figuring out which tests are better for deleting. I think many of my tests will just need to be deleted because they're testing internal functionality that was never truly needed.

I COULD create some wrappers & stuff to make the current api continue working exactly as it is.

I'm going ot have issue copying config array to its addons, possibly though.

TODO (v0.6)

  • review this 600+ line file & relegate most stuff into a history.md file or just delete it. Really, most of it need not be read, such as the day-to-day updates that are written lengthily.
  • benchmark diff between an object calling another object implementing interface vs an object calling a method in an array through __call() ... if the interfaced approach is significantly faster, then perhaps Liaison is just a complete bust & i should abandon it (though i'm not likely going to)

This new version

v0.5 is really slow compared to v0.3. On one of my sites, testing 11 pages went from about 350ms to about 550ms on my localhost. That's ... terrible. 550 / 11 = 50ms per request from 350 / 11 = ~34.8ms. 50ms server response time is awful. I'd like it to be less than 20ms, esepecially with all the overhead of https + script & css files + being on shared hosting.

There is also some quality-of-life stuff i'd like to do

Performance

I believe the main reasons for this slowness are:

  1. by-reference properties
  2. multi-dim properties array
  3. package & addon are subclasses of lia (and the implementation surrounding this is unnecessarily complex)

My solutions are to:

  • rewrite get() to retrieve an addon & then return a property of the addon
    • maybe return this by-reference??
  • rewrite set() to retrieve an addon & then set the property to the addon
  • rewrite append() basically same way as set()
  • I don't know what to do about default()
    • i COULD still have a props array that just stores namespace:dot.property=>value instead of using nested arrays with by-reference
  • rewrite dump() to retrieve all public properties from all addons manually & ... well dump doesn't need to be performant at all bc it is very limited debugging purposes
  • remove copy() method

Polish / Ease of use

  • setup() method: I might add a separate setup() method for addons & packages that is called after the constructor so that configuration can be done after instantiation, before initializing everything
  • ... idk ... there's other things.

Other Notes

  • extending from Addon should never be required to integrate a method or hook with liaison
  • The Addon class should have some nice convenience for setting up addons:
    • shared methods
    • add self to liaison->addons[]
    • shared properties
    • hooks
    • built-in hooks for like onCreated, onPackageReady, onServerReady
  • maybe hooks should be a part of liaison's core

What even is the purpose of liaison?

  1. Create packages for websites that are easily shared & integrated into other sites
  2. Extremely easy on-boarding
  3. Integrate with non-liaison websystems

Benchmarking

phptest -class Benchmark with 100,000 loops yields:

  • InterfaceObjectUsingCall 34.4930ms
    • almost all of the slowdown appears to be due to __call() routing rather than due to using callabls. WOW
  • InterfaceObject 12.4068ms
  • ArrayMethods 36.6769ms
  • ArrayMethodsWithFunctions 32.2399ms
  • InstantiateInterfaceObject 8.9591ms
  • InstantiateArrayMethods 18.4569ms
  • InstantiateArrayMethodsWithFunctions 22.5091ms

Takeway: The approach to code sharing used in Liaison is MORE THAN twice as slow as using the standard approach that uses objects and interfaces ... it's actually THREE TIMES FASTER calling the dependency object directly than it is to use __call() to call the dependency object.

The reason i use the methods array is ... it's just easier a lot of the time. I don't have to write a bunch of interfaces or implement a class. I can just write a method & use that directly. It's also really easy to switch out one method for another. This makes liaison super customizable.

An additional problem with this is: interfaces make it really easy to properly document what methods do & how they are used. This array-methods approach does NOT make it easy to communicate what the method must do.

Evaluation of what it will take to make these changes

To clarify, there are TWO stages of changes. The first is changing the internal implementation of sharing. The second is to add polish. Here, i am evaluating the internal implementation changes. The polish would come after & I am not evaluating that right now.

  • re-implement addon base class (1-2 hours?)
  • delete some tests that explicitly test current liaison features (30 minutes)
  • ideally these internal changes will not cause any breakage in tests
  • re-implement liaison get/set (30-60 minutes)
  • constructor changes in every single addon (1-3 hours)
  • new package base class? & update constructors (1-2 hours)
  • new tests for new internal implementation (1-2 hours)
  • In all my foss libs and websites using liaison, create a new branch & bump the liaison version (2-3 hours)
    • maybe write a simple bin script that creates a new branch & bumps the version so i can do it automatically, but that would probably be a waste of my time
  • Unforseen stuff (4-6 hours)
    • potential changes in liaison libraries

old notes (from v0.5)

polish / niceties / fixes

  • make $package available in a view (being the same package that the view came from)
  • make new packages not override lia:server.package
  • having addon->name not match addon->fqn causes it not to be added to liasion...
  • Redirect addon directly calls header() instead of using a hook to modify the response before headers are sent by the server addon.
  • Ex: POST /user/register redirects to /user/register/ which breaks the POST. Maybe allow POST to work with or without trailing slash?

Apr 8, 2022

  • added dump() method
  • server package now adds itself to fqn_addons: This is likely to cause problems as is uses $this->fqn in the constructor. So if you instantiate two server packages, the 2nd server package will overwrite ... this is bad. But it's how it is for now & it does not cause a breaking change.
  • add a default cache dir so it works out of the box
  • recently added MinimalServer test for documentation purposes

TODO

  • add no-cache header to most responses
  • 3 tests are failing ... fix them!
  • my immediate cache invalidation is not working (at least on reedybear.com), so i have to manually delete the cache dir for every css change. fix this!
  • Write docs for:
    • create a new app
    • add a css or js file to the request
    • .... stuff

Versions (Dec 8, 2021)

v0.5 (with minor changes) will likely become an official beta. Then after some usage, it'll become v1.0. Probably by June, 2022, but who knows?

  • v0.5: Uses Addons instead of Compos. Much more direct access to methods, properties & major overhaul. Internally uses a lot of memory pointers. All addons ARE liaison instances. Packages are too. Lots of other changes. Dec 8, 2021: Under development, nearly ready
  • v0.4: abandoned ... WAS intended to do what v0.5 is while providing backward-compatability to v0.3 setups ...
  • v0.3: abandoned. Uses Compos (Components). A major version ... used ->api('namespace:key', $arg1, $arg2) all over the place ... abandoned dec 8, 2021

Idea (March 25, 2022)

Liaison is confusing because there is so much magic. Liaison holds packages. Packages hold addons. Addons communicate with each other through liaison & through their packages.

There are also settings/configs that can be set through liaison or on an addon directly.

There are global methods which can be directly called on liaison.

There are other things that ought to be inspectable, too - views, routes, resources, idk what else

So the idea is to add debugging features. First, a view (and an associated public route) to display inspection information for all of these.

An integration with code scrawl to inspect an addon class would be really cool. /liaison/debug/class/?name=whatever

And then maybe having certain inspection views display on errors ... like if there's no routes, that could show the inspection for routes.

Exceptions could also be handled nicer. I mean ... every exception could dump liaison debug info, if i want! (seems overkill)

There's also some really weird things, like ... since you don't instantiate addons directly, certain configs have to be instantiated before initializing their package. Idk if that's fixable or properly inspectable ...

I REALLY need to document how to write a custom router & return a route list (need an easy way to make routes for this purpose)

I also need a way to get PACKAGES that don't have any addons ...

PROBLEM (march 23, 2022)

I have to set server.cache.dir BEFORE initializing the main server package:

$lia->set('server.cache.dir', $server_dir.'/cache/');
$server = new \Lia\Package\Server($lia, 'server', $server_dir);

Otherwise, compiled resource files do not deliver!!!

Jan 25, 2022: New stuff

  • added code/class/Router/FastFileRouter.php with an extremely simple and fast routing mechanism for files in a directory
  • added code/file/mime_types/*.txt where there's a file for every extension & the file only contains the mimetype
  • FastRoute was added to the test server deliver script

... this could be used in place of the current (awful) StaticFile implementation

Known Issues (jan 4, 2022)

  • a dynamic portion of a url cannot contain - by default. this is BAD ... slugs contain hyphens, silly

Problems (jan 16)

  • must explicitly add views for a package like: ... not true ... the dir just needs to be /view/ & this is not clear
    • $view = $lia->addon('lia:server.view'); $view->addDir($site->dir.'/views/', $site);
  • $lia->set('server.cache.dir', $dir) must be called BEFORE the main server package is setup
  • use \Lia\Package\Server for most setups ... but it's not obvious

Jan 15, 2022

  • WARNING: when cache dir is not set, now an exception is thrown, instead of just returning false ... somewhere in the cache addon class ... several tests are failing ... I should address this. I might have to roll back. idk
  • i attempted toa dd global args to view, but I commented out the code & just didn't finish it.

Dec 17, 2021

  • review dec 16 TODOs & CONSIDERs
  • add header('Cache: no-cache') to all requests that are NOT static-file requests

Dec 16, 2021

  • DONE view/theme priority (new views always overwrite null-namespace)

  • DONE for view/theme.php also add view/theme/*.css & theme/*.js

  • DONE make $lia->server access $lia->addons['server'] & $lia->server->cache would access $lia->addons['server']->addons['cache']

  • DONE: add config.json file to app dir for server-package

    • DONE: Test it
  • DONE: Add fqn for addons, like lia:server.cache that is accessible from ALL liaison objects by that string. Maybe $lia->addon('lia:sever.cache')...

  • DONE: fix confusion around addon->addon_name & addon->name;

    • removed addon_name and only using addon->name now
  • DONE??: fix _lia scoping. The lia object passed to views is NOT the root object.

    • i couldn't reproduce the bug with my test. The bug came from taeluf.com
  • TODO: Add a logging feature! Just to log messages to a file.

  • TODO?? debug feature to help inspect addons, properties, and more... ?

  • TODO: $this->depend('lia:server.cache') feature (or $this->depend(\Lia\Addon\Cache)?)

  • TODO: review disabled tests & get them passing or remove them

  • TODO: Add robust setup-events for packages/addons

    • all addons within package are loaded
    • all other packages are loaded
    • everything is ready
  • TODO: Add global objects ... example: mdblog->blog would fit well at lia->blog

  • CONSIDER: Does server package REALLY need the default server package to be added in order to function at all?

  • CONSIDER: Should fqn_addons[ns:package.addon-name] also have null-ns, like views do? (leaning yes, but let's wait til I need it)

  • CONSIDER: Removing name from constructor for addons, package, lia. Subclasses generally don't need them ... but then again the server package needs it.

  • CONSIDER: Should package->name be overwritten by config.json[name]?

Dec 15, 2021

  • add onPackageReady() to addons, called by \Lia\Package->ready(), which is NOT called during the package constructor, but IS called during the Server package constructor
  • I was having issues with _lia scoping ... (maybe see mdblog? idr)
  • I want addons to be accessible via __get() & __set()
  • Server package should default it's name to server
  • Should hook become part of liaison proper? (i lean yes) also maybe error & cache? No, cache is thicc. Error logging, maybe? Maybe

Dec 14, 2021

  • TODO: Add a logging feature! Just to log messages to a file.

Dec 10, 2021, end of day

  • I was last working in MdBlog addon ... go look at that
  • Review Server PACKAGE and ensure some simplicity
    • should the server ADDON be a package? (leaning no)
  • need a way to have addons always be stored in their namespace-form, like lia:server.hook or lia:server.seo, etc... So I can have a guaranteed way to reference an addon. Probably just use those simple string keys. Could use an addon() function to retrieve them from current data structuer?
  • i'm not in love with how I set up addons ... idunno ...
  • name/addon_name is still a mess & just needs some thinking & review & updating

Dec 7, 2021

Main:

  • I was working on the server integration test. It is failing, only because the test needs updated. Parts of getting this working:
    • Wrote code/Server.php (server PACKAGE)
    • add addDir() to view addon
    • add dir_to_patterns() to router addon
    • messed with the addon nesting in package (maybe in lia too?)
    • fixed the built in theme view & error/header view
    • wrote test/Server/deliver.php
    • wrote test/run/Server (needs corrected)

Issues:

  • Package->name & package->addon_name & Package::__construct($lia, $package_name) ... confusion, uncertainty, duplication ... the nesting of ->addons is ... idk.
  • need unit test in router for dir-to-patterns & it's helper function(s)
  • PackageAddonIntegration test is failing. Several Liaison tests are still disabled.
  • the ExceptionCatcher is ... bad
    • in set_error_handler, i need to call ExceptionCatcher::throw(new ErrorException(...)), instead of re-throwing there
  • need to test view conflicts

Dec 2, 2021

  • Error header message test is passing because I added an explicit print to the test theme view. I don't know if this is how I want it to work. I need to review this with a fresh mind. Also, the display is checking $this->lia->addons['error']->headerMessage which ... is probably the wrong way to do things.

Dec 1, 2021

  • Error Addon: Started on it ... one test passing ... needs some TLC to get things sorted

TODO

  • Error addon:
    • Review & Update
  • Server addon:
    • test the hooks
    • test deliver() method?
    • general refactor so it just ... makes more sense
  • implement Resource addon tests DeliverJs & DeliverCss after Server is re-done
  • Check Server integration for:
    • Resources
    • ResourceSorter
    • Autoload
  • completely get rid of the response & request objects (& maybe Route object)
    • refactor router
    • refactor server
    • refactor resources

Nov 30, 2021

  • Resources addon changes:
    • Remove forceRecompile config (only using useCache)
    • recompileJsAfter & css is removed (these weren't implemented anyway!)
    • refactor ??

Nov 29, 2021

  • refactor seo addon
  • refactor (slightly) & clean up (massively) and document router addon. Add new (simpler) router addon tests. Separate old router tests into RouterOther tests (bc they are sloppy & I don't want to refactor them).

Nov 26, 2021

I feel a bit uneasy about the work I did today. Everything is working - tests are passing ... but I just feel like some things are undone. I think I want to review, at least, the router addon & look for any api() calls, at the very least.

I also may want to add additional tests - like testing that seo & router work with package & work with liaison -> package -> addons setup

  • Seo Addon
    • converted to addon
    • tests passing (fixed one old one)
    • no exception catcher
  • Router Addon
    • all tests passing (including ones that I had failing previously)
    • no exception catcher ...
    • converted to addon
  • Cache Addon
    • added an exception catcher case

Nov 24, 2021

  • View addon
    • all tests passing
    • added to exception catcher
    • docs written (docblocks)

Nov 23, 2021

NEXT I have some confusion about how to handle scanning?? Or something. Idk. It doesn't matter.

Just working through additional component's one at a time, turning them into addons. Let's keep most changes simple so I can get through it all asap & start using the new system in prod & adding features

DONE

  • add hook handling to exception catcher
  • add scan() method to liaison
  • add prefix integration to hook
  • add hook to package test
  • move GlobalParam to old folder (likely will not use)
  • add prefix/scan test class
  • add OldTests test class

Nov 18, 2021

5:00pm: package integration test written. cache & autoloader both working nicely with it. Need to work on next addon, then update package integration to include it.

Latest: I'm working on prefixes inside the hook addon class code/addon/Hook.php

  • IDEA: Scanner should be an addon & depends feature will make it so easy to use

    • NO. Scanner is fundamental to Liaison.
  • LATER: create a liaison test that creates multiple packages so that they work together.

  • LATER: Add prefix scanning for Hook after implementing it.

  • LATER: (i disabled the tests) The cache class & tests are all passing. AddMethods & AddDotMethods are still failing on the Liaison test. I think I removed nesting of methods on Monday?? This will need addressed, but I want to focus on the addons and making sure new Lia provides what they need, instead of getting caught up on what Lia "should" do. So I'll revisit this decision later after I've written more tests & used Lia more

  • LATER: (i disabled the tests) Package tests AddAddonsPackageAddons & AddAddonsPackage are failing because I used to have $lia->addons['ns']['package'] = $package, but I removed it ... because it caused a test to fail & it just didn't FEEL right having it there. But now I see / remember why it was. I don't know what I prefer. Maybe add a packages property to Lia to hold packages. Maybe keep the package in the addons list. I don't know. I'll write more tests, see what feels right in practice & re-visit this decision. I will not fix the tests until then.

  • NOW? I want to start a new Package class who's entire job is to setup addons & invoke them. I want a base package class for use on any Liaison setup, then a Server package that ties together all the built-in components into a web-server (which is kind of what the old Package does).

  • DONE I want to rename configs to props because ... I think it is more intuitive

Nov 15th, 2021

Success! I re-wrote the autoloader & autoloader tests. I started rewriting cache & its tests. Cache is successfull for everything I've re-implemented. Notes:

  • main cache: set() get(), write(), read()
    • this is a new feature to set key=>value pairs
    • it uses the config stack that exists from liaison & essentially writes $lia->configs['cache'] to disk as php
  • classic file cache: I have not touched it yet & its tests are currently failing

BIG new thing

I have properties declared on both Autoloader & Cache, which each of them use directly. In their constructors, each addon is setting those properties to $addon->prop_name = &$this->configs['prop_name']. I REALLY like this so far, bc it makes a tree of all the values that are set & need to be set. I have no idea about the performance ... But I imagine it's not too bad.

Next

  • finish updating the cache tests & cache class. I do want to keep the ability to cache files. The code needs re-factored & other than that ... I think it's basically fine. I want a cache_file method on liaison as well.

How to think about Liaison

Liaison is a singular object that holds all shared information & methods & addons Addons are objects that provide specific functionality. Packages are objects that set up addons on Liaison

Does Liaison officially recognize packages? I think yes ... I think so Do Addons officially recognize packages? Not normally ... I don't think so Do Packages officially recognize addons? Yes, I think so. Kinda have to

So the entire thing exists to server addons. Addons need to be able to communicate with other addons. Packages helps set things up. Liaison does the actual communication. User-land code ALSO needs to be able to communicate with addons.

Repeat:

  • Addons provide functionality and need to communicate with (and depend upon) other addons
  • Packages provide utility that makes it easier to set up multiple addons
  • Liaison is the object which provides communication featurse between addons
  • User-land code uses Packages to setup addons & uses Liaison to access those addons.

Note:

  • Addons are simply a way of packaging features together. Ex: Cache addon has a suite of functions to handle caching. Alternative Ex: Several cache functions exist & user-land code just registers them to Liaison, no addon needed. This is NOT the intent, but it's how it ought to work.

So, how do I provide global access to methods & properties?

  • $lia->method_name(); calls whatever method was registered
  • $lia->methods[method_name]();
  • $lia->prop_name gets whatever value is registered at $lia->props[prop_name]
  • $lia->props[prop_name]

How do I access packages & addons?

  • $lia->addons['name'] gets whatever addon is registered
  • $lia->packages['name'] gets whatever package is registered
  • $lia->packages['name']->addons['name'] gets an addon from a package

How do I get namespace access to methods and properties?

  • $lia->get('ns.name.prop') gets $lia->packages['ns']->addons['name']->prop where ->prop is a real property on the addon?

How does an Autoloader addon access a cache addon?

  • $this->lia->cache() to call the global cache method
  • $this->lia->call('pkg.addon.cache') to call a specific addon's cache method
  • $this->lia->packages['pkg']->addons['addon']->cache() to call the specific addon's cache method
  • shorthand: $this->lia->addons['cache']->cache() to get the global cache object & call its method

Features

Liaison object has:

  • global methods (set by anyone, normally by addons)
  • global objects (set by anyone, normally by addons or packages)
  • global properties (set by anyone, normally by addons or packages)
  • namespaced methods
  • namespaced objects
  • namespaced properties

So if I have an autoloader class that needs configs & cache, it could access those via: $lia->configs->methods(); or by $lia->config_method where the config objects' method has been explicitly set to liaison

What are my actual needs?

  • one object from which I can access:
    • all packages
    • all addons
    • all root-set properties
    • all root-set methods
  • Does it need to support:
    • $lia->get('addon.propname')?
    • $lia->call('addon.methodname')? Is this good enough:
    • $lia->addons['name']->method();
    • $lia->packages['name']->addons['name']->method();
    • $lia->addons['name']->property
    • $lia->packages['name']->addons['name']->property Can i THEN add convenience methods?
    • $lia->set('pkg.addn.property', 'value');
      • calls $lia->packages['pkg']->addons['addn']->property = value; What am i doing currently?
    • $lia->set('pkg.addn.property', 'value');
      • $lia->configs['pkg]['addn']['property'] = $value;
      • $package->configs === $lia->configs['package']
      • $lia->addons['pkg']->addons['addn']->property = $value;
    • $lia->call('pkg.addn.method',$arg1,$arg2)
      • $lia->methods['pkg']['addn']'method'
      • (would be) $lia->addons['pkg']->addons['addn']->method($arg1,$arg2);

v0.5

v0.5 is like v0.4 except I'm abandoning the entire backwar-compatability idea. I'm going to re-code tests & addons using the new setup instead of trying to make it all fit seamlessly with the old setup.

So v0.4 branch is dead. It will not be developed further, unless I change my mind after working on v0.5 a bit.

Next:

Think about my approach to this refactor. I've spent several hours and a lot of energy making everything backward compatible. It's kind of a nightmare. So I'm not entirely sure how I want to go about things. I may go away from the "constant BC" route & just dive full-in. I really don't want everything to break at once, because fixing THAT will be a nightmare. I WANT to go through pieces of this library one at a time, switching things out as I need. Fuck. I might rewrite some tests too. Idunno. Its the end of the day and it all sounds terrible lol. Need to re-asses with a fresh mind.

I'm also having a hard time understanding how the backward compatability actually benefits me. I think it's so I can switch a site to the newest liaison & have it keep working ... but as long as it's using the BC liaison object and the old components ... I just have to keep both around! Either way, I'm glad the original Liaison object is done for. So freaking glad

  • meh ... "Function name must be a string": Running into this issue because there is a null value at the location a method should exist. Why/how does that null value happen? Can I catch the error with my fancy exception handler?
  • Start turning the components into Addons, one at a time. They will subclass liaison. It will be interesting.

Notes:

  • package is a subclass of addon & addon a subclass of Liaison, but they have significantly different needs, so the constructors don't call parent::constructors. Instead they call $this->copy() which should be a bit more consistent in how it operates between liaison subclasses.
    • perhaps package should not be a subclass of addon?

State

  • DONE setup by-ref properties
  • DONE add methods
  • DONE add api()
  • DONE add get/set
  • DONE test all of the above
  • DONE add addons management
  • DONE add ->_lia for root liaison object & ->lia for parent liaison object
  • DONE start ExceptionCatcher
  • DONE get package tests passing (see Other/OneOffTests.php)
  • DONE Get the Scanner tests passing
    • There are two scanner tests that are NOT passing. Focus on those??
    • Setup the new scanner trait & use it on Lia (see notes below)
  • DONE Start passing individual component tests (one at a time. take it easy. take it slow.)

Re-testing with LiaBC class

  • DONE some tests are stalling due to re-addition of lia:config.default api (see LiaBC where this api is added to liaison)
    • FIXED OneOffTests->ComponentsAndRequestLifecycle
      • done There is an issue with cacheFile() getting a null dir ... but only for the css resources, not the js
      • done spawns from getHeadHtml() ... it is a MESSSSSS
    • FIXED Redirect->GoTo
    • FIXED Error->ErrorHeader
    • FIXED Error->ErrorPage
  • FIXED OneOffTests kind of seem like a nightmare ... uhh idunno
  • WILL NOT FIX Exceptions test are failing & I don't think I'm going to fix it.
  • DONE all the component tests are passing, except for the ones that use a non-bare liaison ... so the only issue now is getting package to work with LiaBC.
  • DONE Other/LiaisonApi.php works. I disabled a couple mediator tests because I don't use mediators ANYWHERE, so it's not worth fixing
  • DONE Other/OneOffTests.php: These are significant integration tets, so these need to go later.
  • DONE ScannerTrait (test/CompoTrait/Scanner.php): The old implementation is SUCH a mess. The whole thing is terrible. I think it may be worth rewriting the scanner from scratch with almost no features. It should be VERY simple. The old version uses $lia->api(...) to call the prefix handler method... I want to have methods that accept callables for particular purposes (like registering an event). When a prefixed-method is found, it should be passed to that setup-function (event registration, etc).
    • I made a new scanner trait with minimal modification to sustain backward compatability.

ScannerTrait: New Design

What does it need to do?

  • have a list of prefixes
  • build an array of methods that have those prefixes
  • pass the methods ([$this, 'method']) to the functions responsible for setting up those prefixes

How do I do it?

  • public array $prefixes = ['on'=>[$event, 'prefix_setup']];
  • get_methods($this) ... substr($method_name,0,$prefix_len)==$prefix ... $methods = [$prefix=>[list_of_methods_on_this_object]]
  • foreach $methods as $prefix=>$method_name: $this->prefixes[$prefix]([$this, $method_name])
    • usually $this->prefixes will refer to the root liaison's array of prefixes (because of $lia->copy()). BUT any lia object (addons/packages, etc) could have it's own array of prefixes that is not referencing the root.

v0.4 Goals

Approaches to Liaising

  • by-reference properties
  • via method calls on the Addon class that remap to Liaison methods

Step 1: Prototyping by-ref

I want to prototype a new system for having addons call liaison methods. I'm tired of $liaison->whatever... & want this to be more streamlined. I want to try copying liaison properties by reference to make it easier. Example:

$lia = new \Liaison();
$package = new \Lia\Package($lia);
    //$package->__construct() will call
        // $this->copy($lia)
            // $this->configs = &$lia->configs;
        // $this->configs['namespace'] = []; 
        // $this->configs = &$this->configs['namespace'];
$addon = new \Lia\Addon($package);
    // $addon->__construct() will do:
        // $this->copy($package) (where packge is a liaison instance)
            // $this->configs = &$package->configs;
        // $this->configs['addon_name'] = [];
        $this->configs = &$this->configs['addon_name'];

So rather than modifying anything, I think I should start with a new class & just try out these basics.

Major Changes Overview

  • Change "compo" to "addon"
  • Add a system for addons to call liaison via $this->liaison_method()
    • MAYBE make addons into instances of liaison & use by-ref properties
  • turn Package into an addon
  • MAYBE Move 'event's onto Liaison, but call them hooks.
  • MAYBE add dependency hooks onto Liaison proper
  • MAYBE add scanning onto Liaison proper

Extended notes / thoughts

Rename compo to addon. Add methods to make them work more friendly with liaison. Like get, set, add method, add prefix. Basically remap all the core liaison functions to auto-include ... Namespace & name.

Turn package into an addon. It will all be much easier to understand then. Then do i add addons to liaison? Does liaison auto-setup an addon? No. The addon sets itself up. This means different types of addons csn be made. Most of mine will be subclasses.

So then package is an addon & basically it sets up dependent addons for certain sub-directories of a dir given to it.

Liaison should have it's own property for addons. A key/val array. & a method to add an addon. The main addon class will call that in its constructor.

Package is an addon but it will contain other addons & provide features for easily setting up addons. Package will probably also host it's own array of addons. But then do addons IN the package have to also call the package's method?

Maybe liaison can have hooks on its get/set methods, then package can just copy when addons are added. This is a performance hit through constant branch checking. I think it would be far more efficient to use a liaison subclass that routes all the methods liaison has, allowing the package to be a liaison instance & the addons it initializes to use it (the package addon) as liaison.

So an addon then would do: $lia->addAddon($this, get_class($this)); which invokes $package's $lia->addAddon($addon, $namespace.':'.$addon_name); call which invokes actual $liaison & does $liaison->addons[$addon_name] = $addon; this all.happens during the addon's constructor. In this chain, package can also maintain it's own addon array.

Should events go on liaison? No i don't think so. Just prefixes, methods, and apis.

Then in my addon's constructor, it will do $this->addMethod('ownMethodName'). $addon->addMethod($methodNameForLiaison, $ownMethodName=(defaults to arg1)), which will call package's addMethod() which roures directly to liaison.

Shooot. What if they all access their own properties directly without any redirects by referencing memory addresses?

So package would do $lia->configs['namespace'] = [];. Then do this.configs = &lia.configs.'namespace'; so the package's configs directly reference the variable held in liaison. Do the same for addons?

Maybe add a copy method to liaison that takes in a liaison object & does the by-ref thing. Package maybe would override copy, call the parent, then do it's own setup. Or just call copy, then do its set up.

Now that package is setup by-ref, it will construct the addons & pass itself to the addon. So when the addon does this.methods[name] = this, it's setting it to package's ref of methods prop. (Errr ... Maybe make methods completely flat, but have an api array that is nested.

So then ... Views. Do i just assert that onto liaison? Like lia.views[name] will work? Perhaps i put it in the namespaced configs & in the flat.

Make a PackageAddon class that responds to hooks in the package. Hooks could be a very simple trait ... Idunno. Maybe hooks/events belong on liaison. ...

Server requires hooks. Some addons can't do their setup until dependent addons have already been setup, which requires them to hook on the dependents or ... Or on the package. I like hooking on the dependents. That means each depended upon addon would have to execute the hook when it was done setting up.

It would make sense for certain addons to broadcast when they are ready. The server addon might respond to the public dir addon broadcasting that it has set up a public dir. And that it's about to. Maybe the server addon could stop it from scanning a public dir if a cache of the routes is recent enough. It makes sense for the server to be responsible for state management while a separate addon is responsibke for scanning & setting up a public directory. Tbh, package is probably where public dir setup belongs. I could add it to router though. Idunno. I feel like routers responsibility is already complex enough that i don't want to add things to it.

Latest

  • updated Route object to is_string($target) && is_file($target), so isFile() doesn't give error when it's non-string target
  • added basic url normalization to the server component. it needs refactored.

Current

  • attempted to add - & : to valid chars to separate dynamic values in a url. But a couple tests are failing, Idk why & it needs to be troubleshot
  • add https://github.com/matthiasmullie/minify as optional dependency
    • Tried cerdic/css-tidy and had problems

v0.3-candidate Plans

  • default() should be able to receive an array to set multiple defaults at once
  • Delete all exceptions except for my base exception

Get/set

Can I do a readonly feature? I may be able to condense some of my simple components into a single one since there's no more data structure management.

  • add get & set to Liaison Proper, doing away with Config component
  • Use $lia->set() & get to store packages, compos, and basically anything that's currently being managed by a single component
    • Router still needs its own internal data structure
  • add arget() and arset() for getting/setting to an array.
    • arset('namespace:some.key', 'keyInTheArray', 'valueForKeyInTheArray')
    • arget('namespace:some.key', 'keyInTheArray') returns valueForKeyInTheArray
    • get(namespace:some.key) returns the array containing keyInTheArray => valueForKeyInTheArray

Events

Since Liaison's goal is to, well, Liaise... I think it should do events, too. It can already to 1-to-1 method calls & I'm adding 1-to-1 property setting / getting. So it would make sense to be able to emit an event. Here's the implementation idea:

  • $lia->emit('namespace:Event.Name', arg1, arg2, arg3)
    • Internally, it creates new Event($lia, arg1, arg2, arg3). then $event->emit()
      • & this way, the event gets listeners from Liaison, then goes through all of them. & each events listener gets an instance of the event object, plus all the passed args. Any values can be set directly to the event object
    • You can also directly do new Event()... & emit it yourself, without the helper method

Code Work

Next

  • add error reporting when a package directory does not exist.
  • (current) Error Component
    • Working on error_goto.
      • Need an Error Route (maybe as a public file, idk. Might make it configurable)
      • Need to setup test. I started the test, but can't really wrap my head around how to do it.
  • Improve error views (header & page)
  • Middleware & Routing
    • Add a 'handleRequest()' method that allows any component to handle a request
    • How does the web-developer decide who's handling requests? The middleware approach might be best. addMiddleWare() & removeMiddleWare() could be used by the Server component... It might use a couple if statements to determine which middlewares to add. But the flow would be the same regardless.
  • Caching:
    • Routes from public files (Router)
    • Classmap of a package (autoloader)
  • Cleaner Route interface/object
  • Cleaner View interface/object

Latest

  • Refactored Scanner to have only two methods & be more efficient. Updated all tests so they're now passing
  • Router component can have additional routers added to it for custom routing.
  • Add append method to Config component
  • Renamed package setup functions to setup_the_thing & added setup() function to move setup out of the constructor.
  • Add namespaces to the view component
  • Modify package to use namespace when calling view component
  • Remove 'lia.packages'. This was a mistype & should have been 'lia.package' all along
  • Convert package list to use namespace instead of name
  • Separate SEO methods/api from Resource compo
  • namespaced apis. converted api('lia.action', 'handler',...$args) to api('lia:action.handler', ...$args)
  • namespaced configs with consistent naming like lia:componame.configName.
  • Ensure there is a default() call for every config

Documentation work

Next

  • Review property docblocks in Liaison class. They don't match with the api namespaces refactor.
  • Continue Docblocking as marked below (in class/Objects/)
  • Don't forget the Objects/ViewCallable.php: Need to review this when I'm doing the view component
  • Write Markdown documentation AFTER docblocking is done
    • Write examples as tests & import

Latest

  • Docblocked everything except compos
  • Updated docblocks on Liaison methods to match api namespace refactor

Things that need to be in markdown documentation

  • Specify your package's namespace in config.json. This is separate from name.
  • List of available configs
  • Full api reference for code/class and code/core
  • Featured API Reference? For only listing featured methods & classes
  • TODO reference file (grouped by file name in a single markdown file)
  • All events & the paramaters they pass

Docblocking status

  • Liaison.php
    • Mostly good
    • api methods (addApi, addApiMethod, etc...) are not well documented. Since I want to remove $handler, I'll probably wait on that.
    • Some things are a little under-documented, but the method-signature and the short function bodies... makes it not that big a deal
    • Not @tagged very well
  • Compo.php
    • Well documented
    • @featured & @tag pretty well
  • Package.php
    • Pretty well documented
    • Not sure about my @tags. There are @tag setup & @featured. I think there could be more organization there.
  • CompoTrait/
    • Scanner.php
      • Docs pretty good.
      • trait needs some rewrite (thus docs will, too)
      • Only the class has @tag internals, component... Idk.
  • Exception/
    • Base.php
      • Wrote @todos.
      • Nothing really needs documented here. You just create an exception
    • *.php
      • I plan to delete the other exceptions & improve BaseException with extensibility. So... No. Not documenting these
  • LiaisonInterface/
    • LifeCycler.php
      • @deprecated & @todo delete, because its not in use far as I could grep
    • PackageLifeCycle.php
      • Documented nicely. But documentation is nearly identical to documentaiton on \Lia\Compo for these methods. Perhaps I can remove identical docs from compo.
  • Objects/
    • IView.php
      • Documented, some @todos
    • View.php
      • Well Documented, some @todos
    • Request.php
      • Well Documented, very basic class
    • Response.php
      • Reasonably well documented. Some @todos
    • Route.php
      • Adequately documented... bad class.
      • No tags... I might want tags
    • ViewCallable.php
      • TODO <- There is no structure. It's all setup by the View component, I think. So its not well setup for documenting
  • Utility/
    • ClassFinder.php
      • Barely documented, because it comes from my Utility repo. So its fine.
    • DotNotation.php
      • documented well enough. No tags
    • FancyClosure.php
      • documented well enough. No tags
    • Files.php
      • Documented well enough. No tags
    • StaticFile.php
      • Documented well enough. No tags
  • ../core/
    • Has not been started

Future Work

View Getters

The base View Component will implement a view() method. This view() method will loop over all active view_getters & call getView() on each of them. The returned view will be an instantiated class with a __toString() method. Exactly one view getter must return a view. Each view getter MUST support namespaced view names.

  • Phad templates & Lia views will both be accessible through $lia->view().
  • Lia\Compo\View will
    • implement a view getter
    • have an add_view_dir($dir, $namespace, $args) function that adds a dir for PSR-4 style view-name loading
    • have an add_view_callable($callable, $fullyQualifiedViewName, $args) function to explicitly add a callable as a view
    • have an add_view($dir, $fullyQualifiedViewName, $args) fucntion to explicitly add a single view
      • this is already implemented
  • Phad\Compo will
    • implement a view getter
    • have an add_view_dir($dir, $namespace, $args) function for psr-4 style view loading
  • Some Future View Component will
    • implement a view getter
    • have an add_whatever function to add whatever.... to be later retrieved by the view getter method