Seditio Source
Root |
./othercms/phpBB3/vendor/s9e/text-formatter/src/Configurator/JavaScript/ConfigOptimizer.php
<?php

/**
* @package   s9e\TextFormatter
* @copyright Copyright (c) 2010-2021 The s9e authors
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
*/
namespace s9e\TextFormatter\Configurator\JavaScript;

/**
* This class creates local variables to deduplicate complex configValues
*/
class ConfigOptimizer
{
   
/**
    * @var array Associative array of ConfigValue instances
    */
   
protected $configValues;

   
/**
    * @var Encoder
    */
   
protected $encoder;

   
/**
    * @var array Associative array with the length of the JavaScript representation of each value
    */
   
protected $jsLengths;

   
/**
    * Constructor
    *
    * @param Encoder $encoder
    */
   
public function __construct(Encoder $encoder)
    {
       
$this->encoder = $encoder;
       
$this->reset();
    }

   
/**
    * Return the var declarations for all deduplicated config values
    *
    * @return string JavaScript code
    */
   
public function getVarDeclarations()
    {
       
asort($this->jsLengths);

       
$src = '';
        foreach (
array_keys($this->jsLengths) as $varName)
        {
           
$configValue = $this->configValues[$varName];
            if (
$configValue->isDeduplicated())
            {
               
$src .= '/** @const */ var ' . $varName . '=' . $this->encoder->encode($configValue->getValue()) . ";\n";
            }
        }

        return
$src;
    }

   
/**
    * Optimize given config object
    *
    * @param  array|Dictionary $object Original config object
    * @return array|Dictionary         Modified config object
    */
   
public function optimize($object)
    {
        return
current($this->optimizeObjectContent([$object]))->getValue();
    }

   
/**
    * Clear the deduplicated config values stored in this instance
    *
    * @return void
    */
   
public function reset()
    {
       
$this->configValues = [];
       
$this->jsLengths    = [];
    }

   
/**
    * Test whether given value can be deduplicated
    *
    * @param  mixed $value
    * @return bool
    */
   
protected function canDeduplicate($value)
    {
        if (
is_array($value) || $value instanceof Dictionary)
        {
           
// Do not deduplicate empty arrays and dictionaries
           
return (bool) count($value);
        }

        return (
$value instanceof Code);
    }

   
/**
    * Mark ConfigValue instances that have been used multiple times
    *
    * @return void
    */
   
protected function deduplicateConfigValues()
    {
       
arsort($this->jsLengths);
        foreach (
array_keys($this->jsLengths) as $varName)
        {
           
$configValue = $this->configValues[$varName];
            if (
$configValue->getUseCount() > 1)
            {
               
$configValue->deduplicate();
            }
        }
    }

   
/**
    * Return the name of the variable that will a given value
    *
    * @param  string $js JavaScript representation of the value
    * @return string
    */
   
protected function getVarName($js)
    {
        return
sprintf('o%08X', crc32($js));
    }

   
/**
    * Test whether given value is iterable
    *
    * @param  mixed $value
    * @return bool
    */
   
protected function isIterable($value)
    {
        return (
is_array($value) || $value instanceof Dictionary);
    }

   
/**
    * Optimize given object's content
    *
    * @param  array|Dictionary $object Original object
    * @return array|Dictionary         Modified object
    */
   
protected function optimizeObjectContent($object)
    {
       
$object = $this->recordObject($object);
       
$this->deduplicateConfigValues();

        return
$object->getValue();
    }

   
/**
    * Record a given config object as a ConfigValue instance
    *
    * @param  array|Code|Dictionary $object Original object
    * @return ConfigValue                   Stored ConfigValue instance
    */
   
protected function recordObject($object)
    {
       
$js      = $this->encoder->encode($object);
       
$varName = $this->getVarName($js);

        if (
$this->isIterable($object))
        {
           
$object = $this->recordObjectContent($object);
        }

        if (!isset(
$this->configValues[$varName]))
        {
           
$this->configValues[$varName] = new ConfigValue($object, $varName);
           
$this->jsLengths[$varName]    = strlen($js);
        }
       
$this->configValues[$varName]->incrementUseCount();

        return
$this->configValues[$varName];
    }

   
/**
    * Record the content of given config object
    *
    * @param  array|Dictionary $object Original object
    * @return array|Dictionary         Modified object containing ConfigValue instances
    */
   
protected function recordObjectContent($object)
    {
        foreach (
$object as $k => $v)
        {
            if (
$this->canDeduplicate($v) && !$this->shouldPreserve($v))
            {
               
$object[$k] = $this->recordObject($v);
            }
        }

        return
$object;
    }

   
/**
    * Test whether given value should be preserved and not deduplicated
    *
    * @param  mixed $value
    * @return bool
    */
   
protected function shouldPreserve($value)
    {
       
// Simple variables should be kept as-is
       
return ($value instanceof Code && preg_match('(^\\w+$)', $value));
    }
}