PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
← All changes | libraries/csstidy/class.csstidy.php +274 -406 1.14 → 3.4 View file →
@@ -7,60 +7,14 @@
7 7 * @author Florian Schmitz, Brett Zamir, Nikolay Matsievsky, Cedric Morin, Christopher Finke, Mark Scherer, Tobias Bäthge
8 8 * @since 1.0.0
9 9 */
10 10
11 +declare(strict_types=1);
12 +
11 13 // Prohibit direct script loading.
12 14 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13 15
14 16 /**
15 - * CSSTidy - CSS Parser and Optimiser
16 - *
17 - * CSS Parser class
18 - *
19 - * Copyright 2005, 2006, 2007 Florian Schmitz
20 - *
21 - * This file is part of CSSTidy.
22 - *
23 - * CSSTidy is free software; you can redistribute it and/or modify
24 - * it under the terms of the GNU Lesser General Public License as published by
25 - * the Free Software Foundation; either version 2.1 of the License, or
26 - * (at your option) any later version.
27 - *
28 - * CSSTidy is distributed in the hope that it will be useful,
29 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 - * GNU Lesser General Public License for more details.
32 - *
33 - * You should have received a copy of the GNU Lesser General Public License
34 - * along with this program. If not, see <https://www.gnu.org/licenses/>.
35 - *
36 - * @license https://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
37 - * @package CSSTidy
38 - * @author Florian Schmitz (floele at gmail dot com) 2005-2007
39 - * @author Brett Zamir (brettz9 at yahoo dot com) 2007
40 - * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
41 - * @author Cedric Morin (cedric at yterium dot com) 2010-2012
42 - * @author Christopher Finke (cfinke at gmail.com) 2012
43 - * @author Mark Scherer (remove $GLOBALS once and for all + PHP5.4 comp) 2012
44 - */
45 -
46 -/**
47 - * Defines constants.
48 - *
49 - * @TODO: Make these class constants of CSSTidy.
50 - * @since 1.0.0
51 - */
52 -define( 'AT_START', 1 );
53 -define( 'AT_END', 2 );
54 -define( 'SEL_START', 3 );
55 -define( 'SEL_END', 4 );
56 -define( 'PROPERTY', 5 );
57 -define( 'VALUE', 6 );
58 -define( 'COMMENT', 7 );
59 -define( 'IMPORTANT_COMMENT', 8 );
60 -define( 'DEFAULT_AT', 41 );
61 -
62 -/**
63 17 * Load the class for printing CSS code.
64 18 *
65 19 * @since 1.0.0
66 20 */
@@ -86,80 +40,79 @@
86 40 */
87 41 class TablePress_CSSTidy {
88 42
89 43 /**
44 + * Defines constants.
45 + *
46 + * @since 1.0.0
47 + */
48 + public const AT_START = 1;
49 + public const AT_END = 2;
50 + public const SEL_START = 3;
51 + public const SEL_END = 4;
52 + public const PROPERTY = 5;
53 + public const VALUE = 6;
54 + public const COMMENT = 7;
55 + public const IMPORTANT_COMMENT = 8;
56 + public const DEFAULT_AT = 41;
57 +
58 + /**
90 59 * The parsed CSS.
91 60 *
92 61 * This array is empty if preserve_css is on.
93 62 *
94 63 * @since 1.0.0
95 - * @var array
96 64 */
97 - public $css = array();
65 + public array $css = array();
98 66
99 67 /**
100 68 * The raw parsed CSS.
101 69 *
102 70 * @since 1.0.0
103 - * @var array
104 71 */
105 - public $tokens = array();
72 + public array $tokens = array();
106 73
107 74 /**
108 75 * Instance of the CSS Printer class.
109 76 *
110 77 * @since 1.0.0
111 - * @var TablePress_CSSTidy_print
112 78 */
113 - public $print;
79 + public \TablePress_CSSTidy_Print $print;
114 80
115 81 /**
116 82 * Instance of the CSS Optimiser class.
117 83 *
118 84 * @since 1.0.0
119 - * @var TablePress_CSSTidy_optimise
120 85 */
121 - public $optimise;
86 + public \TablePress_CSSTidy_Optimise $optimise;
122 87
123 88 /**
124 89 * The CSS charset.
125 90 *
126 91 * @since 1.0.0
127 - * @var string
128 92 */
129 - public $charset = '';
93 + public string $charset = '';
130 94
131 95 /**
132 96 * All @import URLs.
133 97 *
134 98 * @since 1.0.0
135 - * @var array
136 99 */
137 - public $import = array();
100 + public array $import = array();
138 101
139 102 /**
140 103 * The namespace.
141 104 *
142 105 * @since 1.0.0
143 - * @var string
144 106 */
145 - public $namespace = '';
107 + public string $namespace = '';
146 108
147 109 /**
148 - * The CSSTidy version.
149 - *
150 - * @since 1.0.0
151 - * @var string
152 - */
153 - public $version = '1.5.3';
154 -
155 - /**
156 110 * The settings.
157 111 *
158 112 * @since 1.0.0
159 - * @var array
160 113 */
161 - protected $settings = array();
114 + protected array $settings = array();
162 115
163 116 /**
164 117 * The parser-status.
165 118 *
@@ -171,25 +124,23 @@
171 124 * - ic = in comment (ignore everything)
172 125 * - at = in @-block
173 126 *
174 127 * @since 1.0.0
175 - * @var string
176 128 */
177 - protected $status = 'is';
129 + protected string $status = 'is';
178 130
179 131 /**
180 132 * The current at rule (@media).
181 133 *
182 134 * @since 1.0.0
183 - * @var string
184 135 */
185 - public $at = '';
136 + public string $at = '';
186 137
187 138 /**
188 139 * The at rule for next selector (during @font-face or other @).
189 140 *
190 141 * @since 1.0.0
191 - * @var string
142 + * @var string|int
192 143 */
193 144 protected $next_selector_at = '';
194 145
195 146 /**
@@ -195,35 +146,31 @@
195 146 /**
196 147 * The current selector.
197 148 *
198 149 * @since 1.0.0
199 - * @var string
200 150 */
201 - public $selector = '';
151 + public string $selector = '';
202 152
203 153 /**
204 154 * The current property.
205 155 *
206 156 * @since 1.0.0
207 - * @var string
208 157 */
209 - public $property = '';
158 + public string $property = '';
210 159
211 160 /**
212 161 * The position of , in selectors.
213 162 *
214 163 * @since 1.0.0
215 - * @var array
216 164 */
217 - protected $sel_separate = array();
165 + protected array $sel_separate = array();
218 166
219 167 /**
220 168 * The current value.
221 169 *
222 170 * @since 1.0.0
223 - * @var string
224 171 */
225 - public $value = '';
172 + public string $value = '';
226 173
227 174 /**
228 175 * The current sub-value.
229 176 *
@@ -232,107 +179,94 @@
232 179 * "url(foo.png)", "red", and "no-repeat" are sub-values,
233 180 * separated by whitespace.
234 181 *
235 182 * @since 1.0.0
236 - * @var string
237 183 */
238 - public $sub_value = '';
184 + public string $sub_value = '';
239 185
240 186 /**
241 187 * All sub-values for a property.
242 188 *
243 189 * @since 1.0.0
244 - * @var array
245 190 */
246 - protected $sub_value_arr = array();
191 + protected array $sub_value_arr = array();
247 192
248 193 /**
249 194 * The stack of characters that opened the current strings.
250 195 *
251 196 * @since 1.0.0
252 - * @var array
253 197 */
254 - public $str_char = array();
198 + public array $str_char = array();
255 199
256 200 /**
257 201 * [$cur_string description]
258 202 *
259 203 * @since 1.0.0
260 - * @var array
261 204 */
262 - public $cur_string = array();
205 + public array $cur_string = array();
263 206
264 207 /**
265 208 * Status from which the parser switched to ic or instr
266 209 *
267 210 * @since 1.0.0
268 - * @var array
269 211 */
270 - protected $from = array();
212 + protected array $from = array();
271 213
272 214 /**
273 215 * True if in invalid at-rule.
274 216 *
275 217 * @since 1.0.0
276 - * @var bool
277 218 */
278 - protected $invalid_at = false;
219 + protected bool $invalid_at = false;
279 220
280 221 /**
281 222 * True if something has been added to the current selector.
282 223 *
283 224 * @since 1.0.0
284 - * @var bool
285 225 */
286 - protected $added = false;
226 + protected bool $added = false;
287 227
288 228 /**
289 229 * The message log.
290 230 *
291 231 * @since 1.0.0
292 - * @var array
293 232 */
294 - public $log = array();
233 + public array $log = array();
295 234
296 235 /**
297 236 * The line number.
298 237 *
299 238 * @since 1.0.0
300 - * @var int
301 239 */
302 - protected $line = 1;
240 + protected int $line = 1;
303 241
304 242 /**
305 243 * Marks if we need to leave quotes for a string.
306 244 *
307 245 * @since 1.0.0
308 - * @var array
309 246 */
310 - protected $quoted_string = array();
247 + protected array $quoted_string = array();
311 248
312 249 /**
313 250 * List of tokens.
314 251 *
315 252 * @since 1.0.0
316 - * @var string
317 253 */
318 - protected $tokens_list = '';
254 + protected string $tokens_list = '';
319 255
320 256 /**
321 257 * Various CSS Data for CSSTidy.
322 258 *
323 259 * @since 1.0.0
324 - * @var array
325 260 */
326 - public $data = array();
261 + public array $data = array();
327 262
328 263 /**
329 264 * The output templates.
330 265 *
331 266 * @since 1.0.0
332 - * @var array
333 267 */
334 - public $template = array();
268 + public array $template = array();
335 269
336 270 /**
337 271 * Loads standard template and sets default settings.
338 272 *
@@ -338,15 +272,15 @@
338 272 *
339 273 * @since 1.0.0
340 274 */
341 275 public function __construct() {
342 - include __DIR__ . '/data.inc.php';
343 - $this->data = $data;
276 + $this->data = require __DIR__ . '/data.inc.php';
344 277
345 278 $this->settings['remove_bslash'] = true;
346 279 $this->settings['compress_colors'] = true;
347 280 $this->settings['compress_font-weight'] = true;
348 281 $this->settings['lowercase_s'] = false;
282 +
349 283 /*
350 284 * 1 common shorthands optimization
351 285 * 2 + font property optimization
352 286 * 3 + background property optimization
@@ -354,13 +288,15 @@
354 288 $this->settings['optimise_shorthands'] = 1;
355 289 $this->settings['remove_last_;'] = true;
356 290 // Rewrite all properties with lower case, better for later gzipping.
357 291 $this->settings['case_properties'] = 1;
292 +
358 293 /*
359 - * Sort properties in alpabetic order, better for later gzipping,
360 - * but can cause trouble in case of overiding same properties or using hacks.
294 + * Sort properties in alphabetic order, better for later gzipping,
295 + * but can cause trouble in case of overriding same properties or using hacks.
361 296 */
362 297 $this->settings['sort_properties'] = false;
298 +
363 299 /*
364 300 * 1, 3, 5, etc -- Enable sorting selectors inside @media: a{}b{}c{}.
365 301 * 2, 5, 8, etc -- Enable sorting selectors inside one CSS declaration: a,b,c{}.
366 302 * Preserve order by default cause it can break functionality.
@@ -376,16 +312,16 @@
376 312 $this->settings['timestamp'] = false;
377 313 $this->settings['template'] = ''; // say that property exists.
378 314 $this->set_cfg( 'template', 'default' ); // Call load_template.
379 315
380 - $this->print = new TablePress_CSSTidy_print( $this );
381 - $this->optimise = new TablePress_CSSTidy_optimise( $this );
316 + $this->print = new TablePress_CSSTidy_Print( $this );
317 + $this->optimise = new TablePress_CSSTidy_Optimise( $this );
382 318
383 319 $this->tokens_list = &$this->data['csstidy']['tokens'];
384 320 }
385 321
386 322 /**
387 - * Get the value of a setting.
323 + * Gets the value of a setting.
388 324 *
389 325 * @since 1.0.0
390 326 *
391 327 * @param string $setting Setting to get.
@@ -390,9 +326,9 @@
390 326 *
391 327 * @param string $setting Setting to get.
392 328 * @return string|int|bool Value of the setting.
393 329 */
394 - public function get_cfg( $setting ) {
330 + public function get_cfg( string $setting ) /* : string|int|bool */ {
395 331 if ( isset( $this->settings[ $setting ] ) ) {
396 332 return $this->settings[ $setting ];
397 333 }
398 334 return false;
@@ -398,44 +334,21 @@
398 334 return false;
399 335 }
400 336
401 337 /**
402 - * Load a template.
338 + * Sets the value of a setting.
403 339 *
404 340 * @since 1.0.0
405 341 *
406 - * @param string $template Template used by set_cfg to load a template via a configuration setting.
342 + * @param string $setting Setting.
343 + * @param string|int|bool|null $value Optional. Value of the setting.
344 + * @return bool Whether the setting was set.
407 345 */
408 - protected function _load_template( $template ) {
409 - switch ( $template ) {
410 - case 'default':
411 - $this->load_template( 'default' );
412 - break;
413 - case 'highest':
414 - case 'high':
415 - case 'low':
416 - $this->load_template( $template . '_compression' );
417 - break;
418 - default:
419 - $this->load_template( $template );
420 - break;
421 - }
422 - }
423 -
424 - /**
425 - * Set the value of a setting.
426 - *
427 - * @since 1.0.0
428 - *
429 - * @param string $setting Setting.
430 - * @param mixed $value Optional. Value of the setting.
431 - * @return bool [return value]
432 - */
433 - public function set_cfg( $setting, $value = null ) {
346 + public function set_cfg( string $setting, $value = null ): bool {
434 347 if ( isset( $this->settings[ $setting ] ) && '' !== $value ) {
435 348 $this->settings[ $setting ] = $value;
436 349 if ( 'template' === $setting ) {
437 - $this->_load_template( $this->settings['template'] );
350 + $this->load_template( $this->settings['template'] );
438 351 }
439 352 return true;
440 353 }
441 354 return false;
@@ -445,29 +358,29 @@
445 358 * Adds a token to $this->tokens.
446 359 *
447 360 * @since 1.0.0
448 361 *
449 - * @param mixed $type Type.
450 - * @param string $data Data.
451 - * @param bool $do Optional. Add a token even if preserve_css is off.
362 + * @param mixed $type Type.
363 + * @param string $data Data.
364 + * @param bool $force Optional. Add a token even if preserve_css is off.
452 365 */
453 - public function _add_token( $type, $data, $do = false ) {
454 - if ( $this->get_cfg( 'preserve_css' ) || $do ) {
366 + public function _add_token( $type, string $data, bool $force = false ): void {
367 + if ( $this->get_cfg( 'preserve_css' ) || $force ) {
455 368 // nested @...: if opening a new part we just closed, remove the previous closing instead of adding opening.
456 - if ( AT_START === $type
369 + if ( self::AT_START === $type
457 370 && count( $this->tokens )
458 - && ( $last = end( $this->tokens ) )
459 - && AT_END === $last[0]
371 + && ( $last = end( $this->tokens ) ) // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found,Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
372 + && self::AT_END === $last[0]
460 373 && trim( $data ) === $last[1] ) {
461 374 array_pop( $this->tokens );
462 375 } else {
463 - $this->tokens[] = array( $type, ( COMMENT === $type || IMPORTANT_COMMENT === $type ) ? $data : trim( $data ) );
376 + $this->tokens[] = array( $type, ( self::COMMENT === $type || self::IMPORTANT_COMMENT === $type ) ? $data : trim( $data ) );
464 377 }
465 378 }
466 379 }
467 380
468 381 /**
469 - * Add a message to the message log.
382 + * Adds a message to the message log.
470 383 *
471 384 * @since 1.0.0
472 385 *
473 386 * @param string $message Message.
@@ -473,9 +386,9 @@
473 386 * @param string $message Message.
474 387 * @param string $type Type.
475 388 * @param int $line Optional. Line number. -1 will use the current line.
476 389 */
477 - public function log( $message, $type, $line = -1 ) {
390 + public function log( string $message, string $type, int $line = -1 ): void {
478 391 if ( -1 === $line ) {
479 392 $line = $this->line;
480 393 }
481 394 $line = (int) $line;
@@ -488,30 +401,30 @@
488 401 }
489 402 }
490 403
491 404 /**
492 - * Parse Unicode notations and find a replacement character.
405 + * Parses Unicode notations and find a replacement character.
493 406 *
494 407 * @since 1.0.0
495 408 *
496 - * @param string $string String.
497 - * @param int $i i.
409 + * @param string $a_string String.
410 + * @param int $i i.
498 411 * @return string [return value]
499 412 */
500 - public function _unicode( &$string, &$i ) {
413 + public function _unicode( string &$a_string, int &$i ): string {
501 414 ++$i;
502 415 $add = '';
503 416 $replaced = false;
504 417
505 - while ( $i < strlen( $string ) && ( ctype_xdigit( $string[ $i ] ) || ctype_space( $string[ $i ] ) ) && strlen( $add ) < 6 ) {
506 - $add .= $string[ $i ];
507 - if ( ctype_space( $string[ $i ] ) ) {
418 + while ( $i < strlen( $a_string ) && ( ctype_xdigit( $a_string[ $i ] ) || ctype_space( $a_string[ $i ] ) ) && strlen( $add ) < 6 ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
419 + $add .= $a_string[ $i ];
420 + if ( ctype_space( $a_string[ $i ] ) ) {
508 421 break;
509 422 }
510 - $i++;
423 + ++$i;
511 424 }
512 425
513 - if ( hexdec( $add ) > 47 && hexdec( $add ) < 58 || hexdec( $add ) > 64 && hexdec( $add ) < 91 || hexdec( $add ) > 96 && hexdec( $add ) < 123 ) {
426 + if ( ( hexdec( $add ) > 47 && hexdec( $add ) < 58 ) || ( hexdec( $add ) > 64 && hexdec( $add ) < 91 ) || ( hexdec( $add ) > 96 && hexdec( $add ) < 123 ) ) {
514 427 $this->log( 'Replaced unicode notation: Changed \\' . $add . ' to ' . chr( hexdec( $add ) ), 'Information' );
515 428 $add = chr( hexdec( $add ) );
516 429 $replaced = true;
517 430 } else {
@@ -517,13 +430,13 @@
517 430 } else {
518 431 $add = trim( '\\' . $add );
519 432 }
520 433
521 - if ( @ctype_xdigit( $string[ $i + 1 ] ) && ctype_space( $string[ $i ] ) && ! $replaced || ! ctype_space( $string[ $i ] ) ) {
522 - $i--;
434 + if ( ( @ctype_xdigit( $a_string[ $i + 1 ] ) && ctype_space( $a_string[ $i ] ) && ! $replaced ) || ! ctype_space( $a_string[ $i ] ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
435 + --$i;
523 436 }
524 437
525 - if ( '\\' !== $add || ! $this->get_cfg( 'remove_bslash' ) || false !== strpos( $this->tokens_list, $string[ $i + 1 ] ) ) {
438 + if ( '\\' !== $add || ! $this->get_cfg( 'remove_bslash' ) || str_contains( $this->tokens_list, $a_string[ $i + 1 ] ) ) {
526 439 return $add;
527 440 }
528 441
529 442 if ( '\\' === $add ) {
@@ -533,65 +446,20 @@
533 446 return '';
534 447 }
535 448
536 449 /**
537 - * Write formatted output to a file.
538 - *
539 - * @since 1.0.0
540 - *
541 - * @param string $filename File name.
542 - */
543 - public function write_page( $filename ) {
544 - $this->write( $filename, true );
545 - }
546 -
547 - /**
548 - * Write plain output to a file.
549 - *
550 - * @since 1.0.0
551 - *
552 - * @param string $filename File name.
553 - * @param bool $formatted Optional. Whether to print formatted or not.
554 - * @param string $doctype Optional. When printing formatted, is a shorthand for the document type.
555 - * @param bool $externalcss Optional. When printing formatted, indicates whether styles to be attached internally or as an external stylesheet.
556 - * @param string $title Optional. When printing formatted, is the title to be added in the head of the document.
557 - * @param string $lang Optional. When printing formatted, gives a two-letter language code to be added to the output.
558 - */
559 - public function write( $filename, $formatted = false, $doctype = 'xhtml1.1', $externalcss = true, $title = '', $lang = 'en' ) {
560 - $filename .= ( $formatted ) ? '.xhtml' : '.css';
561 -
562 - if ( ! is_dir( 'temp' ) ) {
563 - $madedir = mkdir( 'temp' );
564 - if ( ! $madedir ) {
565 - print 'Could not make directory "temp" in ' . __DIR__;
566 - exit;
567 - }
568 - }
569 - $handle = fopen( 'temp/' . $filename, 'w' );
570 - if ( $handle ) {
571 - if ( ! $formatted ) {
572 - fwrite( $handle, $this->print->plain() );
573 - } else {
574 - fwrite( $handle, $this->print->formatted_page( $doctype, $externalcss, $title, $lang ) );
575 - }
576 - }
577 - fclose( $handle );
578 - }
579 -
580 - /**
581 450 * Loads a new template.
582 451 *
583 452 * @since 1.0.0
584 453 *
585 - * @link http://csstidy.sourceforge.net/templates.php
454 + * @link https://csstidy.sourceforge.net/templates.php
586 455 *
587 - * @param string $content Either file name (if $from_file is true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default".
456 + * @param string $content Either file name (if $from_file is true), content of a template file, "default", "low", high", or "highest".
588 457 * @param bool $from_file Optional. Uses $content as filename if true.
589 458 */
590 - protected function load_template( $content, $from_file = true ) {
591 - $predefined_templates = &$this->data['csstidy']['predefined_templates'];
592 - if ( in_array( $content, array( 'default', 'low_compression', 'high_compression', 'highest_compression' ), true ) ) {
593 - $this->template = $predefined_templates[ $content ];
459 + protected function load_template( string $content, bool $from_file = true ): void {
460 + if ( in_array( $content, array( 'default', 'low', 'high', 'highest' ), true ) ) {
461 + $this->template = $this->data['csstidy']['predefined_templates'][ $content ];
594 462 return;
595 463 }
596 464
597 465 if ( $from_file ) {
@@ -596,16 +464,12 @@
596 464
597 465 if ( $from_file ) {
598 466 $content = strip_tags( file_get_contents( $content ), '<span>' );
599 467 }
468 +
600 469 // Unify newlines (because the output also only uses \n).
601 470 $content = str_replace( "\r\n", "\n", $content );
602 - $template = explode( '|', $content );
603 -
604 - $template_count = count( $template );
605 - for ( $i = 0; $i < $template_count; $i++ ) {
606 - $this->template[ $i ] = $template[ $i ];
607 - }
471 + $this->template = explode( '|', $content );
608 472 }
609 473
610 474 /**
611 475 * Starts parsing from URL.
@@ -612,12 +476,16 @@
612 476 *
613 477 * @since 1.0.0
614 478 *
615 479 * @param string $url URL.
616 - * @return bool
480 + * @return bool Whether the CSS code could be parsed successfully.
617 481 */
618 - protected function parse_from_url( $url ) {
619 - return $this->parse( @file_get_contents( $url ) );
482 + protected function parse_from_url( string $url ): bool {
483 + $data = file_get_contents( $url );
484 + if ( false === $data ) {
485 + return false;
486 + }
487 + return $this->parse( $data );
620 488 }
621 489
622 490 /**
623 491 * Checks if there is a token at the current position.
@@ -623,14 +491,14 @@
623 491 * Checks if there is a token at the current position.
624 492 *
625 493 * @since 1.0.0
626 494 *
627 - * @param string $string String.
495 + * @param string $a_string String.
628 496 * @param int $i i.
629 497 * @return bool [return value]
630 498 */
631 - protected function is_token( &$string, $i ) {
632 - return ( false !== strpos( $this->tokens_list, $string[ $i ] ) && ! $this->escaped( $string, $i ) );
499 + protected function is_token( string &$a_string, int $i ): bool {
500 + return ( str_contains( $this->tokens_list, $a_string[ $i ] ) && ! $this->escaped( $a_string, $i ) );
633 501 }
634 502
635 503 /**
636 504 * Parses CSS in a string. The output is saved as an array in $this->css.
@@ -636,27 +504,27 @@
636 504 * Parses CSS in a string. The output is saved as an array in $this->css.
637 505 *
638 506 * @since 1.0.0
639 507 *
640 - * @param string $string The CSS code.
641 - * @return bool [return value]
508 + * @param string $a_string The CSS code.
509 + * @return bool Whether the CSS code could be parsed successfully.
642 510 */
643 - public function parse( $string ) {
511 + public function parse( string $a_string ): bool {
644 512 // Temporarily set locale to en_US in order to handle floats properly.
645 - $old = @setlocale( LC_ALL, 0 );
646 - @setlocale( LC_ALL, 'C' );
513 + $old = @setlocale( LC_ALL, '0' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
514 + @setlocale( LC_ALL, 'C' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
647 515
648 516 $at_rules = &$this->data['csstidy']['at_rules'];
649 517 $quoted_string_properties = &$this->data['csstidy']['quoted_string_properties'];
650 518
651 519 $this->css = array();
652 - $this->print->input_css = $string;
653 - $string = str_replace( "\r\n", "\n", $string ) . ' ';
520 + $this->print->input_css = $a_string;
521 + $a_string = str_replace( "\r\n", "\n", $a_string ) . ' ';
654 522 $cur_comment = '';
655 523 $cur_at = '';
656 524
657 - for ( $i = 0, $size = strlen( $string ); $i < $size; $i++ ) {
658 - if ( "\n" === $string[ $i ] || "\r" === $string[ $i ] ) {
525 + for ( $i = 0, $size = strlen( $a_string ); $i < $size; $i++ ) {
526 + if ( "\n" === $a_string[ $i ] || "\r" === $a_string[ $i ] ) {
659 527 ++$this->line;
660 528 }
661 529
662 530 switch ( $this->status ) {
@@ -661,46 +529,45 @@
661 529
662 530 switch ( $this->status ) {
663 531 /* Case in at-block */
664 532 case 'at':
665 - if ( $this->is_token( $string, $i ) ) {
666 - if ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] ) {
533 + if ( $this->is_token( $a_string, $i ) ) {
534 + if ( '/' === $a_string[ $i ] && '*' === @$a_string[ $i + 1 ] ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
667 535 $this->status = 'ic';
668 536 ++$i;
669 537 $this->from[] = 'at';
670 - } elseif ( '{' === $string[ $i ] ) {
538 + } elseif ( '{' === $a_string[ $i ] ) {
671 539 $this->status = 'is';
672 540 $this->at = $this->css_new_media_section( $this->at, $cur_at );
673 - $this->_add_token( AT_START, $this->at );
674 - } elseif ( ',' === $string[ $i ] ) {
541 + $this->_add_token( self::AT_START, $this->at );
542 + } elseif ( ',' === $a_string[ $i ] ) {
675 543 $cur_at = trim( $cur_at ) . ',';
676 - } elseif ( '\\' === $string[ $i ] ) {
677 - $cur_at .= $this->_unicode( $string, $i );
544 + } elseif ( '\\' === $a_string[ $i ] ) {
545 + $cur_at .= $this->_unicode( $a_string, $i );
546 + } elseif ( in_array( $a_string[ $i ], array( '(', ')', ':', '.', '/' ), true ) ) {
547 + // Fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5).
548 + // '/' is included for ratios in Opera: (-o-min-device-pixel-ratio: 3/2).
549 + $cur_at .= $a_string[ $i ];
678 550 }
679 - // Fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5)
680 - // '/' is included for ratios in Opera: (-o-min-device-pixel-ratio: 3/2)
681 - elseif ( in_array( $string[ $i ], array( '(', ')', ':', '.', '/' ), true ) ) {
682 - $cur_at .= $string[ $i ];
683 - }
684 551 } else {
685 552 $lastpos = strlen( $cur_at ) - 1;
686 - if ( ! ( ( ctype_space( $cur_at[ $lastpos ] ) || $this->is_token( $cur_at, $lastpos ) && ',' === $cur_at[ $lastpos ] ) && ctype_space( $string[ $i ] ) ) ) {
687 - $cur_at .= $string[ $i ];
553 + if ( ! ( ( ctype_space( $cur_at[ $lastpos ] ) || ( $this->is_token( $cur_at, $lastpos ) && ',' === $cur_at[ $lastpos ] ) ) && ctype_space( $a_string[ $i ] ) ) ) {
554 + $cur_at .= $a_string[ $i ];
688 555 }
689 556 }
690 557 break;
691 558 /* Case in-selector */
692 559 case 'is':
693 - if ( $this->is_token( $string, $i ) ) {
694 - if ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] && '' === trim( $this->selector ) ) {
560 + if ( $this->is_token( $a_string, $i ) ) {
561 + if ( '/' === $a_string[ $i ] && '*' === @$a_string[ $i + 1 ] && '' === trim( $this->selector ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
695 562 $this->status = 'ic';
696 563 ++$i;
697 564 $this->from[] = 'is';
698 - } elseif ( '@' === $string[ $i ] && '' === trim( $this->selector ) ) {
565 + } elseif ( '@' === $a_string[ $i ] && '' === trim( $this->selector ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
699 566 // Check for at-rule.
700 567 $this->invalid_at = true;
701 568 foreach ( $at_rules as $name => $type ) {
702 - if ( ! strcasecmp( substr( $string, $i + 1, strlen( $name ) ), $name ) ) {
569 + if ( ! strcasecmp( substr( $a_string, $i + 1, strlen( $name ) ), $name ) ) {
703 570 if ( 'at' === $type ) {
704 571 $cur_at = '@' . $name;
705 572 } else {
706 573 $this->selector = '@' . $name;
@@ -705,9 +572,9 @@
705 572 } else {
706 573 $this->selector = '@' . $name;
707 574 }
708 575 if ( 'atis' === $type ) {
709 - $this->next_selector_at = ( $this->next_selector_at ? $this->next_selector_at : ( $this->at ? $this->at : DEFAULT_AT ) );
576 + $this->next_selector_at = ( $this->next_selector_at ? $this->next_selector_at : ( $this->at ? $this->at : self::DEFAULT_AT ) );
710 577 $this->at = $this->css_new_media_section( $this->at, ' ', true );
711 578 $type = 'is';
712 579 }
713 580 $this->status = $type;
@@ -720,23 +587,23 @@
720 587 if ( $this->invalid_at ) {
721 588 $this->selector = '@';
722 589 $invalid_at_name = '';
723 590 for ( $j = $i + 1; $j < $size; ++$j ) {
724 - if ( ! ctype_alpha( $string[ $j ] ) ) {
591 + if ( ! ctype_alpha( $a_string[ $j ] ) ) {
725 592 break;
726 593 }
727 - $invalid_at_name .= $string[ $j ];
594 + $invalid_at_name .= $a_string[ $j ];
728 595 }
729 596 $this->log( 'Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning' );
730 597 }
731 - } elseif ( ( '"' === $string[ $i ] || "'" === $string[ $i ] ) ) {
732 - $this->cur_string[] = $string[ $i ];
598 + } elseif ( ( '"' === $a_string[ $i ] || "'" === $a_string[ $i ] ) ) {
599 + $this->cur_string[] = $a_string[ $i ];
733 600 $this->status = 'instr';
734 - $this->str_char[] = $string[ $i ];
601 + $this->str_char[] = $a_string[ $i ];
735 602 $this->from[] = 'is';
736 603 /* Fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
737 - $this->quoted_string[] = ( '=' === $string[ $i - 1 ] );
738 - } elseif ( $this->invalid_at && ';' === $string[ $i ] ) {
604 + $this->quoted_string[] = ( '=' === $a_string[ $i - 1 ] );
605 + } elseif ( $this->invalid_at && ';' === $a_string[ $i ] ) {
739 606 $this->invalid_at = false;
740 607 $this->status = 'is';
741 608 if ( $this->next_selector_at ) {
742 609 $this->at = $this->css_close_media_section( $this->at );
@@ -742,57 +609,57 @@
742 609 $this->at = $this->css_close_media_section( $this->at );
743 610 $this->at = $this->css_new_media_section( $this->at, $this->next_selector_at );
744 611 $this->next_selector_at = '';
745 612 }
746 - } elseif ( '{' === $string[ $i ] ) {
613 + } elseif ( '{' === $a_string[ $i ] ) {
747 614 $this->status = 'ip';
748 615 if ( '' === $this->at ) {
749 - $this->at = $this->css_new_media_section( $this->at, DEFAULT_AT );
616 + $this->at = $this->css_new_media_section( $this->at, (string) self::DEFAULT_AT );
750 617 }
751 618 $this->selector = $this->css_new_selector( $this->at, $this->selector );
752 - $this->_add_token( SEL_START, $this->selector );
619 + $this->_add_token( self::SEL_START, $this->selector );
753 620 $this->added = false;
754 - } elseif ( '}' === $string[ $i ] ) {
755 - $this->_add_token( AT_END, $this->at );
621 + } elseif ( '}' === $a_string[ $i ] ) {
622 + $this->_add_token( self::AT_END, $this->at );
756 623 $this->at = $this->css_close_media_section( $this->at );
757 624 $this->selector = '';
758 625 $this->sel_separate = array();
759 - } elseif ( ',' === $string[ $i ] ) {
626 + } elseif ( ',' === $a_string[ $i ] ) {
760 627 $this->selector = trim( $this->selector ) . ',';
761 628 $this->sel_separate[] = strlen( $this->selector );
762 - } elseif ( '\\' === $string[ $i ] ) {
763 - $this->selector .= $this->_unicode( $string, $i );
764 - } elseif ( '*' === $string[ $i ] && @in_array( $string[ $i + 1 ], array( '.', '#', '[', ':' ), true ) && ( 0 === $i || '/' !== $string[ $i - 1 ] ) ) {
629 + } elseif ( '\\' === $a_string[ $i ] ) {
630 + $this->selector .= $this->_unicode( $a_string, $i );
631 + } elseif ( '*' === $a_string[ $i ] && @in_array( $a_string[ $i + 1 ], array( '.', '#', '[', ':' ), true ) && ( 0 === $i || '/' !== $a_string[ $i - 1 ] ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,Generic.CodeAnalysis.EmptyStatement.DetectedElseif
765 632 // Remove unnecessary universal selector, FS#147, but not comment in selector.
766 633 } else {
767 - $this->selector .= $string[ $i ];
634 + $this->selector .= $a_string[ $i ];
768 635 }
769 636 } else {
770 637 $lastpos = strlen( $this->selector ) - 1;
771 - if ( -1 === $lastpos || ! ( ( ctype_space( $this->selector[ $lastpos ] ) || $this->is_token( $this->selector, $lastpos ) && ',' === $this->selector[ $lastpos ] ) && ctype_space( $string[ $i ] ) ) ) {
772 - $this->selector .= $string[ $i ];
638 + if ( -1 === $lastpos || ! ( ( ctype_space( $this->selector[ $lastpos ] ) || ( $this->is_token( $this->selector, $lastpos ) && ',' === $this->selector[ $lastpos ] ) ) && ctype_space( $a_string[ $i ] ) ) ) {
639 + $this->selector .= $a_string[ $i ];
773 640 }
774 641 }
775 642 break;
776 643 /* Case in-property */
777 644 case 'ip':
778 - if ( $this->is_token( $string, $i ) ) {
779 - if ( ( ':' === $string[ $i ] || '=' === $string[ $i ] ) && '' !== $this->property ) {
645 + if ( $this->is_token( $a_string, $i ) ) {
646 + if ( ( ':' === $a_string[ $i ] || '=' === $a_string[ $i ] ) && '' !== $this->property ) {
780 647 $this->status = 'iv';
781 648 if ( ! $this->get_cfg( 'discard_invalid_properties' ) || $this->property_is_valid( $this->property ) ) {
782 649 $this->property = $this->css_new_property( $this->at, $this->selector, $this->property );
783 650 $this->property = strtolower( $this->property );
784 - $this->_add_token( PROPERTY, $this->property );
651 + $this->_add_token( self::PROPERTY, $this->property );
785 652 }
786 - } elseif ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] && '' === $this->property ) {
653 + } elseif ( '/' === $a_string[ $i ] && '*' === @$a_string[ $i + 1 ] && '' === $this->property ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
787 654 $this->status = 'ic';
788 655 ++$i;
789 656 $this->from[] = 'ip';
790 - } elseif ( '}' === $string[ $i ] ) {
657 + } elseif ( '}' === $a_string[ $i ] ) {
791 658 $this->explode_selectors();
792 659 $this->status = 'is';
793 660 $this->invalid_at = false;
794 - $this->_add_token( SEL_END, $this->selector );
661 + $this->_add_token( self::SEL_END, $this->selector );
795 662 $this->selector = '';
796 663 $this->property = '';
797 664 if ( $this->next_selector_at ) {
798 665 $this->at = $this->css_close_media_section( $this->at );
@@ -798,42 +665,41 @@
798 665 $this->at = $this->css_close_media_section( $this->at );
799 666 $this->at = $this->css_new_media_section( $this->at, $this->next_selector_at );
800 667 $this->next_selector_at = '';
801 668 }
802 - } elseif ( ';' === $string[ $i ] ) {
669 + } elseif ( ';' === $a_string[ $i ] ) {
803 670 $this->property = '';
804 - } elseif ( '\\' === $string[ $i ] ) {
805 - $this->property .= $this->_unicode( $string, $i );
671 + } elseif ( '\\' === $a_string[ $i ] ) {
672 + $this->property .= $this->_unicode( $a_string, $i );
673 + } elseif ( ( '' === $this->property && ! ctype_space( $a_string[ $i ] ) ) || ( '/' === $this->property || '/' === $a_string[ $i ] ) ) {
674 + // else this is dumb IE a hack, keep it.
675 + // including /.
676 + $this->property .= $a_string[ $i ];
806 677 }
807 - // else this is dumb IE a hack, keep it
808 - // including /
809 - elseif ( ( '' === $this->property && ! ctype_space( $string[ $i ] ) ) || ( '/' === $this->property || '/' === $string[ $i ] ) ) {
810 - $this->property .= $string[ $i ];
811 - }
812 - } elseif ( ! ctype_space( $string[ $i ] ) ) {
813 - $this->property .= $string[ $i ];
678 + } elseif ( ! ctype_space( $a_string[ $i ] ) ) {
679 + $this->property .= $a_string[ $i ];
814 680 }
815 681 break;
816 682 /* Case in-value */
817 683 case 'iv':
818 - $pn = ( ( "\n" === $string[ $i ] || "\r" === $string[ $i ] ) && $this->property_is_next( $string, $i + 1 ) || ( strlen( $string ) - 1 ) === $i );
819 - if ( ( $this->is_token( $string, $i ) || $pn ) && ( ! ( ',' === $string[ $i ] && ! ctype_space( $string[ $i + 1 ] ) ) ) ) {
820 - if ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] ) {
684 + $pn = ( ( ( "\n" === $a_string[ $i ] || "\r" === $a_string[ $i ] ) && $this->property_is_next( $a_string, $i + 1 ) ) || ( strlen( $a_string ) - 1 ) === $i );
685 + if ( ( $this->is_token( $a_string, $i ) || $pn ) && ( ! ( ',' === $a_string[ $i ] && ! ctype_space( $a_string[ $i + 1 ] ) ) ) ) {
686 + if ( '/' === $a_string[ $i ] && '*' === @$a_string[ $i + 1 ] ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
821 687 $this->status = 'ic';
822 688 ++$i;
823 689 $this->from[] = 'iv';
824 - } elseif ( ( '"' === $string[ $i ] || "'" === $string[ $i ] || '(' === $string[ $i ] ) ) {
825 - $this->cur_string[] = $string[ $i ];
826 - $this->str_char[] = ( '(' === $string[ $i ] ) ? ')' : $string[ $i ];
690 + } elseif ( ( '"' === $a_string[ $i ] || "'" === $a_string[ $i ] || '(' === $a_string[ $i ] ) ) {
691 + $this->cur_string[] = $a_string[ $i ];
692 + $this->str_char[] = ( '(' === $a_string[ $i ] ) ? ')' : $a_string[ $i ];
827 693 $this->status = 'instr';
828 694 $this->from[] = 'iv';
829 695 $this->quoted_string[] = in_array( strtolower( $this->property ), $quoted_string_properties, true );
830 - } elseif ( ',' === $string[ $i ] ) {
696 + } elseif ( ',' === $a_string[ $i ] ) {
831 697 $this->sub_value = trim( $this->sub_value ) . ',';
832 - } elseif ( '\\' === $string[ $i ] ) {
833 - $this->sub_value .= $this->_unicode( $string, $i );
834 - } elseif ( ';' === $string[ $i ] || $pn ) {
835 - if ( '@' === $this->selector[0] && isset( $at_rules[ substr( $this->selector, 1 ) ] ) && 'iv' === $at_rules[ substr( $this->selector, 1 ) ] ) {
698 + } elseif ( '\\' === $a_string[ $i ] ) {
699 + $this->sub_value .= $this->_unicode( $a_string, $i );
700 + } elseif ( ';' === $a_string[ $i ] || $pn ) {
701 + if ( '' !== $this->selector && '@' === $this->selector[0] && isset( $at_rules[ substr( $this->selector, 1 ) ] ) && 'iv' === $at_rules[ substr( $this->selector, 1 ) ] ) {
836 702 $this->status = 'is';
837 703
838 704 switch ( $this->selector ) {
839 705 case '@charset':
@@ -850,10 +716,10 @@
850 716 $this->sub_value = trim( $this->sub_value );
851 717
852 718 if ( empty( $this->sub_value_arr ) ) {
853 719 // Quote URLs in imports only if they're not already inside url() and not already quoted.
854 - if ( 'url(' !== substr( $this->sub_value, 0, 4 ) ) {
855 - if ( ! ( substr( $this->sub_value, -1 ) === $this->sub_value[0] && in_array( $this->sub_value[0], array( "'", '"' ), true ) ) ) {
720 + if ( ! str_starts_with( $this->sub_value, 'url(' ) ) {
721 + if ( ! str_ends_with( $this->sub_value, $this->sub_value[0] ) && in_array( $this->sub_value[0], array( "'", '"' ), true ) ) {
856 722 $this->sub_value = '"' . $this->sub_value . '"';
857 723 }
858 724 }
859 725 }
@@ -869,14 +735,14 @@
869 735 $this->sel_separate = array();
870 736 } else {
871 737 $this->status = 'ip';
872 738 }
873 - } elseif ( '}' !== $string[ $i ] ) {
874 - $this->sub_value .= $string[ $i ];
739 + } elseif ( '}' !== $a_string[ $i ] ) {
740 + $this->sub_value .= $a_string[ $i ];
875 741 }
876 - if ( ( '}' === $string[ $i ] || ';' === $string[ $i ] || $pn ) && ! empty( $this->selector ) ) {
742 + if ( ( '}' === $a_string[ $i ] || ';' === $a_string[ $i ] || $pn ) && ! empty( $this->selector ) ) {
877 743 if ( '' === $this->at ) {
878 - $this->at = $this->css_new_media_section( $this->at, DEFAULT_AT );
744 + $this->at = $this->css_new_media_section( $this->at, (string) self::DEFAULT_AT );
879 745 }
880 746
881 747 // Case settings.
882 748 if ( $this->get_cfg( 'lowercase_s' ) ) {
@@ -890,9 +756,9 @@
890 756 $this->sub_value = '';
891 757 }
892 758
893 759 $this->value = '';
894 - while ( count( $this->sub_value_arr ) ) {
760 + while ( count( $this->sub_value_arr ) ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
895 761 $sub = array_shift( $this->sub_value_arr );
896 762 if ( strstr( $this->selector, 'font-face' ) ) {
897 763 $sub = $this->quote_font_format( $sub );
898 764 }
@@ -897,9 +763,9 @@
897 763 $sub = $this->quote_font_format( $sub );
898 764 }
899 765
900 766 if ( '' !== $sub ) {
901 - if ( strlen( $this->value ) && ( ',' !== substr( $this->value, -1, 1 ) || $this->get_cfg( 'preserve_css' ) ) ) {
767 + if ( strlen( $this->value ) && ( ! str_ends_with( $this->value, ',' ) || $this->get_cfg( 'preserve_css' ) ) ) {
902 768 $this->value .= ' ';
903 769 }
904 770 $this->value .= $sub;
905 771 }
@@ -909,9 +775,9 @@
909 775
910 776 $valid = $this->property_is_valid( $this->property );
911 777 if ( ( ! $this->invalid_at || $this->get_cfg( 'preserve_css' ) ) && ( ! $this->get_cfg( 'discard_invalid_properties' ) || $valid ) ) {
912 778 $this->css_add_property( $this->at, $this->selector, $this->property, $this->value );
913 - $this->_add_token( VALUE, $this->value );
779 + $this->_add_token( self::VALUE, $this->value );
914 780 $this->optimise->shorthands();
915 781 }
916 782 if ( ! $valid ) {
917 783 if ( $this->get_cfg( 'discard_invalid_properties' ) ) {
@@ -924,11 +790,11 @@
924 790 $this->property = '';
925 791 $this->sub_value_arr = array();
926 792 $this->value = '';
927 793 }
928 - if ( '}' === $string[ $i ] ) {
794 + if ( '}' === $a_string[ $i ] ) {
929 795 $this->explode_selectors();
930 - $this->_add_token( SEL_END, $this->selector );
796 + $this->_add_token( self::SEL_END, $this->selector );
931 797 $this->status = 'is';
932 798 $this->invalid_at = false;
933 799 $this->selector = '';
934 800 if ( $this->next_selector_at ) {
@@ -937,11 +803,11 @@
937 803 $this->next_selector_at = '';
938 804 }
939 805 }
940 806 } elseif ( ! $pn ) {
941 - $this->sub_value .= $string[ $i ];
807 + $this->sub_value .= $a_string[ $i ];
942 808
943 - if ( ctype_space( $string[ $i ] ) || ',' === $string[ $i ] ) {
809 + if ( ctype_space( $a_string[ $i ] ) || ',' === $a_string[ $i ] ) {
944 810 $this->optimise->subvalue();
945 811 if ( '' !== $this->sub_value ) {
946 812 $this->sub_value_arr[] = $this->sub_value;
947 813 $this->sub_value = '';
@@ -953,21 +819,21 @@
953 819 case 'instr':
954 820 $_str_char = $this->str_char[ count( $this->str_char ) - 1 ];
955 821 $_cur_string = $this->cur_string[ count( $this->cur_string ) - 1 ];
956 822 $_quoted_string = $this->quoted_string[ count( $this->quoted_string ) - 1 ];
957 - $temp_add = $string[ $i ];
823 + $temp_add = $a_string[ $i ];
958 824
959 825 // Add another string to the stack. Strings can't be nested inside of quotes, only parentheses,
960 826 // but parentheticals can be nested more than once.
961 - if ( ')' === $_str_char && ( '(' === $string[ $i ] || '"' === $string[ $i ] || '\\' === $string[ $i ] ) && ! $this->escaped( $string, $i ) ) {
962 - $this->cur_string[] = $string[ $i ];
963 - $this->str_char[] = ( '(' === $string[ $i ] ) ? ')' : $string[ $i ];
827 + if ( ')' === $_str_char && ( '(' === $a_string[ $i ] || '"' === $a_string[ $i ] || '\\' === $a_string[ $i ] ) && ! $this->escaped( $a_string, $i ) ) {
828 + $this->cur_string[] = $a_string[ $i ];
829 + $this->str_char[] = ( '(' === $a_string[ $i ] ) ? ')' : $a_string[ $i ];
964 830 $this->from[] = 'instr';
965 - $this->quoted_string[] = ( ')' === $_str_char && '(' !== $string[ $i ] && '(' === trim( $_cur_string ) ) ? $_quoted_string : ( '(' !== $string[ $i ] );
831 + $this->quoted_string[] = ( ')' === $_str_char && '(' !== $a_string[ $i ] && '(' === trim( $_cur_string ) ) ? $_quoted_string : ( '(' !== $a_string[ $i ] );
966 832 continue 2;
967 833 }
968 834
969 - if ( ')' !== $_str_char && ( "\n" === $string[ $i ] || "\r" === $string[ $i ] ) && ! ( '\\' === $string[ $i - 1 ] && ! $this->escaped( $string, $i - 1 ) ) ) {
835 + if ( ')' !== $_str_char && ( "\n" === $a_string[ $i ] || "\r" === $a_string[ $i ] ) && ! ( '\\' === $a_string[ $i - 1 ] && ! $this->escaped( $a_string, $i - 1 ) ) ) {
970 836 $temp_add = '\\A';
971 837 $this->log( 'Fixed incorrect newline in string', 'Warning' );
972 838 }
973 839
@@ -972,22 +838,24 @@
972 838 }
973 839
974 840 $_cur_string .= $temp_add;
975 841
976 - if ( $string[ $i ] === $_str_char && ! $this->escaped( $string, $i ) ) {
842 + if ( $a_string[ $i ] === $_str_char && ! $this->escaped( $a_string, $i ) ) {
977 843 $this->status = array_pop( $this->from );
978 844
979 845 if ( ! preg_match( '|[' . implode( '', $this->data['csstidy']['whitespace'] ) . ']|uis', $_cur_string ) && 'content' !== $this->property ) {
980 846 if ( ! $_quoted_string ) {
981 847 if ( ')' !== $_str_char ) {
982 - // Convert properties like
983 - // font-family: 'Arial';
984 - // to
985 - // font-family: Arial;
986 - // or
987 - // url("abc")
988 - // to
989 - // url(abc)
848 + /*
849 + * Convert properties like
850 + * font-family: 'Arial';
851 + * to
852 + * font-family: Arial;
853 + * or
854 + * url("abc")
855 + * to
856 + * url(abc)
857 + */
990 858 $_cur_string = substr( $_cur_string, 1, -1 );
991 859 }
992 860 } else {
993 861 $_quoted_string = false;
@@ -1003,14 +871,14 @@
1003 871 }
1004 872
1005 873 if ( 'iv' === $this->status ) {
1006 874 if ( ! $_quoted_string ) {
1007 - if ( false !== strpos( $_cur_string, ',' ) ) {
1008 - // We can on only remove space next to ','
875 + if ( str_contains( $_cur_string, ',' ) ) {
876 + // We can on only remove space next to ','.
1009 877 $_cur_string = implode( ',', array_map( 'trim', explode( ',', $_cur_string ) ) );
1010 878 }
1011 879 // and multiple spaces (too expensive).
1012 - if ( false !== strpos( $_cur_string, ' ' ) ) {
880 + if ( str_contains( $_cur_string, ' ' ) ) {
1013 881 $_cur_string = preg_replace( ',\s+,', ' ', $_cur_string );
1014 882 }
1015 883 }
1016 884 $this->sub_value .= $_cur_string;
@@ -1024,20 +892,20 @@
1024 892 }
1025 893 break;
1026 894 /* Case in-comment */
1027 895 case 'ic':
1028 - if ( '*' === $string[ $i ] && '/' === $string[ $i + 1 ] ) {
896 + if ( '*' === $a_string[ $i ] && '/' === $a_string[ $i + 1 ] ) {
1029 897 $this->status = array_pop( $this->from );
1030 - $i++;
1031 - if ( strlen( $cur_comment ) > 1 && 0 === strncmp( $cur_comment, '!', 1 ) ) {
1032 - $this->_add_token( IMPORTANT_COMMENT, $cur_comment );
898 + ++$i;
899 + if ( strlen( $cur_comment ) > 1 && str_starts_with( $cur_comment, '!' ) ) {
900 + $this->_add_token( self::IMPORTANT_COMMENT, $cur_comment );
1033 901 $this->css_add_important_comment( $cur_comment );
1034 902 } else {
1035 - $this->_add_token( COMMENT, $cur_comment );
903 + $this->_add_token( self::COMMENT, $cur_comment );
1036 904 }
1037 905 $cur_comment = '';
1038 906 } else {
1039 - $cur_comment .= $string[ $i ];
907 + $cur_comment .= $a_string[ $i ];
1040 908 }
1041 909 break;
1042 910 }
1043 911 }
@@ -1045,23 +913,23 @@
1045 913 $this->optimise->postparse();
1046 914 $this->print->_reset();
1047 915
1048 916 // Set locale back to original setting.
1049 - @setlocale( LC_ALL, $old );
917 + @setlocale( LC_ALL, $old ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1050 918
1051 919 return ! ( empty( $this->css ) && empty( $this->import ) && empty( $this->charset ) && empty( $this->tokens ) && empty( $this->namespace ) );
1052 920 }
1053 921
1054 922 /**
1055 - * format() in font-face needs quoted values for somes browser (FF at least).
923 + * Quoting: format() in font-face needs quoted values for some browser (FF at least).
1056 924 *
1057 925 * @since 1.0.0
1058 926 *
1059 - * @param mixed $value Value.
927 + * @param string $value Value.
1060 928 * @return string String.
1061 929 */
1062 - protected function quote_font_format( $value ) {
1063 - if ( 0 === strncmp( $value, 'format', 6 ) ) {
930 + protected function quote_font_format( string $value ): string {
931 + if ( str_starts_with( $value, 'format' ) ) {
1064 932 $p = strpos( $value, ')', 7 );
1065 933 $end = substr( $value, $p );
1066 934 $format_strings = $this->parse_string_list( substr( $value, 7, $p - 7 ) );
1067 935 if ( ! $format_strings ) {
@@ -1081,9 +949,9 @@
1081 949 * Explodes selectors.
1082 950 *
1083 951 * @since 1.0.0
1084 952 */
1085 - protected function explode_selectors() {
953 + protected function explode_selectors(): void {
1086 954 // Explode multiple selectors.
1087 955 if ( 1 === $this->get_cfg( 'merge_selectors' ) ) {
1088 956 $new_sels = array();
1089 957 $lastpos = 0;
@@ -1089,9 +957,9 @@
1089 957 $lastpos = 0;
1090 958 $this->sel_separate[] = strlen( $this->selector );
1091 959 foreach ( $this->sel_separate as $num => $pos ) {
1092 960 if ( ( count( $this->sel_separate ) - 1 ) === $num ) {
1093 - $pos += 1;
961 + ++$pos;
1094 962 }
1095 963
1096 964 $new_sels[] = substr( $this->selector, $lastpos, $pos - $lastpos - 1 );
1097 965 $lastpos = $pos;
@@ -1109,28 +977,28 @@
1109 977 $this->sel_separate = array();
1110 978 }
1111 979
1112 980 /**
1113 - * Checks if a character is escaped (and returns true if it is).
981 + * Checks if a character is escaped.
1114 982 *
1115 983 * @since 1.0.0
1116 984 *
1117 - * @param string $string String.
985 + * @param string $a_string String.
1118 986 * @param int $pos Position.
1119 - * @return bool [return value]
987 + * @return bool Whether a character is escaped.
1120 988 */
1121 - public function escaped( &$string, $pos ) {
1122 - return $pos ? ! ( @( '\\' !== $string[ $pos - 1 ] ) || $this->escaped( $string, $pos - 1 ) ) : false;
989 + public function escaped( string &$a_string, int $pos ): bool {
990 + return $pos ? ! ( @( '\\' !== $a_string[ $pos - 1 ] ) || $this->escaped( $a_string, $pos - 1 ) ) : false; // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1123 991 }
1124 992
1125 993 /**
1126 - * Add an important comment to the CSS code (one we want to keep when minifying).
994 + * Adds an important comment to the CSS code (one we want to keep when minifying).
1127 995 *
1128 996 * @since 1.10.0
1129 997 *
1130 998 * @param string $comment CSS Comment.
1131 999 */
1132 - protected function css_add_important_comment($comment) {
1000 + protected function css_add_important_comment( string $comment ): void {
1133 1001 if ( $this->get_cfg( 'preserve_css' ) || '' === trim( $comment ) ) {
1134 1002 return;
1135 1003 }
1136 1004 if ( ! isset( $this->css['!'] ) ) {
@@ -1150,9 +1018,9 @@
1150 1018 * @param string $selector Selector.
1151 1019 * @param string $property Property.
1152 1020 * @param string $new_val New value.
1153 1021 */
1154 - protected function css_add_property( $media, $selector, $property, $new_val ) {
1022 + protected function css_add_property( string $media, string $selector, string $property, string $new_val ): void {
1155 1023 if ( $this->get_cfg( 'preserve_css' ) || '' === trim( $new_val ) ) {
1156 1024 return;
1157 1025 }
1158 1026
@@ -1166,9 +1034,9 @@
1166 1034 }
1167 1035 }
1168 1036
1169 1037 /**
1170 - * Check if a current media section is the continuation of the last one.
1038 + * Checks if a current media section is the continuation of the last one.
1171 1039 * If not increase the name of the media section to avoid a merging.
1172 1040 *
1173 1041 * @since 1.10.0
1174 1042 *
@@ -1174,16 +1042,15 @@
1174 1042 *
1175 1043 * @param int|string $media Media.
1176 1044 * @return int|string [return value]
1177 1045 */
1178 - protected function css_check_last_media_section_or_inc( $media ) {
1046 + protected function css_check_last_media_section_or_inc( $media ) /* : int|string */ {
1179 1047 // Are we starting?
1180 1048 if ( empty( $this->css ) || ! is_array( $this->css ) ) {
1181 1049 return $media;
1182 1050 }
1183 1051 // If the last @media is the same as this, keep it.
1184 - end( $this->css );
1185 - $at = key( $this->css );
1052 + $at = array_key_last( $this->css );
1186 1053 if ( $at === $media ) {
1187 1054 return $media;
1188 1055 }
1189 1056 // Otherwise increase the section in the array.
@@ -1188,9 +1055,9 @@
1188 1055 }
1189 1056 // Otherwise increase the section in the array.
1190 1057 while ( isset( $this->css[ $media ] ) ) {
1191 1058 if ( is_numeric( $media ) ) {
1192 - $media++;
1059 + ++$media;
1193 1060 } else {
1194 1061 $media .= ' ';
1195 1062 }
1196 1063 }
@@ -1197,20 +1064,20 @@
1197 1064 return $media;
1198 1065 }
1199 1066
1200 1067 /**
1201 - * Start a new media section.
1068 + * Starts a new media section.
1202 1069 *
1203 1070 * Check if the media is not already known, else rename it with extra spaces to avoid merging.
1204 1071 *
1205 1072 * @since 1.0.0
1206 1073 *
1207 - * @param string $current_media Media.
1208 - * @param string $media Media.
1209 - * @param bool $at_root
1210 - * @return string [return value]
1074 + * @param string|int $current_media Media.
1075 + * @param string $new_media Media.
1076 + * @param bool $at_root Optional. Default false.
1077 + * @return int|string [return value]
1211 1078 */
1212 - protected function css_new_media_section( $current_media, $new_media, $at_root = false ) {
1079 + protected function css_new_media_section( $current_media, string $new_media, bool $at_root = false ) /* : int|string */ {
1213 1080 if ( $this->get_cfg( 'preserve_css' ) ) {
1214 1081 return $new_media;
1215 1082 }
1216 1083 // If we already are in a media and CSS level is 3, manage nested medias.
@@ -1215,11 +1082,11 @@
1215 1082 }
1216 1083 // If we already are in a media and CSS level is 3, manage nested medias.
1217 1084 if ( $current_media
1218 1085 && ! $at_root
1219 - // numeric $current_media means DEFAULT_AT or inc
1086 + // Numeric $current_media means self::DEFAULT_AT or inc.
1220 1087 && ! is_numeric( $current_media )
1221 - && 0 === strncmp( $this->get_cfg( 'css_level' ), 'CSS3', 4 ) ) {
1088 + && str_starts_with( $this->get_cfg( 'css_level' ), 'CSS3' ) ) {
1222 1089 $new_media = rtrim( $current_media ) . '{' . rtrim( $new_media );
1223 1090 }
1224 1091 return $this->css_check_last_media_section_or_inc( $new_media );
1225 1092 }
@@ -1224,11 +1091,11 @@
1224 1091 return $this->css_check_last_media_section_or_inc( $new_media );
1225 1092 }
1226 1093
1227 1094 /**
1228 - * Close a media section.
1095 + * Closes a media section.
1229 1096 *
1230 - * Find the parent media we were in before or the root.
1097 + * Finds the parent media we were in before or the root.
1231 1098 *
1232 1099 * @since 1.10.0
1233 1100 *
1234 1101 * @param string $current_media Current Media.
@@ -1233,13 +1100,13 @@
1233 1100 *
1234 1101 * @param string $current_media Current Media.
1235 1102 * @return string [return value]
1236 1103 */
1237 - protected function css_close_media_section( $current_media ) {
1104 + protected function css_close_media_section( string $current_media ): string {
1238 1105 if ( $this->get_cfg( 'preserve_css' ) ) {
1239 1106 return '';
1240 1107 }
1241 - if ( false !== strpos( $current_media, '{' ) ) {
1108 + if ( str_contains( $current_media, '{' ) ) {
1242 1109 $current_media = explode( '{', $current_media );
1243 1110 array_pop( $current_media );
1244 1111 $current_media = implode( '{', $current_media );
1245 1112 return $current_media;
@@ -1248,9 +1115,9 @@
1248 1115 return '';
1249 1116 }
1250 1117
1251 1118 /**
1252 - * Start a new selector.
1119 + * Starts a new selector.
1253 1120 *
1254 1121 * If already referenced in this media section, rename it with extra space to avoid merging,
1255 1122 * except if merging is required, or last selector is the same (merge siblings).
1256 1123 * Never merge @font-face.
@@ -1260,14 +1127,14 @@
1260 1127 * @param string $media Media.
1261 1128 * @param string $selector Selector.
1262 1129 * @return string [return value]
1263 1130 */
1264 - protected function css_new_selector( $media, $selector ) {
1131 + protected function css_new_selector( string $media, string $selector ): string {
1265 1132 if ( $this->get_cfg( 'preserve_css' ) ) {
1266 1133 return $selector;
1267 1134 }
1268 1135 $selector = trim( $selector );
1269 - if ( 0 !== strncmp( $selector, '@font-face', 10 ) ) {
1136 + if ( ! str_starts_with( $selector, '@font-face' ) ) {
1270 1137 if ( false !== $this->settings['merge_selectors'] ) {
1271 1138 return $selector;
1272 1139 }
1273 1140
@@ -1275,10 +1142,9 @@
1275 1142 return $selector;
1276 1143 }
1277 1144
1278 1145 // If last is the same, keep it.
1279 - end( $this->css[ $media ] );
1280 - $sel = key( $this->css[ $media ] );
1146 + $sel = array_key_last( $this->css[ $media ] );
1281 1147 if ( $sel === $selector ) {
1282 1148 return $selector;
1283 1149 }
1284 1150 }
@@ -1289,11 +1155,11 @@
1289 1155 return $selector;
1290 1156 }
1291 1157
1292 1158 /**
1293 - * Start a new property.
1159 + * Starts a new property.
1294 1160 *
1295 - * If already references in this selector, rename it with extra space to avoid override.
1161 + * If already referenced in this selector, rename it with extra space to avoid override.
1296 1162 *
1297 1163 * @since 1.0.0
1298 1164 *
1299 1165 * @param string $media Media.
@@ -1300,9 +1166,9 @@
1300 1166 * @param string $selector Selector.
1301 1167 * @param string $property Property.
1302 1168 * @return string [return value]
1303 1169 */
1304 - protected function css_new_property( $media, $selector, $property ) {
1170 + protected function css_new_property( string $media, string $selector, string $property ): string {
1305 1171 if ( $this->get_cfg( 'preserve_css' ) ) {
1306 1172 return $property;
1307 1173 }
1308 1174 if ( empty( $this->css ) || ! isset( $this->css[ $media ][ $selector ] ) || ! $this->css[ $media ][ $selector ] ) {
@@ -1320,13 +1186,13 @@
1320 1186 * Adds CSS to an existing media/selector.
1321 1187 *
1322 1188 * @since 1.0.0
1323 1189 *
1324 - * @param string $media Media.
1325 - * @param string $selector Selector.
1326 - * @param array $css_add Additional CSS.
1190 + * @param string $media Media.
1191 + * @param string $selector Selector.
1192 + * @param array<string, string> $css_add Additional CSS.
1327 1193 */
1328 - public function merge_css_blocks( $media, $selector, array $css_add ) {
1194 + public function merge_css_blocks( string $media, string $selector, array $css_add ): void {
1329 1195 foreach ( $css_add as $property => $value ) {
1330 1196 $this->css_add_property( $media, $selector, $property, $value );
1331 1197 }
1332 1198 }
@@ -1338,11 +1204,11 @@
1338 1204 *
1339 1205 * @param string $value Value.
1340 1206 * @return bool Whether the value has the !important keyword.
1341 1207 */
1342 - public function is_important( &$value ) {
1208 + public function is_important( string &$value ): bool {
1343 1209 return (
1344 - false !== strpos( $value, '!' ) // Quick test.
1210 + str_contains( $value, '!' ) // Quick test.
1345 1211 && ! strcasecmp( substr( str_replace( $this->data['csstidy']['whitespace'], '', $value ), -10, 10 ), '!important' ) );
1346 1212 }
1347 1213
1348 1214 /**
@@ -1352,9 +1218,9 @@
1352 1218 *
1353 1219 * @param string $value Value.
1354 1220 * @return string Value without the !important;
1355 1221 */
1356 - public function gvw_important( $value ) {
1222 + public function gvw_important( string $value ): string {
1357 1223 if ( $this->is_important( $value ) ) {
1358 1224 $value = trim( $value );
1359 1225 $value = substr( $value, 0, -9 );
1360 1226 $value = trim( $value );
@@ -1373,9 +1239,9 @@
1373 1239 * @param string $istring String.
1374 1240 * @param int $pos Position.
1375 1241 * @return bool [return value]
1376 1242 */
1377 - protected function property_is_next( $istring, $pos ) {
1243 + protected function property_is_next( string $istring, int $pos ): bool {
1378 1244 $all_properties = &$this->data['csstidy']['all_properties'];
1379 1245 $istring = substr( $istring, $pos, strlen( $istring ) - $pos );
1380 1246 $pos = strpos( $istring, ':' );
1381 1247 if ( false === $pos ) {
@@ -1396,15 +1262,17 @@
1396 1262 *
1397 1263 * @param string $property Property.
1398 1264 * @return bool Whether the property is valid.
1399 1265 */
1400 - public function property_is_valid( $property ) {
1266 + public function property_is_valid( string $property ): bool {
1401 1267 $property = strtolower( $property );
1402 - if ( in_array( trim( $property ), $this->data['csstidy']['multiple_properties'], true ) ) {
1268 + if ( str_starts_with( $property, '--' ) ) {
1269 + $property = '--custom'; // Replace custom properties with a temporary placeholder that is marked as valid in the list of properties.
1270 + } elseif ( in_array( trim( $property ), $this->data['csstidy']['multiple_properties'], true ) ) {
1403 1271 $property = trim( $property );
1404 1272 }
1405 1273 $all_properties = &$this->data['csstidy']['all_properties'];
1406 - return isset( $all_properties[ $property ] ) && false !== strpos( $all_properties[ $property ], strtoupper( $this->get_cfg( 'css_level' ) ) );
1274 + return isset( $all_properties[ $property ] ) && str_contains( $all_properties[ $property ], strtoupper( $this->get_cfg( 'css_level' ) ) );
1407 1275 }
1408 1276
1409 1277 /**
1410 1278 * Accepts a list of strings (e.g. the argument to format() in a @font-face src property)
@@ -1417,20 +1285,20 @@
1417 1285 * format("abc, def, ghi") => format("abc, def, ghi")
1418 1286 *
1419 1287 * @since 1.0.0
1420 1288 *
1421 - * @param [type] $value [description]
1422 - * @return [type] [description]
1289 + * @param string $value [description].
1290 + * @return string[] [description]
1423 1291 */
1424 - public function parse_string_list( $value ) {
1292 + public function parse_string_list( string $value ): array {
1425 1293 $value = trim( $value );
1426 1294
1427 - // Case: empty
1295 + // Case: empty.
1428 1296 if ( ! $value ) {
1429 1297 return array();
1430 1298 }
1431 1299
1432 - $strings = array();
1300 + $a_strings = array();
1433 1301
1434 1302 $in_str = false;
1435 1303 $current_string = '';
1436 1304
@@ -1436,13 +1304,13 @@
1436 1304
1437 1305 for ( $i = 0, $_len = strlen( $value ); $i < $_len; $i++ ) {
1438 1306 if ( ( ',' === $value[ $i ] || ' ' === $value[ $i ] ) && true === $in_str ) {
1439 1307 $in_str = false;
1440 - $strings[] = $current_string;
1308 + $a_strings[] = $current_string;
1441 1309 $current_string = '';
1442 1310 } elseif ( '"' === $value[ $i ] || "'" === $value[ $i ] ) {
1443 1311 if ( $in_str === $value[ $i ] ) {
1444 - $strings[] = $current_string;
1312 + $a_strings[] = $current_string;
1445 1313 $in_str = false;
1446 1314 $current_string = '';
1447 1315 continue;
1448 1316 } elseif ( ! $in_str ) {
@@ -1447,12 +1315,12 @@
1447 1315 continue;
1448 1316 } elseif ( ! $in_str ) {
1449 1317 $in_str = $value[ $i ];
1450 1318 }
1451 - } else {
1319 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
1452 1320 if ( $in_str ) {
1453 1321 $current_string .= $value[ $i ];
1454 - } else {
1322 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
1455 1323 if ( ! preg_match( '/[\s,]/', $value[ $i ] ) ) {
1456 1324 $in_str = true;
1457 1325 $current_string = $value[ $i ];
1458 1326 }
@@ -1460,11 +1328,11 @@
1460 1328 }
1461 1329 }
1462 1330
1463 1331 if ( $current_string ) {
1464 - $strings[] = $current_string;
1332 + $a_strings[] = $current_string;
1465 1333 }
1466 1334
1467 - return $strings;
1335 + return $a_strings;
1468 1336 }
1469 1337
1470 1338 } // class TablePress_CSSTidy