Skip to content

Commit b01baa1

Browse files
committed
add encoders to prevent css-parser bugs
1 parent 7a15fab commit b01baa1

3 files changed

Lines changed: 268 additions & 0 deletions

File tree

src/Encode.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
namespace Irmmr\RTLCss;
4+
5+
use Irmmr\RTLCss\Encoder\ColorFuncsEncoder;
6+
use Irmmr\RTLCss\Encoder\Encoder;
7+
8+
/**
9+
* class Encode
10+
*
11+
* PHP-CSS-Parser have many problems with using css4 or other
12+
* parsing values.
13+
* This class is an unreliable class that tries to keep away items
14+
* that the CSS parser cannot parse or make some changes to the original
15+
* code so that it can be parsed.
16+
* ! It is better not to use this class at all.
17+
*
18+
* @package Irmmr\RTLCss
19+
*/
20+
class Encode
21+
{
22+
/**
23+
* code holder
24+
* @var string
25+
*/
26+
protected string $code = '';
27+
28+
/**
29+
* encoded code
30+
* @var string
31+
*/
32+
protected string $encoded = '';
33+
34+
/**
35+
* all encoder classes name
36+
*
37+
* @var array
38+
*/
39+
protected array $encoders = [
40+
'color-funcs' => ColorFuncsEncoder::class
41+
];
42+
43+
/**
44+
* all encoder classes (created)
45+
*
46+
* @var array
47+
*/
48+
protected array $classes = [];
49+
50+
/**
51+
* class constructor
52+
*/
53+
public function __construct(string $code)
54+
{
55+
$this->code = $code;
56+
}
57+
58+
/**
59+
* set encoded code
60+
*/
61+
public function setEncoded(string $encoded): void
62+
{
63+
foreach ($this->classes as $id => $cls) {
64+
if (!$cls instanceof Encoder) {
65+
continue;
66+
}
67+
68+
$cls->setEncoded($encoded);
69+
}
70+
}
71+
72+
/**
73+
* encode whole code using encode
74+
*
75+
* @return string
76+
*/
77+
public function encode(): string
78+
{
79+
if (empty($this->encoded)) {
80+
$this->encoded = $this->code;
81+
}
82+
83+
foreach ($this->encoders as $id => $encoder) {
84+
$enc = $this->classes[$id] = new $encoder($this->encoded);
85+
86+
$this->encoded = $enc->encode();
87+
}
88+
89+
return $this->encoded;
90+
}
91+
92+
/**
93+
* decode whole code using decode
94+
*
95+
* @return string
96+
*/
97+
public function decode(): string
98+
{
99+
$decoded = $this->encoded;
100+
101+
foreach ($this->classes as $id => $cls) {
102+
if (!$cls instanceof Encoder) {
103+
continue;
104+
}
105+
106+
$decoded = $cls->decode();
107+
}
108+
109+
return $decoded;
110+
}
111+
}

src/Encoder/ColorFuncsEncoder.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace Irmmr\RTLCss\Encoder;
4+
5+
/**
6+
* class RenderColorFuncs
7+
*
8+
* The CSS parser cannot correctly parse rgba, rgb, and hsl in CSS4.
9+
*
10+
* rgb(from hwb(120deg 10% 20%) r g calc(b + 200))
11+
* rgb(from #0000FF calc(r + 40) calc(g + 40) b)
12+
* rgb(255 255 255 / 50%)
13+
*
14+
* One of the best solutions is to gather all these functions from within
15+
* the code and then add them later. I couldn't think of a better idea.
16+
*
17+
*
18+
* @package Irmmr\RTLCss\Encoder
19+
*/
20+
class ColorFuncsEncoder extends Encoder
21+
{
22+
// :)
23+
protected const RENDER_EVAL = 'RTL_RENDER_COLOR_VAL_';
24+
25+
/**
26+
* color pattern
27+
* created by @chatgpt
28+
*
29+
* @var string
30+
*/
31+
protected string $pattern = '/\b(?:rgb|rgba|hsl|hsla)\s*\(\s*((?:[^()]+|\((?:[^()]+|\([^()]*\))*\))*)\)/';
32+
33+
/**
34+
* detect pattern
35+
*
36+
* @var string
37+
*/
38+
protected string $det_pattern = '/'. self::RENDER_EVAL .'(\d+)/';
39+
40+
/**
41+
* all color replacements
42+
* @var array
43+
*/
44+
protected array $replacement = [];
45+
46+
/**
47+
* get encoded code
48+
*
49+
* @return string
50+
*/
51+
public function encode(): string
52+
{
53+
$counter = 1;
54+
55+
$encoded = preg_replace_callback($this->pattern, function($matches) use (&$counter) {
56+
$replacement = self::RENDER_EVAL . $counter;
57+
58+
$this->replacement[$counter] = $matches[0];
59+
60+
$counter ++;
61+
62+
return $replacement;
63+
}, $this->code);
64+
65+
$this->encoded = $encoded;
66+
67+
return $encoded;
68+
}
69+
70+
/**
71+
* get decoded code
72+
*
73+
* @return string
74+
*/
75+
public function decode(): string
76+
{
77+
return preg_replace_callback($this->det_pattern, function($matches) {
78+
$index = (int) $matches[1];
79+
80+
return $this->replacement[$index] ?? $matches[0];
81+
}, $this->encoded);
82+
}
83+
}

src/Encoder/Encoder.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Irmmr\RTLCss\Encoder;
4+
5+
/**
6+
* abstract class Encoder
7+
*
8+
* @package Irmmr\RTLCss\Encoder
9+
*/
10+
abstract class Encoder
11+
{
12+
/**
13+
* code holder
14+
* @var string
15+
*/
16+
protected string $code = '';
17+
18+
/**
19+
* encoded code
20+
* @var string
21+
*/
22+
protected string $encoded = '';
23+
/**
24+
* class constructor
25+
*/
26+
public function __construct(string $code)
27+
{
28+
$this->code = $code;
29+
}
30+
31+
/**
32+
* get encoded code
33+
*
34+
* @return string
35+
*/
36+
abstract public function encode(): string;
37+
38+
/**
39+
* get decoded code
40+
*
41+
* @return string
42+
*/
43+
abstract public function decode(): string;
44+
45+
/**
46+
* get encoded code
47+
*
48+
* @return string
49+
*/
50+
public function getEncoded(): string
51+
{
52+
return $this->encoded;
53+
}
54+
55+
/**
56+
* update encoded code
57+
*
58+
* @param string $encoded
59+
*/
60+
public function setEncoded(string $encoded): void
61+
{
62+
$this->encoded = $encoded;
63+
}
64+
65+
/**
66+
* get pure code
67+
*
68+
* @return string
69+
*/
70+
public function getCode(): string
71+
{
72+
return $this->code;
73+
}
74+
}

0 commit comments

Comments
 (0)