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 +282 -421 1.12 → 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,23 +312,23 @@
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.
392 - * @return string|bool Value of the setting.
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,52 +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 ) {
434 - if ( is_array( $setting ) && is_null( $value ) ) {
435 - foreach ( $setting as $setprop => $setval ) {
436 - $this->settings[ $setprop ] = $setval;
437 - }
438 - if ( array_key_exists( 'template', $setting ) ) {
439 - $this->_load_template( $this->settings['template'] );
440 - }
441 - return true;
442 - } elseif ( isset( $this->settings[ $setting ] ) && '' !== $value ) {
346 + public function set_cfg( string $setting, $value = null ): bool {
347 + if ( isset( $this->settings[ $setting ] ) && '' !== $value ) {
443 348 $this->settings[ $setting ] = $value;
444 349 if ( 'template' === $setting ) {
445 - $this->_load_template( $this->settings['template'] );
350 + $this->load_template( $this->settings['template'] );
446 351 }
447 352 return true;
448 353 }
449 354 return false;
@@ -453,29 +358,29 @@
453 358 * Adds a token to $this->tokens.
454 359 *
455 360 * @since 1.0.0
456 361 *
457 - * @param mixed $type Type.
458 - * @param string $data Data.
459 - * @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.
460 365 */
461 - public function _add_token( $type, $data, $do = false ) {
462 - 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 ) {
463 368 // nested @...: if opening a new part we just closed, remove the previous closing instead of adding opening.
464 - if ( AT_START === $type
369 + if ( self::AT_START === $type
465 370 && count( $this->tokens )
466 - && ( $last = end( $this->tokens ) )
467 - && 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]
468 373 && trim( $data ) === $last[1] ) {
469 374 array_pop( $this->tokens );
470 375 } else {
471 - $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 ) );
472 377 }
473 378 }
474 379 }
475 380
476 381 /**
477 - * Add a message to the message log.
382 + * Adds a message to the message log.
478 383 *
479 384 * @since 1.0.0
480 385 *
481 386 * @param string $message Message.
@@ -481,13 +386,13 @@
481 386 * @param string $message Message.
482 387 * @param string $type Type.
483 388 * @param int $line Optional. Line number. -1 will use the current line.
484 389 */
485 - public function log( $message, $type, $line = -1 ) {
390 + public function log( string $message, string $type, int $line = -1 ): void {
486 391 if ( -1 === $line ) {
487 392 $line = $this->line;
488 393 }
489 - $line = intval( $line );
394 + $line = (int) $line;
490 395 $add = array(
491 396 'm' => $message,
492 397 't' => $type,
493 398 );
@@ -496,30 +401,30 @@
496 401 }
497 402 }
498 403
499 404 /**
500 - * Parse Unicode notations and find a replacement character.
405 + * Parses Unicode notations and find a replacement character.
501 406 *
502 407 * @since 1.0.0
503 408 *
504 - * @param string $string String.
505 - * @param int $i i.
409 + * @param string $a_string String.
410 + * @param int $i i.
506 411 * @return string [return value]
507 412 */
508 - public function _unicode( &$string, &$i ) {
413 + public function _unicode( string &$a_string, int &$i ): string {
509 414 ++$i;
510 415 $add = '';
511 416 $replaced = false;
512 417
513 - while ( $i < strlen( $string ) && ( ctype_xdigit( $string[ $i ] ) || ctype_space( $string[ $i ] ) ) && strlen( $add ) < 6 ) {
514 - $add .= $string[ $i ];
515 - 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 ] ) ) {
516 421 break;
517 422 }
518 - $i++;
423 + ++$i;
519 424 }
520 425
521 - 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 ) ) {
522 427 $this->log( 'Replaced unicode notation: Changed \\' . $add . ' to ' . chr( hexdec( $add ) ), 'Information' );
523 428 $add = chr( hexdec( $add ) );
524 429 $replaced = true;
525 430 } else {
@@ -525,13 +430,13 @@
525 430 } else {
526 431 $add = trim( '\\' . $add );
527 432 }
528 433
529 - if ( @ctype_xdigit( $string[ $i + 1 ] ) && ctype_space( $string[ $i ] ) && ! $replaced || ! ctype_space( $string[ $i ] ) ) {
530 - $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;
531 436 }
532 437
533 - 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 ] ) ) {
534 439 return $add;
535 440 }
536 441
537 442 if ( '\\' === $add ) {
@@ -541,65 +446,20 @@
541 446 return '';
542 447 }
543 448
544 449 /**
545 - * Write formatted output to a file.
546 - *
547 - * @since 1.0.0
548 - *
549 - * @param string $filename File name.
550 - */
551 - public function write_page( $filename ) {
552 - $this->write( $filename, true );
553 - }
554 -
555 - /**
556 - * Write plain output to a file.
557 - *
558 - * @since 1.0.0
559 - *
560 - * @param string $filename File name.
561 - * @param bool $formatted Optional. Whether to print formatted or not.
562 - * @param string $doctype Optional. When printing formatted, is a shorthand for the document type.
563 - * @param bool $externalcss Optional. When printing formatted, indicates whether styles to be attached internally or as an external stylesheet.
564 - * @param string $title Optional. When printing formatted, is the title to be added in the head of the document.
565 - * @param string $lang Optional. When printing formatted, gives a two-letter language code to be added to the output.
566 - */
567 - public function write( $filename, $formatted = false, $doctype = 'xhtml1.1', $externalcss = true, $title = '', $lang = 'en' ) {
568 - $filename .= ( $formatted ) ? '.xhtml' : '.css';
569 -
570 - if ( ! is_dir( 'temp' ) ) {
571 - $madedir = mkdir( 'temp' );
572 - if ( ! $madedir ) {
573 - print 'Could not make directory "temp" in ' . __DIR__;
574 - exit;
575 - }
576 - }
577 - $handle = fopen( 'temp/' . $filename, 'w' );
578 - if ( $handle ) {
579 - if ( ! $formatted ) {
580 - fwrite( $handle, $this->print->plain() );
581 - } else {
582 - fwrite( $handle, $this->print->formatted_page( $doctype, $externalcss, $title, $lang ) );
583 - }
584 - }
585 - fclose( $handle );
586 - }
587 -
588 - /**
589 450 * Loads a new template.
590 451 *
591 452 * @since 1.0.0
592 453 *
593 - * @link http://csstidy.sourceforge.net/templates.php
454 + * @link https://csstidy.sourceforge.net/templates.php
594 455 *
595 - * @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".
596 457 * @param bool $from_file Optional. Uses $content as filename if true.
597 458 */
598 - protected function load_template( $content, $from_file = true ) {
599 - $predefined_templates = &$this->data['csstidy']['predefined_templates'];
600 - if ( in_array( $content, array( 'default', 'low_compression', 'high_compression', 'highest_compression' ), true ) ) {
601 - $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 ];
602 462 return;
603 463 }
604 464
605 465 if ( $from_file ) {
@@ -604,15 +464,12 @@
604 464
605 465 if ( $from_file ) {
606 466 $content = strip_tags( file_get_contents( $content ), '<span>' );
607 467 }
468 +
608 469 // Unify newlines (because the output also only uses \n).
609 470 $content = str_replace( "\r\n", "\n", $content );
610 - $template = explode( '|', $content );
611 -
612 - for ( $i = 0; $i < count( $template ); $i++ ) {
613 - $this->template[ $i ] = $template[ $i ];
614 - }
471 + $this->template = explode( '|', $content );
615 472 }
616 473
617 474 /**
618 475 * Starts parsing from URL.
@@ -619,12 +476,16 @@
619 476 *
620 477 * @since 1.0.0
621 478 *
622 479 * @param string $url URL.
623 - * @return bool
480 + * @return bool Whether the CSS code could be parsed successfully.
624 481 */
625 - protected function parse_from_url( $url ) {
626 - 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 );
627 488 }
628 489
629 490 /**
630 491 * Checks if there is a token at the current position.
@@ -630,14 +491,14 @@
630 491 * Checks if there is a token at the current position.
631 492 *
632 493 * @since 1.0.0
633 494 *
634 - * @param string $string String.
495 + * @param string $a_string String.
635 496 * @param int $i i.
636 497 * @return bool [return value]
637 498 */
638 - protected function is_token( &$string, $i ) {
639 - 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 ) );
640 501 }
641 502
642 503 /**
643 504 * Parses CSS in a string. The output is saved as an array in $this->css.
@@ -643,28 +504,27 @@
643 504 * Parses CSS in a string. The output is saved as an array in $this->css.
644 505 *
645 506 * @since 1.0.0
646 507 *
647 - * @param string $string The CSS code.
648 - * @return bool [return value]
508 + * @param string $a_string The CSS code.
509 + * @return bool Whether the CSS code could be parsed successfully.
649 510 */
650 - public function parse( $string ) {
511 + public function parse( string $a_string ): bool {
651 512 // Temporarily set locale to en_US in order to handle floats properly.
652 - $old = @setlocale( LC_ALL, 0 );
653 - @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
654 515
655 - $all_properties = &$this->data['csstidy']['all_properties'];
656 516 $at_rules = &$this->data['csstidy']['at_rules'];
657 517 $quoted_string_properties = &$this->data['csstidy']['quoted_string_properties'];
658 518
659 519 $this->css = array();
660 - $this->print->input_css = $string;
661 - $string = str_replace( "\r\n", "\n", $string ) . ' ';
520 + $this->print->input_css = $a_string;
521 + $a_string = str_replace( "\r\n", "\n", $a_string ) . ' ';
662 522 $cur_comment = '';
663 523 $cur_at = '';
664 524
665 - for ( $i = 0, $size = strlen( $string ); $i < $size; $i++ ) {
666 - 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 ] ) {
667 527 ++$this->line;
668 528 }
669 529
670 530 switch ( $this->status ) {
@@ -669,46 +529,45 @@
669 529
670 530 switch ( $this->status ) {
671 531 /* Case in at-block */
672 532 case 'at':
673 - if ( $this->is_token( $string, $i ) ) {
674 - 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
675 535 $this->status = 'ic';
676 536 ++$i;
677 537 $this->from[] = 'at';
678 - } elseif ( '{' === $string[ $i ] ) {
538 + } elseif ( '{' === $a_string[ $i ] ) {
679 539 $this->status = 'is';
680 540 $this->at = $this->css_new_media_section( $this->at, $cur_at );
681 - $this->_add_token( AT_START, $this->at );
682 - } elseif ( ',' === $string[ $i ] ) {
541 + $this->_add_token( self::AT_START, $this->at );
542 + } elseif ( ',' === $a_string[ $i ] ) {
683 543 $cur_at = trim( $cur_at ) . ',';
684 - } elseif ( '\\' === $string[ $i ] ) {
685 - $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 ];
686 550 }
687 - // Fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5)
688 - // '/' is included for ratios in Opera: (-o-min-device-pixel-ratio: 3/2)
689 - elseif ( in_array( $string[ $i ], array( '(', ')', ':', '.', '/' ), true ) ) {
690 - $cur_at .= $string[ $i ];
691 - }
692 551 } else {
693 552 $lastpos = strlen( $cur_at ) - 1;
694 - if ( ! ( ( ctype_space( $cur_at[ $lastpos ] ) || $this->is_token( $cur_at, $lastpos ) && ',' === $cur_at[ $lastpos ] ) && ctype_space( $string[ $i ] ) ) ) {
695 - $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 ];
696 555 }
697 556 }
698 557 break;
699 558 /* Case in-selector */
700 559 case 'is':
701 - if ( $this->is_token( $string, $i ) ) {
702 - 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
703 562 $this->status = 'ic';
704 563 ++$i;
705 564 $this->from[] = 'is';
706 - } elseif ( '@' === $string[ $i ] && '' === trim( $this->selector ) ) {
565 + } elseif ( '@' === $a_string[ $i ] && '' === trim( $this->selector ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
707 566 // Check for at-rule.
708 567 $this->invalid_at = true;
709 568 foreach ( $at_rules as $name => $type ) {
710 - if ( ! strcasecmp( substr( $string, $i + 1, strlen( $name ) ), $name ) ) {
569 + if ( ! strcasecmp( substr( $a_string, $i + 1, strlen( $name ) ), $name ) ) {
711 570 if ( 'at' === $type ) {
712 571 $cur_at = '@' . $name;
713 572 } else {
714 573 $this->selector = '@' . $name;
@@ -713,9 +572,9 @@
713 572 } else {
714 573 $this->selector = '@' . $name;
715 574 }
716 575 if ( 'atis' === $type ) {
717 - $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 ) );
718 577 $this->at = $this->css_new_media_section( $this->at, ' ', true );
719 578 $type = 'is';
720 579 }
721 580 $this->status = $type;
@@ -728,23 +587,23 @@
728 587 if ( $this->invalid_at ) {
729 588 $this->selector = '@';
730 589 $invalid_at_name = '';
731 590 for ( $j = $i + 1; $j < $size; ++$j ) {
732 - if ( ! ctype_alpha( $string[ $j ] ) ) {
591 + if ( ! ctype_alpha( $a_string[ $j ] ) ) {
733 592 break;
734 593 }
735 - $invalid_at_name .= $string[ $j ];
594 + $invalid_at_name .= $a_string[ $j ];
736 595 }
737 596 $this->log( 'Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning' );
738 597 }
739 - } elseif ( ( '"' === $string[ $i ] || "'" === $string[ $i ] ) ) {
740 - $this->cur_string[] = $string[ $i ];
598 + } elseif ( ( '"' === $a_string[ $i ] || "'" === $a_string[ $i ] ) ) {
599 + $this->cur_string[] = $a_string[ $i ];
741 600 $this->status = 'instr';
742 - $this->str_char[] = $string[ $i ];
601 + $this->str_char[] = $a_string[ $i ];
743 602 $this->from[] = 'is';
744 603 /* Fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
745 - $this->quoted_string[] = ( '=' === $string[ $i - 1 ] );
746 - } elseif ( $this->invalid_at && ';' === $string[ $i ] ) {
604 + $this->quoted_string[] = ( '=' === $a_string[ $i - 1 ] );
605 + } elseif ( $this->invalid_at && ';' === $a_string[ $i ] ) {
747 606 $this->invalid_at = false;
748 607 $this->status = 'is';
749 608 if ( $this->next_selector_at ) {
750 609 $this->at = $this->css_close_media_section( $this->at );
@@ -750,57 +609,57 @@
750 609 $this->at = $this->css_close_media_section( $this->at );
751 610 $this->at = $this->css_new_media_section( $this->at, $this->next_selector_at );
752 611 $this->next_selector_at = '';
753 612 }
754 - } elseif ( '{' === $string[ $i ] ) {
613 + } elseif ( '{' === $a_string[ $i ] ) {
755 614 $this->status = 'ip';
756 615 if ( '' === $this->at ) {
757 - $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 );
758 617 }
759 618 $this->selector = $this->css_new_selector( $this->at, $this->selector );
760 - $this->_add_token( SEL_START, $this->selector );
619 + $this->_add_token( self::SEL_START, $this->selector );
761 620 $this->added = false;
762 - } elseif ( '}' === $string[ $i ] ) {
763 - $this->_add_token( AT_END, $this->at );
621 + } elseif ( '}' === $a_string[ $i ] ) {
622 + $this->_add_token( self::AT_END, $this->at );
764 623 $this->at = $this->css_close_media_section( $this->at );
765 624 $this->selector = '';
766 625 $this->sel_separate = array();
767 - } elseif ( ',' === $string[ $i ] ) {
626 + } elseif ( ',' === $a_string[ $i ] ) {
768 627 $this->selector = trim( $this->selector ) . ',';
769 628 $this->sel_separate[] = strlen( $this->selector );
770 - } elseif ( '\\' === $string[ $i ] ) {
771 - $this->selector .= $this->_unicode( $string, $i );
772 - } 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
773 632 // Remove unnecessary universal selector, FS#147, but not comment in selector.
774 633 } else {
775 - $this->selector .= $string[ $i ];
634 + $this->selector .= $a_string[ $i ];
776 635 }
777 636 } else {
778 637 $lastpos = strlen( $this->selector ) - 1;
779 - if ( -1 === $lastpos || ! ( ( ctype_space( $this->selector[ $lastpos ] ) || $this->is_token( $this->selector, $lastpos ) && ',' === $this->selector[ $lastpos ] ) && ctype_space( $string[ $i ] ) ) ) {
780 - $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 ];
781 640 }
782 641 }
783 642 break;
784 643 /* Case in-property */
785 644 case 'ip':
786 - if ( $this->is_token( $string, $i ) ) {
787 - 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 ) {
788 647 $this->status = 'iv';
789 648 if ( ! $this->get_cfg( 'discard_invalid_properties' ) || $this->property_is_valid( $this->property ) ) {
790 649 $this->property = $this->css_new_property( $this->at, $this->selector, $this->property );
791 650 $this->property = strtolower( $this->property );
792 - $this->_add_token( PROPERTY, $this->property );
651 + $this->_add_token( self::PROPERTY, $this->property );
793 652 }
794 - } 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
795 654 $this->status = 'ic';
796 655 ++$i;
797 656 $this->from[] = 'ip';
798 - } elseif ( '}' === $string[ $i ] ) {
657 + } elseif ( '}' === $a_string[ $i ] ) {
799 658 $this->explode_selectors();
800 659 $this->status = 'is';
801 660 $this->invalid_at = false;
802 - $this->_add_token( SEL_END, $this->selector );
661 + $this->_add_token( self::SEL_END, $this->selector );
803 662 $this->selector = '';
804 663 $this->property = '';
805 664 if ( $this->next_selector_at ) {
806 665 $this->at = $this->css_close_media_section( $this->at );
@@ -806,42 +665,41 @@
806 665 $this->at = $this->css_close_media_section( $this->at );
807 666 $this->at = $this->css_new_media_section( $this->at, $this->next_selector_at );
808 667 $this->next_selector_at = '';
809 668 }
810 - } elseif ( ';' === $string[ $i ] ) {
669 + } elseif ( ';' === $a_string[ $i ] ) {
811 670 $this->property = '';
812 - } elseif ( '\\' === $string[ $i ] ) {
813 - $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 ];
814 677 }
815 - // else this is dumb IE a hack, keep it
816 - // including /
817 - elseif ( ( '' === $this->property && ! ctype_space( $string[ $i ] ) ) || ( '/' === $this->property || '/' === $string[ $i ] ) ) {
818 - $this->property .= $string[ $i ];
819 - }
820 - } elseif ( ! ctype_space( $string[ $i ] ) ) {
821 - $this->property .= $string[ $i ];
678 + } elseif ( ! ctype_space( $a_string[ $i ] ) ) {
679 + $this->property .= $a_string[ $i ];
822 680 }
823 681 break;
824 682 /* Case in-value */
825 683 case 'iv':
826 - $pn = ( ( "\n" === $string[ $i ] || "\r" === $string[ $i ] ) && $this->property_is_next( $string, $i + 1 ) || ( strlen( $string ) - 1 ) === $i );
827 - if ( ( $this->is_token( $string, $i ) || $pn ) && ( ! ( ',' === $string[ $i ] && ! ctype_space( $string[ $i + 1 ] ) ) ) ) {
828 - 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
829 687 $this->status = 'ic';
830 688 ++$i;
831 689 $this->from[] = 'iv';
832 - } elseif ( ( '"' === $string[ $i ] || "'" === $string[ $i ] || '(' === $string[ $i ] ) ) {
833 - $this->cur_string[] = $string[ $i ];
834 - $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 ];
835 693 $this->status = 'instr';
836 694 $this->from[] = 'iv';
837 695 $this->quoted_string[] = in_array( strtolower( $this->property ), $quoted_string_properties, true );
838 - } elseif ( ',' === $string[ $i ] ) {
696 + } elseif ( ',' === $a_string[ $i ] ) {
839 697 $this->sub_value = trim( $this->sub_value ) . ',';
840 - } elseif ( '\\' === $string[ $i ] ) {
841 - $this->sub_value .= $this->_unicode( $string, $i );
842 - } elseif ( ';' === $string[ $i ] || $pn ) {
843 - 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 ) ] ) {
844 702 $this->status = 'is';
845 703
846 704 switch ( $this->selector ) {
847 705 case '@charset':
@@ -858,10 +716,10 @@
858 716 $this->sub_value = trim( $this->sub_value );
859 717
860 718 if ( empty( $this->sub_value_arr ) ) {
861 719 // Quote URLs in imports only if they're not already inside url() and not already quoted.
862 - if ( 'url(' !== substr( $this->sub_value, 0, 4 ) ) {
863 - 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 ) ) {
864 722 $this->sub_value = '"' . $this->sub_value . '"';
865 723 }
866 724 }
867 725 }
@@ -877,14 +735,14 @@
877 735 $this->sel_separate = array();
878 736 } else {
879 737 $this->status = 'ip';
880 738 }
881 - } elseif ( '}' !== $string[ $i ] ) {
882 - $this->sub_value .= $string[ $i ];
739 + } elseif ( '}' !== $a_string[ $i ] ) {
740 + $this->sub_value .= $a_string[ $i ];
883 741 }
884 - if ( ( '}' === $string[ $i ] || ';' === $string[ $i ] || $pn ) && ! empty( $this->selector ) ) {
742 + if ( ( '}' === $a_string[ $i ] || ';' === $a_string[ $i ] || $pn ) && ! empty( $this->selector ) ) {
885 743 if ( '' === $this->at ) {
886 - $this->at = $this->css_new_media_section( DEFAULT_AT );
744 + $this->at = $this->css_new_media_section( $this->at, (string) self::DEFAULT_AT );
887 745 }
888 746
889 747 // Case settings.
890 748 if ( $this->get_cfg( 'lowercase_s' ) ) {
@@ -898,9 +756,9 @@
898 756 $this->sub_value = '';
899 757 }
900 758
901 759 $this->value = '';
902 - while ( count( $this->sub_value_arr ) ) {
760 + while ( count( $this->sub_value_arr ) ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
903 761 $sub = array_shift( $this->sub_value_arr );
904 762 if ( strstr( $this->selector, 'font-face' ) ) {
905 763 $sub = $this->quote_font_format( $sub );
906 764 }
@@ -905,9 +763,9 @@
905 763 $sub = $this->quote_font_format( $sub );
906 764 }
907 765
908 766 if ( '' !== $sub ) {
909 - 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' ) ) ) {
910 768 $this->value .= ' ';
911 769 }
912 770 $this->value .= $sub;
913 771 }
@@ -917,9 +775,9 @@
917 775
918 776 $valid = $this->property_is_valid( $this->property );
919 777 if ( ( ! $this->invalid_at || $this->get_cfg( 'preserve_css' ) ) && ( ! $this->get_cfg( 'discard_invalid_properties' ) || $valid ) ) {
920 778 $this->css_add_property( $this->at, $this->selector, $this->property, $this->value );
921 - $this->_add_token( VALUE, $this->value );
779 + $this->_add_token( self::VALUE, $this->value );
922 780 $this->optimise->shorthands();
923 781 }
924 782 if ( ! $valid ) {
925 783 if ( $this->get_cfg( 'discard_invalid_properties' ) ) {
@@ -932,23 +790,24 @@
932 790 $this->property = '';
933 791 $this->sub_value_arr = array();
934 792 $this->value = '';
935 793 }
936 - if ( '}' === $string[ $i ] ) {
794 + if ( '}' === $a_string[ $i ] ) {
937 795 $this->explode_selectors();
938 - $this->_add_token( SEL_END, $this->selector );
796 + $this->_add_token( self::SEL_END, $this->selector );
939 797 $this->status = 'is';
940 798 $this->invalid_at = false;
941 799 $this->selector = '';
942 800 if ( $this->next_selector_at ) {
943 - $this->at = $this->css_new_media_section( $this->next_selector_at );
801 + $this->at = $this->css_close_media_section( $this->at );
802 + $this->at = $this->css_new_media_section( $this->at, $this->next_selector_at );
944 803 $this->next_selector_at = '';
945 804 }
946 805 }
947 806 } elseif ( ! $pn ) {
948 - $this->sub_value .= $string[ $i ];
807 + $this->sub_value .= $a_string[ $i ];
949 808
950 - if ( ctype_space( $string[ $i ] ) || ',' === $string[ $i ] ) {
809 + if ( ctype_space( $a_string[ $i ] ) || ',' === $a_string[ $i ] ) {
951 810 $this->optimise->subvalue();
952 811 if ( '' !== $this->sub_value ) {
953 812 $this->sub_value_arr[] = $this->sub_value;
954 813 $this->sub_value = '';
@@ -960,21 +819,21 @@
960 819 case 'instr':
961 820 $_str_char = $this->str_char[ count( $this->str_char ) - 1 ];
962 821 $_cur_string = $this->cur_string[ count( $this->cur_string ) - 1 ];
963 822 $_quoted_string = $this->quoted_string[ count( $this->quoted_string ) - 1 ];
964 - $temp_add = $string[ $i ];
823 + $temp_add = $a_string[ $i ];
965 824
966 825 // Add another string to the stack. Strings can't be nested inside of quotes, only parentheses,
967 826 // but parentheticals can be nested more than once.
968 - if ( ')' === $_str_char && ( '(' === $string[ $i ] || '"' === $string[ $i ] || '\\' === $string[ $i ] ) && ! $this->escaped( $string, $i ) ) {
969 - $this->cur_string[] = $string[ $i ];
970 - $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 ];
971 830 $this->from[] = 'instr';
972 - $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 ] );
973 832 continue 2;
974 833 }
975 834
976 - 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 ) ) ) {
977 836 $temp_add = '\\A';
978 837 $this->log( 'Fixed incorrect newline in string', 'Warning' );
979 838 }
980 839
@@ -979,22 +838,24 @@
979 838 }
980 839
981 840 $_cur_string .= $temp_add;
982 841
983 - if ( $string[ $i ] === $_str_char && ! $this->escaped( $string, $i ) ) {
842 + if ( $a_string[ $i ] === $_str_char && ! $this->escaped( $a_string, $i ) ) {
984 843 $this->status = array_pop( $this->from );
985 844
986 845 if ( ! preg_match( '|[' . implode( '', $this->data['csstidy']['whitespace'] ) . ']|uis', $_cur_string ) && 'content' !== $this->property ) {
987 846 if ( ! $_quoted_string ) {
988 847 if ( ')' !== $_str_char ) {
989 - // Convert properties like
990 - // font-family: 'Arial';
991 - // to
992 - // font-family: Arial;
993 - // or
994 - // url("abc")
995 - // to
996 - // 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 + */
997 858 $_cur_string = substr( $_cur_string, 1, -1 );
998 859 }
999 860 } else {
1000 861 $_quoted_string = false;
@@ -1010,14 +871,14 @@
1010 871 }
1011 872
1012 873 if ( 'iv' === $this->status ) {
1013 874 if ( ! $_quoted_string ) {
1014 - if ( false !== strpos( $_cur_string, ',' ) ) {
1015 - // We can on only remove space next to ','
875 + if ( str_contains( $_cur_string, ',' ) ) {
876 + // We can on only remove space next to ','.
1016 877 $_cur_string = implode( ',', array_map( 'trim', explode( ',', $_cur_string ) ) );
1017 878 }
1018 879 // and multiple spaces (too expensive).
1019 - if ( false !== strpos( $_cur_string, ' ' ) ) {
880 + if ( str_contains( $_cur_string, ' ' ) ) {
1020 881 $_cur_string = preg_replace( ',\s+,', ' ', $_cur_string );
1021 882 }
1022 883 }
1023 884 $this->sub_value .= $_cur_string;
@@ -1031,20 +892,20 @@
1031 892 }
1032 893 break;
1033 894 /* Case in-comment */
1034 895 case 'ic':
1035 - if ( '*' === $string[ $i ] && '/' === $string[ $i + 1 ] ) {
896 + if ( '*' === $a_string[ $i ] && '/' === $a_string[ $i + 1 ] ) {
1036 897 $this->status = array_pop( $this->from );
1037 - $i++;
1038 - if ( strlen( $cur_comment ) > 1 && 0 === strncmp( $cur_comment, '!', 1 ) ) {
1039 - $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 );
1040 901 $this->css_add_important_comment( $cur_comment );
1041 902 } else {
1042 - $this->_add_token( COMMENT, $cur_comment );
903 + $this->_add_token( self::COMMENT, $cur_comment );
1043 904 }
1044 905 $cur_comment = '';
1045 906 } else {
1046 - $cur_comment .= $string[ $i ];
907 + $cur_comment .= $a_string[ $i ];
1047 908 }
1048 909 break;
1049 910 }
1050 911 }
@@ -1052,23 +913,23 @@
1052 913 $this->optimise->postparse();
1053 914 $this->print->_reset();
1054 915
1055 916 // Set locale back to original setting.
1056 - @setlocale( LC_ALL, $old );
917 + @setlocale( LC_ALL, $old ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1057 918
1058 919 return ! ( empty( $this->css ) && empty( $this->import ) && empty( $this->charset ) && empty( $this->tokens ) && empty( $this->namespace ) );
1059 920 }
1060 921
1061 922 /**
1062 - * 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).
1063 924 *
1064 925 * @since 1.0.0
1065 926 *
1066 - * @param mixed $value Value.
927 + * @param string $value Value.
1067 928 * @return string String.
1068 929 */
1069 - protected function quote_font_format( $value ) {
1070 - if ( 0 === strncmp( $value, 'format', 6 ) ) {
930 + protected function quote_font_format( string $value ): string {
931 + if ( str_starts_with( $value, 'format' ) ) {
1071 932 $p = strpos( $value, ')', 7 );
1072 933 $end = substr( $value, $p );
1073 934 $format_strings = $this->parse_string_list( substr( $value, 7, $p - 7 ) );
1074 935 if ( ! $format_strings ) {
@@ -1088,9 +949,9 @@
1088 949 * Explodes selectors.
1089 950 *
1090 951 * @since 1.0.0
1091 952 */
1092 - protected function explode_selectors() {
953 + protected function explode_selectors(): void {
1093 954 // Explode multiple selectors.
1094 955 if ( 1 === $this->get_cfg( 'merge_selectors' ) ) {
1095 956 $new_sels = array();
1096 957 $lastpos = 0;
@@ -1096,9 +957,9 @@
1096 957 $lastpos = 0;
1097 958 $this->sel_separate[] = strlen( $this->selector );
1098 959 foreach ( $this->sel_separate as $num => $pos ) {
1099 960 if ( ( count( $this->sel_separate ) - 1 ) === $num ) {
1100 - $pos += 1;
961 + ++$pos;
1101 962 }
1102 963
1103 964 $new_sels[] = substr( $this->selector, $lastpos, $pos - $lastpos - 1 );
1104 965 $lastpos = $pos;
@@ -1116,28 +977,28 @@
1116 977 $this->sel_separate = array();
1117 978 }
1118 979
1119 980 /**
1120 - * Checks if a character is escaped (and returns true if it is).
981 + * Checks if a character is escaped.
1121 982 *
1122 983 * @since 1.0.0
1123 984 *
1124 - * @param string $string String.
985 + * @param string $a_string String.
1125 986 * @param int $pos Position.
1126 - * @return bool [return value]
987 + * @return bool Whether a character is escaped.
1127 988 */
1128 - public function escaped( &$string, $pos ) {
1129 - 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
1130 991 }
1131 992
1132 993 /**
1133 - * 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).
1134 995 *
1135 996 * @since 1.10.0
1136 997 *
1137 998 * @param string $comment CSS Comment.
1138 999 */
1139 - protected function css_add_important_comment($comment) {
1000 + protected function css_add_important_comment( string $comment ): void {
1140 1001 if ( $this->get_cfg( 'preserve_css' ) || '' === trim( $comment ) ) {
1141 1002 return;
1142 1003 }
1143 1004 if ( ! isset( $this->css['!'] ) ) {
@@ -1157,9 +1018,9 @@
1157 1018 * @param string $selector Selector.
1158 1019 * @param string $property Property.
1159 1020 * @param string $new_val New value.
1160 1021 */
1161 - 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 {
1162 1023 if ( $this->get_cfg( 'preserve_css' ) || '' === trim( $new_val ) ) {
1163 1024 return;
1164 1025 }
1165 1026
@@ -1173,9 +1034,9 @@
1173 1034 }
1174 1035 }
1175 1036
1176 1037 /**
1177 - * 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.
1178 1039 * If not increase the name of the media section to avoid a merging.
1179 1040 *
1180 1041 * @since 1.10.0
1181 1042 *
@@ -1181,16 +1042,15 @@
1181 1042 *
1182 1043 * @param int|string $media Media.
1183 1044 * @return int|string [return value]
1184 1045 */
1185 - protected function css_check_last_media_section_or_inc( $media ) {
1046 + protected function css_check_last_media_section_or_inc( $media ) /* : int|string */ {
1186 1047 // Are we starting?
1187 - if ( ! $this->css || ! is_array( $this->css ) || empty( $this->css ) ) {
1048 + if ( empty( $this->css ) || ! is_array( $this->css ) ) {
1188 1049 return $media;
1189 1050 }
1190 1051 // If the last @media is the same as this, keep it.
1191 - end( $this->css );
1192 - $at = key( $this->css );
1052 + $at = array_key_last( $this->css );
1193 1053 if ( $at === $media ) {
1194 1054 return $media;
1195 1055 }
1196 1056 // Otherwise increase the section in the array.
@@ -1195,9 +1055,9 @@
1195 1055 }
1196 1056 // Otherwise increase the section in the array.
1197 1057 while ( isset( $this->css[ $media ] ) ) {
1198 1058 if ( is_numeric( $media ) ) {
1199 - $media++;
1059 + ++$media;
1200 1060 } else {
1201 1061 $media .= ' ';
1202 1062 }
1203 1063 }
@@ -1204,20 +1064,20 @@
1204 1064 return $media;
1205 1065 }
1206 1066
1207 1067 /**
1208 - * Start a new media section.
1068 + * Starts a new media section.
1209 1069 *
1210 1070 * Check if the media is not already known, else rename it with extra spaces to avoid merging.
1211 1071 *
1212 1072 * @since 1.0.0
1213 1073 *
1214 - * @param string $current_media Media.
1215 - * @param string $media Media.
1216 - * @param bool $at_root
1217 - * @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]
1218 1078 */
1219 - 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 */ {
1220 1080 if ( $this->get_cfg( 'preserve_css' ) ) {
1221 1081 return $new_media;
1222 1082 }
1223 1083 // If we already are in a media and CSS level is 3, manage nested medias.
@@ -1222,11 +1082,11 @@
1222 1082 }
1223 1083 // If we already are in a media and CSS level is 3, manage nested medias.
1224 1084 if ( $current_media
1225 1085 && ! $at_root
1226 - // numeric $current_media means DEFAULT_AT or inc
1086 + // Numeric $current_media means self::DEFAULT_AT or inc.
1227 1087 && ! is_numeric( $current_media )
1228 - && 0 === strncmp( $this->get_cfg( 'css_level' ), 'CSS3', 4 ) ) {
1088 + && str_starts_with( $this->get_cfg( 'css_level' ), 'CSS3' ) ) {
1229 1089 $new_media = rtrim( $current_media ) . '{' . rtrim( $new_media );
1230 1090 }
1231 1091 return $this->css_check_last_media_section_or_inc( $new_media );
1232 1092 }
@@ -1231,11 +1091,11 @@
1231 1091 return $this->css_check_last_media_section_or_inc( $new_media );
1232 1092 }
1233 1093
1234 1094 /**
1235 - * Close a media section.
1095 + * Closes a media section.
1236 1096 *
1237 - * Find the parent media we were in before or the root.
1097 + * Finds the parent media we were in before or the root.
1238 1098 *
1239 1099 * @since 1.10.0
1240 1100 *
1241 1101 * @param string $current_media Current Media.
@@ -1240,13 +1100,13 @@
1240 1100 *
1241 1101 * @param string $current_media Current Media.
1242 1102 * @return string [return value]
1243 1103 */
1244 - protected function css_close_media_section( $current_media ) {
1104 + protected function css_close_media_section( string $current_media ): string {
1245 1105 if ( $this->get_cfg( 'preserve_css' ) ) {
1246 1106 return '';
1247 1107 }
1248 - if ( false !== strpos( $current_media, '{' ) ) {
1108 + if ( str_contains( $current_media, '{' ) ) {
1249 1109 $current_media = explode( '{', $current_media );
1250 1110 array_pop( $current_media );
1251 1111 $current_media = implode( '{', $current_media );
1252 1112 return $current_media;
@@ -1255,9 +1115,9 @@
1255 1115 return '';
1256 1116 }
1257 1117
1258 1118 /**
1259 - * Start a new selector.
1119 + * Starts a new selector.
1260 1120 *
1261 1121 * If already referenced in this media section, rename it with extra space to avoid merging,
1262 1122 * except if merging is required, or last selector is the same (merge siblings).
1263 1123 * Never merge @font-face.
@@ -1267,25 +1127,24 @@
1267 1127 * @param string $media Media.
1268 1128 * @param string $selector Selector.
1269 1129 * @return string [return value]
1270 1130 */
1271 - protected function css_new_selector( $media, $selector ) {
1131 + protected function css_new_selector( string $media, string $selector ): string {
1272 1132 if ( $this->get_cfg( 'preserve_css' ) ) {
1273 1133 return $selector;
1274 1134 }
1275 1135 $selector = trim( $selector );
1276 - if ( 0 !== strncmp( $selector, '@font-face', 10 ) ) {
1136 + if ( ! str_starts_with( $selector, '@font-face' ) ) {
1277 1137 if ( false !== $this->settings['merge_selectors'] ) {
1278 1138 return $selector;
1279 1139 }
1280 1140
1281 - if ( ! $this->css || ! isset( $this->css[ $media ] ) || ! $this->css[ $media ] ) {
1141 + if ( empty( $this->css ) || ! isset( $this->css[ $media ] ) || ! $this->css[ $media ] ) {
1282 1142 return $selector;
1283 1143 }
1284 1144
1285 1145 // If last is the same, keep it.
1286 - end( $this->css[ $media ] );
1287 - $sel = key( $this->css[ $media ] );
1146 + $sel = array_key_last( $this->css[ $media ] );
1288 1147 if ( $sel === $selector ) {
1289 1148 return $selector;
1290 1149 }
1291 1150 }
@@ -1296,11 +1155,11 @@
1296 1155 return $selector;
1297 1156 }
1298 1157
1299 1158 /**
1300 - * Start a new property.
1159 + * Starts a new property.
1301 1160 *
1302 - * 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.
1303 1162 *
1304 1163 * @since 1.0.0
1305 1164 *
1306 1165 * @param string $media Media.
@@ -1307,13 +1166,13 @@
1307 1166 * @param string $selector Selector.
1308 1167 * @param string $property Property.
1309 1168 * @return string [return value]
1310 1169 */
1311 - protected function css_new_property( $media, $selector, $property ) {
1170 + protected function css_new_property( string $media, string $selector, string $property ): string {
1312 1171 if ( $this->get_cfg( 'preserve_css' ) ) {
1313 1172 return $property;
1314 1173 }
1315 - if ( ! $this->css || ! isset( $this->css[ $media ][ $selector ] ) || ! $this->css[ $media ][ $selector ] ) {
1174 + if ( empty( $this->css ) || ! isset( $this->css[ $media ][ $selector ] ) || ! $this->css[ $media ][ $selector ] ) {
1316 1175 return $property;
1317 1176 }
1318 1177
1319 1178 while ( isset( $this->css[ $media ][ $selector ][ $property ] ) ) {
@@ -1327,13 +1186,13 @@
1327 1186 * Adds CSS to an existing media/selector.
1328 1187 *
1329 1188 * @since 1.0.0
1330 1189 *
1331 - * @param string $media Media.
1332 - * @param string $selector Selector.
1333 - * @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.
1334 1193 */
1335 - public function merge_css_blocks( $media, $selector, array $css_add ) {
1194 + public function merge_css_blocks( string $media, string $selector, array $css_add ): void {
1336 1195 foreach ( $css_add as $property => $value ) {
1337 1196 $this->css_add_property( $media, $selector, $property, $value );
1338 1197 }
1339 1198 }
@@ -1345,11 +1204,11 @@
1345 1204 *
1346 1205 * @param string $value Value.
1347 1206 * @return bool Whether the value has the !important keyword.
1348 1207 */
1349 - public function is_important( &$value ) {
1208 + public function is_important( string &$value ): bool {
1350 1209 return (
1351 - false !== strpos( $value, '!' ) // Quick test.
1210 + str_contains( $value, '!' ) // Quick test.
1352 1211 && ! strcasecmp( substr( str_replace( $this->data['csstidy']['whitespace'], '', $value ), -10, 10 ), '!important' ) );
1353 1212 }
1354 1213
1355 1214 /**
@@ -1359,9 +1218,9 @@
1359 1218 *
1360 1219 * @param string $value Value.
1361 1220 * @return string Value without the !important;
1362 1221 */
1363 - public function gvw_important( $value ) {
1222 + public function gvw_important( string $value ): string {
1364 1223 if ( $this->is_important( $value ) ) {
1365 1224 $value = trim( $value );
1366 1225 $value = substr( $value, 0, -9 );
1367 1226 $value = trim( $value );
@@ -1380,9 +1239,9 @@
1380 1239 * @param string $istring String.
1381 1240 * @param int $pos Position.
1382 1241 * @return bool [return value]
1383 1242 */
1384 - protected function property_is_next( $istring, $pos ) {
1243 + protected function property_is_next( string $istring, int $pos ): bool {
1385 1244 $all_properties = &$this->data['csstidy']['all_properties'];
1386 1245 $istring = substr( $istring, $pos, strlen( $istring ) - $pos );
1387 1246 $pos = strpos( $istring, ':' );
1388 1247 if ( false === $pos ) {
@@ -1403,15 +1262,17 @@
1403 1262 *
1404 1263 * @param string $property Property.
1405 1264 * @return bool Whether the property is valid.
1406 1265 */
1407 - public function property_is_valid( $property ) {
1266 + public function property_is_valid( string $property ): bool {
1408 1267 $property = strtolower( $property );
1409 - 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 ) ) {
1410 1271 $property = trim( $property );
1411 1272 }
1412 1273 $all_properties = &$this->data['csstidy']['all_properties'];
1413 - 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' ) ) );
1414 1275 }
1415 1276
1416 1277 /**
1417 1278 * Accepts a list of strings (e.g. the argument to format() in a @font-face src property)
@@ -1424,20 +1285,20 @@
1424 1285 * format("abc, def, ghi") => format("abc, def, ghi")
1425 1286 *
1426 1287 * @since 1.0.0
1427 1288 *
1428 - * @param [type] $value [description]
1429 - * @return [type] [description]
1289 + * @param string $value [description].
1290 + * @return string[] [description]
1430 1291 */
1431 - public function parse_string_list( $value ) {
1292 + public function parse_string_list( string $value ): array {
1432 1293 $value = trim( $value );
1433 1294
1434 - // Case: empty
1295 + // Case: empty.
1435 1296 if ( ! $value ) {
1436 1297 return array();
1437 1298 }
1438 1299
1439 - $strings = array();
1300 + $a_strings = array();
1440 1301
1441 1302 $in_str = false;
1442 1303 $current_string = '';
1443 1304
@@ -1443,13 +1304,13 @@
1443 1304
1444 1305 for ( $i = 0, $_len = strlen( $value ); $i < $_len; $i++ ) {
1445 1306 if ( ( ',' === $value[ $i ] || ' ' === $value[ $i ] ) && true === $in_str ) {
1446 1307 $in_str = false;
1447 - $strings[] = $current_string;
1308 + $a_strings[] = $current_string;
1448 1309 $current_string = '';
1449 1310 } elseif ( '"' === $value[ $i ] || "'" === $value[ $i ] ) {
1450 1311 if ( $in_str === $value[ $i ] ) {
1451 - $strings[] = $current_string;
1312 + $a_strings[] = $current_string;
1452 1313 $in_str = false;
1453 1314 $current_string = '';
1454 1315 continue;
1455 1316 } elseif ( ! $in_str ) {
@@ -1454,12 +1315,12 @@
1454 1315 continue;
1455 1316 } elseif ( ! $in_str ) {
1456 1317 $in_str = $value[ $i ];
1457 1318 }
1458 - } else {
1319 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
1459 1320 if ( $in_str ) {
1460 1321 $current_string .= $value[ $i ];
1461 - } else {
1322 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
1462 1323 if ( ! preg_match( '/[\s,]/', $value[ $i ] ) ) {
1463 1324 $in_str = true;
1464 1325 $current_string = $value[ $i ];
1465 1326 }
@@ -1467,11 +1328,11 @@
1467 1328 }
1468 1329 }
1469 1330
1470 1331 if ( $current_string ) {
1471 - $strings[] = $current_string;
1332 + $a_strings[] = $current_string;
1472 1333 }
1473 1334
1474 - return $strings;
1335 + return $a_strings;
1475 1336 }
1476 1337
1477 1338 } // class TablePress_CSSTidy