Completed
Push — merge/css-tidy ( fbe73f )
by
unknown
16:38
created

csstidy_print::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * CSSTidy - CSS Parser and Optimiser
5
 *
6
 * CSS Printing class
7
 * This class prints CSS data generated by csstidy.
8
 *
9
 * Copyright 2005, 2006, 2007 Florian Schmitz
10
 *
11
 * This file is part of CSSTidy.
12
 *
13
 *   CSSTidy is free software; you can redistribute it and/or modify
14
 *   it under the terms of the GNU Lesser General Public License as published by
15
 *   the Free Software Foundation; either version 2.1 of the License, or
16
 *   (at your option) any later version.
17
 *
18
 *   CSSTidy is distributed in the hope that it will be useful,
19
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 *   GNU Lesser General Public License for more details.
22
 *
23
 *   You should have received a copy of the GNU Lesser General Public License
24
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
 *
26
 * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
27
 * @package csstidy
28
 * @author Florian Schmitz (floele at gmail dot com) 2005-2007
29
 * @author Brett Zamir (brettz9 at yahoo dot com) 2007
30
 * @author Cedric Morin (cedric at yterium dot com) 2010
31
 */
32
33
/**
34
 * CSS Printing class
35
 *
36
 * This class prints CSS data generated by csstidy.
37
 *
38
 * @package csstidy
39
 * @author Florian Schmitz (floele at gmail dot com) 2005-2006
40
 * @version 1.0.1
41
 */
42
class csstidy_print {
43
44
	/**
45
	 * Saves the input CSS string
46
	 * @var string
47
	 * @access private
48
	 */
49
	public $input_css = '';
50
	/**
51
	 * Saves the formatted CSS string
52
	 * @var string
53
	 * @access public
54
	 */
55
	public $output_css = '';
56
	/**
57
	 * Saves the formatted CSS string (plain text)
58
	 * @var string
59
	 * @access public
60
	 */
61
	public $output_css_plain = '';
62
63
	function csstidy_print(&$css) {
64
		$this->__construct($css);
65
	}
66
	/**
67
	 * Constructor
68
	 * @param array $css contains the class csstidy
69
	 * @access private
70
	 * @version 1.0
71
	 */
72
	function __construct(&$css) {
73
		$this->parser = & $css;
0 ignored issues
show
Bug introduced by
The property parser 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...
74
		$this->css = & $css->css;
0 ignored issues
show
Bug introduced by
The property css 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...
75
		$this->template = & $css->template;
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...
76
		$this->tokens = & $css->tokens;
0 ignored issues
show
Bug introduced by
The property tokens 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...
77
		$this->charset = & $css->charset;
0 ignored issues
show
Bug introduced by
The property charset 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...
78
		$this->import = & $css->import;
0 ignored issues
show
Bug introduced by
The property import 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...
79
		$this->namespace = & $css->namespace;
0 ignored issues
show
Bug introduced by
The property namespace 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...
80
	}
81
82
	/**
83
	 * Resets output_css and output_css_plain (new css code)
84
	 * @access private
85
	 * @version 1.0
86
	 */
87
	function _reset() {
88
		$this->output_css = '';
89
		$this->output_css_plain = '';
90
	}
91
92
	/**
93
	 * Returns the CSS code as plain text
94
	 * @param string $default_media default @media to add to selectors without any @media
95
	 * @return string
96
	 * @access public
97
	 * @version 1.0
98
	 */
99
	function plain($default_media='') {
100
		$this->_print(true, $default_media);
101
		return $this->output_css_plain;
102
	}
103
104
	/**
105
	 * Returns the formatted CSS code
106
	 * @param string $default_media default @media to add to selectors without any @media
107
	 * @return string
108
	 * @access public
109
	 * @version 1.0
110
	 */
111
	function formatted($default_media='') {
112
		$this->_print(false, $default_media);
113
		return $this->output_css;
114
	}
115
116
	/**
117
	 * Returns the formatted CSS code to make a complete webpage
118
	 * @param string $doctype shorthand for the document type
119
	 * @param bool $externalcss indicates whether styles to be attached internally or as an external stylesheet
120
	 * @param string $title title to be added in the head of the document
121
	 * @param string $lang two-letter language code to be added to the output
122
	 * @return string
123
	 * @access public
124
	 * @version 1.4
125
	 */
126
	function formatted_page($doctype='xhtml1.1', $externalcss=true, $title='', $lang='en') {
127
		switch ($doctype) {
128
			case 'xhtml1.0strict':
129
				$doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
130
			"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
131
				break;
132
			case 'xhtml1.1':
133
			default:
134
				$doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
135
				"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
136
				break;
137
		}
138
139
		$output = $cssparsed = '';
0 ignored issues
show
Unused Code introduced by
$cssparsed is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
140
		$this->output_css_plain = & $output;
141
142
		$output .= $doctype_output . "\n" . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $lang . '"';
143
		$output .= ( $doctype === 'xhtml1.1') ? '>' : ' lang="' . $lang . '">';
144
		$output .= "\n<head>\n    <title>$title</title>";
145
146
		if ($externalcss) {
147
			$output .= "\n    <style type=\"text/css\">\n";
148
			$cssparsed = file_get_contents('cssparsed.css');
149
			$output .= $cssparsed; // Adds an invisible BOM or something, but not in css_optimised.php
150
			$output .= "\n</style>";
151
		} else {
152
			$output .= "\n" . '    <link rel="stylesheet" type="text/css" href="cssparsed.css" />';
153
//			}
154
		}
155
		$output .= "\n</head>\n<body><code id=\"copytext\">";
156
		$output .= $this->formatted();
157
		$output .= '</code>' . "\n" . '</body></html>';
158
		return $this->output_css_plain;
159
	}
160
161
	/**
162
	 * Returns the formatted CSS Code and saves it into $this->output_css and $this->output_css_plain
163
	 * @param bool $plain plain text or not
164
	 * @param string $default_media default @media to add to selectors without any @media
165
	 * @access private
166
	 * @version 2.0
167
	 */
168
	function _print($plain = false, $default_media='') {
169
		if ($this->output_css && $this->output_css_plain) {
170
			return;
171
		}
172
173
		$output = '';
174
		if (!$this->parser->get_cfg('preserve_css')) {
175
			$this->_convert_raw_css($default_media);
176
		}
177
178
		$template = & $this->template;
179
180
		if ($plain) {
181
			$template = array_map('strip_tags', $template);
182
		}
183
184
		if ($this->parser->get_cfg('timestamp')) {
185
			array_unshift($this->tokens, array(COMMENT, ' CSSTidy ' . $this->parser->version . ': ' . date('r') . ' '));
186
		}
187
188
		if (!empty($this->charset)) {
189
			$output .= $template[0] . '@charset ' . $template[5] . $this->charset . $template[6];
190
		}
191
192
		if (!empty($this->import)) {
193
			for ($i = 0, $size = count($this->import); $i < $size; $i++) {
194
				$import_components = explode(' ', $this->import[$i]);
195
				if (substr($import_components[0], 0, 4) === 'url(' && substr($import_components[0], -1, 1) === ')') {
196
					$import_components[0] = '\'' . trim(substr($import_components[0], 4, -1), "'\"") . '\'';
197
					$this->import[$i] = implode(' ', $import_components);
198
					$this->parser->log('Optimised @import : Removed "url("', 'Information');
199
				}
200
				$output .= $template[0] . '@import ' . $template[5] . $this->import[$i] . $template[6];
201
			}
202
		}
203
204
		if (!empty($this->namespace)) {
205
			if (substr($this->namespace, 0, 4) === 'url(' && substr($this->namespace, -1, 1) === ')') {
206
				$this->namespace = '\'' . substr($this->namespace, 4, -1) . '\'';
207
				$this->parser->log('Optimised @namespace : Removed "url("', 'Information');
208
			}
209
			$output .= $template[0] . '@namespace ' . $template[5] . $this->namespace . $template[6];
210
		}
211
212
		$output .= $template[13];
213
		$in_at_out = '';
214
		$out = & $output;
215
216
		foreach ($this->tokens as $key => $token) {
217
			switch ($token[0]) {
218
				case AT_START:
219
					$out .= $template[0] . $this->_htmlsp($token[1], $plain) . $template[1];
220
					$out = & $in_at_out;
221
					break;
222
223
				case SEL_START:
224
					if ($this->parser->get_cfg('lowercase_s'))
225
						$token[1] = strtolower($token[1]);
226
					$out .= ( $token[1]{0} !== '@') ? $template[2] . $this->_htmlsp($token[1], $plain) : $template[0] . $this->_htmlsp($token[1], $plain);
227
					$out .= $template[3];
228
					break;
229
230
				case PROPERTY:
231
					if ($this->parser->get_cfg('case_properties') === 2) {
232
						$token[1] = strtoupper($token[1]);
233
					} elseif ($this->parser->get_cfg('case_properties') === 1) {
234
						$token[1] = strtolower($token[1]);
235
					}
236
					$out .= $template[4] . $this->_htmlsp($token[1], $plain) . ':' . $template[5];
237
					break;
238
239
				case VALUE:
240
					$out .= $this->_htmlsp($token[1], $plain);
241
					if ($this->_seeknocomment($key, 1) == SEL_END && $this->parser->get_cfg('remove_last_;')) {
242
						$out .= str_replace(';', '', $template[6]);
243
					} else {
244
						$out .= $template[6];
245
					}
246
					break;
247
248
				case SEL_END:
249
					$out .= $template[7];
250
					if ($this->_seeknocomment($key, 1) != AT_END)
251
						$out .= $template[8];
252
					break;
253
254
				case AT_END:
255
					$out = & $output;
256
					$out .= $template[10] . str_replace("\n", "\n" . $template[10], $in_at_out);
257
					$in_at_out = '';
258
					$out .= $template[9];
259
					break;
260
261
				case COMMENT:
262
					$out .= $template[11] . '/*' . $this->_htmlsp($token[1], $plain) . '*/' . $template[12];
263
					break;
264
			}
265
		}
266
267
		$output = trim($output);
268
269
		if (!$plain) {
270
			$this->output_css = $output;
271
			$this->_print(true);
272
		} else {
273
			// If using spaces in the template, don't want these to appear in the plain output
274
			$this->output_css_plain = str_replace('&#160;', '', $output);
275
		}
276
	}
277
278
	/**
279
	 * Gets the next token type which is $move away from $key, excluding comments
280
	 * @param integer $key current position
281
	 * @param integer $move move this far
282
	 * @return mixed a token type
283
	 * @access private
284
	 * @version 1.0
285
	 */
286
	function _seeknocomment($key, $move) {
287
		$go = ($move > 0) ? 1 : -1;
288
		for ($i = $key + 1; abs($key - $i) - 1 < abs($move); $i += $go) {
289
			if (!isset($this->tokens[$i])) {
290
				return;
291
			}
292
			if ($this->tokens[$i][0] == COMMENT) {
293
				$move += 1;
294
				continue;
295
			}
296
			return $this->tokens[$i][0];
297
		}
298
	}
299
300
	/**
301
	 * Converts $this->css array to a raw array ($this->tokens)
302
	 * @param string $default_media default @media to add to selectors without any @media
303
	 * @access private
304
	 * @version 1.0
305
	 */
306
	function _convert_raw_css($default_media='') {
307
		$this->tokens = array();
308
309
		foreach ($this->css as $medium => $val) {
310
			if ($this->parser->get_cfg('sort_selectors'))
311
				ksort($val);
312 View Code Duplication
			if (intval($medium) < DEFAULT_AT) {
313
				$this->parser->_add_token(AT_START, $medium, true);
314
			}
315
			elseif ($default_media) {
316
				$this->parser->_add_token(AT_START, $default_media, true);
317
			}
318
319
			foreach ($val as $selector => $vali) {
320
				if ($this->parser->get_cfg('sort_properties'))
321
					ksort($vali);
322
				$this->parser->_add_token(SEL_START, $selector, true);
323
324
				foreach ($vali as $property => $valj) {
325
					$this->parser->_add_token(PROPERTY, $property, true);
326
					$this->parser->_add_token(VALUE, $valj, true);
327
				}
328
329
				$this->parser->_add_token(SEL_END, $selector, true);
330
			}
331
332 View Code Duplication
			if (intval($medium) < DEFAULT_AT) {
333
				$this->parser->_add_token(AT_END, $medium, true);
334
			}
335
			elseif ($default_media) {
336
				$this->parser->_add_token(AT_END, $default_media, true);
337
			}
338
		}
339
	}
340
341
	/**
342
	 * Same as htmlspecialchars, only that chars are not replaced if $plain !== true. This makes  print_code() cleaner.
343
	 * @param string $string
344
	 * @param bool $plain
345
	 * @return string
346
	 * @see csstidy_print::_print()
347
	 * @access private
348
	 * @version 1.0
349
	 */
350
	function _htmlsp($string, $plain) {
351
		if (!$plain) {
352
			return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
353
		}
354
		return $string;
355
	}
356
357
	/**
358
	 * Get compression ratio
359
	 * @access public
360
	 * @return float
361
	 * @version 1.2
362
	 */
363
	function get_ratio() {
364
		if (!$this->output_css_plain) {
365
			$this->formatted();
366
		}
367
		return round((strlen($this->input_css) - strlen($this->output_css_plain)) / strlen($this->input_css), 3) * 100;
368
	}
369
370
	/**
371
	 * Get difference between the old and new code in bytes and prints the code if necessary.
372
	 * @access public
373
	 * @return string
374
	 * @version 1.1
375
	 */
376
	function get_diff() {
377
		if (!$this->output_css_plain) {
378
			$this->formatted();
379
		}
380
381
		$diff = strlen($this->output_css_plain) - strlen($this->input_css);
382
383
		if ($diff > 0) {
384
			return '+' . $diff;
385
		} elseif ($diff == 0) {
386
			return '+-' . $diff;
387
		}
388
389
		return $diff;
390
	}
391
392
	/**
393
	 * Get the size of either input or output CSS in KB
394
	 * @param string $loc default is "output"
395
	 * @access public
396
	 * @return integer
397
	 * @version 1.0
398
	 */
399
	function size($loc = 'output') {
400
		if ($loc === 'output' && !$this->output_css) {
401
			$this->formatted();
402
		}
403
404
		if ($loc === 'input') {
405
			return (strlen($this->input_css) / 1000);
406
		} else {
407
			return (strlen($this->output_css_plain) / 1000);
408
		}
409
	}
410
411
}
412