Augmentation vers version 3.3.0
This commit is contained in:
279
vendor/s9e/regexp-builder/src/Serializer.php
vendored
Normal file
279
vendor/s9e/regexp-builder/src/Serializer.php
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package s9e\RegexpBuilder
|
||||
* @copyright Copyright (c) 2016-2018 The s9e Authors
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
namespace s9e\RegexpBuilder;
|
||||
|
||||
use s9e\RegexpBuilder\MetaCharacters;
|
||||
use s9e\RegexpBuilder\Output\OutputInterface;
|
||||
|
||||
class Serializer
|
||||
{
|
||||
/**
|
||||
* @var Escaper
|
||||
*/
|
||||
protected $escaper;
|
||||
|
||||
/**
|
||||
* @var MetaCharacters
|
||||
*/
|
||||
protected $meta;
|
||||
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* @param OutputInterface $output
|
||||
* @parm MetaCharacters $meta
|
||||
* @param Escaper $escaper
|
||||
*/
|
||||
public function __construct(OutputInterface $output, MetaCharacters $meta, Escaper $escaper)
|
||||
{
|
||||
$this->escaper = $escaper;
|
||||
$this->meta = $meta;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize given strings into a regular expression
|
||||
*
|
||||
* @param array[] $strings
|
||||
* @return string
|
||||
*/
|
||||
public function serializeStrings(array $strings)
|
||||
{
|
||||
$info = $this->analyzeStrings($strings);
|
||||
$alternations = array_map([$this, 'serializeString'], $info['strings']);
|
||||
if (!empty($info['chars']))
|
||||
{
|
||||
// Prepend the character class to the list of alternations
|
||||
array_unshift($alternations, $this->serializeCharacterClass($info['chars']));
|
||||
}
|
||||
|
||||
$expr = implode('|', $alternations);
|
||||
if ($this->needsParentheses($info))
|
||||
{
|
||||
$expr = '(?:' . $expr . ')';
|
||||
}
|
||||
|
||||
return $expr . $info['quantifier'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze given strings to determine how to serialize them
|
||||
*
|
||||
* The returned array may contains any of the following elements:
|
||||
*
|
||||
* - (string) quantifier Either '' or '?'
|
||||
* - (array) chars List of values from single-char strings
|
||||
* - (array) strings List of multi-char strings
|
||||
*
|
||||
* @param array[] $strings
|
||||
* @return array
|
||||
*/
|
||||
protected function analyzeStrings(array $strings)
|
||||
{
|
||||
$info = ['alternationsCount' => 0, 'quantifier' => ''];
|
||||
if ($strings[0] === [])
|
||||
{
|
||||
$info['quantifier'] = '?';
|
||||
unset($strings[0]);
|
||||
}
|
||||
|
||||
$chars = $this->getChars($strings);
|
||||
if (count($chars) > 1)
|
||||
{
|
||||
++$info['alternationsCount'];
|
||||
$info['chars'] = array_values($chars);
|
||||
$strings = array_diff_key($strings, $chars);
|
||||
}
|
||||
|
||||
$info['strings'] = array_values($strings);
|
||||
$info['alternationsCount'] += count($strings);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the portion of strings that are composed of a single character
|
||||
*
|
||||
* @param array[]
|
||||
* @return array String key => value
|
||||
*/
|
||||
protected function getChars(array $strings)
|
||||
{
|
||||
$chars = [];
|
||||
foreach ($strings as $k => $string)
|
||||
{
|
||||
if ($this->isChar($string))
|
||||
{
|
||||
$chars[$k] = $string[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $chars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of ranges that cover all given values
|
||||
*
|
||||
* @param integer[] $values Ordered list of values
|
||||
* @return array[] List of ranges in the form [start, end]
|
||||
*/
|
||||
protected function getRanges(array $values)
|
||||
{
|
||||
$i = 0;
|
||||
$cnt = count($values);
|
||||
$start = $values[0];
|
||||
$end = $start;
|
||||
$ranges = [];
|
||||
while (++$i < $cnt)
|
||||
{
|
||||
if ($values[$i] === $end + 1)
|
||||
{
|
||||
++$end;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ranges[] = [$start, $end];
|
||||
$start = $end = $values[$i];
|
||||
}
|
||||
}
|
||||
$ranges[] = [$start, $end];
|
||||
|
||||
return $ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether given string represents a single character
|
||||
*
|
||||
* @param array $string
|
||||
* @return bool
|
||||
*/
|
||||
protected function isChar(array $string)
|
||||
{
|
||||
return count($string) === 1 && is_int($string[0]) && MetaCharacters::isChar($string[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether an expression is quantifiable based on the strings info
|
||||
*
|
||||
* @param array $info
|
||||
* @return bool
|
||||
*/
|
||||
protected function isQuantifiable(array $info)
|
||||
{
|
||||
$strings = $info['strings'];
|
||||
|
||||
return empty($strings) || $this->isSingleQuantifiableString($strings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a list of strings contains only one single quantifiable string
|
||||
*
|
||||
* @param array[] $strings
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSingleQuantifiableString(array $strings)
|
||||
{
|
||||
return count($strings) === 1 && count($strings[0]) === 1 && MetaCharacters::isQuantifiable($strings[0][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether an expression needs parentheses based on the strings info
|
||||
*
|
||||
* @param array $info
|
||||
* @return bool
|
||||
*/
|
||||
protected function needsParentheses(array $info)
|
||||
{
|
||||
return ($info['alternationsCount'] > 1 || ($info['quantifier'] && !$this->isQuantifiable($info)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a given list of values into a character class
|
||||
*
|
||||
* @param integer[] $values
|
||||
* @return string
|
||||
*/
|
||||
protected function serializeCharacterClass(array $values)
|
||||
{
|
||||
$expr = '[';
|
||||
foreach ($this->getRanges($values) as list($start, $end))
|
||||
{
|
||||
$expr .= $this->serializeCharacterClassUnit($start);
|
||||
if ($end > $start)
|
||||
{
|
||||
if ($end > $start + 1)
|
||||
{
|
||||
$expr .= '-';
|
||||
}
|
||||
$expr .= $this->serializeCharacterClassUnit($end);
|
||||
}
|
||||
}
|
||||
$expr .= ']';
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a given value to be used in a character class
|
||||
*
|
||||
* @param integer $value
|
||||
* @return string
|
||||
*/
|
||||
protected function serializeCharacterClassUnit($value)
|
||||
{
|
||||
return $this->serializeValue($value, 'escapeCharacterClass');
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an element from a string
|
||||
*
|
||||
* @param array|integer $element
|
||||
* @return string
|
||||
*/
|
||||
protected function serializeElement($element)
|
||||
{
|
||||
return (is_array($element)) ? $this->serializeStrings($element) : $this->serializeLiteral($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a given value to be used as a literal
|
||||
*
|
||||
* @param integer $value
|
||||
* @return string
|
||||
*/
|
||||
protected function serializeLiteral($value)
|
||||
{
|
||||
return $this->serializeValue($value, 'escapeLiteral');
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a given string into a regular expression
|
||||
*
|
||||
* @param array $string
|
||||
* @return string
|
||||
*/
|
||||
protected function serializeString(array $string)
|
||||
{
|
||||
return implode('', array_map([$this, 'serializeElement'], $string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a given value
|
||||
*
|
||||
* @param integer $value
|
||||
* @param string $escapeMethod
|
||||
* @return string
|
||||
*/
|
||||
protected function serializeValue($value, $escapeMethod)
|
||||
{
|
||||
return ($value < 0) ? $this->meta->getExpression($value) : $this->escaper->$escapeMethod($this->output->output($value));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user