Packages

A Liaison package wraps together a bunch of fuctionality into a single directory and integrates seamlessly with Liaison.

A Liaison package includes addons, public files (routes), views, methods, hooks, configuration, a bootsrap file, custom Package subclass and more (or less!). A most basic package will typically include public files and views.

Most Liaison features can be accessed without using a package, such as if you just want to add a route or two, but Packages are generally recommended for building Liaison-powered websites. If you're integrating existing Liaison packages into a non-Liaison website, you likely won't create your own package.

There is a base package class, but most Liaison packages will extend from the Server package subclass, and this documentation is geared toward that Server subclass.

Related: Views, Routes, Addons, Package Base Class, Server Package Subclass, Redirect Addon, Http Addon, Configure Site

Docs

  • Useful Methods
  • Initialize a package
  • Directory Structure
  • Configs (config.json)
  • bootstrap.php
  • Subclassing Server Package
  • Package Lifecycle
  • Methods to override

Useful Methods

  • $package->dir('/rel-dir/') gets the path to a directory within the package. Does NOT remove any path traversal or double-slashes.
  • $package->url('/some-relative-url/') prepends the package's base_url and returns a full path (no domain). Any double-slashes are removed. Does NOT remove any path traversal.
  • $package->goto('/some-relative-url/') redirects to a path within $package, using the Redirect addon. (same rules as url() method)
  • $package->config(string $key) get a config from a package
  • $package->setConfig(string $key, mixed $value) set a configuration value

Initialize a package

A package can use the built-in Server package class or a subclass of it.

<?php  
// Create your site's package, containing your routes, views, theme, addons, hooks, and more  
$site_package = new \Lia\Package\Server($lia, 'vendor:namespace', __DIR__.'/Site/', '/base_url/');    
  
// or using a subclass  
$site_package = new \My\WebsitePackage($lia);   
// base_url could be overwritten, but the package fqn and directory should typically be pre-defined in your package class's code.  

Directory Structure

This is the structure for a typical package, with each file and directory being optional.

packages/Site/  
    - config.json <- Package settings  
    - bootstrap.php <- called right after the package is initialized. Use `$this` for your `Package`.  
    - public/ <- Public files to be routed to.   
        - index.php <- home page  
        - contact.php <- /contact/  
    - view/ <- Views  
        - theme.php <- site layout  
        - theme.css <- automatically included when theme is displayed  
    - addon/ <- Addons (extend \Lia\Addon)  
        - MarkdownSupport.php <- A custom addon to add features to Liaison  
    - class/ <- Point composer's autoloader to this directory. The Autoload addon within Liaison is not recommended.  

Configs (config.json)

The config.json configures some settings on a package, and provides dynamic configurability for other packages to reference. For example, an addon that enables markdown support might check if an added package set vendor:namespace.markdown_support = true in its config.json.

(NOTE: For site-wide configurations, see Configure Site)

Built-in configuratinos available:

{  
    "base_url": "/my-package/",  
    "fqn": "vendor:namespace",  
    "public_file_params": {}  
}  

Notes:

  • $package->config(string $key) get a config from a package
  • $package->setConfig(string $key, mixed $value) set a configuration value
  • base_url defaults to /, can be set in config.json or can be overwritten via a paramater passed to the constructor.
  • fqn is the fully qualified name of a package
  • public_file_params are extract()ed and exposed to public files within the package.
  • 2025-02-11: The config.json is loaded BEFORE the bootstrap is loaded, however base_url, fqn, and public_file_params are set AFTER bootstrapping. base_url and fqn will be overwritten by configs/constructor, whereas public_file_params will be array_merge()d (config ovewrites same-named-keys). This implementation is likely to be changed.

bootstrap.php

If bootstrap.php exists in the root of your package, it is called at the end of Package::__construct(), prior to any additional setup done by the Server package subclass.

Example boostrap.php, adding some routes & protecting requests to /admin/ pages within it:

<?php  
/**  
* This file is loaded after the base Package class is initialized, before addons are loaded. For access to addons within your package, call `$this->load_addons()`. For guarantees that other packages are loaded, setup hooks here (*assuming you've loaded the main package first*).  
*  
* @param $this \Lia\Package the package this bootstrap file belongs to (typically a subclass)  
*/  
    $package = $this;  
    $lia = $this->lia;  
  
    // Add a directory of additional routes for admin pages  
    \Lia\Addon\Router::from($lia)  
        ->addDirectoryRoutes($package, \My\ClassOfConsts::DIR_ADMIN, $package->url('/admin/'),['.php']);  
        // `['.php']` is an array of extensions to be hidden. i.e. public2/bear.php becomes /bear/  
  
  
    // protect all requests to `admin/*` with a hook, only if this package is being requested.  
    $lia->hook(\Lia\Hooks::ROUTES_FILTERED,  
        function(\Lia\Obj\Route $route) use ($package){  
            if ($route->package() !== $package)return; // we're not filtering for other's packages.  
  
            $admin_url = $package->url("/admin/"); // we must consider the base_url if the package is portable.  
  
            if (substr($route->url(), 0, strlen($admin_url)) == $admin_url){  
                // current_user_is_admin() is NOT part of Liaison, but might be part of your package subclass.  
                if (!$package->current_user_is_admin()){  
                    // Elsewhere, you might catch \My\Exception and display a message to the user.  
                    throw new \My\Exception(\My\ClassOfConsts::ERR_ADMIN_REQUIRED);  
                }  
            }  
        }  
    );  
  

Subclassing Server Package

A custom Package class makes it easier for others to setup your package, allows you to declare properties and methods, and the constructor can serve as a bootstrapper.

Minimal Example:

<?php  
  
namespace ReedyBear\PageCreator;  
  
class Package extends \Lia\Package\Server {  
  
    public string $fqn = 'reedybear:page_creator';  
    public string $name = 'page_creator';  
  
    // define a custom constructor for easier setup, or add a public static method  
    public function __construct(\Lia $lia, ?string $base_url=null){  
        parent::__construct($lia, $this->fqn, \ReedyBear\PageCreator::DIR_PACKAGE, $base_url);  
    }  
  
    // define convenience methods for your addons to call  
  
    // override lifecycle methods (see below docs)  
  
}  

Package Lifecycle

All onSOMETHING() methods are empty and meant to be overridden (only if needed).

When a Package is instantiated, the following happens:

($this referring to the package that is being instantiated.)

  1. PACKAGE_DIR/config.json is loaded to $this->config array.
  2. $this->onConfigured($this->config) is called.
  3. PACKAGE_DIR/bootstrap.php is executed.
  4. $this->onBootstrapped() is called.
  5. Addons are loaded from PACKAGE_DIR/addons/ into $this->addons.
  6. $this->onAddonsLoaded($this->addons) is called.
  7. $this->load_global_configs(array $configs) is called with values that were set via $lia->configure($global_configs).
    • These configs are propagated to loaded addons' properties, unless you override this method.
    • $addon->filter_global_configs(array $addon_configs): array is called for each addon before configs are set. You may override this in each addon to return modified configuration values before they are set.
  8. $addon->onParentReady($this) is called for each loaded addon
  9. onPackageAdded($this) is called on each other package (and on each of their addons)
  10. $this->onPackageAdded($other_package) is called for each other package. (and called on each of $this->addons)

Methods to override

Overridable methods are below. See Package Base Class for more information.

<?php  
  
    /**   
     * Called after configurations are loaded from `PACKAGE_DIR/config.json`, before bootstrapping.  
     *  
     * @override to modify configurations, add new configs dynamically, or otherwise prepare for bootstrapping.  
     *  
     * @param array $configs the configs that were loaded from disk. These have already been set to `$this->configs`.  
     */  
    public function onConfigured(array $configs){  
    }  
  
    /**  
     * Called after bootstrap file is `require`d (PACKAGE_DIR/bootstrap.php). This is called even if there is no bootstrap file.  
     *  
     * @override to bootstrap further. Typically, bootstrap.php is preferred.  
     */  
    public function onBootstrapped(){  
    }  
  
    /**  
     * Called after addons have been instantiated. Addons may not be fully setup yet, as addon->onParentReady() has not been called yet.  
     *  
     * @override to do additional setup that depends upon addons  
     *  
     * @param array $addons addons that were loaded, in no particular order.  
     */  
    public function onAddonsLoaded(array $addons){  
    }  
  
    /**  
     * Get global configs from Liaison and propagate them to this package's addons. Called after onAddonsLoaded.  
     *  
     * @override to customize the way global configs are setup  
     * @param $addon_configs array<string addon_name, array configured_key_value_pairs> array of configs for each addon within the package  
     */  
    protected function load_global_configs(array $addon_configs){  
        //... this has a default implementation to propagate key=>value pairs to addon properties, but can be overridden for custom implementation.  
        // You may alternatively wish to override `\Lia\Addon::filter_global_configs(array $configs): array`, which does no filtering by default.  
    }  
  
    /**  
     * Called when any other package is added, after it is fully setup (config, bootstrap, and addons have been loaded).  
     *  
     * This is also called for previously-added packages. When Package 2 is added, Package1->onPackageAdded(Package2) will be called AND Package2->onPackageAdded(Package1) will be called.  
     *  
     * Never called for itself. When Package 2 is added, Package2->onPackageAdded(Package2) will never be called.  
     */  
    public function onPackageAdded(\Lia\Package $package){  
    }  

The following methods can be overridden in Addons:

<?php  
    /**   
     * Called after the addon's parent package has finished loading. (configs loaded, bootstrapped, all other addons instantiated).   
     *  
     * onPackageAdded() has not been called yet.  
     */  
    public function onParentReady(\Lia\Package $package){  
    }  
  
    /**   
     * Called after a Package is added to Liaison, after it has been fully initialized, after configs loaded, bootstrapped, all other addons instantiated, and `onParentReady()` called on each of its addons.  
     *  
     */  
    public function onPackageAdded(\Lia\Package $package){  
    }  
  
    /**  
     * Return array of configs that can be set to this addon from global configuration.  
     *  
     * @override to return a modified array of configs that will be set to this addon. Alternatively, you could do custom setting here and then return an empty array.  
     */  
    public function filter_global_configs(array $configs): array {  
        return $configs;  
    }