<?php
class Lia {
/**
* Array of addons without namespaces, like 'router'
*/
public $addons = [];
/**
* array of addons with namespaces, like 'lia:server.router'
*/
public $fqn_addons = [];
/**
* array of global methods
*/
public $methods = [];
/** Array of packages identified by names like 'server' */
public $packages = [];
/** packages where key is fqn, like 'lia:server' */
public $fqn_packages = [];
/** key=>value properties */
public array $props = [];
/**
* Array where `key` is the prefix and `value` is a callable that is executed when a prefixed-method is found
* callables must accept following args:
* function($object, $method, $dot_name){}
* Where $dot_name has the prefix removed & each `_` replaced with `.`
*/
public $prefixes = [];
public function __construct(){
}
/**
* Scan object for prefixed methods & pass them to the prefix handler
*
* @param $prefix a method prefix to scan for like 'on'
* @param $object the object to scan for methods
*/
public function scan(string $prefix, object $object){
$methods = get_class_methods($object);
if (!isset($this->prefixes[$prefix])){
throw new \Exception("No handler for prefix '$prefix'.\n\n");
}
$handler = $this->prefixes[$prefix];
$len = strlen($prefix);
foreach ($methods as $m){
if (substr($m,0,$len)==$prefix){
$dot_name = strtolower(substr($m,$len));
$dot_name = str_replace('_','.',$dot_name);
$handler($object, $m, $dot_name);
}
}
}
public function __parse__($key,&$addon,&$prop){
$pos = strpos($key, ':');
if ($pos!==false){
$pos2 = strpos($key,'.');
$pos3 = strpos($key,'.', $pos2+1);
$addon_name = substr($key,0,$pos3);
$addon = $this->fqn_addons[$addon_name];
$prop = substr($key,$pos3+1);
return;
}
$pos2 = strpos($key,'.');
if ($pos2!==false){
$addon_name = substr($key,0,$pos2);
$addon = $this->addons[$addon_name]??null;
$prop = substr($key, $pos2+1);
return;
}
}
/**
* Set a key/value to an addon
*
* @param $key a key like 'lia:server.cache.dir`, `cache.dir`
* @param $value value to set to the property on the given addon
*/
public function set($key, $value){
$this->__parse__($key, $addon, $prop);
if ($addon==null){
$this->props[$key] = $value;
} else {
$addon->$prop = $value;
}
}
/**
* @param $value a value to append to an array property on the given addon
* @param $key a key like `lia:server.cache.dir` or `cache.dir`
*/
public function append($key,$value){
$this->__parse__($key, $addon, $prop);
$addon->$prop[] = $value;
}
/**
* @param $key a key like `lia:server.cache.dir` or `cache.dir`
*/
public function get($key){
$this->__parse__($key, $addon, $prop);
if ($addon==null){
return $this->props[$key];
} else {
return $addon->$prop; //= $value;
}
}
public function has($key){
$this->__parse__($key, $addon, $prop);
if ($addon==null){
return isset($this->props[$key]);
} else {
return isset($addon->$prop);
}
}
// public function default($key, $value){
// $conf = &$this->props;
// foreach (explode('.', $key) as $key){
// $conf = &$conf[$key];
// }
// if (empty($conf)){
// $conf = $value;
// }
// }
/**
* Add an addon by it's fully qualified name
* @param $addon, generally a `\Lia\Addon`, but can be any object
* @param $fqn the fully qualified name like `namespace:package.addon_name`
*/
public function addAddon(object $addon, string $fqn){
$this->fqn_addons[$fqn] = $addon;
$pos = strrpos($fqn, '.');
$name = substr($fqn, $pos+1);
$this->addons[$name] = $addon;
}
// public function addAddon($addon, $name){
// $this->addons[$name] = $addon;
// }
public function addMethod($name, $callable){
$methods = &$this->methods;
foreach (explode('.', $name) as $key){
$methods = &$methods[$key];
}
$methods = $callable;
}
public function has_method(string $method_name): bool {
return isset($this->methods[$method_name]);
}
// public function _api($name, ...$args){
// return $this->_lia->api($name,...$args);
// }
// public function api($name, ...$args){
// $methods = &$this->methods;
// foreach (explode('.', $name) as $key){
// $methods = &$methods[$key];
// }
//
// return $methods(...$args);
// }
/**
* @param $prefix a method prefix
* @param $method a callable that will handle any methods using the prefix
*/
public function addPrefix(string $prefix, callable $method){
// var_dump("AddPrefix: $prefix");
$this->prefixes[$prefix] = $method;
}
/**
* Get an addon by it's fully-qualified-name
* @param $fqn string like 'lia:server.router'
*/
public function addon($fqn): \Lia\Addon {
if (!isset($this->fqn_addons[$fqn])){
throw new \Lia\Exception(\Lia\Exception::ADDON_NOT_SET,$fqn,implode(", ", array_keys($this->fqn_addons)));
}
return $this->fqn_addons[$fqn];
}
/**
* Get a package by it's fully-qualified-name
* @param $fqn string like 'lia:server'
*/
public function package($fqn): \Lia\Package {
if (!isset($this->fqn_packages[$fqn])){
throw new \Lia\Exception(\Lia\Exception::PACKAGE_NOT_SET,$fqn,implode(", ", array_keys($this->fqn_packages)));
}
return $this->fqn_packages[$fqn];
}
public function __call($method, $args){
return $this->methods[$method](...$args);
}
/**
* get the named addon
* @param $addon the name of the addon
*/
public function __get($addon){
$a = $this->addons[$addon];
return $a;
}
/**
* get the named addon
* @param $name the addon name
* @param $addon the addon to set
*/
public function __set($name, $addon){
$this->addons[$name] = $addon;
}
public function dump_thing($thing){
$thing = $this->__map_array__($thing);
print_r($thing);
unset($thing);
}
/**
* Dump a bunch of info about liaison. Methods. Addons. Properties.
* @param $thing a variable to dump or null to dump liaison fully
*/
public function dump($thing=null){
$c = [$this, '__map_array__'];
if ($thing!=null){
print_r($c($thing));
return;
}
echo "\n\n\n-----------\nMethods:\n";
$methods = array_map($c, $this->methods);
print_r($methods);
unset($methods);
echo "\n\n\n-----------\nLia Props:\n";
$props = array_map($c, $this->props);
print_r($props);
unset($props);
echo "\n\n\n-----------\nPrefixes:\n";
$prefixes = array_map($c, $this->prefixes);
print_r($prefixes);
unset($prefixes);
echo "\n\n\n";
echo "\n\n\n-----------\nAddons, Short Name:\n";
$addons = array_map($c, $this->addons);
print_r($addons);
unset($addons);
echo "\n\n\n-----------\nAddons, fqn:\n";
$fqn_addons = array_map($c,$this->fqn_addons);
print_r($fqn_addons);
unset($fqn_addons);
echo "\n\n\n-----------\nPackage Properties Tree:\n";
$package_props = [];
$addon_props = [];
foreach ($this->packages as $k=>$p){
$package_props[$k] = array_map($c,get_object_vars($p));
foreach ($p->addons as $addon_key => $addon){
$addon_props[$k][$addon_key] = array_map($c,get_object_vars($addon));
}
}
print_r($package_props);
unset($package_props);
echo "\n\n\n-----------\nAddon Properties Tree:\n";
print_r($addon_props);
unset($addon_props);
}
public function __map_array__($value){
if (is_callable($value)&&is_array($value)
&&is_object($value[0]))return get_class($value[0]).'#'.spl_object_id($value[0]).'->'.$value[1];
if (is_object($value))return get_class($value).'#'.spl_object_id($value);
if (is_array($value))return array_map([$this, '__map_array__'], $value);
return $value;
}
}