ice framework documentation ice doc v1.10.1
Class Ice

Assets

    
namespace Ice;

/**
 * Assets helper is designed to management css/js resources.
 *
 * @package     Ice/Assets
 * @category    Helper
 * @author      Ice Team
 * @copyright   (c) 2014-2023 Ice Team
 * @license     http://iceframework.org/license
 */
class Assets
{
    protected di;
    protected collections = [] { set, get };
    protected options = [] { set };

    const NEVER = 0;
    const NOT_EXIST = 1;
    const IF_CHANGE = 2;
    const ALWAYS = 3;

    /**
     * Assets constructor. Fetch Di and set it as a property.
     */
    public function __construct()
    {
        let this->di = Di::$fetch();
    }

    /**
     * Retrieve a single option.
     *
     * @param string key The data key
     * @param mixed defaultValue The value to return if data key does not exist
     * @return mixed
     */
    public function getOption(var key, var defaultValue = null)
    {
        var value;

        if fetch value, this->options[key] {
            return value;
        }

        return defaultValue;
    }

    /**
     * Add resource to assets, autodetect type.
     *
     * @param mixed parameters Parameters of link/script/style
     * @param string version Version appending to the uri
     * @param string Collection Collection name
     * @param mixed minify Local minify option
     * @return object this
     */
    public function add(var parameters, string version = null, string collection = null, var minify = null)
    {
        var content, type, link;

        if typeof parameters == "string" {
            let link = parameters,
                parameters = [],
                parameters[] = link;
        }

        if !fetch content, parameters["content"] {
            fetch content, parameters[0];
        }

        fetch type, parameters["type"];

        if ends_with(content, ".css") || type == "text/css" {
            if !collection {
                let collection = "css";
            }
            this->addCss(parameters, version, collection, minify);
        } elseif ends_with(content, ".js") || type == "text/javascript" {
            if !collection {
                let collection = "js";
            }
            this->addJs(parameters, version, collection, minify);
        }

        return this;
    }

    /**
     * Add CSS resource to assets.
     *
     * @param array parameters Parameters of link/style
     * @param string version Version appending to the uri
     * @param string Collection Collection name
     * @param mixed minify Local minify option
     * @return object this
     */
    public function addCss(array! parameters, string version = null, string collection = "css", var minify = null)
    {

        var content, local, tag;

        let tag = this->di->get("tag");

        if !fetch content, parameters["content"] {
            fetch content, parameters[0];
        }

        if !fetch local, parameters["local"] {
            let local = true;
        }

        // If local minify is not set
        if minify === null {
            // Get global minify option, default is NEVER
            let minify = this->getOption("minify", self::NEVER);
        }

        // Do not minify external or minified resources
        if !local || ends_with(content, ".min.css") {
            let minify = self::NEVER;
        }

        // Check if resource is inline or in file
        if isset parameters["content"] {
            this->addToCollection(collection, tag->style(["content": minify ? this->minify(content, "css") : content]));
        } else {
            let parameters["href"] = this->prepare(content, "css", minify) . (version ? "?v=" . version : "");
            this->addToCollection(collection, tag->link(parameters));
        }

        return this;
    }

    /**
     * Add JS resource to assets.
     *
     * @param array parameters Parameters of script
     * @param string version Version appending to the uri
     * @param string Collection Collection name
     * @param mixed minify Local minify option
     * @return object this
     */
    public function addJs(array! parameters, string version = null, string collection = "js", var minify = null)
    {
        var content, local, tag;

        let tag = this->di->get("tag");

        if !fetch content, parameters["content"] {
            fetch content, parameters[0];
        }

        if !fetch local, parameters["local"] {
            let local = true;
        }

        // If local minify is not set
        if minify === null {
            // Get global minify option, default is NEVER
            let minify = this->getOption("minify", self::NEVER);
        }

        // Do not minify external or minified resources
        if !local || ends_with(content, ".min.js") {
            let minify = self::NEVER;
        }

        // Check if resource is inline or in file
        if isset parameters["content"] {
            this->addToCollection(collection, tag->script(["content": minify ? this->minify(content, "js") : content]));
        } else {
            let parameters["src"] = this->prepare(content, "js", minify) . (version ? "?v=" . version : "");
            this->addToCollection(collection, tag->script(parameters));
        }

        return this;
    }

    /**
     * Add an asset to the collection.
     *
     * @param string key Collection name
     * @param string value Asset HTML code
     */
    public function addToCollection(key, value)
    {
        if !isset this->collections[key] {
            let this->collections[key] = [];
        }

        let this->collections[key][] = value;
    }

    /**
     * Get the CSS default collection.
     *
     * @return array
     */
    public function getCss()
    {
        return this->get("css");
    }

    /**
     * Get the JS default collection.
     *
     * @return array
     */
    public function getJs()
    {
        return this->get("js");
    }

    /**
     * Get some collection.
     *
     * @param string key Collection name
     * @return array
     */
    public function get(string key)
    {
        var collection;

        fetch collection, this->collections[key];

        return collection ? collection : [];
    }

    /**
     * Minify content.
     *
     * @param string content Input text to minify
     * @param string type Type of content
     * @return string
     */
    protected function minify(string! content, type)
    {
        return this->di->get("filter")->sanitize(content, type . "min");
    }

    /**
     * Prepare resource.
     *
     * @param string uri The uri/url source path
     * @param string type Type of content
     * @param int minify Option of minify
     * @return string New path to the source
     */
    protected function prepare(string! uri, string type, var minify)
    {
        var source, target, dir, file, uriMin, destination, exist, old, minified;

        let source = this->getOption("source"),
            target = this->getOption("target"),
            dir = dirname(uri) . DIRECTORY_SEPARATOR,
            file = basename(uri, "." . type),
            uriMin = target . dir . file . ".min." . type,
            destination = source . uriMin,
            exist = false;

        switch minify {
            case self::NOT_EXIST:
                let minify = !file_exists(destination);
            break;
            case self::IF_CHANGE:
                if !file_exists(destination) {
                    let minify = true;
                } else {
                    let minify = md5_file(destination);
                }
            break;
            case self::ALWAYS:
                let minify = true;
            break;
            default: // self::NEVER:
                let minify = false;

                if this->getOption("forceMinified") {
                    let exist = file_exists(destination);
                }
            break;
        }

        if !minify {
            return exist ? uriMin : uri;
        } else {
            let minified = this->minify(file_get_contents(source . uri), type);

            // Check if file was changed
            if typeof minify == "string" {
                if minify != md5(minified) {
                    let minify = true;
                }
            }

            if minify === true {

                if !is_dir(dirname(destination)) {
                    let old = umask(0);

                    mkdir(dirname(destination), 0777, true);
                    umask(old);
                }

                if file_put_contents(destination, minified) === false {
                    throw new Exception("Directory can't be written");
                }
            }

            return uriMin;
        }
    }
}