steeffffeen/FancyManiaLinks

View on GitHub
FML/Script/Script.php

Summary

Maintainability
B
5 hrs
Test Coverage
B
89%
<?php

namespace FML\Script;

use FML\Script\Features\ScriptFeature;

// TODO: rename the protected getXYZ methods to getXZYText (point out that it's a compiled string and not an array)

/**
 * Class representing the ManiaLink Script
 *
 * @author    steeffeen <mail@steeffeen.com>
 * @copyright FancyManiaLinks Copyright © 2017 Steffen Schröder
 * @license   http://www.gnu.org/licenses/ GNU General Public License, Version 3
 */
class Script
{

    /*
     * Constants
     */
    const TICKINTERVAL    = 250;
    const VAR_ScriptStart = "FML_ScriptStart";
    const VAR_LoopCounter = "FML_LoopCounter";
    const VAR_LastTick    = "FML_LastTick";

    /**
     * @var ScriptFeature[] $features Script Features
     */
    protected $features = array();

    /**
     * @var ScriptInclude[] $includes Script Includes
     */
    protected $includes = array();

    /**
     * @var ScriptConstant[] $constants Script Constants
     */
    protected $constants = array();

    /**
     * @var ScriptFunction[] $functions Script Functions
     */
    protected $functions = array();

    /**
     * @var ScriptLabel[] $customLabels Custom Script Labels
     */
    protected $customLabels = array();

    /**
     * @var ScriptLabel[] $genericLabels Generic Script Labels
     */
    protected $genericLabels = array();

    /**
     * Get all Script Includes
     *
     * @return ScriptInclude[]
     */
    public function getScriptIncludes()
    {
        return $this->includes;
    }

    /**
     * Set a Script Include
     *
     * @api
     * @param string|ScriptInclude $file      Include file or ScriptInclude
     * @param string               $namespace Include namespace
     * @return static
     */
    public function setScriptInclude($file, $namespace = null)
    {
        if ($file instanceof ScriptInclude) {
            $scriptInclude = $file;
        } else {
            $scriptInclude = new ScriptInclude($file, $namespace);
        }
        $this->includes[$scriptInclude->getNamespace()] = $scriptInclude;
        return $this;
    }

    /**
     * Get all Script Constants
     *
     * @return ScriptConstant[]
     */
    public function getScriptConstants()
    {
        return $this->constants;
    }

    /**
     * Add a Script Constant
     *
     * @api
     * @param string|ScriptConstant $name  Constant name or ScriptConstant
     * @param string                $value Constant value
     * @return static
     */
    public function addScriptConstant($name, $value = null)
    {
        if ($name instanceof ScriptConstant) {
            $scriptConstant = $name;
        } else {
            $scriptConstant = new ScriptConstant($name, $value);
        }
        if (!in_array($scriptConstant, $this->constants)) {
            array_push($this->constants, $scriptConstant);
        }
        return $this;
    }

    /**
     * Get all Script Functions
     *
     * @return ScriptFunction[]
     */
    public function getScriptFunctions()
    {
        return $this->functions;
    }

    /**
     * Add a Script Function
     *
     * @api
     * @param string|ScriptFunction $name Function name or ScriptFunction
     * @param string                $text Function text
     * @return static
     */
    public function addScriptFunction($name, $text = null)
    {
        if ($name instanceof ScriptFunction) {
            $scriptFunction = $name;
        } else {
            $scriptFunction = new ScriptFunction($name, $text);
        }
        if (!in_array($scriptFunction, $this->functions)) {
            array_push($this->functions, $scriptFunction);
        }
        return $this;
    }

    /**
     * Add a custom Script text
     *
     * @api
     * @param string|ScriptLabel $name Label name or ScriptLabel
     * @param string             $text Script text
     * @return static
     */
    public function addCustomScriptLabel($name, $text = null)
    {
        if ($name instanceof ScriptLabel) {
            $scriptLabel = $name;
        } else {
            $scriptLabel = new ScriptLabel($name, $text);
        }
        if (!in_array($scriptLabel, $this->customLabels)) {
            array_push($this->customLabels, $scriptLabel);
        }
        return $this;
    }

    /**
     * Get all generic Script Labels
     *
     * @return ScriptLabel[]
     */
    public function getGenericScriptLabels()
    {
        return $this->genericLabels;
    }

    /**
     * Append a generic Script text for the next rendering
     *
     * @TODO: get rid of generic script labels approach
     * @param string|ScriptLabel $name     Label name or ScriptLabel
     * @param string             $text     Script text
     * @param bool               $isolated (optional) Whether to isolate the Label Script
     * @return static
     */
    public function appendGenericScriptLabel($name, $text = null, $isolated = false)
    {
        if ($name instanceof ScriptLabel) {
            $scriptLabel = $name;
        } else {
            $scriptLabel = new ScriptLabel($name, $text, $isolated);
        }
        if (!in_array($scriptLabel, $this->genericLabels)) {
            array_push($this->genericLabels, $scriptLabel);
        }
        return $this;
    }

    /**
     * Remove all generic Script texts
     *
     * @TODO: get rid of generic script labels approach
     * @return static
     */
    public function resetGenericScriptLabels()
    {
        $this->genericLabels = array();
        return $this;
    }

    /**
     * Add a Script Feature
     *
     * @api
     * @param ScriptFeature $feature Script Feature
     * @return static
     */
    public function addFeature(ScriptFeature $feature)
    {
        if (!in_array($feature, $this->features, true)) {
            array_push($this->features, $feature);
        }
        return $this;
    }

    /**
     * Load the given Script Feature
     *
     * @param ScriptFeature $scriptFeature Script Feature to load
     * @return static
     */
    public function loadFeature(ScriptFeature $scriptFeature)
    {
        $scriptFeature->prepare($this);
        return $this;
    }

    /**
     * Load the given Script Features
     *
     * @param ScriptFeature[] $scriptFeatures Script Features to load
     * @return static
     */
    public function loadFeatures(array $scriptFeatures)
    {
        foreach ($scriptFeatures as $scriptFeature) {
            $this->loadFeature($scriptFeature);
        }
        return $this;
    }

    /**
     * Check if the Script has content so that it needs to be rendered
     *
     * @return bool
     */
    public function needsRendering()
    {
        if ($this->features || $this->customLabels || $this->genericLabels) {
            return true;
        }
        return false;
    }

    /**
     * Build the complete Script text
     *
     * @return string
     */
    public function buildScriptText()
    {
        $scriptText = PHP_EOL;
        $scriptText .= $this->getHeaderComment();
        $scriptText .= $this->getIncludes();
        $scriptText .= $this->getConstants();
        $scriptText .= $this->getFunctions();
        $scriptText .= $this->getLabels();
        $scriptText .= $this->getMainFunction();
        return $scriptText;
    }

    /**
     * Render the Script
     *
     * @param \DOMDocument $domDocument DOMDocument for which the Script should be created
     * @return \DOMElement
     */
    public function render(\DOMDocument $domDocument)
    {
        $this->loadFeatures($this->features);

        $scriptXml  = $domDocument->createElement("script");
        $scriptText = $this->buildScriptText();

        $scriptComment = $domDocument->createComment($scriptText);
        $scriptXml->appendChild($scriptComment);

        return $scriptXml;
    }

    /**
     * Get the header comment
     *
     * @return string
     */
    protected function getHeaderComment()
    {
        $headerComment = '/**************************************************
*     FancyManiaLinks';
        if (defined('FML_VERSION')) {
            $headerComment .= ' v' . FML_VERSION;
        }
        $headerComment .= ' by steeffeen     *
*   http://github.com/steeffeen/FancyManiaLinks   *
**************************************************/

';
        return $headerComment;
    }

    /**
     * Get the Includes text
     *
     * @return string
     */
    protected function getIncludes()
    {
        $includesText = implode(PHP_EOL, $this->includes);
        return $includesText;
    }

    /**
     * Get the Constants text
     *
     * @return string
     */
    protected function getConstants()
    {
        $constantsText = implode(PHP_EOL, $this->constants);
        return $constantsText;
    }

    /**
     * Get the Functions text
     *
     * @return string
     */
    protected function getFunctions()
    {
        $functionsText = implode(PHP_EOL, $this->functions);
        return $functionsText;
    }

    /**
     * Get the Labels text
     *
     * @return string
     */
    protected function getLabels()
    {
        $customLabelsText  = implode(PHP_EOL, $this->customLabels);
        $genericLabelsText = implode(PHP_EOL, $this->genericLabels);
        return $customLabelsText . $genericLabelsText;
    }

    /**
     * Get the main function text
     *
     * @return string
     */
    protected function getMainFunction()
    {
        $mainFunction = '
Void FML_Dummy() {}
main() {
    declare ' . self::VAR_ScriptStart . ' = Now;
    +++' . ScriptLabel::ONINIT . '+++
    declare ' . self::VAR_LoopCounter . ' = 0;
    declare ' . self::VAR_LastTick . ' = 0;
    while (True) {
        yield;
        foreach (Event in PendingEvents) {
            switch (Event.Type) {
                case CMlEvent::Type::EntrySubmit: {
                    +++' . ScriptLabel::ENTRYSUBMIT . '+++
                }
                case CMlEvent::Type::KeyPress: {
                    +++' . ScriptLabel::KEYPRESS . '+++
                }
                case CMlEvent::Type::MouseClick: {
                    +++' . ScriptLabel::MOUSECLICK . '+++
                    +++' . ScriptLabel::MOUSECLICK2 . '+++
                }
                case CMlEvent::Type::MouseOut: {
                    +++' . ScriptLabel::MOUSEOUT . '+++
                }
                case CMlEvent::Type::MouseOver: {
                    +++' . ScriptLabel::MOUSEOVER . '+++
                }
            }
        }
        +++' . ScriptLabel::LOOP . '+++
        ' . self::VAR_LoopCounter . ' += 1;
        if (' . self::VAR_LastTick . ' + ' . self::TICKINTERVAL . ' > Now) continue;
        +++' . ScriptLabel::TICK . '+++ 
        ' . self::VAR_LastTick . ' = Now;
    }
}';
        return $mainFunction;
    }

    /**
     * Get the string representation
     *
     * @return string
     */
    public function __toString()
    {
        return $this->buildScriptText();
    }

}