Completed
Push — update/wordads-shortcode-name ( 7e5709...86de40 )
by
unknown
11:35
created

csstidy::property_is_valid()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 4
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * CSSTidy - CSS Parser and Optimiser
5
 *
6
 * CSS Parser class
7
 *
8
 * Copyright 2005, 2006, 2007 Florian Schmitz
9
 *
10
 * This file is part of CSSTidy.
11
 *
12
 *   CSSTidy is free software; you can redistribute it and/or modify
13
 *   it under the terms of the GNU Lesser General Public License as published by
14
 *   the Free Software Foundation; either version 2.1 of the License, or
15
 *   (at your option) any later version.
16
 *
17
 *   CSSTidy is distributed in the hope that it will be useful,
18
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 *   GNU Lesser General Public License for more details.
21
 *
22
 *   You should have received a copy of the GNU Lesser General Public License
23
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
26
 * @package csstidy
27
 * @author Florian Schmitz (floele at gmail dot com) 2005-2007
28
 * @author Brett Zamir (brettz9 at yahoo dot com) 2007
29
 * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
30
 * @author Cedric Morin (cedric at yterium dot com) 2010
31
 */
32
/**
33
 * Defines ctype functions if required
34
 *
35
 * @version 1.0
36
 */
37
require_once( dirname( __FILE__ ) . '/class.csstidy_ctype.php' );
38
39
/**
40
 * Various CSS data needed for correct optimisations etc.
41
 *
42
 * @version 1.3
43
 */
44
require( dirname( __FILE__ ) . '/data.inc.php' );
45
46
/**
47
 * Contains a class for printing CSS code
48
 *
49
 * @version 1.0
50
 */
51
require( dirname( __FILE__ ) . '/class.csstidy_print.php' );
52
53
/**
54
 * Contains a class for optimising CSS code
55
 *
56
 * @version 1.0
57
 */
58
require( dirname( __FILE__ ) . '/class.csstidy_optimise.php' );
59
60
/**
61
 * CSS Parser class
62
 *
63
64
 * This class represents a CSS parser which reads CSS code and saves it in an array.
65
 * In opposite to most other CSS parsers, it does not use regular expressions and
66
 * thus has full CSS2 support and a higher reliability.
67
 * Additional to that it applies some optimisations and fixes to the CSS code.
68
 * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
69
 * @package csstidy
70
 * @author Florian Schmitz (floele at gmail dot com) 2005-2006
71
 * @version 1.3.1
72
 */
73
class csstidy {
74
75
	/**
76
	 * Saves the parsed CSS. This array is empty if preserve_css is on.
77
	 * @var array
78
	 * @access public
79
	 */
80
	public $css = array();
81
	/**
82
	 * Saves the parsed CSS (raw)
83
	 * @var array
84
	 * @access private
85
	 */
86
	public $tokens = array();
87
	/**
88
	 * Printer class
89
	 * @see csstidy_print
90
	 * @var object
91
	 * @access public
92
	 */
93
	public $print;
94
	/**
95
	 * Optimiser class
96
	 * @see csstidy_optimise
97
	 * @var object
98
	 * @access private
99
	 */
100
	public $optimise;
101
	/**
102
	 * Saves the CSS charset (@charset)
103
	 * @var string
104
	 * @access private
105
	 */
106
	public $charset = '';
107
	/**
108
	 * Saves all @import URLs
109
	 * @var array
110
	 * @access private
111
	 */
112
	public $import = array();
113
	/**
114
	 * Saves the namespace
115
	 * @var string
116
	 * @access private
117
	 */
118
	public $namespace = '';
119
	/**
120
	 * Contains the version of csstidy
121
	 * @var string
122
	 * @access private
123
	 */
124
	public $version = '1.3';
125
	/**
126
	 * Stores the settings
127
	 * @var array
128
	 * @access private
129
	 */
130
	public $settings = array();
131
	/**
132
	 * Saves the parser-status.
133
	 *
134
	 * Possible values:
135
	 * - is = in selector
136
	 * - ip = in property
137
	 * - iv = in value
138
	 * - instr = in string (started at " or ' or ( )
139
	 * - ic = in comment (ignore everything)
140
	 * - at = in @-block
141
	 *
142
	 * @var string
143
	 * @access private
144
	 */
145
	public $status = 'is';
146
	/**
147
	 * Saves the current at rule (@media)
148
	 * @var string
149
	 * @access private
150
	 */
151
	public $at = '';
152
	/**
153
	 * Saves the current selector
154
	 * @var string
155
	 * @access private
156
	 */
157
	public $selector = '';
158
	/**
159
	 * Saves the current property
160
	 * @var string
161
	 * @access private
162
	 */
163
	public $property = '';
164
	/**
165
	 * Saves the position of , in selectors
166
	 * @var array
167
	 * @access private
168
	 */
169
	public $sel_separate = array();
170
	/**
171
	 * Saves the current value
172
	 * @var string
173
	 * @access private
174
	 */
175
	public $value = '';
176
	/**
177
	 * Saves the current sub-value
178
	 *
179
	 * Example for a subvalue:
180
	 * background:url(foo.png) red no-repeat;
181
	 * "url(foo.png)", "red", and  "no-repeat" are subvalues,
182
	 * separated by whitespace
183
	 * @var string
184
	 * @access private
185
	 */
186
	public $sub_value = '';
187
	/**
188
	 * Array which saves all subvalues for a property.
189
	 * @var array
190
	 * @see sub_value
191
	 * @access private
192
	 */
193
	public $sub_value_arr = array();
194
	/**
195
	 * Saves the stack of characters that opened the current strings
196
	 * @var array
197
	 * @access private
198
	 */
199
	public $str_char = array();
200
	public $cur_string = array();
201
	/**
202
	 * Status from which the parser switched to ic or instr
203
	 * @var array
204
	 * @access private
205
	 */
206
	public $from = array();
207
	/**
208
	/**
209
	 * =true if in invalid at-rule
210
	 * @var bool
211
	 * @access private
212
	 */
213
	public $invalid_at = false;
214
	/**
215
	 * =true if something has been added to the current selector
216
	 * @var bool
217
	 * @access private
218
	 */
219
	public $added = false;
220
	/**
221
	 * Array which saves the message log
222
	 * @var array
223
	 * @access private
224
	 */
225
	public $log = array();
226
	/**
227
	 * Saves the line number
228
	 * @var integer
229
	 * @access private
230
	 */
231
	public $line = 1;
232
	/**
233
	 * Marks if we need to leave quotes for a string
234
	 * @var array
235
	 * @access private
236
	 */
237
	public $quoted_string = array();
238
239
	/**
240
	 * List of tokens
241
	 * @var string
242
	 */
243
	public $tokens_list = "";
244
245
	/**
246
	 * Loads standard template and sets default settings
247
	 * @access private
248
	 * @version 1.3
249
	 */
250
	function __construct() {
251
		$this->settings['remove_bslash'] = true;
252
		$this->settings['compress_colors'] = true;
253
		$this->settings['compress_font-weight'] = true;
254
		$this->settings['lowercase_s'] = false;
255
		/*
256
		  1 common shorthands optimization
257
		  2 + font property optimization
258
		  3 + background property optimization
259
		 */
260
		$this->settings['optimise_shorthands'] = 1;
261
		$this->settings['remove_last_;'] = true;
262
		/* rewrite all properties with low case, better for later gzip OK, safe*/
263
		$this->settings['case_properties'] = 1;
264
		/* sort properties in alpabetic order, better for later gzip
265
		 * but can cause trouble in case of overiding same propertie or using hack
266
		 */
267
		$this->settings['sort_properties'] = false;
268
		/*
269
		  1, 3, 5, etc -- enable sorting selectors inside @media: a{}b{}c{}
270
		  2, 5, 8, etc -- enable sorting selectors inside one CSS declaration: a,b,c{}
271
		  preserve order by default cause it can break functionnality
272
		 */
273
		$this->settings['sort_selectors'] = 0;
274
		/* is dangeroues to be used: CSS is broken sometimes */
275
		$this->settings['merge_selectors'] = 0;
276
		/* preserve or not browser hacks */
277
		$this->settings['discard_invalid_selectors'] = false;
278
		$this->settings['discard_invalid_properties'] = false;
279
		$this->settings['css_level'] = 'CSS2.1';
280
		$this->settings['preserve_css'] = false;
281
		$this->settings['timestamp'] = false;
282
		$this->settings['template'] = ''; // say that propertie exist
283
		$this->set_cfg('template','default'); // call load_template
284
		$this->optimise = new csstidy_optimise($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<csstidy>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
285
286
		$this->tokens_list = & $GLOBALS['csstidy']['tokens'];
287
	}
288
289
	function csstidy() {
290
		$this->__construct();
291
	}
292
293
	/**
294
	 * Get the value of a setting.
295
	 * @param string $setting
296
	 * @access public
297
	 * @return mixed
298
	 * @version 1.0
299
	 */
300
	function get_cfg($setting) {
301
		if (isset($this->settings[$setting])) {
302
			return $this->settings[$setting];
303
		}
304
		return false;
305
	}
306
307
	/**
308
	 * Load a template
309
	 * @param string $template used by set_cfg to load a template via a configuration setting
310
	 * @access private
311
	 * @version 1.4
312
	 */
313
	function _load_template($template) {
314
		switch ($template) {
315
			case 'default':
316
				$this->load_template('default');
317
				break;
318
319
			case 'highest':
320
				$this->load_template('highest_compression');
321
				break;
322
323
			case 'high':
324
				$this->load_template('high_compression');
325
				break;
326
327
			case 'low':
328
				$this->load_template('low_compression');
329
				break;
330
331
			default:
332
				$this->load_template($template);
333
				break;
334
		}
335
	}
336
337
	/**
338
	 * Set the value of a setting.
339
	 * @param string $setting
340
	 * @param mixed $value
341
	 * @access public
342
	 * @return bool
343
	 * @version 1.0
344
	 */
345
	function set_cfg($setting, $value=null) {
346
		if (is_array($setting) && $value === null) {
347
			foreach ($setting as $setprop => $setval) {
348
				$this->settings[$setprop] = $setval;
349
			}
350
			if (array_key_exists('template', $setting)) {
351
				$this->_load_template($this->settings['template']);
352
			}
353
			return true;
354
		} else if (isset($this->settings[$setting]) && $value !== '') {
355
			$this->settings[$setting] = $value;
356
			if ($setting === 'template') {
357
				$this->_load_template($this->settings['template']);
358
			}
359
			return true;
360
		}
361
		return false;
362
	}
363
364
	/**
365
	 * Adds a token to $this->tokens
366
	 * @param mixed $type
367
	 * @param string $data
368
	 * @param bool $do add a token even if preserve_css is off
369
	 * @access private
370
	 * @version 1.0
371
	 */
372
	function _add_token($type, $data, $do = false) {
373
		if ($this->get_cfg('preserve_css') || $do) {
374
			$this->tokens[] = array($type, ($type == COMMENT) ? $data : trim($data));
375
		}
376
	}
377
378
	/**
379
	 * Add a message to the message log
380
	 * @param string $message
381
	 * @param string $type
382
	 * @param integer $line
383
	 * @access private
384
	 * @version 1.0
385
	 */
386
	function log($message, $type, $line = -1) {
387
		if ($line === -1) {
388
			$line = $this->line;
389
		}
390
		$line = intval($line);
391
		$add = array('m' => $message, 't' => $type);
392
		if (!isset($this->log[$line]) || !in_array($add, $this->log[$line])) {
393
			$this->log[$line][] = $add;
394
		}
395
	}
396
397
	/**
398
	 * Parse unicode notations and find a replacement character
399
	 * @param string $string
400
	 * @param integer $i
401
	 * @access private
402
	 * @return string
403
	 * @version 1.2
404
	 */
405
	function _unicode(&$string, &$i) {
406
		++$i;
407
		$add = '';
408
		$replaced = false;
409
410
		while ($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6) {
411
			$add .= $string{$i};
412
413
			if (ctype_space($string{$i})) {
414
				break;
415
			}
416
			$i++;
417
		}
418
419
		if (hexdec($add) > 47 && hexdec($add) < 58 || hexdec($add) > 64 && hexdec($add) < 91 || hexdec($add) > 96 && hexdec($add) < 123) {
420
			$this->log('Replaced unicode notation: Changed \\' . $add . ' to ' . chr(hexdec($add)), 'Information');
421
			$add = chr(hexdec($add));
422
			$replaced = true;
423
		} else {
424
			$add = trim('\\' . $add);
425
		}
426
427
		if (@ctype_xdigit($string{$i + 1}) && ctype_space($string{$i})
428
						&& !$replaced || !ctype_space($string{$i})) {
429
			$i--;
430
		}
431
432
		if ($add !== '\\' || !$this->get_cfg('remove_bslash') || strpos($this->tokens_list, $string{$i + 1}) !== false) {
433
			return $add;
434
		}
435
436
		if ($add === '\\') {
437
			$this->log('Removed unnecessary backslash', 'Information');
438
		}
439
		return '';
440
	}
441
442
	/**
443
	 * Write formatted output to a file
444
	 * @param string $filename
445
	 * @param string $doctype when printing formatted, is a shorthand for the document type
446
	 * @param bool $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet
447
	 * @param string $title when printing formatted, is the title to be added in the head of the document
448
	 * @param string $lang when printing formatted, gives a two-letter language code to be added to the output
449
	 * @access public
450
	 * @version 1.4
451
	 */
452
	function write_page($filename, $doctype='xhtml1.1', $externalcss=true, $title='', $lang='en') {
453
		$this->write($filename, true);
454
	}
455
456
	/**
457
	 * Write plain output to a file
458
	 * @param string $filename
459
	 * @param bool $formatted whether to print formatted or not
460
	 * @param string $doctype when printing formatted, is a shorthand for the document type
461
	 * @param bool $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet
462
	 * @param string $title when printing formatted, is the title to be added in the head of the document
463
	 * @param string $lang when printing formatted, gives a two-letter language code to be added to the output
464
	 * @param bool $pre_code whether to add pre and code tags around the code (for light HTML formatted templates)
465
	 * @access public
466
	 * @version 1.4
467
	 */
468
	function write($filename, $formatted=false, $doctype='xhtml1.1', $externalcss=true, $title='', $lang='en', $pre_code=true) {
469
		$filename .= ( $formatted) ? '.xhtml' : '.css';
470
471
		if (!is_dir('temp')) {
472
			$madedir = mkdir('temp');
473
			if (!$madedir) {
474
				print 'Could not make directory "temp" in ' . dirname(__FILE__);
475
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method write() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
476
			}
477
		}
478
		$handle = fopen('temp/' . $filename, 'w');
479
		if ($handle) {
480
			if (!$formatted) {
481
				fwrite($handle, $this->print->plain());
482
			} else {
483
				fwrite($handle, $this->print->formatted_page($doctype, $externalcss, $title, $lang, $pre_code));
484
			}
485
		}
486
		fclose($handle);
487
	}
488
489
	/**
490
	 * Loads a new template
491
	 * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default"
492
	 * @param bool $from_file uses $content as filename if true
493
	 * @access public
494
	 * @version 1.1
495
	 * @see http://csstidy.sourceforge.net/templates.php
496
	 */
497
	function load_template($content, $from_file=true) {
498
		$predefined_templates = & $GLOBALS['csstidy']['predefined_templates'];
499
		if ($content === 'high_compression' || $content === 'default' || $content === 'highest_compression' || $content === 'low_compression') {
500
			$this->template = $predefined_templates[$content];
0 ignored issues
show
Bug introduced by
The property template does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
501
			return;
502
		}
503
504
505
		if ($from_file) {
506
			$content = strip_tags(file_get_contents($content), '<span>');
507
		}
508
		$content = str_replace("\r\n", "\n", $content); // Unify newlines (because the output also only uses \n)
509
		$template = explode('|', $content);
510
511
		for ($i = 0; $i < count($template); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
512
			$this->template[$i] = $template[$i];
513
		}
514
	}
515
516
	/**
517
	 * Starts parsing from URL
518
	 * @param string $url
519
	 * @access public
520
	 * @version 1.0
521
	 */
522
	function parse_from_url($url) {
523
		return $this->parse(@file_get_contents($url));
524
	}
525
526
	/**
527
	 * Checks if there is a token at the current position
528
	 * @param string $string
529
	 * @param integer $i
530
	 * @access public
531
	 * @version 1.11
532
	 */
533
	function is_token(&$string, $i) {
534
		return (strpos($this->tokens_list, $string{$i}) !== false && !csstidy::escaped($string, $i));
535
	}
536
537
	/**
538
	 * Parses CSS in $string. The code is saved as array in $this->css
539
	 * @param string $string the CSS code
540
	 * @access public
541
	 * @return bool
542
	 * @version 1.1
543
	 */
544
	function parse($string) {
545
		// Temporarily set locale to en_US in order to handle floats properly
546
		$old = @setlocale(LC_ALL, 0);
547
		@setlocale(LC_ALL, 'C');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
548
549
		// PHP bug? Settings need to be refreshed in PHP4
550
		$this->print = new csstidy_print($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<csstidy>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
551
		//$this->optimise = new csstidy_optimise($this);
552
553
		$all_properties = & $GLOBALS['csstidy']['all_properties'];
554
		$at_rules = & $GLOBALS['csstidy']['at_rules'];
555
		$quoted_string_properties = & $GLOBALS['csstidy']['quoted_string_properties'];
556
557
		$this->css = array();
558
		$this->print->input_css = $string;
559
		$string = str_replace("\r\n", "\n", $string) . ' ';
560
		$cur_comment = '';
561
562
		for ($i = 0, $size = strlen($string); $i < $size; $i++) {
563
			if ($string{$i} === "\n" || $string{$i} === "\r") {
564
				++$this->line;
565
			}
566
567
			switch ($this->status) {
568
				/* Case in at-block */
569
				case 'at':
570
					if (csstidy::is_token($string, $i)) {
571
						if ($string{$i} === '/' && @$string{$i + 1} === '*') {
572
							$this->status = 'ic';
573
							++$i;
574
							$this->from[] = 'at';
575
						} elseif ($string{$i} === '{') {
576
							$this->status = 'is';
577
							$this->at = $this->css_new_media_section($this->at);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->css_new_media_section($this->at) can also be of type integer or double. However, the property $at is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
578
							$this->_add_token(AT_START, $this->at);
579
						} elseif ($string{$i} === ',') {
580
							$this->at = trim($this->at) . ',';
581
						} elseif ($string{$i} === '\\') {
582
							$this->at .= $this->_unicode($string, $i);
583
						}
584
						// fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5)
585
						// '/' is included for ratios in Opera: (-o-min-device-pixel-ratio: 3/2)
586 View Code Duplication
						elseif (in_array($string{$i}, array('(', ')', ':', '.', '/'))) {
587
							$this->at .= $string{$i};
588
						}
589
					} else {
590
						$lastpos = strlen($this->at) - 1;
591
						if (!( (ctype_space($this->at{$lastpos}) || csstidy::is_token($this->at, $lastpos) && $this->at{$lastpos} === ',') && ctype_space($string{$i}))) {
592
							$this->at .= $string{$i};
593
						}
594
					}
595
					break;
596
597
				/* Case in-selector */
598
				case 'is':
599
					if (csstidy::is_token($string, $i)) {
600
						if ($string{$i} === '/' && @$string{$i + 1} === '*' && trim($this->selector) == '') {
601
							$this->status = 'ic';
602
							++$i;
603
							$this->from[] = 'is';
604
						} elseif ($string{$i} === '@' && trim($this->selector) == '') {
605
							// Check for at-rule
606
							$this->invalid_at = true;
607
							foreach ($at_rules as $name => $type) {
608
								if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) {
609
									($type === 'at') ? $this->at = '@' . $name : $this->selector = '@' . $name;
610
									$this->status = $type;
611
									$i += strlen($name);
612
									$this->invalid_at = false;
613
								}
614
							}
615
616
							if ($this->invalid_at) {
617
								$this->selector = '@';
618
								$invalid_at_name = '';
619
								for ($j = $i + 1; $j < $size; ++$j) {
620
									if (!ctype_alpha($string{$j})) {
621
										break;
622
									}
623
									$invalid_at_name .= $string{$j};
624
								}
625
								$this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning');
626
							}
627
						} elseif (($string{$i} === '"' || $string{$i} === "'")) {
628
							$this->cur_string[] = $string{$i};
629
							$this->status = 'instr';
630
							$this->str_char[] = $string{$i};
631
							$this->from[] = 'is';
632
							/* fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
633
							$this->quoted_string[] = ($string{$i - 1} == '=' );
634
						} elseif ($this->invalid_at && $string{$i} === ';') {
635
							$this->invalid_at = false;
636
							$this->status = 'is';
637
						} elseif ($string{$i} === '{') {
638
							$this->status = 'ip';
639
							if($this->at == '') {
640
								$this->at = $this->css_new_media_section(DEFAULT_AT);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->css_new_media_section(DEFAULT_AT) can also be of type integer or double. However, the property $at is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
641
							}
642
							$this->selector = $this->css_new_selector($this->at,$this->selector);
643
							$this->_add_token(SEL_START, $this->selector);
644
							$this->added = false;
645
						} elseif ($string{$i} === '}') {
646
							$this->_add_token(AT_END, $this->at);
647
							$this->at = '';
648
							$this->selector = '';
649
							$this->sel_separate = array();
650
						} elseif ($string{$i} === ',') {
651
							$this->selector = trim($this->selector) . ',';
652
							$this->sel_separate[] = strlen($this->selector);
653
						} elseif ($string{$i} === '\\') {
654
							$this->selector .= $this->_unicode($string, $i);
655 View Code Duplication
						} elseif ($string{$i} === '*' && @in_array($string{$i + 1}, array('.', '#', '[', ':'))) {
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
656
							// remove unnecessary universal selector, FS#147
657
						} else {
658
							$this->selector .= $string{$i};
659
						}
660
					} else {
661
						$lastpos = strlen($this->selector) - 1;
662
						if ($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || csstidy::is_token($this->selector, $lastpos) && $this->selector{$lastpos} === ',') && ctype_space($string{$i}))) {
663
							$this->selector .= $string{$i};
664
						}
665
						else if (ctype_space($string{$i}) && $this->get_cfg('preserve_css') && !$this->get_cfg('merge_selectors')) {
666
							$this->selector .= $string{$i};
667
						}
668
					}
669
					break;
670
671
				/* Case in-property */
672
				case 'ip':
673
					if (csstidy::is_token($string, $i)) {
674
						if (($string{$i} === ':' || $string{$i} === '=') && $this->property != '') {
675
							$this->status = 'iv';
676
							if (!$this->get_cfg('discard_invalid_properties') || csstidy::property_is_valid($this->property)) {
677
								$this->property = $this->css_new_property($this->at,$this->selector,$this->property);
678
								$this->_add_token(PROPERTY, $this->property);
679
							}
680
						} elseif ($string{$i} === '/' && @$string{$i + 1} === '*' && $this->property == '') {
681
							$this->status = 'ic';
682
							++$i;
683
							$this->from[] = 'ip';
684 View Code Duplication
						} elseif ($string{$i} === '}') {
685
							$this->explode_selectors();
686
							$this->status = 'is';
687
							$this->invalid_at = false;
688
							$this->_add_token(SEL_END, $this->selector);
689
							$this->selector = '';
690
							$this->property = '';
691
						} elseif ($string{$i} === ';') {
692
							$this->property = '';
693
						} elseif ($string{$i} === '\\') {
694
							$this->property .= $this->_unicode($string, $i);
695
						}
696
						// else this is dumb IE a hack, keep it
697
						elseif ($this->property=='' AND !ctype_space($string{$i})) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
698
							$this->property .= $string{$i};
699
						}
700
					}
701
					elseif (!ctype_space($string{$i})) {
702
						$this->property .= $string{$i};
703
					}
704
					break;
705
706
				/* Case in-value */
707
				case 'iv':
708
					$pn = (($string{$i} === "\n" || $string{$i} === "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1);
709
					if ((csstidy::is_token($string, $i) || $pn) && (!($string{$i} == ',' && !ctype_space($string{$i+1})))) {
710
						if ($string{$i} === '/' && @$string{$i + 1} === '*') {
711
							$this->status = 'ic';
712
							++$i;
713
							$this->from[] = 'iv';
714
						} elseif (($string{$i} === '"' || $string{$i} === "'" || $string{$i} === '(')) {
715
							$this->cur_string[] = $string{$i};
716
							$this->str_char[] = ($string{$i} === '(') ? ')' : $string{$i};
717
							$this->status = 'instr';
718
							$this->from[] = 'iv';
719
							$this->quoted_string[] = in_array(strtolower($this->property), $quoted_string_properties);
720
						} elseif ($string{$i} === ',') {
721
							$this->sub_value = trim($this->sub_value) . ',';
722
						} elseif ($string{$i} === '\\') {
723
							$this->sub_value .= $this->_unicode($string, $i);
724
						} elseif ($string{$i} === ';' || $pn) {
725
							if ($this->selector{0} === '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] === 'iv') {
726
								$this->status = 'is';
727
728
								switch ($this->selector) {
729
									case '@charset':
730
										/* Add quotes to charset */
731
										$this->sub_value_arr[] = '"' . trim($this->sub_value) . '"';
732
										$this->charset = $this->sub_value_arr[0];
733
										break;
734
									case '@namespace':
735
										/* Add quotes to namespace */
736
										$this->sub_value_arr[] = '"' . trim($this->sub_value) . '"';
737
										$this->namespace = implode(' ', $this->sub_value_arr);
738
										break;
739
									case '@import':
740
										$this->sub_value = trim($this->sub_value);
741
742
										if (empty($this->sub_value_arr)) {
743
											// Quote URLs in imports only if they're not already inside url() and not already quoted.
744
											if (substr($this->sub_value, 0, 4) != 'url(') {
745
												if (!($this->sub_value{0} == substr($this->sub_value, -1) && in_array($this->sub_value{0}, array("'", '"')))) {
746
													$this->sub_value = '"' . $this->sub_value . '"';
747
												}
748
											}
749
										}
750
751
										$this->sub_value_arr[] = $this->sub_value;
752
										$this->import[] = implode(' ', $this->sub_value_arr);
753
										break;
754
								}
755
756
								$this->sub_value_arr = array();
757
								$this->sub_value = '';
758
								$this->selector = '';
759
								$this->sel_separate = array();
760
							} else {
761
								$this->status = 'ip';
762
							}
763
						} elseif ($string{$i} !== '}') {
764
							$this->sub_value .= $string{$i};
765
						}
766
						if (($string{$i} === '}' || $string{$i} === ';' || $pn) && !empty($this->selector)) {
767
							if ($this->at == '') {
768
								$this->at = $this->css_new_media_section(DEFAULT_AT);
769
							}
770
771
							// case settings
772
							if ($this->get_cfg('lowercase_s')) {
773
								$this->selector = strtolower($this->selector);
774
							}
775
							$this->property = strtolower($this->property);
776
777
							$this->optimise->subvalue();
778
							if ($this->sub_value != '') {
779
								if (substr($this->sub_value, 0, 6) == 'format') {
780
									$format_strings = csstidy::parse_string_list(substr($this->sub_value, 7, -1));
781
									if (!$format_strings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $format_strings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
782
										$this->sub_value = "";
783
									}
784
									else {
785
										$this->sub_value = "format(";
786
787
										foreach ($format_strings as $format_string) {
788
											$this->sub_value .= '"' . str_replace('"', '\\"', $format_string) . '",';
789
										}
790
791
										$this->sub_value = substr($this->sub_value, 0, -1) . ")";
792
									}
793
								}
794
								if ($this->sub_value != '') {
795
									$this->sub_value_arr[] = $this->sub_value;
796
								}
797
								$this->sub_value = '';
798
							}
799
800
							$this->value = array_shift($this->sub_value_arr);
801
							while(count($this->sub_value_arr)){
802
								//$this->value .= (substr($this->value,-1,1)==','?'':' ').array_shift($this->sub_value_arr);
803
								$this->value .= ' '.array_shift($this->sub_value_arr);
804
							}
805
806
							$this->optimise->value();
807
808
							$valid = csstidy::property_is_valid($this->property);
809
							if ((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) {
810
								$this->css_add_property($this->at, $this->selector, $this->property, $this->value);
811
								$this->_add_token(VALUE, $this->value);
812
								$this->optimise->shorthands();
813
							}
814
							if (!$valid) {
815
								if ($this->get_cfg('discard_invalid_properties')) {
816
									$this->log('Removed invalid property: ' . $this->property, 'Warning');
817
								} else {
818
									$this->log('Invalid property in ' . strtoupper($this->get_cfg('css_level')) . ': ' . $this->property, 'Warning');
819
								}
820
							}
821
822
							$this->property = '';
823
							$this->sub_value_arr = array();
824
							$this->value = '';
825
						}
826 View Code Duplication
						if ($string{$i} === '}') {
827
							$this->explode_selectors();
828
							$this->_add_token(SEL_END, $this->selector);
829
							$this->status = 'is';
830
							$this->invalid_at = false;
831
							$this->selector = '';
832
						}
833
					} elseif (!$pn) {
834
						$this->sub_value .= $string{$i};
835
836
						if (ctype_space($string{$i}) || $string{$i} == ',') {
837
							$this->optimise->subvalue();
838
							if ($this->sub_value != '') {
839
								$this->sub_value_arr[] = $this->sub_value;
840
								$this->sub_value = '';
841
							}
842
						}
843
					}
844
					break;
845
846
				/* Case in string */
847
				case 'instr':
848
					$_str_char = $this->str_char[count($this->str_char)-1];
849
					$_cur_string = $this->cur_string[count($this->cur_string)-1];
850
					$temp_add = $string{$i};
851
852
					// Add another string to the stack. Strings can't be nested inside of quotes, only parentheses, but
853
					// parentheticals can be nested more than once.
854
					if ($_str_char === ")" && ($string{$i} === "(" || $string{$i} === '"' || $string{$i} === '\'') && !csstidy::escaped($string, $i)) {
855
						$this->cur_string[] = $string{$i};
856
						$this->str_char[] = $string{$i} == "(" ? ")" : $string{$i};
857
						$this->from[] = 'instr';
858
						$this->quoted_string[] = !($string{$i} === "(");
859
						continue;
860
					}
861
862
					if ($_str_char !== ")" && ($string{$i} === "\n" || $string{$i} === "\r") && !($string{$i - 1} === '\\' && !csstidy::escaped($string, $i - 1))) {
863
						$temp_add = "\\A";
864
						$this->log('Fixed incorrect newline in string', 'Warning');
865
					}
866
867
					$_cur_string .= $temp_add;
868
869
					if ($string{$i} === $_str_char && !csstidy::escaped($string, $i)) {
870
						$_quoted_string = array_pop($this->quoted_string);
871
872
						$this->status = array_pop($this->from);
873
874
						if (!preg_match('|[' . implode('', $GLOBALS['csstidy']['whitespace']) . ']|uis', $_cur_string) && $this->property !== 'content') {
875
							if (!$_quoted_string) {
876
								if ($_str_char !== ')') {
877
									// Convert properties like
878
									// font-family: 'Arial';
879
									// to
880
									// font-family: Arial;
881
									// or
882
									// url("abc")
883
									// to
884
									// url(abc)
885
									$_cur_string = substr($_cur_string, 1, -1);
886
								}
887
							} else {
888
								$_quoted_string = false;
889
							}
890
						}
891
892
						array_pop($this->cur_string);
893
						array_pop($this->str_char);
894
895
						if ($_str_char === ")") {
896
							$_cur_string = "(" . trim(substr($_cur_string, 1, -1)) . ")";
897
						}
898
899
						if ($this->status === 'iv') {
900
							if (!$_quoted_string){
901
								if (strpos($_cur_string,',')!==false)
902
									// we can on only remove space next to ','
903
									$_cur_string = implode(',',array_map('trim',explode(',',$_cur_string)));
904
								// and multiple spaces (too expensive)
905
								if (strpos($_cur_string,'  ')!==false)
906
									$_cur_string = preg_replace(",\s+,"," ",$_cur_string);
907
							}
908
							$this->sub_value .= $_cur_string;
909
						} elseif ($this->status === 'is') {
910
							$this->selector .= $_cur_string;
911
						} elseif ($this->status === 'instr') {
912
							$this->cur_string[count($this->cur_string)-1] .= $_cur_string;
913
						}
914
					}
915
					else {
916
						$this->cur_string[count($this->cur_string)-1] = $_cur_string;
917
					}
918
					break;
919
920
				/* Case in-comment */
921
				case 'ic':
922
					if ($string{$i} === '*' && $string{$i + 1} === '/') {
923
						$this->status = array_pop($this->from);
924
						$i++;
925
						$this->_add_token(COMMENT, $cur_comment);
926
						$cur_comment = '';
927
					} else {
928
						$cur_comment .= $string{$i};
929
					}
930
					break;
931
			}
932
		}
933
934
		$this->optimise->postparse();
935
936
		$this->print->_reset();
937
938
		@setlocale(LC_ALL, $old); // Set locale back to original setting
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
939
940
		return!(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace));
941
	}
942
943
	/**
944
	 * Explodes selectors
945
	 * @access private
946
	 * @version 1.0
947
	 */
948
	function explode_selectors() {
949
		// Explode multiple selectors
950
		if ($this->get_cfg('merge_selectors') === 1) {
951
			$new_sels = array();
952
			$lastpos = 0;
953
			$this->sel_separate[] = strlen($this->selector);
954
			foreach ($this->sel_separate as $num => $pos) {
955
				if ($num == count($this->sel_separate) - 1) {
956
					$pos += 1;
957
				}
958
959
				$new_sels[] = substr($this->selector, $lastpos, $pos - $lastpos - 1);
960
				$lastpos = $pos;
961
			}
962
963
			if (count($new_sels) > 1) {
964
				foreach ($new_sels as $selector) {
965
					if (isset($this->css[$this->at][$this->selector])) {
966
						$this->merge_css_blocks($this->at, $selector, $this->css[$this->at][$this->selector]);
967
					}
968
				}
969
				unset($this->css[$this->at][$this->selector]);
970
			}
971
		}
972
		$this->sel_separate = array();
973
	}
974
975
	/**
976
	 * Checks if a character is escaped (and returns true if it is)
977
	 * @param string $string
978
	 * @param integer $pos
979
	 * @access public
980
	 * @return bool
981
	 * @version 1.02
982
	 */
983
	static function escaped(&$string, $pos) {
984
		return!(@($string{$pos - 1} !== '\\') || csstidy::escaped($string, $pos - 1));
985
	}
986
987
	/**
988
	 * Adds a property with value to the existing CSS code
989
	 * @param string $media
990
	 * @param string $selector
991
	 * @param string $property
992
	 * @param string $new_val
993
	 * @access private
994
	 * @version 1.2
995
	 */
996
	function css_add_property($media, $selector, $property, $new_val) {
997
		if ($this->get_cfg('preserve_css') || trim($new_val) == '') {
998
			return;
999
		}
1000
1001
		$this->added = true;
1002
		if (isset($this->css[$media][$selector][$property])) {
1003
			if ((csstidy::is_important($this->css[$media][$selector][$property]) && csstidy::is_important($new_val)) || !csstidy::is_important($this->css[$media][$selector][$property])) {
1004
				$this->css[$media][$selector][$property] = trim($new_val);
1005
			}
1006
		} else {
1007
			$this->css[$media][$selector][$property] = trim($new_val);
1008
		}
1009
	}
1010
1011
	/**
1012
	 * Start a new media section.
1013
	 * Check if the media is not already known,
1014
	 * else rename it with extra spaces
1015
	 * to avoid merging
1016
	 *
1017
	 * @param string $media
1018
	 * @return string
1019
	 */
1020
	function css_new_media_section($media){
1021
		if($this->get_cfg('preserve_css')) {
1022
			return $media;
1023
		}
1024
1025
		// if the last @media is the same as this
1026
		// keep it
1027
		if (!$this->css OR !is_array($this->css) OR empty($this->css)){
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1028
			return $media;
1029
		}
1030
		end($this->css);
1031
		list($at,) = each($this->css);
1032
		if ($at == $media){
1033
			return $media;
1034
		}
1035
		while (isset($this->css[$media]))
1036
			if (is_numeric($media))
1037
				$media++;
1038
			else
1039
				$media .= " ";
1040
		return $media;
1041
	}
1042
1043
	/**
1044
	 * Start a new selector.
1045
	 * If already referenced in this media section,
1046
	 * rename it with extra space to avoid merging
1047
	 * except if merging is required,
1048
	 * or last selector is the same (merge siblings)
1049
	 *
1050
	 * never merge @font-face
1051
	 *
1052
	 * @param string $media
1053
	 * @param string $selector
1054
	 * @return string
1055
	 */
1056
	function css_new_selector($media,$selector){
1057
		if($this->get_cfg('preserve_css')) {
1058
			return $selector;
1059
		}
1060
		$selector = trim($selector);
1061
		if (strncmp($selector,"@font-face",10)!=0){
1062
			if ($this->settings['merge_selectors'] != false)
1063
				return $selector;
1064
1065 View Code Duplication
			if (!$this->css OR !isset($this->css[$media]) OR !$this->css[$media])
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1066
				return $selector;
1067
1068
			// if last is the same, keep it
1069
			end($this->css[$media]);
1070
			list($sel,) = each($this->css[$media]);
1071
			if ($sel == $selector){
1072
				return $selector;
1073
			}
1074
		}
1075
1076
		while (isset($this->css[$media][$selector]))
1077
			$selector .= " ";
1078
		return $selector;
1079
	}
1080
1081
	/**
1082
	 * Start a new propertie.
1083
	 * If already references in this selector,
1084
	 * rename it with extra space to avoid override
1085
	 *
1086
	 * @param string $media
1087
	 * @param string $selector
1088
	 * @param string $property
1089
	 * @return string
1090
	 */
1091
	function css_new_property($media, $selector, $property){
1092
		if($this->get_cfg('preserve_css')) {
1093
			return $property;
1094
		}
1095 View Code Duplication
		if (!$this->css OR !isset($this->css[$media][$selector]) OR !$this->css[$media][$selector])
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1096
			return $property;
1097
1098
		while (isset($this->css[$media][$selector][$property]))
1099
			$property .= " ";
1100
1101
		return $property;
1102
	}
1103
1104
	/**
1105
	 * Adds CSS to an existing media/selector
1106
	 * @param string $media
1107
	 * @param string $selector
1108
	 * @param array $css_add
1109
	 * @access private
1110
	 * @version 1.1
1111
	 */
1112
	function merge_css_blocks($media, $selector, $css_add) {
1113
		foreach ($css_add as $property => $value) {
1114
			$this->css_add_property($media, $selector, $property, $value, false);
0 ignored issues
show
Unused Code introduced by
The call to csstidy::css_add_property() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1115
		}
1116
	}
1117
1118
	/**
1119
	 * Checks if $value is !important.
1120
	 * @param string $value
1121
	 * @return bool
1122
	 * @access public
1123
	 * @version 1.0
1124
	 */
1125
	static function is_important(&$value) {
1126
		return (!strcasecmp(substr(str_replace($GLOBALS['csstidy']['whitespace'], '', $value), -10, 10), '!important'));
1127
	}
1128
1129
	/**
1130
	 * Returns a value without !important
1131
	 * @param string $value
1132
	 * @return string
1133
	 * @access public
1134
	 * @version 1.0
1135
	 */
1136
	static function gvw_important($value) {
1137
		if (csstidy::is_important($value)) {
1138
			$value = trim($value);
1139
			$value = substr($value, 0, -9);
1140
			$value = trim($value);
1141
			$value = substr($value, 0, -1);
1142
			$value = trim($value);
1143
			return $value;
1144
		}
1145
		return $value;
1146
	}
1147
1148
	/**
1149
	 * Checks if the next word in a string from pos is a CSS property
1150
	 * @param string $istring
1151
	 * @param integer $pos
1152
	 * @return bool
1153
	 * @access private
1154
	 * @version 1.2
1155
	 */
1156
	function property_is_next($istring, $pos) {
1157
		$all_properties = & $GLOBALS['csstidy']['all_properties'];
1158
		$istring = substr($istring, $pos, strlen($istring) - $pos);
1159
		$pos = strpos($istring, ':');
1160
		if ($pos === false) {
1161
			return false;
1162
		}
1163
		$istring = strtolower(trim(substr($istring, 0, $pos)));
1164
		if (isset($all_properties[$istring])) {
1165
			$this->log('Added semicolon to the end of declaration', 'Warning');
1166
			return true;
1167
		}
1168
		return false;
1169
	}
1170
1171
	/**
1172
	 * Checks if a property is valid
1173
	 * @param string $property
1174
	 * @return bool;
0 ignored issues
show
Documentation introduced by
The doc-type bool; could not be parsed: Expected "|" or "end of type", but got ";" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1175
	 * @access public
1176
	 * @version 1.0
1177
	 */
1178
	function property_is_valid($property) {
1179
		$property = strtolower($property);
1180
		if (in_array(trim($property), $GLOBALS['csstidy']['multiple_properties'])) $property = trim($property);
1181
		$all_properties = & $GLOBALS['csstidy']['all_properties'];
1182
		return (isset($all_properties[$property]) && strpos($all_properties[$property], strtoupper($this->get_cfg('css_level'))) !== false );
1183
	}
1184
1185
	/**
1186
	 * Accepts a list of strings (e.g., the argument to format() in a @font-face src property)
1187
	 * and returns a list of the strings.  Converts things like:
1188
	 *
1189
	 * format(abc) => format("abc")
1190
	 * format(abc def) => format("abc","def")
1191
	 * format(abc "def") => format("abc","def")
1192
	 * format(abc, def, ghi) => format("abc","def","ghi")
1193
	 * format("abc",'def') => format("abc","def")
1194
	 * format("abc, def, ghi") => format("abc, def, ghi")
1195
	 *
1196
	 * @param string
1197
	 * @return array
1198
	 */
1199
1200
	function parse_string_list($value) {
1201
		$value = trim($value);
1202
1203
		// Case: empty
1204
		if (!$value) return array();
1205
1206
		$strings = array();
1207
1208
		$in_str = false;
1209
		$current_string = "";
1210
1211
		for ($i = 0, $_len = strlen($value); $i < $_len; $i++) {
1212
			if (($value{$i} == "," || $value{$i} === " ") && $in_str === true) {
1213
				$in_str = false;
1214
				$strings[] = $current_string;
1215
				$current_string = "";
1216
			}
1217
			else if ($value{$i} == '"' || $value{$i} == "'"){
1218
				if ($in_str === $value{$i}) {
1219
					$strings[] = $current_string;
1220
					$in_str = false;
1221
					$current_string = "";
1222
					continue;
1223
				}
1224
				else if (!$in_str) {
1225
					$in_str = $value{$i};
1226
				}
1227
			}
1228
			else {
1229
				if ($in_str){
1230
					$current_string .= $value{$i};
1231
				}
1232
				else {
1233
					if (!preg_match("/[\s,]/", $value{$i})) {
1234
						$in_str = true;
1235
						$current_string = $value{$i};
1236
					}
1237
				}
1238
			}
1239
		}
1240
1241
		if ($current_string) {
1242
			$strings[] = $current_string;
1243
		}
1244
1245
		return $strings;
1246
	}
1247
}
1248