├── .gitignore
├── LICENSE
├── README.md
├── code.txt
├── composer.json
├── demo.html
├── images
├── dark.png
└── light.png
├── index.php
└── src
├── Highlight.php
└── theme.json
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /.idea
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 chrysu76
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP Prettify
2 |
3 | Creates a syntax highlighted version of the given PHP code.
4 |
5 | ```bash
6 | # PHP 8
7 | composer require ghostff/php_prettify
8 |
9 | # Older PHP version
10 | composer require ghostff/php_prettify:5.4.093021
11 | composer require ghostff/php_prettify:7.0.093021
12 | ```
13 |
14 | ```php
15 | ', (new Highlight)->render('$name = "foobar"'), '';
20 | ````
21 |
22 | #### Highlighting a file
23 | ```php
24 | ', (new Highlight)
29 | ->setTheme('bittr') // Sets code highlight theme.
30 | ->setStyle('body {margin:0;padding:0}') // Append css to default to style.
31 | ->setHighlight(22, ['style' => 'color:red']) // Add html attributes to selected line(tr).
32 | ->showLineNumber(1, false) // Show line number starting from line 1 and prevent selection of line number.
33 | ->render('code.txt', true),
34 | '';
35 | ```
36 |
37 | 
38 |
--------------------------------------------------------------------------------
/code.txt:
--------------------------------------------------------------------------------
1 | bindings[$abstract]['shared']) && ($counter != 0))
21 | {
22 | #cast to bool
23 | (bool) $shared = $this->bindings[$abstract]['shared'];
24 | }
25 | else
26 | {
27 | $counter--;
28 | $shared = false;
29 | }
30 |
31 | #function type dictation
32 | self::hash(hash('sha1', 'foobar'), $this->hash());
33 | return (isset($this->instances[$abstract]) || $shared === true);
34 | }
35 | }
36 |
37 | enum Suit: string {
38 | case Clubs = '♣';
39 | case Diamonds = '♦';
40 | case Hearts = '♥';
41 | case Spades = '♠';
42 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ghostff/php_prettify",
3 | "description": "Creates a syntax highlighted version of the given PHP code.",
4 | "type": "library",
5 | "license": "MIT",
6 | "autoload": {
7 | "psr-4": {
8 | "ghostff\\Highlight\\": "src"
9 | }
10 | },
11 | "authors": [
12 | {
13 | "name": "ghostff",
14 | "email": "frankchris@hotmail.com"
15 | }
16 | ],
17 | "require": {
18 | "php": ">=8.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dayle Rees Colour Scheme Previews
5 |
23 |
24 |
25 |
26 |
default
27 |
28 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
29 |
30 |
31 |
32 |
bittr
33 |
34 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
35 |
36 |
37 |
38 |
dark
39 |
40 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
41 |
42 |
43 |
44 |
owerri
45 |
46 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
47 |
48 |
49 |
50 |
light
51 |
52 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
53 |
54 |
55 |
56 |
steady
57 |
58 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
59 |
60 |
61 |
62 |
imo
63 |
64 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
65 |
66 |
67 |
68 |
yola
69 |
70 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
71 |
72 |
73 |
74 |
git
75 |
76 |
1 | <?php |
2 | |
3 | namespace Text; |
4 | use PhpPrettify as Php; |
5 | |
6 | class Text extends Namespace\TextProcess implements Php |
7 | { |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | protected $resolved = array(); |
14 | |
15 | public function isShared(string $abstract, int &$counter): bool |
16 | { |
17 | self::init(mt_rand(1, 55), parent::__construct(), ROOT_DIR); |
18 | if (isset($this->bindings[$abstract]['shared']) && ($counter != 0)) |
19 | { |
20 | |
21 | (bool) $shared = $this->bindings[$abstract]['shared']; |
22 | } |
23 | else |
24 | { |
25 | $counter--; |
26 | $shared = false; |
27 | } |
28 | |
29 | |
30 | self::hash(hash('sha1', 'foobar'), $this->hash()); |
31 | return (isset($this->instances[$abstract]) || $shared === true); |
32 | } |
33 | } |
| |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/images/dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ghostff/php_prettify/a0d1b09e279272003f025f0913236591260e8193/images/dark.png
--------------------------------------------------------------------------------
/images/light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ghostff/php_prettify/a0d1b09e279272003f025f0913236591260e8193/images/light.png
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | ',
6 | (new ghostff\Highlight\Highlight())
7 | ->setTheme('bittr') // Sets code highlight theme.
8 | ->setStyle('body {margin:0;padding:0}') // Append css to default to style.
9 | // ->setHighlight(22, ['style' => 'color:red']) // Add html attributes to selected line(tr).
10 | // ->showLineNumber(1, false) // Show line number starting from line 1 and prevent selection of line number.
11 | ->render('code.txt', true),
12 | '';
13 |
14 | ?>
--------------------------------------------------------------------------------
/src/Highlight.php:
--------------------------------------------------------------------------------
1 | \()\b/';
53 | private string $keywords_ptrn = '/(?\:|\:\<\/span\>)\s*(?:\\:)*(string|bool|array|float|int|callable|void|never)/';
70 | private string $curly_braces_ptrn = '/[\{\}]/';
71 | private string $parameter_type_ptrn = '/(?[\$|&])/';
72 | private string $square_bracket_ptrn = '/\[|\]/';
73 | private string $multi_line_comment_ptrn = '/\/\*|\*\//';
74 |
75 | /**
76 | * check and highlight user defined or php pre defined function.
77 | *
78 | * @param string $code
79 | * @return string
80 | */
81 | private function isFunction(string $code): string
82 | {
83 | return preg_replace_callback('/([n|t]?.?)\b(\w+)(?=\s\(|\()/', function (array $arg): string
84 | {
85 | $back = $arg[1];
86 | $func = $arg[2];
87 | if ($back == 'n ' || $back == 't;' || ($back == ':' && $func != 'array')) {
88 | return "{$back}custom_function}\" class=\"custom_function\">{$func}";
89 | }
90 | elseif (function_exists($func)) {
91 | return "{$back}php_function}\" class=\"php_function\">{$func}";
92 | }
93 |
94 | return $arg[0];
95 | }, $code);
96 | }
97 |
98 | /**
99 | * processes supplied text
100 | *
101 | * @param string $code
102 | * @param string|null $file_name
103 | * @param bool $cache
104 | * @return string
105 | */
106 | private function format(string $code, ?string $file_name, bool $cache): string
107 | {
108 | $code = str_replace(
109 | ['', '\\\\'],
110 | ['PP_PHP_LONG_TAG_OPEN', 'PP_PHP_SHORT_TAG_OPEN', 'PP_PHP_CLOSE_TAG', 'PP_PHP_DOUBLE_BACK_SLASH'],
111 | $code
112 | );
113 |
114 | $code = htmlspecialchars($code, ENT_NOQUOTES);
115 | $new_code = null;
116 | $start_number = $this->start_line;
117 | $is_MLQ = false; #is_multi_line_quote
118 | $is_MLC = false; #is_multi_line_comment
119 | $QO = false; #quote_opened
120 | $QT = null; #qoute_type
121 | $separator = "\n";
122 | $token = strtok($code, $separator);
123 |
124 | while ($token !== false)
125 | {
126 | $line = rtrim($token, PHP_EOL);
127 | if ($line == '') {
128 | $line = ' ';
129 | }
130 |
131 | $gui_line_number = $this->show_line_number ? "line_selectable ? '' : ' unselectable="on"') . ">{$start_number} | " : ' | ';
132 |
133 | if (isset($this->highlight[$start_number]))
134 | {
135 | $highlight_attr = '';
136 | foreach ($this->highlight[$start_number] as $key => $values) {
137 | $highlight_attr .= " {$key}=\"{$values}\"";
138 | }
139 | $gui_highlight = " | ";
140 | }
141 | else
142 | {
143 | $gui_highlight = '
';
144 | }
145 |
146 | $new_code .= "{$gui_highlight}{$gui_line_number}";
147 | if (! $is_MLQ)
148 | {
149 | if ($is_MLC) {
150 | $line = "multi_line_comment}\" class=\"strip multi_line_comment\">{$line}";
151 | }
152 |
153 | $comment = $this->multi_line_comment;
154 | $line = preg_replace_callback($this->multi_line_comment_ptrn, function(array $matches) use (&$is_MLC, $comment): string {
155 | if ($matches[0] == '*/') {
156 | $is_MLC = false;
157 | return "{$matches[0]}";
158 | }
159 |
160 | $is_MLC = true;
161 | return "{$matches[0]}";
162 | }, $line);
163 | }
164 |
165 | if (! $is_MLC)
166 | {
167 | if ($is_MLQ) {
168 | $line = "quote}\" class=\"strip quote\">{$line}";
169 | }
170 |
171 | #single line comment
172 | $SLC = false;
173 | $line = preg_replace_callback($this->quote_ptrn, function(array $matches) use (&$QO, &$is_MLQ, &$SLC, &$QT): string
174 | {
175 | if ($QO) {
176 | if ($QT == $matches[2]) {
177 | $is_MLQ = false;
178 | $QO = false;
179 |
180 | return "{$matches[0]}";
181 | }
182 |
183 | return $matches[0];
184 | }
185 | else {
186 | if ((strpos($matches[1], '//') !== false) || (strpos($matches[1], '#') !== false) || $SLC) {
187 | $SLC = true;
188 | return $matches[0];
189 | }
190 |
191 | $QO = true;
192 | $QT = $matches[2];
193 | $is_MLQ = true;
194 |
195 | return "{$matches[1]}quote}\" class=\"strip quote\">{$matches[2]}";
196 | }
197 | }, $line);
198 | }
199 |
200 | $pattern = [
201 | $this->operators_ptrn,
202 | $this->number_ptrn,
203 | $this->class_ptrn,
204 | preg_replace('/\s\s+/', '', $this->keywords_ptrn),
205 | $this->function_ptrn,
206 | $this->variable_ptrn,
207 | $this->cast_ptrn,
208 | $this->constant_ptrn,
209 | $this->parenthesis_ptrn,
210 | $this->curly_braces_ptrn,
211 | $this->null_ptrn,
212 | $this->self_ptrn,
213 | $this->parent_ptrn,
214 | $this->bool_ptrn,
215 | $this->attribute_ptrn,
216 | $this->comment_ptrn,
217 | $this->square_bracket_ptrn,
218 | $this->parameter_type_ptrn,
219 | $this->return_type_ptrn,
220 | '/PP_PHP_LONG_TAG_OPEN/',
221 | '/PP_PHP_SHORT_TAG_OPEN/',
222 | '/PP_PHP_CLOSE_TAG/',
223 | '/PP_PHP_DOUBLE_BACK_SLASH/'
224 | ];
225 |
226 | $replacement = [
227 | "operators}\" class=\"operators\">\$0",
228 | "number}\" class=\"number\">\$0",
229 | "class}\" class=\"number\">\$0",
230 | "keywords}\" class=\"keyword\">\$0",
231 | "function}\" class=\"function\">\$1",
232 | "variable}\" class=\"variable\">\$0",
233 | "cast}\" class=\"cast\">\$0",
234 | "constant}\" class=\"constant\">\$0",
235 | "parenthesis}\" class=\"parenthesis\">\$0",
236 | "curly_braces}\" class=\"curly_braces\">\$0",
237 | "null}\" class=\"null\">\$0",
238 | "self}\" class=\"self\">\$0",
239 | "parent}\" class=\"parent\">\$0",
240 | "bool}\" class=\"bool\">\$0",
241 | "attribute}\" class=\"attribute\">\$0",
242 | "comment}\" class=\"strip comment\">\$0",
243 | "square_bracket}\" class=\"square_bracket\">\$0",
244 | "parameter_type}\" class=\"parameter_type\">\$0",
245 | "return_type}\" class=\"return_type\">\$0",
246 | "tag_open}\" class=\"tag long\"><?php",
247 | "tag_open}\" class=\"tag short\"><?=",
248 | "tag_close}\" class=\"tag clode\">?>",
249 | '\\\\\\',
250 | ];
251 |
252 | $line = $this->isFunction($line);
253 | $line = preg_replace($pattern, $replacement, $line);
254 | $new_code .= "{$line}
";
255 | $token = strtok($separator);
256 | $start_number++;
257 | }
258 |
259 | $new_code .= ' | |
';
260 | $new_code = str_replace(['\"', '\\\'', ' '], ['"', '\'', ' '], $new_code);
261 | $pretty = "";
262 |
263 | if ($cache) {
264 | $this->cache($file_name, $pretty);
265 | }
266 |
267 | return $pretty;
268 | }
269 |
270 | /**
271 | * caches formatted strings and handles gc
272 | *
273 | * @param string $file_name
274 | * @param string|null $content
275 | * @return string
276 | */
277 | private function cache(string $file_name, string $content = null): string
278 | {
279 | $file_name = $this->cache_path . DIRECTORY_SEPARATOR . "{$file_name}.cache";
280 | if ($content == null) {
281 | return is_readable($file_name) ? file_get_contents($file_name) : '';
282 | }
283 |
284 | if (! is_dir($this->cache_path)) {
285 | mkdir($this->cache_path, 0775, true);
286 | }
287 |
288 | if (is_writable($this->cache_path)) {
289 | file_put_contents($file_name, $content);
290 | }
291 |
292 | return '';
293 | }
294 |
295 | /**
296 | * @param string $path
297 | * @return $this
298 | */
299 | public function setCachePath(string $path): Highlight
300 | {
301 | $this->cache_path = $path;
302 |
303 | return $this;
304 | }
305 |
306 | /**
307 | * Sets global styles. (This should always be called after setTheme(if using any))
308 | *
309 | * @param string $css
310 | * @param bool $override Keep default css or not.
311 | * @return $this
312 | */
313 | public function setStyle(string $css, bool $override = false): Highlight
314 | {
315 | $this->style = $override ? $css : "{$this->style}{$css}";
316 |
317 | return $this;
318 | }
319 |
320 | /**
321 | * displays line numbers
322 | *
323 | * @param int $start_line
324 | * @param bool $selectable If line should be selectable.
325 | * @return Highlight
326 | */
327 | public function showLineNumber(int $start_line = 1, bool $selectable = true): Highlight
328 | {
329 | $this->start_line = $start_line;
330 | $this->line_selectable = $selectable;
331 | $this->show_line_number = true;
332 |
333 | return $this;
334 | }
335 |
336 |
337 | /**
338 | * adds html attributes to line table > tr
339 | *
340 | * @param int $line
341 | * @param array $attributes
342 | * @return Highlight
343 | */
344 | public function setHighlight(int $line, array $attributes = []): Highlight
345 | {
346 | $this->highlight[$line] = $attributes;
347 |
348 | return $this;
349 | }
350 |
351 | /**
352 | * @param string $name
353 | * @return Highlight
354 | * @throws \Exception
355 | */
356 | public function setTheme(string $name): Highlight
357 | {
358 | $theme_file = __DIR__ . DIRECTORY_SEPARATOR . 'theme.json';
359 | if (file_exists($theme_file))
360 | {
361 | $all_themes = json_decode(file_get_contents($theme_file), true, JSON_THROW_ON_ERROR);
362 |
363 | if (! isset($all_themes[$name])) {
364 | throw new \Exception("\"{$name}\" is not a valid theme name");
365 | }
366 |
367 | $buildStyle = function (array $styles): string {
368 | $style = '';
369 | foreach ($styles as $n => $v) {
370 | $style .= "{$n}:{$v};";
371 | }
372 | return $style;
373 | };
374 |
375 | foreach ($all_themes[$name] as $name => &$value) {
376 | $this->{$name} = is_array($value) ? $buildStyle($value) : $value;
377 | }
378 | }
379 |
380 | return $this;
381 | }
382 |
383 | /**
384 | * check if code is a file or a string then renders accordingly
385 | *
386 | * @param string $code
387 | * @param bool $is_file
388 | * @param bool $cache
389 | * @return string
390 | */
391 | public function render(string $code, bool $is_file = false, bool $cache = false): string
392 | {
393 | if ($is_file)
394 | {
395 | if ($cache && ($cached = $this->cache($code)) !== null) {
396 | return $cached;
397 | }
398 |
399 | return $this->format(file_get_contents($code), $code, $cache);
400 | }
401 |
402 | return $this->format($code, null, $cache);
403 | }
404 |
405 | }
406 |
--------------------------------------------------------------------------------
/src/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "bittr": {
3 | "style": ".CH-php{background-color:#2b303b;color:white}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
4 | "keywords": { "color": "#26A6A6", "font-weight": "bold"},
5 | "cast": { "color": "#038C8C" },
6 | "null": { "color": "#FF5D38" },
7 | "bool": { "color": "#FF5D38" },
8 | "self": { "color": "#FF5D38" },
9 | "quote": { "color": "#BCD42A" },
10 | "class": { "color": "#dee3ec" },
11 | "parent": { "color": "#FF5D38" },
12 | "number": { "color": "#A4AC21" },
13 | "attribute": { "color": "#FFFFFF", "opacity": "0.5" },
14 | "comment": { "color": "#6e7a94", "font-style": "italic" },
15 | "tag_open": { "color": "#F00000" },
16 | "function": { "color": "#FFFFFF" },
17 | "variable": { "color": "#aec2e0" },
18 | "constant": { "color": "#8C4D03" },
19 | "tag_close": { "color": "#F00000" },
20 | "operators": { "color": "#26A6A6" },
21 | "semi_colon": { "color": "#dee3ec" },
22 | "parenthesis": { "color": "#FFFFFF" },
23 | "return_type": { "color": "#FE648A" },
24 | "php_function": { "color": "#6367A7" },
25 | "curly_braces": { "color": "#dee3ec" },
26 | "parameter_type": { "color": "#E3093F" },
27 | "square_bracket": { "color": "#F46164" },
28 | "custom_function": { "color": "#DAEFA2" },
29 | "multi_line_comment": { "color": "#6e7a94", "font-style": "italic" }
30 | },
31 |
32 | "dark": {
33 | "style": ".CH-php{background-color:#000000;color:white}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
34 | "keywords": { "color": "#1CC3E8"},
35 | "cast": { "color": "#038C8C" },
36 | "null": { "color": "#68C244" },
37 | "bool": { "color": "#68C244" },
38 | "self": { "color": "#68C244" },
39 | "quote": { "color": "#BCD42A" },
40 | "class": { "color": "#dee3ec" },
41 | "parent": { "color": "#FF5D38" },
42 | "number": { "color": "#A4AC21" },
43 | "attribute": { "color": "#FFFFFF", "opacity": "0.5" },
44 | "comment": { "color": "#494B4D", "font-style": "italic" },
45 | "tag_open": { "color": "#F00000" },
46 | "function": { "color": "#FFFFFF" },
47 | "variable": { "color": "#aec2e0" },
48 | "constant": { "color": "#8C4D03" },
49 | "tag_close": { "color": "#F00000" },
50 | "operators": { "color": "#F08D24" },
51 | "semi_colon": { "color": "#dee3ec" },
52 | "parenthesis": { "color": "#FFFFFF" },
53 | "return_type": { "color": "#FE648A" },
54 | "php_function": { "color": "#6367A7" },
55 | "curly_braces": { "color": "#dee3ec" },
56 | "parameter_type": { "color": "#E3093F" },
57 | "square_bracket": { "color": "#F46164" },
58 | "custom_function": { "color": "#DAEFA2" },
59 | "multi_line_comment": { "color": "#494B4D", "font-style": "italic" }
60 | },
61 |
62 |
63 | "owerri": {
64 | "style": ".CH-php{background-color:#2E2B2C;color:white}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
65 | "keywords": { "color": "#C491C4"},
66 | "cast": { "color": "#038C8C" },
67 | "null": { "color": "#ED1153" },
68 | "bool": { "color": "#ED1153" },
69 | "self": { "color": "#ED1153" },
70 | "quote": { "color": "#B866FA" },
71 | "class": { "color": "#dee3ec" },
72 | "parent": { "color": "#FF5D38" },
73 | "number": { "color": "#A4AC21" },
74 | "attribute": { "color": "#FFFFFF", "opacity": "0.5" },
75 | "comment": { "color": "#696363", "font-style": "italic" },
76 | "tag_open": { "color": "#F00000" },
77 | "function": { "color": "#FFFFFF" },
78 | "variable": { "color": "#dee3ec" },
79 | "constant": { "color": "#8C4D03" },
80 | "tag_close": { "color": "#F00000" },
81 | "operators": { "color": "#ED1153" },
82 | "semi_colon": { "color": "#dee3ec" },
83 | "parenthesis": { "color": "#FFFFFF" },
84 | "return_type": { "color": "#FE648A" },
85 | "php_function": { "color": "#6367A7" },
86 | "curly_braces": { "color": "#dee3ec" },
87 | "parameter_type": { "color": "#E3093F" },
88 | "square_bracket": { "color": "#F46164" },
89 | "custom_function": { "color": "#DAEFA2" },
90 | "multi_line_comment": { "color": "#696363", "font-style": "italic" }
91 | },
92 |
93 | "light": {
94 | "style": ".CH-php{background-color:#FFFFFF;color:black}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
95 | "keywords": { "color": "#4ea1df"},
96 | "cast": { "color": "#038C8C" },
97 | "null": { "color": "#f66153" },
98 | "bool": { "color": "#f66153" },
99 | "self": { "color": "#f66153" },
100 | "quote": { "color": "#4ea1df" },
101 | "class": { "color": "#000000" },
102 | "parent": { "color": "#FF5D38" },
103 | "number": { "color": "#A4AC21" },
104 | "attribute": { "color": "#000000", "opacity": "0.5" },
105 | "comment": { "color": "#bbbbbb", "font-style": "italic" },
106 | "tag_open": { "color": "#F00000" },
107 | "function": { "color": "#555555" },
108 | "variable": { "color": "#555555" },
109 | "constant": { "color": "#8C4D03" },
110 | "tag_close": { "color": "#F00000" },
111 | "operators": { "color": "#ED1153" },
112 | "semi_colon": { "color": "#555555" },
113 | "parenthesis": { "color": "#555555" },
114 | "return_type": { "color": "#FE648A" },
115 | "php_function": { "color": "#6367A7" },
116 | "curly_braces": { "color": "#555555" },
117 | "parameter_type": { "color": "#E3093F" },
118 | "square_bracket": { "color": "#F46164" },
119 | "custom_function": { "color": "#808dd3" },
120 | "multi_line_comment": { "color": "#bbbbbb", "font-style": "italic" }
121 | },
122 |
123 | "steady": {
124 | "style": ".CH-php{background-color:#36312C;color:#EBD1B7}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
125 | "keywords": { "color": "#95CC5E"},
126 | "cast": { "color": "#038C8C" },
127 | "null": { "color": "#DB784D" },
128 | "bool": { "color": "#DB784D" },
129 | "self": { "color": "#DB784D" },
130 | "quote": { "color": "#F8BB39" },
131 | "class": { "color": "#EBD1B7" },
132 | "parent": { "color": "#FF5D38" },
133 | "number": { "color": "#A4AC21" },
134 | "attribute": { "color": "#FFFFFF", "opacity": "0.5" },
135 | "comment": { "color": "#bbbbbb", "font-style": "italic" },
136 | "tag_open": { "color": "#F00000" },
137 | "function": { "color": "#EBD1B7" },
138 | "variable": { "color": "#EBD1B7" },
139 | "constant": { "color": "#8C4D03" },
140 | "tag_close": { "color": "#F00000" },
141 | "operators": { "color": "#ED1153" },
142 | "semi_colon": { "color": "#555555" },
143 | "parenthesis": { "color": "#EBD1B7" },
144 | "return_type": { "color": "#FE648A" },
145 | "php_function": { "color": "#6367A7" },
146 | "curly_braces": { "color": "#EBD1B7" },
147 | "parameter_type": { "color": "#E3093F" },
148 | "square_bracket": { "color": "#F46164" },
149 | "custom_function": { "color": "#60A365" },
150 | "multi_line_comment": { "color": "#bbbbbb", "font-style": "italic" }
151 | },
152 |
153 |
154 | "imo": {
155 | "style": ".CH-php{background-color:#FFFFFF;color:#555555}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
156 | "keywords": { "color": "#445588"},
157 | "cast": { "color": "#038C8C" },
158 | "null": { "color": "#008080" },
159 | "bool": { "color": "#008080" },
160 | "self": { "color": "#008080" },
161 | "quote": { "color": "#DD1144" },
162 | "class": { "color": "#555555" },
163 | "parent": { "color": "#FF5D38" },
164 | "number": { "color": "#A4AC21" },
165 | "attribute": { "color": "#000000", "opacity": "0.5" },
166 | "comment": { "color": "#bbbbbb", "font-style": "italic" },
167 | "tag_open": { "color": "#F00000" },
168 | "function": { "color": "#555555" },
169 | "variable": { "color": "#008080" },
170 | "constant": { "color": "#8C4D03" },
171 | "tag_close": { "color": "#F00000" },
172 | "operators": { "color": "#ED1153" },
173 | "semi_colon": { "color": "#555555" },
174 | "parenthesis": { "color": "#555555" },
175 | "return_type": { "color": "#FE648A" },
176 | "php_function": { "color": "#6367A7" },
177 | "curly_braces": { "color": "#555555" },
178 | "parameter_type": { "color": "#E3093F" },
179 | "square_bracket": { "color": "#F46164" },
180 | "custom_function": { "color": "#445588" },
181 | "multi_line_comment": { "color": "#bbbbbb", "font-style": "italic" }
182 | },
183 |
184 | "yola": {
185 | "style": ".CH-php{background-color:#131c26;color:white}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
186 | "keywords": { "color": "#88B7FF"},
187 | "cast": { "color": "#C71FC1" },
188 | "null": { "color": "#FFFFFF" },
189 | "bool": { "color": "#1591D7" },
190 | "self": { "color": "#88B7FF" },
191 | "quote": { "color": "#6db679" },
192 | "class": { "color": "#FFFFFF" },
193 | "parent": { "color": "#FFFFFF" },
194 | "number": { "color": "#FF4F51" },
195 | "attribute": { "color": "#FFFFFF", "opacity": "0.5" },
196 | "comment": { "color": "#B7B7B7", "font-style": "italic" },
197 | "tag_open": { "color": "#EF62FC" },
198 | "function": { "color": "#88B7FF" },
199 | "variable": { "color": "#DB97E4" },
200 | "constant": { "color": "#FFFFFF" },
201 | "tag_close": { "color": "#EF62FC" },
202 | "operators": { "color": "#FFFFFF" },
203 | "semi_colon": { "color": "#555555" },
204 | "parenthesis": { "color": "#FFFFFF" },
205 | "return_type": { "color": "#FE648A" },
206 | "php_function": { "color": "#6367A7" },
207 | "curly_braces": { "color": "#FFFFFF" },
208 | "parameter_type": { "color": "#f2368a" },
209 | "square_bracket": { "color": "#FFFFFF" },
210 | "custom_function": { "color": "#FFC13B" },
211 | "multi_line_comment": { "color": "#B7B7B7", "font-style": "italic" }
212 | },
213 |
214 | "git": {
215 | "style": ".CH-php{background-color:#FFFFFF;color:#555555}.CH-php .strip span{color:inherit !important;all:initial !important;all:unset !important}td[unselectable]{ user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none; }",
216 | "keywords": { "color": "#a71d5d"},
217 | "cast": { "color": "#C71FC1" },
218 | "null": { "color": "#FFFFFF" },
219 | "bool": { "color": "#1591D7" },
220 | "self": { "color": "#5555555" },
221 | "quote": { "color": "#183691" },
222 | "class": { "color": "#795da3" },
223 | "parent": { "color": "#555555" },
224 | "number": { "color": "#FF4F51" },
225 | "attribute": { "color": "#000000", "opacity": "0.5" },
226 | "comment": { "color": "#969896", "font-style": "italic" },
227 | "tag_open": { "color": "#555555" },
228 | "function": { "color": "#a71d5d" },
229 | "variable": { "color": "#555555" },
230 | "constant": { "color": "#0086b3" },
231 | "tag_close": { "color": "#555555" },
232 | "operators": { "color": "#a71d5d" },
233 | "semi_colon": { "color": "#555555" },
234 | "parenthesis": { "color": "#555555" },
235 | "return_type": { "color": "#0086b3" },
236 | "php_function": { "color": "#0086b3" },
237 | "curly_braces": { "color": "#555555" },
238 | "parameter_type": { "color": "#0086b3" },
239 | "square_bracket": { "color": "#555555" },
240 | "custom_function": { "color": "#795da3" },
241 | "multi_line_comment": { "color": "#969896", "font-style": "italic" }
242 | }
243 | }
244 |
245 |
246 |
247 |
248 |
--------------------------------------------------------------------------------