primo commit
This commit is contained in:
169
libraries/vendor/enshrined/svg-sanitize/src/ElementReference/Resolver.php
vendored
Normal file
169
libraries/vendor/enshrined/svg-sanitize/src/ElementReference/Resolver.php
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
namespace enshrined\svgSanitize\ElementReference;
|
||||
|
||||
use enshrined\svgSanitize\data\XPath;
|
||||
use enshrined\svgSanitize\Exceptions\NestingException;
|
||||
use enshrined\svgSanitize\Helper;
|
||||
|
||||
class Resolver
|
||||
{
|
||||
/**
|
||||
* @var XPath
|
||||
*/
|
||||
protected $xPath;
|
||||
|
||||
/**
|
||||
* @var Subject[]
|
||||
*/
|
||||
protected $subjects = [];
|
||||
|
||||
/**
|
||||
* @var array DOMElement[]
|
||||
*/
|
||||
protected $elementsToRemove = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $useNestingLimit;
|
||||
|
||||
public function __construct(XPath $xPath, $useNestingLimit)
|
||||
{
|
||||
$this->xPath = $xPath;
|
||||
$this->useNestingLimit = $useNestingLimit;
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
$this->collectIdentifiedElements();
|
||||
$this->processReferences();
|
||||
$this->determineInvalidSubjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves one subject by element.
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param bool $considerChildren Whether to search in Subject's children as well
|
||||
* @return Subject|null
|
||||
*/
|
||||
public function findByElement(\DOMElement $element, $considerChildren = false)
|
||||
{
|
||||
foreach ($this->subjects as $subject) {
|
||||
if (
|
||||
$element === $subject->getElement()
|
||||
|| $considerChildren && Helper::isElementContainedIn($element, $subject->getElement())
|
||||
) {
|
||||
return $subject;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves subjects (plural!) by element id - in theory malformed
|
||||
* DOM might have same ids assigned to different elements and leaving
|
||||
* it to client/browser implementation which element to actually use.
|
||||
*
|
||||
* @param string $elementId
|
||||
* @return Subject[]
|
||||
*/
|
||||
public function findByElementId($elementId)
|
||||
{
|
||||
return array_filter(
|
||||
$this->subjects,
|
||||
function (Subject $subject) use ($elementId) {
|
||||
return $elementId === $subject->getElementId();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects elements having `id` attribute (those that can be referenced).
|
||||
*/
|
||||
protected function collectIdentifiedElements()
|
||||
{
|
||||
/** @var \DOMNodeList|\DOMElement[] $elements */
|
||||
$elements = $this->xPath->query('//*[@id]');
|
||||
foreach ($elements as $element) {
|
||||
$this->subjects[$element->getAttribute('id')] = new Subject($element, $this->useNestingLimit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes references from and to elements having `id` attribute concerning
|
||||
* their occurrence in `<use ... xlink:href="#identifier">` statements.
|
||||
*/
|
||||
protected function processReferences()
|
||||
{
|
||||
$useNodeName = $this->xPath->createNodeName('use');
|
||||
foreach ($this->subjects as $subject) {
|
||||
$useElements = $this->xPath->query(
|
||||
$useNodeName . '[@href or @xlink:href]',
|
||||
$subject->getElement()
|
||||
);
|
||||
|
||||
/** @var \DOMElement $useElement */
|
||||
foreach ($useElements as $useElement) {
|
||||
$useId = Helper::extractIdReferenceFromHref(
|
||||
Helper::getElementHref($useElement)
|
||||
);
|
||||
if ($useId === null || !isset($this->subjects[$useId])) {
|
||||
continue;
|
||||
}
|
||||
$subject->addUse($this->subjects[$useId]);
|
||||
$this->subjects[$useId]->addUsedIn($subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines and tags infinite loops.
|
||||
*/
|
||||
protected function determineInvalidSubjects()
|
||||
{
|
||||
foreach ($this->subjects as $subject) {
|
||||
|
||||
if (in_array($subject->getElement(), $this->elementsToRemove)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$useId = Helper::extractIdReferenceFromHref(
|
||||
Helper::getElementHref($subject->getElement())
|
||||
);
|
||||
|
||||
try {
|
||||
if ($useId === $subject->getElementId()) {
|
||||
$this->markSubjectAsInvalid($subject);
|
||||
} elseif ($subject->hasInfiniteLoop()) {
|
||||
$this->markSubjectAsInvalid($subject);
|
||||
}
|
||||
} catch (NestingException $e) {
|
||||
$this->elementsToRemove[] = $e->getElement();
|
||||
$this->markSubjectAsInvalid($subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the elements that caused a nesting exception.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getElementsToRemove() {
|
||||
return $this->elementsToRemove;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Subject is invalid for some reason, therefore we should
|
||||
* remove it and all it's child usages.
|
||||
*
|
||||
* @param Subject $subject
|
||||
*/
|
||||
protected function markSubjectAsInvalid(Subject $subject) {
|
||||
$this->elementsToRemove = array_merge(
|
||||
$this->elementsToRemove,
|
||||
$subject->clearInternalAndGetAffectedElements()
|
||||
);
|
||||
}
|
||||
}
|
||||
153
libraries/vendor/enshrined/svg-sanitize/src/ElementReference/Subject.php
vendored
Normal file
153
libraries/vendor/enshrined/svg-sanitize/src/ElementReference/Subject.php
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
namespace enshrined\svgSanitize\ElementReference;
|
||||
|
||||
class Subject
|
||||
{
|
||||
/**
|
||||
* @var \DOMElement
|
||||
*/
|
||||
protected $element;
|
||||
|
||||
/**
|
||||
* @var Usage[]
|
||||
*/
|
||||
protected $useCollection = [];
|
||||
|
||||
/**
|
||||
* @var Usage[]
|
||||
*/
|
||||
protected $usedInCollection = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $useNestingLimit;
|
||||
|
||||
/**
|
||||
* Subject constructor.
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param int $useNestingLimit
|
||||
*/
|
||||
public function __construct(\DOMElement $element, $useNestingLimit)
|
||||
{
|
||||
$this->element = $element;
|
||||
$this->useNestingLimit = $useNestingLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DOMElement
|
||||
*/
|
||||
public function getElement()
|
||||
{
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getElementId()
|
||||
{
|
||||
return $this->element->getAttribute('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $subjects Previously processed subjects
|
||||
* @param int $level The current level of nesting.
|
||||
* @return bool
|
||||
* @throws \enshrined\svgSanitize\Exceptions\NestingException
|
||||
*/
|
||||
public function hasInfiniteLoop(array $subjects = [], $level = 1)
|
||||
{
|
||||
if ($level > $this->useNestingLimit) {
|
||||
throw new \enshrined\svgSanitize\Exceptions\NestingException('Nesting level too high, aborting', 1570713498, null, $this->getElement());
|
||||
}
|
||||
|
||||
if (in_array($this, $subjects, true)) {
|
||||
return true;
|
||||
}
|
||||
$subjects[] = $this;
|
||||
foreach ($this->useCollection as $usage) {
|
||||
if ($usage->getSubject()->hasInfiniteLoop($subjects, $level + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Subject $subject
|
||||
*/
|
||||
public function addUse(Subject $subject)
|
||||
{
|
||||
if ($subject === $this) {
|
||||
throw new \LogicException('Cannot add self usage', 1570713416);
|
||||
}
|
||||
$identifier = $subject->getElementId();
|
||||
if (isset($this->useCollection[$identifier])) {
|
||||
$this->useCollection[$identifier]->increment();
|
||||
return;
|
||||
}
|
||||
$this->useCollection[$identifier] = new Usage($subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Subject $subject
|
||||
*/
|
||||
public function addUsedIn(Subject $subject)
|
||||
{
|
||||
if ($subject === $this) {
|
||||
throw new \LogicException('Cannot add self as usage', 1570713417);
|
||||
}
|
||||
$identifier = $subject->getElementId();
|
||||
if (isset($this->usedInCollection[$identifier])) {
|
||||
$this->usedInCollection[$identifier]->increment();
|
||||
return;
|
||||
}
|
||||
$this->usedInCollection[$identifier] = new Usage($subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $accumulated
|
||||
* @return int
|
||||
*/
|
||||
public function countUse($accumulated = false)
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->useCollection as $use) {
|
||||
$useCount = $use->getSubject()->countUse();
|
||||
$count += $use->getCount() * ($accumulated ? 1 + $useCount : max(1, $useCount));
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function countUsedIn()
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->usedInCollection as $usedIn) {
|
||||
$count += $usedIn->getCount() * max(1, $usedIn->getSubject()->countUsedIn());
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the internal arrays (to free up memory as they can get big)
|
||||
* and return all the child usages DOMElement's
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function clearInternalAndGetAffectedElements()
|
||||
{
|
||||
$elements = array_map(function(Usage $usage) {
|
||||
return $usage->getSubject()->getElement();
|
||||
}, $this->useCollection);
|
||||
|
||||
$this->usedInCollection = [];
|
||||
$this->useCollection = [];
|
||||
|
||||
return $elements;
|
||||
}
|
||||
}
|
||||
49
libraries/vendor/enshrined/svg-sanitize/src/ElementReference/Usage.php
vendored
Normal file
49
libraries/vendor/enshrined/svg-sanitize/src/ElementReference/Usage.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace enshrined\svgSanitize\ElementReference;
|
||||
|
||||
class Usage
|
||||
{
|
||||
/**
|
||||
* @var Subject
|
||||
*/
|
||||
protected $subject;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $count;
|
||||
|
||||
/**
|
||||
* @param Subject $subject
|
||||
* @param int $count
|
||||
*/
|
||||
public function __construct(Subject $subject, $count = 1)
|
||||
{
|
||||
$this->subject = $subject;
|
||||
$this->count = (int)$count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $by
|
||||
*/
|
||||
public function increment($by = 1)
|
||||
{
|
||||
$this->count += (int)$by;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Subject
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user