Files
2026-07-01 16:44:17 +03:00

382 lines
6.9 KiB
PHP

<?php namespace ZN\Hypertext;
/**
* ZN PHP Web Framework
*
* "Simplicity is the ultimate sophistication." ~ Da Vinci
*
* @package ZN
* @license MIT [http://opensource.org/licenses/MIT]
* @author Ozan UYKUN [ozan@znframework.com]
*/
class ScriptParser
{
/**
* Ignore Case
*
* @var bool
*/
public $ignoreCase = false;
/**
* Escape Character
*/
public $escapeChar = '';
/**
* EXPRESSION
*
* @const int
*/
const EXPRESSION = 0;
/**
* REPLACEMENT
*
* @const int
*/
const REPLACEMENT = 1;
/**
* LENGTH
*
* @const int
*/
const LENGTH = 2;
/**
* Protected groups
*
* @var string
*/
protected $groups = '/\\(/';//g
/**
* Protected subreplace
*
* @var string
*/
protected $subreplace = '/\\$\\d/';
/**
* Protected indexed
*
* @var string
*/
protected $indexed = '/^\\$\\d+$/';
/**
* Protected trim
*
* @var string
*/
protected $trim = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';
/**
* Protected escape
*
* @var string
*/
protected $escape = '/\\\./';//g
/**
* Protected quote
*
* @var string
*/
protected $quote = '/\'/';
/**
* Protected deleted
*
* @var string
*/
protected $deleted = '/\\x01[^\\x01]*\\x01/';//g
/**
* Protected escaped characters
*
* @var array
*/
protected $escaped = [];
/**
* Protected patterns stored by index
*
* @var array
*/
protected $patterns = [];
/**
* Protected buffer
*
* @var string
*/
protected $buffer;
/**
* add
*
* @param string $expression
* @param string $replacement
*/
public function add($expression, $replacement = '')
{
# count the number of sub-expressions
# add one because each pattern is itself a sub-expression
$length = 1 + preg_match_all($this->groups, $this->internalEscape((string)$expression), $out);
# treat only strings $replacement
if( is_string($replacement) )
{
# does the pattern deal with sub-expressions?
if( preg_match($this->subreplace, $replacement) )
{
# a simple lookup? (e.g. "$2")
if( preg_match($this->indexed, $replacement) )
{
# store the index (used for fast retrieval of matched strings)
$replacement = (int)(substr($replacement, 1)) - 1;
}
else
{
# a complicated lookup (e.g. "Hello $2 $1")
# build a function to do the lookup
$quote = preg_match($this->quote, $this->internalEscape($replacement)) ? '"' : "'";
$replacement =
[
'fn' => 'backReferences',
'data' =>
[
'replacement' => $replacement,
'length' => $length,
'quote' => $quote
]
];
}
}
}
# pass the modified arguments
$this->adding($expression ?: '/^$/', $replacement, $length);
}
/**
* Exec
*
* @param string
*
* @return string
*/
public function exec($string)
{
# execute the global replacement
$this->escaped = [];
# simulate the _patterns.toSTring of Dean
$regexp = '/';
foreach( $this->patterns as $reg )
{
$regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';
}
$regexp = substr($regexp, 0, -1) . '/';
$regexp.= ($this->ignoreCase) ? 'i' : '';
$string = $this->escape($string, $this->escapeChar);
$string = preg_replace_callback($regexp, [&$this, 'replacement'], $string);
$string = $this->unescape($string, $this->escapeChar);
return preg_replace($this->deleted, '', $string);
}
/**
* reset
*
* @codeCoverageIgnore
*/
public function reset()
{
# clear the patterns collection so that this object may be re-used
$this->patterns = [];
}
/**
* Protected adding
*
* create and add a new pattern to the patterns collection
*/
protected function adding()
{
$arguments = func_get_args();
$this->patterns[] = $arguments;
}
/**
* Protected replacemenet
*
* this is the global replace function (it's quite complicated)
*/
protected function replacement($arguments)
{
if( empty($arguments) )
{
return ''; // @codeCoverageIgnore
}
$i = 1; $j = 0;
# loop through the patterns
while( isset($this->patterns[$j]) )
{
$pattern = $this->patterns[$j++];
# do we have a result?
if( isset($arguments[$i]) && ($arguments[$i] != '') )
{
$replacement = $pattern[self::REPLACEMENT];
if( is_array($replacement) && isset($replacement['fn']) )
{
if( isset($replacement['data']) )
{
$this->buffer = $replacement['data'];
}
return call_user_func([&$this, $replacement['fn']], $arguments, $i);
}
elseif( is_int($replacement) )
{
return $arguments[$replacement + $i];
}
$delete = ($this->escapeChar == '' || strpos($arguments[$i], $this->escapeChar) === false) ? '' : "\x01" . $arguments[$i] . "\x01";
return $delete . $replacement;
# skip over references to sub-expressions
}
else
{
$i += $pattern[self::LENGTH];
}
}
return ''; // @codeCoverageIgnore
}
/**
* Protected back references
*/
protected function backReferences($match, $offset)
{
$replacement = $this->buffer['replacement'];
$quote = $this->buffer['quote'];
$i = $this->buffer['length'];
while( $i )
{
$replacement = str_replace('$'.$i--, $match[$offset + $i] ?? '', $replacement ?? '');
}
return $replacement;
}
/**
* Protected replace name
*/
protected function replaceName($match, $offset)
{
$length = strlen($match[$offset + 2] ?? '');
$start = $length - max($length - strlen($match[$offset + 3] ?? ''), 0);
return substr($match[$offset + 1] ?? '', $start, $length) . ($match[$offset + 4] ?? '');
}
/**
* Protected replace encoded
*/
protected function replaceEncoded($match, $offset)
{
return $this->buffer[$match[$offset]];
}
/**
* Protected escape
*
* encode escaped characters
*/
protected function escape($string, $escapeChar)
{
if( $escapeChar )
{
$this->buffer = $escapeChar;
return preg_replace_callback('/\\' . $escapeChar . '(.)' .'/', [&$this, 'escapeBis'], $string);
}
return $string;
}
/**
* Protected escape bis
*/
protected function escapeBis($match)
{
$this->escaped[] = $match[1];
return $this->buffer;
}
/**
* Protected unescape
*
* decode escaped characters
*/
protected function unescape($string, $escapeChar)
{
if( $escapeChar )
{
$regexp = '/'.'\\'.$escapeChar.'/';
$this->buffer = ['escapeChar'=> $escapeChar, 'i' => 0];
return preg_replace_callback($regexp, [&$this, 'unescapeBis'], $string);
}
return $string;
}
/**
* Protected unescape bis
*/
protected function unescapeBis()
{
if( isset($this->escaped[$this->buffer['i']]) && $this->escaped[$this->buffer['i']] != '' )
{
$temp = $this->escaped[$this->buffer['i']];
}
else
{
$temp = ''; // @codeCoverageIgnore
}
$this->buffer['i']++;
return $this->buffer['escapeChar'] . $temp;
}
/**
* Protected internal escape
*/
protected function internalEscape($string)
{
return preg_replace($this->escape, '', $string);
}
}