Seditio Source
Root |
./othercms/phpBB3/vendor/s9e/text-formatter/src/Configurator/RendererGenerators/PHP/XPathConvertor.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\RendererGenerators\PHP;

use
RuntimeException;
use
s9e\TextFormatter\Configurator\RecursiveParser;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\BooleanFunctions;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\BooleanOperators;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Comparisons;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Core;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Math;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\MultiByteStringManipulation;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\PHP80Functions;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\SingleByteStringFunctions;
use
s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\SingleByteStringManipulation;

class
XPathConvertor
{
   
/**
    * @var array Array of togglable PHP features ("mbstring" and "php80")
    */
   
public $features;

   
/**
    * @var RecursiveParser
    */
   
protected $parser;

   
/**
    * Constructor
    */
   
public function __construct(RecursiveParser $parser = null)
    {
       
$this->features = [
           
'mbstring' => extension_loaded('mbstring'),
           
'php80'    => version_compare(PHP_VERSION, '8.0', '>=')
        ];
        if (isset(
$parser))
        {
           
$this->parser = $parser;
        }
    }

   
/**
    * Convert an XPath expression (used in a condition) into PHP code
    *
    * This method is similar to convertXPath() but it selectively replaces some simple conditions
    * with the corresponding DOM method for performance reasons
    *
    * @param  string $expr XPath expression
    * @return string       PHP code
    */
   
public function convertCondition($expr)
    {
       
// Replace @attr with boolean(@attr) in boolean expressions
       
$expr = preg_replace(
           
'((^|(?<!\\bboolean)\\(\\s*|\\b(?:and|or)\\s*)([\\(\\s]*)([$@][-\\w]+|@\\*)([\\)\\s]*)(?=$|\\s+(?:and|or)))',
           
'$1$2boolean($3)$4',
           
trim($expr)
        );

       
// Replace not(boolean(@attr)) with not(@attr)
       
$expr = preg_replace(
           
'(not\\(boolean\\(([$@][-\\w]+)\\)\\))',
           
'not($1)',
           
$expr
       
);

        try
        {
            return
$this->getParser()->parse($expr)['value'];
        }
        catch (
RuntimeException $e)
        {
           
// Do nothing
       
}

       
// If the condition does not seem to contain a relational expression, or start with a
        // function call, we wrap it inside of a boolean() call
       
if (!preg_match('([=<>]|\\bor\\b|\\band\\b|^[-\\w]+\\s*\\()', $expr))
        {
           
$expr = 'boolean(' . $expr . ')';
        }

        return
'$this->xpath->evaluate(' . $this->exportXPath($expr) . ',$node)';
    }

   
/**
    * Convert an XPath expression (used as value) into PHP code
    *
    * @param  string $expr XPath expression
    * @return string       PHP code
    */
   
public function convertXPath($expr)
    {
       
$expr = trim($expr);
        try
        {
            return
$this->getParser()->parse($expr)['value'];
        }
        catch (
RuntimeException $e)
        {
           
// Do nothing
       
}

       
// Make sure the expression evaluates as a string
       
if (!preg_match('(^[-\\w]*s(?:late|pace|tring)[-\\w]*\\()', $expr))
        {
           
$expr = 'string(' . $expr . ')';
        }

        return
'$this->xpath->evaluate(' . $this->exportXPath($expr) . ',$node)';
    }

   
/**
    * Export an XPath expression as PHP with special consideration for XPath variables
    *
    * Will return PHP source representing the XPath expression, with special consideration for XPath
    * variables which are returned as a method call to XPath::export()
    *
    * @param  string $expr XPath expression
    * @return string       PHP representation of the expression
    */
   
protected function exportXPath($expr)
    {
       
$phpTokens = [];
        foreach (
$this->tokenizeXPathForExport($expr) as [$type, $content])
        {
           
$methodName  = 'exportXPath' . $type;
           
$phpTokens[] = $this->$methodName($content);
        }

        return
implode('.', $phpTokens);
    }

   
/**
    * Convert a "current()" XPath expression to its PHP source representation
    *
    * @return string
    */
   
protected function exportXPathCurrent()
    {
        return
'$node->getNodePath()';
    }

   
/**
    * Convert a fragment of an XPath expression to its PHP source representation
    *
    * @param  string $fragment
    * @return string
    */
   
protected function exportXPathFragment($fragment)
    {
        return
var_export($fragment, true);
    }

   
/**
    * Convert an XSLT parameter to its PHP source representation
    *
    * @param  string $param Parameter, including the leading $
    * @return string
    */
   
protected function exportXPathParam($param)
    {
       
$paramName = ltrim($param, '$');

        return
'$this->getParamAsXPath(' . var_export($paramName, true) . ')';
    }

   
/**
    * Generate and return the a parser with the default set of matchers
    *
    * @return RecursiveParser
    */
   
protected function getDefaultParser()
    {
       
$parser     = new RecursiveParser;
       
$matchers   = [];
       
$matchers[] = new SingleByteStringFunctions($parser);
       
$matchers[] = new BooleanFunctions($parser);
       
$matchers[] = new BooleanOperators($parser);
       
$matchers[] = new Comparisons($parser);
       
$matchers[] = new Core($parser);
       
$matchers[] = new Math($parser);
        if (!empty(
$this->features['mbstring']))
        {
           
$matchers[] = new MultiByteStringManipulation($parser);
        }
       
$matchers[] = new SingleByteStringManipulation($parser);
        if (!empty(
$this->features['php80']))
        {
           
$matchers[] = new PHP80Functions($parser);
        }

       
$parser->setMatchers($matchers);

        return
$parser;
    }

   
/**
    * Return (and if necessary, create) the cached instance of the XPath parser
    *
    * @return RecursiveParser
    */
   
protected function getParser(): RecursiveParser
   
{
        if (!isset(
$this->parser))
        {
           
$this->parser = $this->getDefaultParser();
        }

        return
$this->parser;
    }

   
/**
    * Tokenize an XPath expression for use in PHP
    *
    * @param  string $expr XPath expression
    * @return array
    */
   
protected function tokenizeXPathForExport($expr)
    {
       
$tokenExprs = [
           
'(*:Current)\\bcurrent\\(\\)',
           
'(*:Param)\\$\\w+',
           
'(*:Fragment)(?:"[^"]*"|\'[^\']*\'|(?!current\\(\\)|\\$\\w).)++'
       
];
       
preg_match_all('(' . implode('|', $tokenExprs) . ')s', $expr, $matches, PREG_SET_ORDER);

       
$tokens = [];
        foreach (
$matches as $m)
        {
           
$tokens[] = [$m['MARK'], $m[0]];
        }

        return
$tokens;
    }
}