Skip to content

Commit 3217e69

Browse files
committed
Added the directive token to handle @ directives.
Added minify functions in property.php. cssdoc::minify() now cascades through all tokens. Updated CSS tests to test new interface.
1 parent d249a33 commit 3217e69

File tree

8 files changed

+393
-197
lines changed

8 files changed

+393
-197
lines changed

src/autoload.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
'hexydec\\html\\cssmin' => $dir.'/cssmin.php',
1616
'hexydec\\css\\cssdoc' => __DIR__.'/cssdoc/cssdoc.php',
1717
'hexydec\\css\\mediaquery' => __DIR__.'/cssdoc/tokens/mediaquery.php',
18+
'hexydec\\css\\directive' => __DIR__.'/cssdoc/tokens/directive.php',
1819
'hexydec\\css\\rule' => __DIR__.'/cssdoc/tokens/rule.php',
1920
'hexydec\\css\\selector' => __DIR__.'/cssdoc/tokens/selector.php',
2021
'hexydec\\css\\property' => __DIR__.'/cssdoc/tokens/property.php'

src/cssdoc/cssdoc.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ class cssdoc {
2222
'comma' => ',',
2323
'colon' => ':',
2424
'semicolon' => ';',
25-
'string' => '!?[^\[\]{}\(\):;,>+=~\^$!"\/ \n\r\t]++'
25+
'directive' => '@[a-z-]++',
26+
'string' => '!?[^\[\]{}\(\):;,>+~\^$!" \n\r\t]++'
2627
];
2728

2829
/**
@@ -42,7 +43,8 @@ class cssdoc {
4243
];
4344

4445
protected $output = [
45-
'output' => 'beautify'
46+
'output' => 'minify',
47+
'prefix' => ''
4648
];
4749

4850
protected $document;
@@ -188,6 +190,14 @@ protected function parse(array &$tokens) : bool {
188190
* @return void
189191
*/
190192
public function minify(array $minify = []) : void {
193+
$minify = array_merge($this->config, $minify);
194+
195+
// set email options
196+
if ($minify['email']) {
197+
$minify['maxline'] = 800;
198+
$minify['shortenhex'] = false;
199+
}
200+
$this->document->minify($minify);
191201
}
192202

193203
/**

src/cssdoc/tokens/directive.php

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
declare(strict_types = 1);
3+
namespace hexydec\css;
4+
5+
class directive {
6+
7+
/**
8+
* @var rule The parent rule object
9+
*/
10+
protected $root;
11+
12+
/**
13+
* @var string The name of the directive
14+
*/
15+
protected $directive;
16+
17+
/**
18+
* @var string The value of the directive
19+
*/
20+
protected $content = null;
21+
22+
/**
23+
* @var array An array of properties
24+
*/
25+
protected $properties = [];
26+
27+
/**
28+
* Constructs the comment object
29+
*
30+
* @param cssdoc $root The parent htmldoc object
31+
*/
32+
public function __construct(mediaquery $root) {
33+
$this->root = $root;
34+
}
35+
36+
/**
37+
* Parses an array of tokens into an HTML documents
38+
*
39+
* @param array &$tokens An array of tokens generated by tokenise()
40+
* @param array $config An array of configuration options
41+
* @return void
42+
*/
43+
public function parse(array &$tokens) : void {
44+
$directive = true;
45+
$token = current($tokens);
46+
do {
47+
switch ($token['type']) {
48+
case 'directive':
49+
$this->directive = $token['value'];
50+
break;
51+
case 'string':
52+
case 'quotes':
53+
if ($directive) {
54+
$this->content = $token['value'];
55+
} else {
56+
$item = new property($this);
57+
$item->parse($tokens);
58+
$this->properties[] = $item;
59+
}
60+
break;
61+
case 'curlyopen':
62+
$directive = false;
63+
break;
64+
case 'semicolon':
65+
case 'curlyclose':
66+
break 2;
67+
}
68+
} while (($token = next($tokens)) !== false);
69+
}
70+
71+
/**
72+
* Minifies the internal representation of the comment
73+
*
74+
* @param array $minify An array of minification options controlling which operations are performed
75+
* @return void
76+
*/
77+
public function minify(array $minify) : void {
78+
}
79+
80+
/**
81+
* Compile the property to a string
82+
*
83+
* @param array $options An array of compilation options
84+
* @return void
85+
*/
86+
public function compile(array $options) : string {
87+
$b = $options['output'] != 'minify';
88+
$css = $this->directive;
89+
if ($this->content) {
90+
$css .= ' '.$this->content;
91+
}
92+
if ($this->properties) {
93+
$css .= $b ? ' {' : '{';
94+
95+
// compile properties
96+
$tab = $b ? "\n\t" : '';
97+
foreach ($this->properties AS $item) {
98+
$css .= $tab.$item->compile($options);
99+
}
100+
$css .= $b ? "\n".$options['prefix'].'}' : '}';
101+
} else {
102+
$css .= ';';
103+
}
104+
return $css;
105+
}
106+
}

src/cssdoc/tokens/mediaquery.php

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ class mediaquery {
99
*/
1010
protected $root;
1111

12+
/**
13+
* @var array An array of media query parameters
14+
*/
15+
protected $media = [];
16+
1217
/**
1318
* @var array An array of child token objects
1419
*/
@@ -19,8 +24,9 @@ class mediaquery {
1924
*
2025
* @param cssdoc $root The parent htmldoc object
2126
*/
22-
public function __construct(cssdoc $root) {
27+
public function __construct(cssdoc $root, array $media = null) {
2328
$this->root = $root;
29+
$this->media = $media;
2430
}
2531

2632
/**
@@ -31,24 +37,94 @@ public function __construct(cssdoc $root) {
3137
* @return void
3238
*/
3339
public function parse(array &$tokens) : bool {
40+
$default = $rule = [
41+
'media' => false,
42+
'only' => false,
43+
'not' => false,
44+
'properties' => []
45+
];
3446

3547
// parse tokens
36-
while (($token = next($tokens)) !== false) {
48+
$token = current($tokens);
49+
do {
3750
switch ($token['type']) {
38-
case 'string':
51+
case 'directive':
52+
53+
// parse media query
3954
if ($token['value'] == '@media') {
40-
$item = new mediaquery($this->root);
55+
$media = [];
56+
$rule = $default;
57+
while (($token = next($tokens)) !== false) {
58+
switch ($token['type']) {
59+
case 'string':
60+
if ($token['value'] == 'only') {
61+
$rule['only'] = true;
62+
} elseif ($token['value'] == 'not') {
63+
$rule['not'] = true;
64+
} elseif ($token['value'] != 'and') {
65+
$rule['media'] = $token['value'];
66+
}
67+
break;
68+
case 'bracketopen':
69+
$compare = false;
70+
while (($token = next($tokens)) !== false && $token['type'] != 'bracketclose') {
71+
if ($token['type'] == 'string') {
72+
if (!$compare) {
73+
$prop = $token['value'];
74+
} elseif ($compare == ':') {
75+
$rule['properties'][$prop] = $token['value'];
76+
$prop = false;
77+
$compare = false;
78+
} else {
79+
if (intval($prop)) {
80+
$rule['properties']['min-'.$token['value']] = $prop;
81+
$prop = 'max'.$token['value'];
82+
} else {
83+
$rule['properties'][$prop] = $token['value'];
84+
}
85+
$prop = false;
86+
$compare = false;
87+
}
88+
} elseif ($token['type'] == 'colon') {
89+
$compare = ':';
90+
} elseif ($token['type'] == 'comparison' && $token['value'] == '<=') {
91+
$compare = '<=';
92+
}
93+
}
94+
if ($prop) {
95+
$rule['properties'][$prop] = null;
96+
}
97+
break;
98+
case 'comma':
99+
$media[] = $rule;
100+
$rule = $default;
101+
break;
102+
case 'curlyopen':
103+
break 2;
104+
}
105+
}
106+
$media[] = $rule;
107+
// var_dump($media);
108+
109+
// create media query object
110+
$item = new mediaquery($this->root, $media);
41111
$item->parse($tokens);
42112
$this->rules[] = $item;
43113
} else {
44-
prev($tokens);
45-
$item = new rule($this);
114+
$item = new directive($this);
46115
$item->parse($tokens);
47116
$this->rules[] = $item;
48117
}
49118
break;
119+
case 'string':
120+
$item = new rule($this);
121+
$item->parse($tokens);
122+
$this->rules[] = $item;
123+
break;
124+
case 'curlyclose':
125+
break 2;
50126
}
51-
}
127+
} while (($token = next($tokens)) !== false);
52128
return !!$this->rules;
53129
}
54130

@@ -59,6 +135,9 @@ public function parse(array &$tokens) : bool {
59135
* @return void
60136
*/
61137
public function minify(array $minify) : void {
138+
foreach ($this->rules AS $item) {
139+
$item->minify($minify);
140+
}
62141
}
63142

64143
/**
@@ -70,13 +149,38 @@ public function minify(array $minify) : void {
70149
public function compile(array $options) : string {
71150
$b = $options['output'] != 'minify';
72151
$css = '';
152+
if ($this->media) {
153+
$css .= '@media ';
154+
$media = [];
155+
foreach ($this->media AS $item) {
156+
$query = '';
157+
$join = '';
158+
if ($item['media']) {
159+
$query .= trim(($item['only'] ? ' only' : '').($item['not'] ? ' not' : '').($item['media'] ? ' '.$item['media'] : ''));
160+
$join = ' and ';
161+
}
162+
foreach ($item['properties'] AS $key => $prop) {
163+
$query .= $join.'('.$key.($prop === null ? '' : ':'.($b ? ' ' : '').$prop).')';
164+
$join = ' and ';
165+
}
166+
$media[] = $query;
167+
}
168+
$css .= implode($b ? ', ' : ',', $media);
169+
$css .= $b ? " {\n" : '{';
170+
if ($b) {
171+
$options['prefix'] = "\t";
172+
}
173+
}
73174

74175
// compile selectors
75176
$join = '';
76177
foreach ($this->rules AS $item) {
77178
$css .= $join.$item->compile($options);
78179
$join = $b ? "\n\n" : '';
79180
}
181+
if ($this->media) {
182+
$css .= '}';
183+
}
80184
return $css;
81185
}
82186
}

0 commit comments

Comments
 (0)