App.php

<?php

namespace Lia;

/**
 * Liaison App
 * 
 */
class App implements AppInterface {

    /** Namespace */
    protected string $namespace;

    /**
     * Root dir of the app
     */
    public string $dir;

    /**
     * Liaison instance this app is in
     */
    public \Lia $lia;

    /** Addons in this app */
    public array $addons = [];

    /**
     * The app's configs, usually in a config.json file in the app's root dir, unless overridden.
     */
    public array $config = [];

    /**
     * @param $lia
     * @param $fqn a string like `vendor:package_name`
     * @param $dir the root dir of this package
     */
    public function __construct(\Lia $lia, string $dir){
        $this->lia = $lia;
        $this->dir = $dir;
        $config_file=$dir.'/config.json';
        if (!file_exists($config_file)){
            throw new \Exception("Config file 'config.json' MUST exist in the root of your app's dir '$dir'\n");
        }
        try {
            $this->config = json_decode(file_get_contents($config_file),true, 512, JSON_THROW_ON_ERROR);
        } catch (\Exception $e){
                $lia->throw($e, "JSON file '$config_file' failed to `json_decode()`");
        }
        if (!isset($this->config['namespace']) && !isset($this->namespace)){
            throw new \Exception("You MUST set 'namespace' to a string value in your 'config.json' at the root of your app's dir '$dir' OR set `protected string \$namespace = 'your_namespace';` in your subclass.");
        }
        $this->namespace = $this->config['namespace'];
        $lia->addApp($this);

        if (isset($this->config['addon_dir'])){
            $this->load_addons($this->config['addon_dir']);
        } else {
            $this->load_addons();
        }

    }

    public function getLia(): \Lia {
        return $this->lia;
    }

    public function getNamespace(): string {
        return $this->namespace;
    }

    /**
     * Get the absolute path to a directory or file within the app
     */
    public function getPath(string $relative_path = ""): string {
        return str_replace('//','/',$this->dir.'/'.$relative_path);
    }

    /**
     * 
     * @throws if $config_name is not set
     */
    public function getConfig(string $config_name): mixed {
        if (!isset($this->config[$config_name])){
            $ns = $this->getNamespace();
            $class = get_class($this);
            throw new \Exception("Config '$config_name' is not set on app '$ns' (class '$class')");
        }
        return $this->config[$config_name];
    }

    /**
     * Check if the named config is set for your app
     */
    public function hasConfig(string $config_name): bool {
        return isset($this->config[$config_name]);
    }


    /**
     * Load THIS app's addons from the addon dir.
     *
     * @param $sub_dir directory where addons are stored defaults to 'addon'. config 'addon_dir' can be used.
     */
    protected function load_addons($sub_dir = 'addon'){
        $dir = $this->dir.'/'.$sub_dir;
        
        $classes = \Lia\Utility\ClassFinder::classesFromDir($dir);
        if (count($classes)==0)return;
        $addons = [];
        foreach ($classes as $info){
            // if (!in_array('Lia\\iCore\\Compo',$info['interfaces']))continue;
            $className = $info['class'];
            $addons[] = $addon = new $className($this);
            $this->addAddon($addon);
        }

        foreach ($this->addons as $addon){
            $addon->onAddonsLoaded($this, $addons);
        }

    }

    public function addAddon(\Lia\AddonInterface $addon){
        $this->addons[$addon->getName()] = $addon;
    }

    /**
     * Get an addon by its name
     */
    public function getAddon(string $addon_name): \Lia\AddonInterface{
        if (!isset($this->addons[$addon_name])){
            throw new \Exception("Addon '$addon_name' does not exist on this app (namespace:'".$this->getNamespace()."', class:".get_class($this).")");
        }

        return $this->addons[$addon_name];
    }

    /**
     * Get the sub-directory.
     *
     * @return $sub_dir appended to $this->dir
     */
    public function dir(string $sub_dir): string {
        return $this->dir.'/'.$sub_dir.'/';
    }


    /** called before an HTTP request or CLI input is processed */
    public function onLiaisonReady(\Lia $lia){
        foreach ($this->config['addons']??[] as $addon_name => $value){
            $addon = $this->lia->addon($addon_name);
            $addon->onEnabledByApp($this, $value);
        }
    }

    /** called after input is processed */
    public function onFinish(\Lia $lia){}

    /** Called when `$lia->terminate()` is called, which will `exit` after the `onTerminate()` event. */
    public function onTerminate(\Lia $lia){}

    /** called by $lia->deliver() after onReady() event */
    public function onStartRequest(\Lia\Http\Request $request, \Lia\Http\Response $response){}

    /** called by $lia->execute() after onReady() event */
    public function onCliReady(\Lia\CliExecution $cli){}
}