tbela99 / css

A CSS parser and minifier and sourcemap generator written in PHP
Other
14 stars 1 forks source link

Removing last selector causes exception #125

Closed rinart73 closed 1 year ago

rinart73 commented 1 year ago

I have the following CSS:

.widget-rss.red .title,
.widget-recent .title {
  color: red;
}
aside .widget-rss:hover {
  background: #fff;
}

I'm trying to remove .widget-rss from selectors to get this:

.widget-recent .title {
  color: red;
}

I'm using the following code:

foreach ($stylesheet->query("[value*='.widget-rss']") as $p) {
  foreach($p->getSelector() as $selector) {
    if(strpos($selector, '.widget-rss') !== false) {
      $p->removeSelector($selector);
    }
  }
}
$output = $renderer->render($stylesheet);

And I'm getting exception:

Fatal error: Uncaught Exception: invalid token '.widget-recent .title' in C:\Projects\TestProject\vendor\tbela99\css\src\TBela\CSS\Value.php:79
Stack trace:
#0 C:\Projects\TestProject\vendor\tbela99\css\src\TBela\CSS\Renderer.php(631): TBela\CSS\Value::renderTokens()
#1 C:\Projects\TestProject\vendor\tbela99\css\src\TBela\CSS\Renderer.php(329): TBela\CSS\Renderer->renderSelector()
#2 C:\Projects\TestProject\vendor\tbela99\css\src\TBela\CSS\Renderer.php(481): TBela\CSS\Renderer->renderRule()
#3 C:\Projects\TestProject\vendor\tbela99\css\src\TBela\CSS\Renderer.php(105): TBela\CSS\Renderer->renderCollection()
#4 C:\Projects\TestProject\vendor\tbela99\css\src\TBela\CSS\Renderer.php(77): TBela\CSS\Renderer->renderAst()
#5 C:\Projects\TestProject\test.php(58): TBela\CSS\Renderer->render()
#6 {main}
  thrown in C:\Projects\TestProject\vendor\tbela99\css\src\TBela\CSS\Value.php on line 79

I think I know why. When I'm calling removeSelector https://github.com/tbela99/css/blob/a2a33bd0641661c4abd58f187a090e20431bb506/src/TBela/CSS/Element/Rule.php#L114 it uses array_diff. This function preserves array keys. As a result, since .widget-rss.red .title gets removed, there is a gap in selector array at index 0. This causes array to not pass the following check in Renderer cause it specifically checks for index 0 https://github.com/tbela99/css/blob/a2a33bd0641661c4abd58f187a090e20431bb506/src/TBela/CSS/Renderer.php#L621

My suggestion is to alter removeSelector function from this:

$this->ast->selector = array_diff($this->ast->selector, $selector);

to this

$this->ast->selector = array_values(array_diff($this->ast->selector, $selector));
tbela99 commented 1 year ago

removing the last selector will throw an exception, you will need to wrap the code in a try / catch block