This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the ILess |
||
5 | * |
||
6 | * For the full copyright and license information, please view the LICENSE |
||
7 | * file that was distributed with this source code. |
||
8 | */ |
||
9 | |||
10 | namespace ILess\Node; |
||
11 | |||
12 | use ILess\Context; |
||
13 | use ILess\DefaultFunc; |
||
14 | use ILess\Exception\ParserException; |
||
15 | use ILess\Node; |
||
16 | use ILess\Output\OutputInterface; |
||
17 | use ILess\Util\Serializer; |
||
18 | use ILess\Visitor\VisitorInterface; |
||
19 | |||
20 | /** |
||
21 | * Ruleset. |
||
22 | */ |
||
23 | class RulesetNode extends Node implements MarkableAsReferencedInterface, |
||
24 | MakeableImportantInterface, ConditionMatchableInterface, |
||
25 | ReferencedInterface, \Serializable |
||
26 | { |
||
27 | /** |
||
28 | * Ruleset paths like: `#foo #bar`. |
||
29 | * |
||
30 | * @var array |
||
31 | */ |
||
32 | public $paths = []; |
||
33 | |||
34 | /** |
||
35 | * Strict imports flag. |
||
36 | * |
||
37 | * @var bool |
||
38 | */ |
||
39 | public $strictImports = false; |
||
40 | |||
41 | /** |
||
42 | * Array of selectors. |
||
43 | * |
||
44 | * @var array |
||
45 | */ |
||
46 | public $selectors = []; |
||
47 | |||
48 | /** |
||
49 | * Is first root? |
||
50 | * |
||
51 | * @var bool |
||
52 | */ |
||
53 | public $firstRoot = false; |
||
54 | |||
55 | /** |
||
56 | * Is root? |
||
57 | * |
||
58 | * @var bool |
||
59 | */ |
||
60 | public $root = false; |
||
61 | |||
62 | /** |
||
63 | * Array of rules. |
||
64 | * |
||
65 | * @var array |
||
66 | */ |
||
67 | public $rules; |
||
68 | |||
69 | /** |
||
70 | * Allow imports flag. |
||
71 | * |
||
72 | * @var bool |
||
73 | */ |
||
74 | public $allowImports = false; |
||
75 | |||
76 | /** |
||
77 | * Multi media flag. |
||
78 | * |
||
79 | * @var bool |
||
80 | */ |
||
81 | public $multiMedia = false; |
||
82 | |||
83 | /** |
||
84 | * Array of extends. |
||
85 | * |
||
86 | * @var array |
||
87 | */ |
||
88 | public $allExtends; |
||
89 | |||
90 | /** |
||
91 | * Extend on every path flag. |
||
92 | * |
||
93 | * @var bool |
||
94 | */ |
||
95 | public $extendOnEveryPath = false; |
||
96 | |||
97 | /** |
||
98 | * Original ruleset id. |
||
99 | * |
||
100 | * @var RulesetNode|null |
||
101 | */ |
||
102 | public $originalRuleset; |
||
103 | |||
104 | /** |
||
105 | * The id. |
||
106 | * |
||
107 | * @var int |
||
108 | */ |
||
109 | public $rulesetId; |
||
110 | |||
111 | /** |
||
112 | * Node type. |
||
113 | * |
||
114 | * @var string |
||
115 | */ |
||
116 | protected $type = 'Ruleset'; |
||
117 | |||
118 | /** |
||
119 | * Array of lookups. |
||
120 | * |
||
121 | * @var array |
||
122 | */ |
||
123 | protected $lookups = []; |
||
124 | |||
125 | /** |
||
126 | * Variables cache array. |
||
127 | * |
||
128 | * @var array |
||
129 | * |
||
130 | * @see resetCache |
||
131 | */ |
||
132 | protected $variables; |
||
133 | |||
134 | /** |
||
135 | * Internal flag. Selectors are referenced. |
||
136 | * |
||
137 | * @var bool |
||
138 | * |
||
139 | * @see markAsReferenced |
||
140 | */ |
||
141 | protected $isReferenced = false; |
||
142 | |||
143 | /** |
||
144 | * @var \ILess\FunctionRegistry |
||
145 | */ |
||
146 | public $functionRegistry; |
||
147 | |||
148 | /** |
||
149 | * @var array|null |
||
150 | */ |
||
151 | public $functions; |
||
152 | |||
153 | /** |
||
154 | * Constructor. |
||
155 | * |
||
156 | * @param array $selectors Array of selectors |
||
157 | * @param array $rules Array of rules |
||
158 | * @param bool $strictImports Strict imports? |
||
159 | */ |
||
160 | public function __construct(array $selectors, array $rules, $strictImports = false) |
||
161 | { |
||
162 | $this->selectors = $selectors; |
||
163 | $this->rules = $rules; |
||
164 | $this->strictImports = (boolean) $strictImports; |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * {@inheritdoc} |
||
169 | */ |
||
170 | public function accept(VisitorInterface $visitor) |
||
171 | { |
||
172 | if ($this->paths) { |
||
173 | $visitor->visitArray($this->paths, true); |
||
174 | } elseif ($this->selectors) { |
||
175 | $this->selectors = $visitor->visitArray($this->selectors); |
||
0 ignored issues
–
show
|
|||
176 | } |
||
177 | if ($this->rules) { |
||
178 | $this->rules = $visitor->visitArray($this->rules); |
||
0 ignored issues
–
show
It seems like
$visitor->visitArray($this->rules) of type * is incompatible with the declared type array of property $rules .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
179 | } |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * Compiles the node. |
||
184 | * |
||
185 | * @param Context $context The context |
||
186 | * @param array|null $arguments Array of arguments |
||
187 | * @param bool|null $important Important flag |
||
188 | * |
||
189 | * @throws ParserException |
||
190 | * |
||
191 | * @return RulesetNode |
||
192 | */ |
||
193 | public function compile(Context $context, $arguments = null, $important = null) |
||
194 | { |
||
195 | // compile selectors |
||
196 | $selectors = []; |
||
197 | $hasOnePassingSelector = false; |
||
198 | |||
199 | if ($count = count($this->selectors)) { |
||
200 | DefaultFunc::error(new ParserException('it is currently only allowed in parametric mixin guards')); |
||
201 | for ($i = 0; $i < $count; ++$i) { |
||
202 | $selector = $this->selectors[$i]->compile($context); |
||
203 | /* @var $selector SelectorNode */ |
||
204 | $selectors[] = $selector; |
||
205 | if ($selector->compiledCondition) { |
||
206 | $hasOnePassingSelector = true; |
||
207 | } |
||
208 | } |
||
209 | DefaultFunc::reset(); |
||
210 | } else { |
||
211 | $hasOnePassingSelector = true; |
||
212 | } |
||
213 | |||
214 | $ruleset = new self($selectors, $this->rules, $this->strictImports); |
||
215 | $ruleset->originalRuleset = $this; |
||
216 | $ruleset->root = $this->root; |
||
217 | $ruleset->firstRoot = $this->firstRoot; |
||
218 | $ruleset->allowImports = $this->allowImports; |
||
219 | |||
220 | if ($this->debugInfo) { |
||
221 | $ruleset->debugInfo = $this->debugInfo; |
||
222 | } |
||
223 | |||
224 | if (!$hasOnePassingSelector) { |
||
225 | $ruleset->rules = []; |
||
226 | } |
||
227 | |||
228 | // inherit a function registry from the frames stack when possible; |
||
229 | // otherwise from the global registry |
||
230 | $found = null; |
||
231 | foreach ($context->frames as $i => $frame) { |
||
232 | if ($frame->functionRegistry) { |
||
233 | $found = $frame->functionRegistry; |
||
234 | break; |
||
235 | } |
||
236 | } |
||
237 | |||
238 | $registry = $found ? $found : $context->getFunctionRegistry(); |
||
239 | $ruleset->functionRegistry = $registry->inherit(); |
||
240 | |||
241 | // push the current ruleset to the frames stack |
||
242 | $context->unshiftFrame($ruleset); |
||
243 | |||
244 | // current selectors |
||
245 | array_unshift($context->selectors, $this->selectors); |
||
246 | |||
247 | // Evaluate imports |
||
248 | if ($ruleset->root || $ruleset->allowImports || !$ruleset->strictImports) { |
||
249 | $ruleset->compileImports($context); |
||
250 | } |
||
251 | |||
252 | // count after compile imports was called |
||
253 | $rulesetCount = count($ruleset->rules); |
||
254 | |||
255 | // Store the frames around mixin definitions, |
||
256 | // so they can be evaluated like closures when the time comes. |
||
257 | foreach ($ruleset->rules as $i => $rule) { |
||
258 | /* @var $rule RuleNode */ |
||
259 | if ($rule && $rule->compileFirst()) { |
||
260 | $ruleset->rules[$i] = $rule->compile($context); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | $mediaBlockCount = count($context->mediaBlocks); |
||
265 | |||
266 | // Evaluate mixin calls. |
||
267 | for ($i = 0; $i < $rulesetCount; ++$i) { |
||
268 | $rule = $ruleset->rules[$i]; |
||
269 | |||
270 | if ($rule instanceof MixinCallNode) { |
||
271 | $rule = $rule->compile($context); |
||
272 | $temp = []; |
||
273 | foreach ($rule as $r) { |
||
274 | if (($r instanceof RuleNode) && $r->variable) { |
||
275 | // do not pollute the scope if the variable is |
||
276 | // already there. consider returning false here |
||
277 | // but we need a way to "return" variable from mixins |
||
278 | if (!$ruleset->variable($r->name)) { |
||
279 | $temp[] = $r; |
||
280 | } |
||
281 | } else { |
||
282 | $temp[] = $r; |
||
283 | } |
||
284 | } |
||
285 | $tempCount = count($temp) - 1; |
||
286 | array_splice($ruleset->rules, $i, 1, $temp); |
||
287 | $rulesetCount += $tempCount; |
||
288 | $i += $tempCount; |
||
289 | $ruleset->resetCache(); |
||
290 | } elseif ($rule instanceof RulesetCallNode) { |
||
291 | $rule = $rule->compile($context); |
||
292 | $rules = []; |
||
293 | foreach ($rule->rules as $r) { |
||
294 | if (($r instanceof RuleNode && $r->variable)) { |
||
295 | continue; |
||
296 | } |
||
297 | $rules[] = $r; |
||
298 | } |
||
299 | |||
300 | array_splice($ruleset->rules, $i, 1, $rules); |
||
301 | $tempCount = count($rules); |
||
302 | $rulesetCount += $tempCount - 1; |
||
303 | $i += $tempCount - 1; |
||
304 | $ruleset->resetCache(); |
||
305 | } |
||
306 | } |
||
307 | |||
308 | // Evaluate everything else |
||
309 | for ($i = 0; $i < count($ruleset->rules); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
310 | $rule = $ruleset->rules[$i]; |
||
311 | /* @var $rule Node */ |
||
312 | if ($rule && !$rule->compileFirst()) { |
||
313 | $ruleset->rules[$i] = $rule instanceof CompilableInterface ? $rule->compile($context) : $rule; |
||
314 | } |
||
315 | } |
||
316 | |||
317 | // Evaluate everything else |
||
318 | for ($i = 0; $i < count($ruleset->rules); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
319 | $rule = $ruleset->rules[$i]; |
||
320 | // for rulesets, check if it is a css guard and can be removed |
||
321 | if ($rule instanceof self && count($rule->selectors) === 1) { |
||
322 | // check if it can be folded in (e.g. & where) |
||
323 | if ($rule->selectors[0]->isJustParentSelector()) { |
||
324 | array_splice($ruleset->rules, $i--, 1); |
||
325 | for ($j = 0; $j < count($rule->rules); ++$j) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
326 | $subRule = $rule->rules[$j]; |
||
327 | if (!($subRule instanceof RuleNode) || !$subRule->variable) { |
||
328 | array_splice($ruleset->rules, ++$i, 0, [$subRule]); |
||
329 | } |
||
330 | } |
||
331 | } |
||
332 | } |
||
333 | } |
||
334 | |||
335 | // Pop the stack |
||
336 | $context->shiftFrame(); |
||
337 | array_shift($context->selectors); |
||
338 | |||
339 | if ($mediaBlockCount) { |
||
340 | for ($i = $mediaBlockCount, $count = count($context->mediaBlocks); $i < $count; ++$i) { |
||
341 | $context->mediaBlocks[$i]->bubbleSelectors($selectors); |
||
342 | } |
||
343 | } |
||
344 | |||
345 | return $ruleset; |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Compiles the imports. |
||
350 | * |
||
351 | * @param Context $context |
||
352 | */ |
||
353 | public function compileImports(Context $context) |
||
354 | { |
||
355 | for ($i = 0; $i < count($this->rules); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
356 | $rule = $this->rules[$i]; |
||
357 | |||
358 | if (!($rule instanceof ImportNode)) { |
||
359 | continue; |
||
360 | } |
||
361 | |||
362 | $importRules = $rule->compile($context); |
||
363 | |||
364 | if (is_array($importRules) && count($importRules)) { |
||
365 | array_splice($this->rules, $i, 1, $importRules); |
||
366 | $i += count($importRules) - 1; |
||
367 | } else { |
||
368 | array_splice($this->rules, $i, 1, [$importRules]); |
||
369 | } |
||
370 | |||
371 | $this->resetCache(); |
||
372 | } |
||
373 | } |
||
374 | |||
375 | /** |
||
376 | * Resets the cache for variables and lookups. |
||
377 | */ |
||
378 | public function resetCache() |
||
379 | { |
||
380 | $this->variables = null; |
||
0 ignored issues
–
show
It seems like
null of type null is incompatible with the declared type array of property $variables .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
381 | $this->rulesets = []; |
||
382 | $this->lookups = []; |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * Returns the variable by name. |
||
387 | * |
||
388 | * @param string $name |
||
389 | * |
||
390 | * @return RuleNode |
||
391 | */ |
||
392 | public function variable($name) |
||
393 | { |
||
394 | $vars = $this->variables(); |
||
395 | |||
396 | return isset($vars[$name]) ? $vars[$name] : null; |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * Returns an array of variables. |
||
401 | * |
||
402 | * @return array |
||
403 | */ |
||
404 | public function variables() |
||
405 | { |
||
406 | if ($this->variables === null) { |
||
407 | $this->variables = []; |
||
408 | foreach ($this->rules as $r) { |
||
409 | if ($r instanceof RuleNode && $r->variable == true) { |
||
410 | $this->variables[$r->name] = $r; |
||
411 | } |
||
412 | // when evaluating variables in an import statement, imports have not been eval'd |
||
413 | // so we need to go inside import statements. |
||
414 | // guard against root being a string (in the case of inlined less) |
||
415 | if ($r instanceof ImportNode && self::methodExists($r->root, 'variables')) { |
||
416 | $vars = $r->root->variables(); |
||
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
ILess\Node as the method variables() does only exist in the following sub-classes of ILess\Node : ILess\Node\MixinDefinitionNode , ILess\Node\RulesetNode . Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
![]() |
|||
417 | foreach ($vars as $name => $value) { |
||
418 | $this->variables[$name] = $value; |
||
419 | } |
||
420 | } |
||
421 | } |
||
422 | } |
||
423 | |||
424 | return $this->variables; |
||
425 | } |
||
426 | |||
427 | /** |
||
428 | * {@inheritdoc} |
||
429 | */ |
||
430 | public function generateCSS(Context $context, OutputInterface $output) |
||
431 | { |
||
432 | if (!$this->root) { |
||
433 | ++$context->tabLevel; |
||
434 | } |
||
435 | |||
436 | $tabRuleStr = $tabSetStr = ''; |
||
437 | if (!$context->compress && $context->tabLevel) { |
||
438 | $tabRuleStr = str_repeat(' ', $context->tabLevel); |
||
439 | $tabSetStr = str_repeat(' ', $context->tabLevel - 1); |
||
440 | } |
||
441 | |||
442 | $ruleNodes = $rulesetNodes = $charsetRuleNodes = []; |
||
443 | $charsetNodeIndex = 0; |
||
444 | $importNodeIndex = 0; |
||
445 | |||
446 | for ($i = 0; $i < count($this->rules); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
447 | $rule = $this->rules[$i]; |
||
448 | if ($rule instanceof CommentNode) { |
||
449 | if ($importNodeIndex === $i) { |
||
450 | ++$importNodeIndex; |
||
451 | } |
||
452 | array_push($ruleNodes, $rule); |
||
453 | } elseif ($rule instanceof DirectiveNode && $rule->isCharset()) { |
||
454 | array_splice($ruleNodes, $charsetNodeIndex, 0, [$rule]); |
||
455 | ++$charsetNodeIndex; |
||
456 | ++$importNodeIndex; |
||
457 | } elseif ($rule instanceof ImportNode) { |
||
458 | array_splice($ruleNodes, $importNodeIndex, 0, [$rule]); |
||
459 | ++$importNodeIndex; |
||
460 | } else { |
||
461 | array_push($ruleNodes, $rule); |
||
462 | } |
||
463 | } |
||
464 | |||
465 | $ruleNodes = array_merge($charsetRuleNodes, $ruleNodes); |
||
466 | |||
467 | // If this is the root node, we don't render |
||
468 | // a selector, or {}. |
||
469 | if (!$this->root) { |
||
470 | if ($this->debugInfo) { |
||
471 | // debug? |
||
472 | $debugInfo = self::getDebugInfo($context, $this, $tabSetStr); |
||
473 | if ($debugInfo) { |
||
474 | $output->add($debugInfo)->add($tabSetStr); |
||
475 | } |
||
476 | } |
||
477 | |||
478 | $sep = $context->compress ? ',' : (",\n" . $tabSetStr); |
||
479 | for ($i = 0, $count = count($this->paths); $i < $count; ++$i) { |
||
480 | $path = $this->paths[$i]; |
||
481 | /* @var $path SelectorNode */ |
||
482 | if (!($pathSubCnt = count($path))) { |
||
483 | continue; |
||
484 | } |
||
485 | |||
486 | if ($i > 0) { |
||
487 | $output->add($sep); |
||
488 | } |
||
489 | |||
490 | $context->firstSelector = true; |
||
491 | $path[0]->generateCSS($context, $output); |
||
492 | $context->firstSelector = false; |
||
493 | |||
494 | for ($j = 1; $j < $pathSubCnt; ++$j) { |
||
495 | $path[$j]->generateCSS($context, $output); |
||
496 | } |
||
497 | } |
||
498 | |||
499 | $output->add(($context->compress ? '{' : " {\n") . $tabRuleStr); |
||
500 | } |
||
501 | |||
502 | // Compile rules and rulesets |
||
503 | for ($i = 0, $ruleNodesCount = count($ruleNodes); $i < $ruleNodesCount; ++$i) { |
||
504 | $rule = $ruleNodes[$i]; |
||
505 | /* @var $rule RuleNode */ |
||
506 | if ($i + 1 === $ruleNodesCount) { |
||
507 | $context->lastRule = true; |
||
508 | } |
||
509 | |||
510 | $currentLastRule = $context->lastRule; |
||
511 | |||
512 | if ($rule->isRulesetLike()) { |
||
513 | $context->lastRule = false; |
||
514 | } |
||
515 | |||
516 | if ($rule instanceof GenerateCSSInterface) { |
||
517 | $rule->generateCSS($context, $output); |
||
518 | } elseif ($rule->value) { |
||
519 | $output->add((string) $rule->value); |
||
520 | } |
||
521 | |||
522 | $context->lastRule = $currentLastRule; |
||
523 | |||
524 | if (!$context->lastRule) { |
||
525 | $output->add($context->compress ? '' : ("\n" . $tabRuleStr)); |
||
526 | } else { |
||
527 | $context->lastRule = false; |
||
528 | } |
||
529 | } |
||
530 | |||
531 | if (!$this->root) { |
||
532 | $output->add($context->compress ? '}' : "\n" . $tabSetStr . '}'); |
||
533 | --$context->tabLevel; |
||
534 | } |
||
535 | |||
536 | if (!$output->isEmpty() && !$context->compress && $this->firstRoot) { |
||
537 | $output->add("\n"); |
||
538 | } |
||
539 | } |
||
540 | |||
541 | /** |
||
542 | * Marks as referenced. |
||
543 | */ |
||
544 | public function markReferenced() |
||
545 | { |
||
546 | foreach ($this->selectors as $s) { |
||
547 | /* @var $s SelectorNode */ |
||
548 | $s->markReferenced(); |
||
549 | } |
||
550 | |||
551 | foreach ($this->rules as $r) { |
||
552 | if ($r instanceof MarkableAsReferencedInterface) { |
||
553 | $r->markReferenced(); |
||
554 | } |
||
555 | } |
||
556 | } |
||
557 | |||
558 | /** |
||
559 | * Is referenced? |
||
560 | * |
||
561 | * @return bool |
||
562 | */ |
||
563 | public function getIsReferenced() |
||
564 | { |
||
565 | foreach ($this->paths as $path) { |
||
566 | foreach ($path as $p) { |
||
567 | if ($p instanceof ReferencedInterface && $p->getIsReferenced()) { |
||
568 | return true; |
||
569 | } |
||
570 | } |
||
571 | } |
||
572 | |||
573 | foreach ($this->selectors as $selector) { |
||
574 | if ($selector instanceof ReferencedInterface && $selector->getIsReferenced()) { |
||
575 | return true; |
||
576 | } |
||
577 | } |
||
578 | |||
579 | return false; |
||
580 | } |
||
581 | |||
582 | /** |
||
583 | * Returns ruleset with nodes marked as important. |
||
584 | * |
||
585 | * @return RulesetNode |
||
586 | */ |
||
587 | public function makeImportant() |
||
588 | { |
||
589 | $importantRules = []; |
||
590 | foreach ($this->rules as $rule) { |
||
591 | if ($rule instanceof MakeableImportantInterface) { |
||
592 | $importantRules[] = $rule->makeImportant(); |
||
593 | } else { |
||
594 | $importantRules[] = $rule; |
||
595 | } |
||
596 | } |
||
597 | |||
598 | return new self($this->selectors, $importantRules, $this->strictImports); |
||
599 | } |
||
600 | |||
601 | /** |
||
602 | * Match arguments. |
||
603 | * |
||
604 | * @param array $args |
||
605 | * @param Context $context |
||
606 | * |
||
607 | * @return bool |
||
608 | */ |
||
609 | public function matchArgs(array $args, Context $context) |
||
610 | { |
||
611 | return !is_array($args) || count($args) === 0; |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * Match condition. |
||
616 | * |
||
617 | * @param array $arguments |
||
618 | * @param Context $context |
||
619 | * |
||
620 | * @return bool |
||
621 | */ |
||
622 | public function matchCondition(array $arguments, Context $context) |
||
623 | { |
||
624 | $lastSelector = $this->selectors[count($this->selectors) - 1]; |
||
625 | if (!$lastSelector->compiledCondition) { |
||
626 | return false; |
||
627 | } |
||
628 | |||
629 | if ($lastSelector->condition && |
||
630 | !$lastSelector->condition->compile(Context::createCopyForCompilation($context, $context->frames)) |
||
631 | ) { |
||
632 | return false; |
||
633 | } |
||
634 | |||
635 | return true; |
||
636 | } |
||
637 | |||
638 | /** |
||
639 | * Returns an array of rulesets. |
||
640 | * |
||
641 | * @return array |
||
642 | */ |
||
643 | public function rulesets() |
||
644 | { |
||
645 | $result = []; |
||
646 | foreach ($this->rules as $rule) { |
||
647 | if ($rule instanceof self || $rule instanceof MixinDefinitionNode) { |
||
648 | $result[] = $rule; |
||
649 | } |
||
650 | } |
||
651 | |||
652 | return $result; |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * Finds a selector. |
||
657 | * |
||
658 | * @param SelectorNode $selector |
||
659 | * @param RulesetNode $self |
||
660 | * @param Context $context |
||
661 | * @param mixed $filter |
||
662 | * |
||
663 | * @return array |
||
664 | */ |
||
665 | public function find(SelectorNode $selector, Context $context, RulesetNode $self = null, $filter = null) |
||
666 | { |
||
667 | $key = $selector->toCSS($context); |
||
668 | if (!$self) { |
||
669 | $self = $this; |
||
670 | } |
||
671 | |||
672 | if (!array_key_exists($key, $this->lookups)) { |
||
673 | $rules = []; |
||
674 | |||
675 | foreach ($this->rulesets() as $rule) { |
||
676 | if ($rule === $self) { |
||
677 | continue; |
||
678 | } |
||
679 | |||
680 | foreach ($rule->selectors as $ruleSelector) { |
||
681 | /* @var $ruleSelector SelectorNode */ |
||
682 | $match = $selector->match($ruleSelector); |
||
683 | if ($match) { |
||
684 | if (count($selector->elements) > $match) { |
||
685 | if (!$filter || call_user_func($filter, $rule)) { |
||
686 | /* @var $rule RulesetNode */ |
||
687 | $foundMixins = $rule->find(new SelectorNode(array_slice($selector->elements, $match)), |
||
688 | $context, $self, $filter); |
||
689 | for ($i = 0; $i < count($foundMixins); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
690 | array_push($foundMixins[$i]['path'], $rule); |
||
691 | } |
||
692 | $rules = array_merge($rules, $foundMixins); |
||
693 | } |
||
694 | } else { |
||
695 | $rules[] = [ |
||
696 | 'rule' => $rule, |
||
697 | 'path' => [], |
||
698 | ]; |
||
699 | } |
||
700 | break; |
||
701 | } |
||
702 | } |
||
703 | } |
||
704 | |||
705 | $this->lookups[$key] = $rules; |
||
706 | } |
||
707 | |||
708 | return $this->lookups[$key]; |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * Joins selectors. |
||
713 | * |
||
714 | * @param array $context |
||
715 | * @param array $selectors |
||
716 | * |
||
717 | * @return array |
||
718 | */ |
||
719 | public function joinSelectors(array $context, array $selectors) |
||
720 | { |
||
721 | $paths = []; |
||
722 | |||
723 | foreach ($selectors as $selector) { |
||
724 | $this->joinSelector($paths, $context, $selector); |
||
725 | } |
||
726 | |||
727 | return $paths; |
||
728 | } |
||
729 | |||
730 | /** |
||
731 | * Replace all parent selectors inside `$inSelector` by content of `$context` array |
||
732 | * resulting selectors are returned inside `$paths` array |
||
733 | * returns true if `$inSelector` contained at least one parent selector. |
||
734 | * |
||
735 | * @param array $paths |
||
736 | * @param array $context |
||
737 | * @param $inSelector |
||
738 | * |
||
739 | * @return bool |
||
740 | */ |
||
741 | private function replaceParentSelector(array &$paths, array $context, $inSelector) |
||
742 | { |
||
743 | $hadParentSelector = false; |
||
744 | $currentElements = []; |
||
745 | $newSelectors = [[]]; |
||
746 | |||
747 | for ($i = 0; $i < count($inSelector->elements); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
748 | $el = $inSelector->elements[$i]; |
||
749 | if ($el->value !== '&') { |
||
750 | $nestedSelector = $this->findNestedSelector($el); |
||
751 | if ($nestedSelector !== null) { |
||
752 | $this->mergeElementsOnToSelectors($currentElements, $newSelectors); |
||
753 | $nestedPaths = $replacedNewSelectors = []; |
||
754 | $replaced = $this->replaceParentSelector($nestedPaths, $context, $nestedSelector); |
||
755 | $hadParentSelector = $hadParentSelector || $replaced; |
||
756 | // the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors |
||
757 | for ($k = 0; $k < count($nestedPaths); ++$k) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
758 | $replacementSelector = $this->createSelector($this->createParenthesis($nestedPaths[$k], $el), |
||
759 | $el); |
||
760 | $this->addAllReplacementsIntoPath($newSelectors, [$replacementSelector], $el, $inSelector, |
||
761 | $replacedNewSelectors); |
||
762 | } |
||
763 | $newSelectors = $replacedNewSelectors; |
||
764 | $currentElements = []; |
||
765 | } else { |
||
766 | $currentElements[] = $el; |
||
767 | } |
||
768 | } else { |
||
769 | $hadParentSelector = true; |
||
770 | // the new list of selectors to add |
||
771 | $selectorsMultiplied = []; |
||
772 | // merge the current list of non parent selector elements |
||
773 | // on to the current list of selectors to add |
||
774 | $this->mergeElementsOnToSelectors($currentElements, $newSelectors); |
||
775 | |||
776 | for ($j = 0; $j < count($newSelectors); ++$j) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
777 | $sel = $newSelectors[$j]; |
||
778 | // if we don't have any parent paths, the & might be in a mixin so that it can be used |
||
779 | // whether there are parents or not |
||
780 | if (count($context) === 0) { |
||
781 | // the combinator used on el should now be applied to the next element instead so that |
||
782 | // it is not lost |
||
783 | if (count($sel) > 0) { |
||
784 | $sel[0]->elements[] = new ElementNode($el->combinator, '', $el->index, |
||
785 | $el->currentFileInfo); |
||
786 | } |
||
787 | $selectorsMultiplied[] = $sel; |
||
788 | } else { |
||
789 | // and the parent selectors |
||
790 | for ($k = 0; $k < count($context); ++$k) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
791 | // We need to put the current selectors |
||
792 | // then join the last selector's elements on to the parents selectors |
||
793 | $newSelectorPath = $this->addReplacementIntoPath($sel, $context[$k], $el, $inSelector); |
||
794 | $selectorsMultiplied[] = $newSelectorPath; |
||
795 | } |
||
796 | } |
||
797 | } |
||
798 | |||
799 | // our new selectors has been multiplied, so reset the state |
||
800 | $newSelectors = $selectorsMultiplied; |
||
801 | |||
802 | $currentElements = []; |
||
803 | } |
||
804 | } |
||
805 | |||
806 | // if we have any elements left over (e.g. .a& .b == .b) |
||
807 | // add them on to all the current selectors |
||
808 | $this->mergeElementsOnToSelectors($currentElements, $newSelectors); |
||
809 | |||
810 | for ($i = 0; $i < count($newSelectors); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
811 | $count = count($newSelectors[$i]); |
||
812 | if ($count > 0) { |
||
813 | $paths[] = &$newSelectors[$i]; // reference the selector! |
||
814 | $lastSelector = $newSelectors[$i][$count - 1]; |
||
815 | /* @var $lastSelector SelectorNode */ |
||
816 | $newSelectors[$i][$count - 1] = $lastSelector->createDerived($lastSelector->elements, |
||
817 | $inSelector->extendList); |
||
818 | } |
||
819 | } |
||
820 | |||
821 | return $hadParentSelector; |
||
822 | } |
||
823 | |||
824 | private function findNestedSelector($element) |
||
825 | { |
||
826 | if (!($element->value instanceof ParenNode)) { |
||
827 | return; |
||
828 | } |
||
829 | |||
830 | /* @var $element ParenNode */ |
||
831 | $mayBeSelector = $element->value->value; |
||
832 | if (!($mayBeSelector instanceof SelectorNode)) { |
||
833 | return; |
||
834 | } |
||
835 | |||
836 | return $mayBeSelector; |
||
837 | } |
||
838 | |||
839 | /** |
||
840 | * @param $containedElement |
||
841 | * @param $originalElement |
||
842 | * |
||
843 | * @return SelectorNode |
||
844 | */ |
||
845 | private function createSelector($containedElement, $originalElement) |
||
846 | { |
||
847 | $element = new ElementNode(null, $containedElement, $originalElement->index, $originalElement->currentFileInfo); |
||
848 | $selector = new SelectorNode([$element]); |
||
849 | |||
850 | return $selector; |
||
851 | } |
||
852 | |||
853 | /** |
||
854 | * @param $elementsToPak |
||
855 | * @param $originalElement |
||
856 | * |
||
857 | * @return ParenNode |
||
858 | */ |
||
859 | private function createParenthesis($elementsToPak, $originalElement) |
||
860 | { |
||
861 | if (count($elementsToPak) === 0) { |
||
862 | $replacementParen = new ParenNode($elementsToPak[0]); |
||
863 | } else { |
||
864 | $insideParent = []; |
||
865 | for ($j = 0; $j < count($elementsToPak); ++$j) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
866 | $insideParent[] = new ElementNode(null, $elementsToPak[$j], $originalElement->index, |
||
867 | $originalElement->currentFileInfo); |
||
868 | } |
||
869 | $replacementParen = new ParenNode(new SelectorNode($insideParent)); |
||
870 | } |
||
871 | |||
872 | return $replacementParen; |
||
873 | } |
||
874 | |||
875 | /** |
||
876 | * @param $beginningPath |
||
877 | * @param $addPath |
||
878 | * @param ElementNode $replacedElement |
||
879 | * @param SelectorNode $originalSelector |
||
880 | * |
||
881 | * @return array |
||
882 | */ |
||
883 | private function addReplacementIntoPath( |
||
884 | $beginningPath, |
||
885 | $addPath, |
||
886 | ElementNode $replacedElement, |
||
887 | SelectorNode $originalSelector |
||
888 | ) { |
||
889 | // our new selector path |
||
890 | $newSelectorPath = []; |
||
891 | |||
892 | // construct the joined selector - if & is the first thing this will be empty, |
||
893 | // if not newJoinedSelector will be the last set of elements in the selector |
||
894 | if (count($beginningPath) > 0) { |
||
895 | $newSelectorPath = $beginningPath; |
||
896 | $lastSelector = array_pop($newSelectorPath); |
||
897 | $newJoinedSelector = $originalSelector->createDerived($lastSelector->elements); |
||
898 | } else { |
||
899 | $newJoinedSelector = $originalSelector->createDerived([]); |
||
900 | } |
||
901 | |||
902 | if (count($addPath) > 0) { |
||
903 | $combinator = $replacedElement->combinator; |
||
904 | $parentEl = $addPath[0]->elements[0]; |
||
905 | /* @var $parentEl ElementNode */ |
||
906 | if ($combinator->emptyOrWhitespace && !$parentEl->combinator->emptyOrWhitespace) { |
||
907 | $combinator = $parentEl->combinator; |
||
908 | } |
||
909 | $newJoinedSelector->elements[] = new ElementNode($combinator, $parentEl->value, $replacedElement->index, |
||
910 | $replacedElement->currentFileInfo); |
||
911 | $newJoinedSelector->elements = array_merge($newJoinedSelector->elements, |
||
912 | array_slice($addPath[0]->elements, 1)); |
||
913 | } |
||
914 | |||
915 | // now add the joined selector - but only if it is not empty |
||
916 | if (count($newJoinedSelector->elements) !== 0) { |
||
917 | $newSelectorPath[] = $newJoinedSelector; |
||
918 | } |
||
919 | |||
920 | if (count($addPath) > 1) { |
||
921 | $newSelectorPath = array_merge($newSelectorPath, array_slice($addPath, 1)); |
||
922 | } |
||
923 | |||
924 | return $newSelectorPath; |
||
925 | } |
||
926 | |||
927 | private function addAllReplacementsIntoPath( |
||
928 | $beginningPath, |
||
929 | $addPaths, |
||
930 | $replacedElement, |
||
931 | $originalSelector, |
||
932 | &$result |
||
933 | ) { |
||
934 | for ($j = 0; $j < count($beginningPath); ++$j) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
935 | $newSelectorPath = $this->addReplacementIntoPath($beginningPath[$j], $addPaths, $replacedElement, |
||
936 | $originalSelector); |
||
937 | $result[] = $newSelectorPath; |
||
938 | } |
||
939 | |||
940 | return $result; |
||
941 | } |
||
942 | |||
943 | /** |
||
944 | * Joins a selector. |
||
945 | * |
||
946 | * @param array $paths |
||
947 | * @param array $context |
||
948 | * @param SelectorNode $selector The selector |
||
949 | */ |
||
950 | private function joinSelector(array &$paths, array $context, SelectorNode $selector) |
||
951 | { |
||
952 | $newPaths = []; |
||
953 | $hasParentSelector = $this->replaceParentSelector($newPaths, $context, $selector); |
||
954 | |||
955 | if (!$hasParentSelector) { |
||
956 | if (count($context) > 0) { |
||
957 | $newPaths = []; |
||
958 | for ($i = 0; $i < count($context); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
959 | $newPaths[] = array_merge($context[$i], [$selector]); |
||
960 | } |
||
961 | } else { |
||
962 | $newPaths = [[$selector]]; |
||
963 | } |
||
964 | } |
||
965 | |||
966 | for ($i = 0; $i < count($newPaths); ++$i) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
967 | $paths[] = $newPaths[$i]; |
||
968 | } |
||
969 | } |
||
970 | |||
971 | public function mergeElementsOnToSelectors(array $elements, array &$selectors) |
||
972 | { |
||
973 | if (count($elements) === 0) { |
||
974 | return; |
||
975 | } |
||
976 | |||
977 | if (count($selectors) === 0) { |
||
978 | $selectors[] = [new SelectorNode($elements)]; |
||
979 | |||
980 | return; |
||
981 | } |
||
982 | |||
983 | foreach ($selectors as &$sel) { |
||
984 | // if the previous thing in sel is a parent this needs to join on to it |
||
985 | if (count($sel) > 0) { |
||
986 | $last = count($sel) - 1; |
||
987 | $sel[$last] = $sel[$last]->createDerived(array_merge($sel[$last]->elements, $elements)); |
||
988 | } else { |
||
989 | $sel[] = new SelectorNode($elements); |
||
990 | } |
||
991 | } |
||
992 | } |
||
993 | |||
994 | /** |
||
995 | * @return bool |
||
996 | */ |
||
997 | public function isRulesetLike() |
||
998 | { |
||
999 | return true; |
||
1000 | } |
||
1001 | |||
1002 | public function serialize() |
||
1003 | { |
||
1004 | $vars = get_object_vars($this); |
||
1005 | |||
1006 | return Serializer::serialize($vars); |
||
1007 | } |
||
1008 | |||
1009 | public function unserialize($serialized) |
||
1010 | { |
||
1011 | $unserialized = Serializer::unserialize($serialized); |
||
1012 | foreach ($unserialized as $var => $val) { |
||
1013 | $this->$var = $val; |
||
1014 | } |
||
1015 | } |
||
1016 | } |
||
1017 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..