|
18 | 18 | use Magento\Framework\Filter\DirectiveProcessor\TemplateDirective;
|
19 | 19 | use Magento\Framework\Filter\DirectiveProcessor\VarDirective;
|
20 | 20 | use Magento\Framework\Stdlib\StringUtils;
|
| 21 | +use Magento\Framework\Filter\Template\SignatureProvider; |
| 22 | +use Magento\Framework\Filter\Template\FilteringDepthMeter; |
21 | 23 |
|
22 | 24 | /**
|
23 | 25 | * Template filter
|
@@ -100,24 +102,44 @@ class Template implements \Zend_Filter_Interface
|
100 | 102 | */
|
101 | 103 | private $variableResolver;
|
102 | 104 |
|
| 105 | + /** |
| 106 | + * @var SignatureProvider|null |
| 107 | + */ |
| 108 | + private $signatureProvider; |
| 109 | + |
| 110 | + /** |
| 111 | + * @var FilteringDepthMeter|null |
| 112 | + */ |
| 113 | + private $filteringDepthMeter; |
| 114 | + |
103 | 115 | /**
|
104 | 116 | * @param StringUtils $string
|
105 | 117 | * @param array $variables
|
106 | 118 | * @param DirectiveProcessorInterface[] $directiveProcessors
|
107 | 119 | * @param VariableResolverInterface|null $variableResolver
|
| 120 | + * @param SignatureProvider|null $signatureProvider |
| 121 | + * @param FilteringDepthMeter|null $filteringDepthMeter |
108 | 122 | */
|
109 | 123 | public function __construct(
|
110 | 124 | StringUtils $string,
|
111 | 125 | $variables = [],
|
112 | 126 | $directiveProcessors = [],
|
113 |
| - VariableResolverInterface $variableResolver = null |
| 127 | + VariableResolverInterface $variableResolver = null, |
| 128 | + SignatureProvider $signatureProvider = null, |
| 129 | + FilteringDepthMeter $filteringDepthMeter = null |
114 | 130 | ) {
|
115 | 131 | $this->string = $string;
|
116 | 132 | $this->setVariables($variables);
|
117 | 133 | $this->directiveProcessors = $directiveProcessors;
|
118 | 134 | $this->variableResolver = $variableResolver ?? ObjectManager::getInstance()
|
119 | 135 | ->get(VariableResolverInterface::class);
|
120 | 136 |
|
| 137 | + $this->signatureProvider = $signatureProvider ?? ObjectManager::getInstance() |
| 138 | + ->get(SignatureProvider::class); |
| 139 | + |
| 140 | + $this->filteringDepthMeter = $filteringDepthMeter ?? ObjectManager::getInstance() |
| 141 | + ->get(FilteringDepthMeter::class); |
| 142 | + |
121 | 143 | if (empty($directiveProcessors)) {
|
122 | 144 | $this->directiveProcessors = [
|
123 | 145 | 'depend' => ObjectManager::getInstance()->get(DependDirective::class),
|
@@ -180,22 +202,118 @@ public function filter($value)
|
180 | 202 | )->render());
|
181 | 203 | }
|
182 | 204 |
|
| 205 | + $this->filteringDepthMeter->descend(); |
| 206 | + |
| 207 | + // Processing of template directives. |
| 208 | + $templateDirectivesResults = $this->processDirectives($value); |
| 209 | + |
| 210 | + foreach ($templateDirectivesResults as $result) { |
| 211 | + $value = str_replace($result['directive'], $result['output'], $value); |
| 212 | + } |
| 213 | + |
| 214 | + // Processing of deferred directives received from child templates |
| 215 | + // or nested directives. |
| 216 | + $deferredDirectivesResults = $this->processDirectives($value, true); |
| 217 | + |
| 218 | + foreach ($deferredDirectivesResults as $result) { |
| 219 | + $value = str_replace($result['directive'], $result['output'], $value); |
| 220 | + } |
| 221 | + |
| 222 | + if ($this->filteringDepthMeter->showMark() > 1) { |
| 223 | + // Signing own deferred directives (if any). |
| 224 | + $signature = $this->signatureProvider->get(); |
| 225 | + |
| 226 | + foreach ($templateDirectivesResults as $result) { |
| 227 | + if ($result['directive'] === $result['output']) { |
| 228 | + $value = str_replace( |
| 229 | + $result['output'], |
| 230 | + $signature . $result['output'] . $signature, |
| 231 | + $value |
| 232 | + ); |
| 233 | + } |
| 234 | + } |
| 235 | + } |
| 236 | + |
| 237 | + $value = $this->afterFilter($value); |
| 238 | + |
| 239 | + $this->filteringDepthMeter->ascend(); |
| 240 | + |
| 241 | + return $value; |
| 242 | + } |
| 243 | + |
| 244 | + /** |
| 245 | + * Processes template directives and returns an array that contains results produced by each directive. |
| 246 | + * |
| 247 | + * @param string $value |
| 248 | + * @param bool $isSigned |
| 249 | + * |
| 250 | + * @return array |
| 251 | + * |
| 252 | + * @throws InvalidArgumentException |
| 253 | + * @throws \Magento\Framework\Exception\LocalizedException |
| 254 | + */ |
| 255 | + private function processDirectives($value, $isSigned = false): array |
| 256 | + { |
| 257 | + $results = []; |
| 258 | + |
183 | 259 | foreach ($this->directiveProcessors as $directiveProcessor) {
|
184 | 260 | if (!$directiveProcessor instanceof DirectiveProcessorInterface) {
|
185 | 261 | throw new InvalidArgumentException(
|
186 | 262 | 'Directive processors must implement ' . DirectiveProcessorInterface::class
|
187 | 263 | );
|
188 | 264 | }
|
189 | 265 |
|
190 |
| - if (preg_match_all($directiveProcessor->getRegularExpression(), $value, $constructions, PREG_SET_ORDER)) { |
| 266 | + $pattern = $directiveProcessor->getRegularExpression(); |
| 267 | + |
| 268 | + if ($isSigned) { |
| 269 | + $pattern = $this->embedSignatureIntoPattern($pattern); |
| 270 | + } |
| 271 | + |
| 272 | + if (preg_match_all($pattern, $value, $constructions, PREG_SET_ORDER)) { |
191 | 273 | foreach ($constructions as $construction) {
|
192 | 274 | $replacedValue = $directiveProcessor->process($construction, $this, $this->templateVars);
|
193 |
| - $value = str_replace($construction[0], $replacedValue, $value); |
| 275 | + |
| 276 | + $results[] = [ |
| 277 | + 'directive' => $construction[0], |
| 278 | + 'output' => $replacedValue |
| 279 | + ]; |
194 | 280 | }
|
195 | 281 | }
|
196 | 282 | }
|
197 | 283 |
|
198 |
| - return $this->afterFilter($value); |
| 284 | + return $results; |
| 285 | + } |
| 286 | + |
| 287 | + /** |
| 288 | + * Modifies given regular expression pattern to be able to recognize signed directives. |
| 289 | + * |
| 290 | + * @param string $pattern |
| 291 | + * |
| 292 | + * @return string |
| 293 | + * |
| 294 | + * @throws \Magento\Framework\Exception\LocalizedException |
| 295 | + */ |
| 296 | + private function embedSignatureIntoPattern(string $pattern): string |
| 297 | + { |
| 298 | + $signature = $this->signatureProvider->get(); |
| 299 | + |
| 300 | + $closingDelimiters = [ |
| 301 | + '(' => ')', |
| 302 | + '{' => '}', |
| 303 | + '[' => ']', |
| 304 | + '<' => '>' |
| 305 | + ]; |
| 306 | + |
| 307 | + $closingDelimiter = $openingDelimiter = substr(trim($pattern), 0, 1); |
| 308 | + |
| 309 | + if (array_key_exists($openingDelimiter, $closingDelimiters)) { |
| 310 | + $closingDelimiter = $closingDelimiters[$openingDelimiter]; |
| 311 | + } |
| 312 | + |
| 313 | + $pattern = substr_replace($pattern, $signature, strpos($pattern, $openingDelimiter) + 1, 0); |
| 314 | + $pattern = substr_replace($pattern, $signature, strrpos($pattern, $closingDelimiter), 0); |
| 315 | + |
| 316 | + return $pattern; |
199 | 317 | }
|
200 | 318 |
|
201 | 319 | /**
|
|
0 commit comments