PHP Classes

File: src/Plugin/Code/CodeGenerator.php

Recommend this page to a friend!
  Packages of Thierry Feuzeu   Jaxon   src/Plugin/Code/CodeGenerator.php   Download  
File: src/Plugin/Code/CodeGenerator.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Jaxon
Call PHP classes from JavaScript using AJAX
Author: By
Last change:
Date: 1 year ago
Size: 8,984 bytes
 

Contents

Class file image Download
<?php

/**
 * CodeGenerator.php - Jaxon code generator
 *
 * Generate HTML, CSS and Javascript code for Jaxon.
 *
 * @package jaxon-core
 * @author Thierry Feuzeu <thierry.feuzeu@gmail.com>
 * @copyright 2016 Thierry Feuzeu <thierry.feuzeu@gmail.com>
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
 * @link https://github.com/jaxon-php/jaxon-core
 */

namespace Jaxon\Plugin\Code;

use Jaxon\Di\Container;
use Jaxon\Plugin\AbstractPlugin;
use Jaxon\Plugin\CodeGeneratorInterface;
use Jaxon\Utils\Http\UriException;
use Jaxon\Utils\Template\TemplateEngine;

use function array_map;
use function array_merge;
use function count;
use function implode;
use function is_subclass_of;
use function ksort;
use function md5;
use function trim;

class CodeGenerator
{
    /**
     * @var AssetManager
     */
    private $xAssetManager;

    /**
     * The classes that generate code
     *
     * @var array<string>
     */
    protected $aCodeGenerators = [];

    /**
     * @var string
     */
    protected $sJsOptions;

    /**
     * @var array
     */
    protected $aCss = [];

    /**
     * @var array
     */
    protected $aJs = [];

    /**
     * @var array
     */
    protected $aCodeJs = [];

    /**
     * @var array
     */
    protected $aCodeJsBefore = [];

    /**
     * @var array
     */
    protected $aCodeJsAfter = [];

    /**
     * @var array
     */
    protected $aCodeJsFiles = [];

    /**
     * @var string
     */
    protected $bGenerated = false;

    /**
     * The constructor
     *
     * @param string $sVersion
     * @param Container $di
     * @param TemplateEngine $xTemplateEngine
     */
    public function __construct(private string $sVersion, private Container $di,
        private TemplateEngine $xTemplateEngine)
    {
        // The Jaxon library config is on top.
        $this->addCodeGenerator(ConfigScriptGenerator::class, 0);
        // The ready script comes after.
        $this->addCodeGenerator(ReadyScriptGenerator::class, 200);
    }

    /**
     * Add a code generator to the list
     *
     * @param string $sClassName    The code generator class
     * @param int $nPriority    The desired priority, used to order the plugins
     *
     * @return void
     */
    public function addCodeGenerator(string $sClassName, int $nPriority): void
    {
        while(isset($this->aCodeGenerators[$nPriority]))
        {
            $nPriority++;
        }
        $this->aCodeGenerators[$nPriority] = $sClassName;
    }

    /**
     * @param string $sClassName
     *
     * @return CodeGeneratorInterface
     */
    private function getCodeGenerator(string $sClassName): CodeGeneratorInterface
    {
        return $this->di->g($sClassName);
    }

    /**
     * Generate a hash for all the javascript code generated by the library
     *
     * @return string
     */
    public function getHash(): string
    {
        $aHashes = array_map(function($sClassName) {
            return $this->getCodeGenerator($sClassName)->getHash();
        }, $this->aCodeGenerators);
        $aHashes[] = $this->sVersion;
        return md5(implode('', $aHashes));
    }

    /**
     * Render a template in the 'plugins' subdir
     *
     * @param string $sTemplate    The template filename
     * @param array $aVars    The template variables
     *
     * @return string
     */
    private function render(string $sTemplate, array $aVars = []): string
    {
        $aVars['sJsOptions'] = $this->sJsOptions;
        return $this->xTemplateEngine->render("jaxon::plugins/$sTemplate", $aVars);
    }

    /**
     * Generate the Jaxon CSS and js codes for a given plugin
     *
     * @param CodeGeneratorInterface $xGenerator
     *
     * @return void
     */
    private function generatePluginCodes(CodeGeneratorInterface $xGenerator): void
    {
        if(!is_subclass_of($xGenerator, AbstractPlugin::class) ||
            $this->xAssetManager->shallIncludeAssets($xGenerator))
        {
            // HTML tags for CSS
            if(($sCss = trim($xGenerator->getCss(), " \n")) !== '')
            {
                $this->aCss[] = $sCss;
            }
            // HTML tags for js
            if(($sJs = trim($xGenerator->getJs(), " \n")) !== '')
            {
                $this->aJs[] = $sJs;
            }
        }

        // Additional js codes
        if(($xJsCode = $xGenerator->getJsCode()) !== null)
        {
            if(($sJs = trim($xJsCode->sJs, " \n")) !== '')
            {
                $this->aCodeJs[] = $sJs;
            }
            if(($sJsBefore = trim($xJsCode->sJsBefore, " \n")) !== '')
            {
                $this->aCodeJsBefore[] = $sJsBefore;
            }
            if(($sJsAfter = trim($xJsCode->sJsAfter, " \n")) !== '')
            {
                $this->aCodeJsAfter[] = $sJsAfter;
            }
            $this->aCodeJsFiles = array_merge($this->aCodeJsFiles, $xJsCode->aFiles);
        }
    }

    /**
     * Generate the Jaxon CSS ans js codes
     *
     * @return void
     * @throws UriException
     */
    private function generateCodes(): void
    {
        if($this->bGenerated)
        {
            return;
        }

        $this->xAssetManager = $this->di->getAssetManager();
        $this->sJsOptions = $this->xAssetManager->getJsOptions();

        // Sort the code generators by ascending priority
        ksort($this->aCodeGenerators);

        foreach($this->aCodeGenerators as $sClassName)
        {
            $this->generatePluginCodes($this->getCodeGenerator($sClassName));
        }

        // Load the Jaxon lib js files, after the other libs js files.
        $this->aJs[] = trim($this->render('includes.js', [
            'aUrls' => $this->xAssetManager->getJsLibFiles(),
        ]));

        // The codes are already generated.
        $this->bGenerated = true;
    }

    /**
     * Get the HTML tags to include Jaxon CSS code and files into the page
     *
     * @return string
     * @throws UriException
     */
    public function getCss(): string
    {
        $this->generateCodes();
        return implode("\n\n", $this->aCss);
    }

    /**
     * Get the HTML tags to include Jaxon javascript files into the page
     *
     * @return string
     * @throws UriException
     */
    public function getJs(): string
    {
        $this->generateCodes();
        return implode("\n\n", $this->aJs);
    }

    /**
     * Get the Javascript code
     *
     * @return string
     */
    public function getJsScript(): string
    {
        foreach($this->aCodeGenerators as $sClassName)
        {
            $xGenerator = $this->getCodeGenerator($sClassName);
            // Javascript code
            if(($sJsScript = trim($xGenerator->getScript(), " \n")) !== '')
            {
                $aJsScript[] = $sJsScript;
            }
        }
        return implode("\n\n", $aJsScript);
    }

    /**
     * @param bool $bIncludeJs Also get the JS files
     * @param bool $bIncludeCss Also get the CSS files
     *
     * @return array<string>
     */
    private function renderCodes(bool $bIncludeJs, bool $bIncludeCss): array
    {
        $aCodes = [];
        if($bIncludeCss)
        {
            $aCodes[] = $this->getCss();
        }
        if($bIncludeJs)
        {
            $aCodes[] = $this->getJs();
        }

        $sUrl = !$this->xAssetManager->shallCreateJsFiles() ? '' :
            $this->xAssetManager->createJsFiles($this);
        // Wrap the js code into the corresponding HTML tag.
        $aCodes[] = $sUrl !== '' ?
            $this->render('include.js', ['sUrl' => $sUrl]) :
            $this->render('wrapper.js', ['sScript' => $this->getJsScript()]);

        // Wrap the js codes into HTML tags.
        if(count($this->aCodeJsBefore) > 0)
        {
            $sScript = implode("\n\n", $this->aCodeJsBefore);
            $aCodes[] = $this->render('wrapper.js', ['sScript' => $sScript]);
        }
        if(count($this->aCodeJs) > 0)
        {
            $sScript = implode("\n\n", $this->aCodeJs);
            $aCodes[] = $this->render('wrapper.js', ['sScript' => $sScript]);
        }
        if(count($this->aCodeJsFiles) > 0)
        {
            $aCodes[] = $this->render('includes.js', ['aUrls' => $this->aCodeJsFiles]);
        }
        if(count($this->aCodeJsAfter) > 0)
        {
            $sScript = implode("\n\n", $this->aCodeJsAfter);
            $aCodes[] = $this->render('wrapper.js', ['sScript' => $sScript]);
        }
        return $aCodes;
    }

    /**
     * Get the javascript code to be sent to the browser
     *
     * @param bool $bIncludeJs Also get the JS files
     * @param bool $bIncludeCss Also get the CSS files
     *
     * @return string
     * @throws UriException
     */
    public function getScript(bool $bIncludeJs, bool $bIncludeCss): string
    {
        $this->generateCodes();
        $aCodes = $this->renderCodes($bIncludeJs, $bIncludeCss);
        return implode("\n\n", $aCodes);
    }
}