PluginProbe
bBlocks – Essential Gutenberg Blocks & Patterns Collection / 2.1.8
bBlocks – Essential Gutenberg Blocks & Patterns Collection v2.1.8
2.1.8 2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.43 2.0.42 2.0.41 2.0.40 2.0.39 2.0.38 trunk 1.0 1.1 1.2 1.3 1.4 1.5 1.5.1 1.5.2 1.5.3 All 108 releases
b-blocks / vendor / enshrined / svg-sanitize / src / ElementReference / Resolver.php

Resolver.php in bBlocks – Essential Gutenberg Blocks & Patterns Collection 2.1.8, at vendor/enshrined/svg-sanitize/src/ElementReference/Resolver.php

174 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace enshrined\svgSanitize\ElementReference;
3
4 use enshrined\svgSanitize\data\XPath;
5 use enshrined\svgSanitize\Exceptions\NestingException;
6 use enshrined\svgSanitize\Helper;
7
8 class Resolver
9 {
10 /**
11 * @var XPath
12 */
13 protected $xPath;
14
15 /**
16 * @var Subject[]
17 */
18 protected $subjects = [];
19
20 /**
21 * @var array DOMElement[]
22 */
23 protected $elementsToRemove = [];
24
25 /**
26 * @var int
27 */
28 protected $useNestingLimit;
29
30 public function __construct(XPath $xPath, $useNestingLimit)
31 {
32 $this->xPath = $xPath;
33 $this->useNestingLimit = $useNestingLimit;
34 }
35
36 public function collect()
37 {
38 $this->collectIdentifiedElements();
39 $this->processReferences();
40 $this->determineInvalidSubjects();
41 }
42
43 /**
44 * Resolves one subject by element.
45 *
46 * @param \DOMElement $element
47 * @param bool $considerChildren Whether to search in Subject's children as well
48 * @return Subject|null
49 */
50 public function findByElement(\DOMElement $element, $considerChildren = false)
51 {
52 foreach ($this->subjects as $subject) {
53 if (
54 $element === $subject->getElement()
55 || $considerChildren && Helper::isElementContainedIn($element, $subject->getElement())
56 ) {
57 return $subject;
58 }
59 }
60 return null;
61 }
62
63 /**
64 * Resolves subjects (plural!) by element id - in theory malformed
65 * DOM might have same ids assigned to different elements and leaving
66 * it to client/browser implementation which element to actually use.
67 *
68 * @param string $elementId
69 * @return Subject[]
70 */
71 public function findByElementId($elementId)
72 {
73 return array_filter(
74 $this->subjects,
75 function (Subject $subject) use ($elementId) {
76 return $elementId === $subject->getElementId();
77 }
78 );
79 }
80
81 /**
82 * Collects elements having `id` attribute (those that can be referenced).
83 */
84 protected function collectIdentifiedElements()
85 {
86 /** @var \DOMNodeList|\DOMElement[] $elements */
87 $elements = $this->xPath->query('//*[@id]');
88 foreach ($elements as $element) {
89 $this->subjects[$element->getAttribute('id')] = new Subject($element, $this->useNestingLimit);
90 }
91 }
92
93 /**
94 * Processes references from and to elements having `id` attribute concerning
95 * their occurrence in `<use ... xlink:href="#identifier">` statements.
96 */
97 protected function processReferences()
98 {
99 // Note: the href attribute is deliberately not filtered in the XPath predicate.
100 // XPath attribute matching is case sensitive, so `[@href or @xlink:href]` would
101 // skip `<use HrEf="#id">`/`<use xlink:HrEf="#id">` - names that
102 // `Sanitizer::cleanHrefAttributes()` normalizes back to `href`/`xlink:href`
103 // afterwards, which would hand back a live reference the graph never saw.
104 $useNodeName = $this->xPath->createNodeName('use');
105 foreach ($this->subjects as $subject) {
106 $useElements = $this->xPath->query(
107 $useNodeName,
108 $subject->getElement()
109 );
110
111 /** @var \DOMElement $useElement */
112 foreach ($useElements as $useElement) {
113 $useId = Helper::extractIdReferenceFromHref(
114 Helper::getElementHref($useElement)
115 );
116 if ($useId === null || !isset($this->subjects[$useId])) {
117 continue;
118 }
119 $subject->addUse($this->subjects[$useId]);
120 $this->subjects[$useId]->addUsedIn($subject);
121 }
122 }
123 }
124
125 /**
126 * Determines and tags infinite loops.
127 */
128 protected function determineInvalidSubjects()
129 {
130 foreach ($this->subjects as $subject) {
131
132 if (in_array($subject->getElement(), $this->elementsToRemove)) {
133 continue;
134 }
135
136 $useId = Helper::extractIdReferenceFromHref(
137 Helper::getElementHref($subject->getElement())
138 );
139
140 try {
141 if ($useId === $subject->getElementId()) {
142 $this->markSubjectAsInvalid($subject);
143 } elseif ($subject->hasInfiniteLoop()) {
144 $this->markSubjectAsInvalid($subject);
145 }
146 } catch (NestingException $e) {
147 $this->elementsToRemove[] = $e->getElement();
148 $this->markSubjectAsInvalid($subject);
149 }
150 }
151 }
152
153 /**
154 * Get all the elements that caused a nesting exception.
155 *
156 * @return array
157 */
158 public function getElementsToRemove() {
159 return $this->elementsToRemove;
160 }
161
162 /**
163 * The Subject is invalid for some reason, therefore we should
164 * remove it and all it's child usages.
165 *
166 * @param Subject $subject
167 */
168 protected function markSubjectAsInvalid(Subject $subject) {
169 $this->elementsToRemove = array_merge(
170 $this->elementsToRemove,
171 $subject->clearInternalAndGetAffectedElements()
172 );
173 }
174 }