153 lines
No EOL
3.8 KiB
PHP
153 lines
No EOL
3.8 KiB
PHP
<?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;
|
|
}
|
|
} |