Skip to content

Commit 6ef7a24

Browse files
committed
syncing with the original repository
1 parent 83b6984 commit 6ef7a24

23 files changed

+2261
-730
lines changed

CSSParser.php

Lines changed: 124 additions & 514 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 218 additions & 206 deletions
Large diffs are not rendered by default.

lib/CSSList.php

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
<?php
2+
3+
/**
4+
* A CSSList is the most generic container available. Its contents include CSSRuleSet as well as other CSSList objects.
5+
* Also, it may contain CSSImport and CSSCharset objects stemming from @-rules.
6+
*/
7+
abstract class CSSList {
8+
private $aContents;
9+
10+
public function __construct() {
11+
$this->aContents = array();
12+
}
13+
14+
public function append($oItem) {
15+
$this->aContents[] = $oItem;
16+
}
17+
18+
/**
19+
* Removes an item from the CSS list.
20+
* @param CSSRuleSet|CSSImport|CSSCharset|CSSList $oItemToRemove May be a CSSRuleSet (most likely a CSSDeclarationBlock), a CSSImport, a CSSCharset or another CSSList (most likely a CSSMediaQuery)
21+
*/
22+
public function remove($oItemToRemove) {
23+
$iKey = array_search($oItemToRemove, $this->aContents, true);
24+
if($iKey !== false) {
25+
unset($this->aContents[$iKey]);
26+
}
27+
}
28+
29+
public function removeDeclarationBlockBySelector($mSelector, $bRemoveAll = false) {
30+
if($mSelector instanceof CSSDeclarationBlock) {
31+
$mSelector = $mSelector->getSelectors();
32+
}
33+
if(!is_array($mSelector)) {
34+
$mSelector = explode(',', $mSelector);
35+
}
36+
foreach($mSelector as $iKey => &$mSel) {
37+
if(!($mSel instanceof CSSSelector)) {
38+
$mSel = new CSSSelector($mSel);
39+
}
40+
}
41+
foreach($this->aContents as $iKey => $mItem) {
42+
if(!($mItem instanceof CSSDeclarationBlock)) {
43+
continue;
44+
}
45+
if($mItem->getSelectors() == $mSelector) {
46+
unset($this->aContents[$iKey]);
47+
if(!$bRemoveAll) {
48+
return;
49+
}
50+
}
51+
}
52+
}
53+
54+
public function __toString() {
55+
$sResult = '';
56+
foreach($this->aContents as $oContent) {
57+
$sResult .= $oContent->__toString();
58+
}
59+
return $sResult;
60+
}
61+
62+
public function getContents() {
63+
return $this->aContents;
64+
}
65+
66+
protected function allDeclarationBlocks(&$aResult) {
67+
foreach($this->aContents as $mContent) {
68+
if($mContent instanceof CSSDeclarationBlock) {
69+
$aResult[] = $mContent;
70+
} else if($mContent instanceof CSSList) {
71+
$mContent->allDeclarationBlocks($aResult);
72+
}
73+
}
74+
}
75+
76+
protected function allRuleSets(&$aResult) {
77+
foreach($this->aContents as $mContent) {
78+
if($mContent instanceof CSSRuleSet) {
79+
$aResult[] = $mContent;
80+
} else if($mContent instanceof CSSList) {
81+
$mContent->allRuleSets($aResult);
82+
}
83+
}
84+
}
85+
86+
protected function allValues($oElement, &$aResult, $sSearchString = null, $bSearchInFunctionArguments = false) {
87+
if($oElement instanceof CSSList) {
88+
foreach($oElement->getContents() as $oContent) {
89+
$this->allValues($oContent, $aResult, $sSearchString, $bSearchInFunctionArguments);
90+
}
91+
} else if($oElement instanceof CSSRuleSet) {
92+
foreach($oElement->getRules($sSearchString) as $oRule) {
93+
$this->allValues($oRule, $aResult, $sSearchString, $bSearchInFunctionArguments);
94+
}
95+
} else if($oElement instanceof CSSRule) {
96+
$this->allValues($oElement->getValue(), $aResult, $sSearchString, $bSearchInFunctionArguments);
97+
} else if($oElement instanceof CSSValueList) {
98+
if($bSearchInFunctionArguments || !($oElement instanceof CSSFunction)) {
99+
foreach($oElement->getListComponents() as $mComponent) {
100+
$this->allValues($mComponent, $aResult, $sSearchString, $bSearchInFunctionArguments);
101+
}
102+
}
103+
} else {
104+
//Non-List CSSValue or String (CSS identifier)
105+
$aResult[] = $oElement;
106+
}
107+
}
108+
109+
protected function allSelectors(&$aResult, $sSpecificitySearch = null) {
110+
foreach($this->getAllDeclarationBlocks() as $oBlock) {
111+
foreach($oBlock->getSelectors() as $oSelector) {
112+
if($sSpecificitySearch === null) {
113+
$aResult[] = $oSelector;
114+
} else {
115+
$sComparison = "\$bRes = {$oSelector->getSpecificity()} $sSpecificitySearch;";
116+
eval($sComparison);
117+
if($bRes) {
118+
$aResult[] = $oSelector;
119+
}
120+
}
121+
}
122+
}
123+
}
124+
}
125+
126+
/**
127+
* The root CSSList of a parsed file. Contains all top-level css contents, mostly declaration blocks, but also any @-rules encountered.
128+
*/
129+
class CSSDocument extends CSSList {
130+
/**
131+
* Gets all CSSDeclarationBlock objects recursively.
132+
*/
133+
public function getAllDeclarationBlocks() {
134+
$aResult = array();
135+
$this->allDeclarationBlocks($aResult);
136+
return $aResult;
137+
}
138+
139+
/**
140+
* @deprecated use getAllDeclarationBlocks()
141+
*/
142+
public function getAllSelectors() {
143+
return $this->getAllDeclarationBlocks();
144+
}
145+
146+
/**
147+
* Returns all CSSRuleSet objects found recursively in the tree.
148+
*/
149+
public function getAllRuleSets() {
150+
$aResult = array();
151+
$this->allRuleSets($aResult);
152+
return $aResult;
153+
}
154+
155+
/**
156+
* Returns all CSSValue objects found recursively in the tree.
157+
* @param (object|string) $mElement the CSSList or CSSRuleSet to start the search from (defaults to the whole document). If a string is given, it is used as rule name filter (@see{CSSRuleSet->getRules()}).
158+
* @param (bool) $bSearchInFunctionArguments whether to also return CSSValue objects used as CSSFunction arguments.
159+
*/
160+
public function getAllValues($mElement = null, $bSearchInFunctionArguments = false) {
161+
$sSearchString = null;
162+
if($mElement === null) {
163+
$mElement = $this;
164+
} else if(is_string($mElement)) {
165+
$sSearchString = $mElement;
166+
$mElement = $this;
167+
}
168+
$aResult = array();
169+
$this->allValues($mElement, $aResult, $sSearchString, $bSearchInFunctionArguments);
170+
return $aResult;
171+
}
172+
173+
/**
174+
* Returns all CSSSelector objects found recursively in the tree.
175+
* Note that this does not yield the full CSSDeclarationBlock that the selector belongs to (and, currently, there is no way to get to that).
176+
* @param $sSpecificitySearch An optional filter by specificity. May contain a comparison operator and a number or just a number (defaults to "==").
177+
* @example getSelectorsBySpecificity('>= 100')
178+
*/
179+
public function getSelectorsBySpecificity($sSpecificitySearch = null) {
180+
if(is_numeric($sSpecificitySearch) || is_numeric($sSpecificitySearch[0])) {
181+
$sSpecificitySearch = "== $sSpecificitySearch";
182+
}
183+
$aResult = array();
184+
$this->allSelectors($aResult, $sSpecificitySearch);
185+
return $aResult;
186+
}
187+
188+
/**
189+
* Expands all shorthand properties to their long value
190+
*/
191+
public function expandShorthands()
192+
{
193+
foreach($this->getAllDeclarationBlocks() as $oDeclaration)
194+
{
195+
$oDeclaration->expandShorthands();
196+
}
197+
}
198+
199+
/*
200+
* Create shorthands properties whenever possible
201+
*/
202+
public function createShorthands()
203+
{
204+
foreach($this->getAllDeclarationBlocks() as $oDeclaration)
205+
{
206+
$oDeclaration->createShorthands();
207+
}
208+
}
209+
}
210+
211+
/**
212+
* A CSSList consisting of the CSSList and CSSList objects found in a @media query.
213+
*/
214+
class CSSMediaQuery extends CSSList {
215+
private $sQuery;
216+
217+
public function __construct() {
218+
parent::__construct();
219+
$this->sQuery = null;
220+
}
221+
222+
public function setQuery($sQuery) {
223+
$this->sQuery = $sQuery;
224+
}
225+
226+
public function getQuery() {
227+
return $this->sQuery;
228+
}
229+
230+
public function __toString() {
231+
$sResult = "@media {$this->sQuery} {";
232+
$sResult .= parent::__toString();
233+
$sResult .= '}';
234+
return $sResult;
235+
}
236+
}

lib/CSSProperties.php

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
/**
4+
* Class representing an @import rule.
5+
*/
6+
class CSSImport {
7+
private $oLocation;
8+
private $sMediaQuery;
9+
10+
public function __construct(CSSURL $oLocation, $sMediaQuery) {
11+
$this->oLocation = $oLocation;
12+
$this->sMediaQuery = $sMediaQuery;
13+
}
14+
15+
public function setLocation($oLocation) {
16+
$this->oLocation = $oLocation;
17+
}
18+
19+
public function getLocation() {
20+
return $this->oLocation;
21+
}
22+
23+
public function __toString() {
24+
return "@import ".$this->oLocation->__toString().($this->sMediaQuery === null ? '' : ' '.$this->sMediaQuery).';';
25+
}
26+
}
27+
28+
/**
29+
* Class representing an @charset rule.
30+
* The following restrictions apply:
31+
* • May not be found in any CSSList other than the CSSDocument.
32+
* • May only appear at the very top of a CSSDocument’s contents.
33+
* • Must not appear more than once.
34+
*/
35+
class CSSCharset {
36+
private $sCharset;
37+
38+
public function __construct($sCharset) {
39+
$this->sCharset = $sCharset;
40+
}
41+
42+
public function setCharset($sCharset) {
43+
$this->sCharset = $sCharset;
44+
}
45+
46+
public function getCharset() {
47+
return $this->sCharset;
48+
}
49+
50+
public function __toString() {
51+
return "@charset {$this->sCharset->__toString()};";
52+
}
53+
}
54+
55+
/**
56+
* Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this class.
57+
*/
58+
class CSSSelector {
59+
const
60+
NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
61+
(\.[\w]+) # classes
62+
|
63+
\[(\w+) # attributes
64+
|
65+
(\:( # pseudo classes
66+
link|visited|active
67+
|hover|focus
68+
|lang
69+
|target
70+
|enabled|disabled|checked|indeterminate
71+
|root
72+
|nth-child|nth-last-child|nth-of-type|nth-last-of-type
73+
|first-child|last-child|first-of-type|last-of-type
74+
|only-child|only-of-type
75+
|empty|contains
76+
))
77+
/ix',
78+
ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
79+
((^|[\s\+\>\~]+)[\w]+ # elements
80+
|
81+
\:{1,2}( # pseudo-elements
82+
after|before
83+
|first-letter|first-line
84+
|selection
85+
)
86+
)/ix';
87+
88+
private $sSelector;
89+
private $iSpecificity;
90+
91+
public function __construct($sSelector, $bCalculateSpecificity = false) {
92+
$this->setSelector($sSelector);
93+
if($bCalculateSpecificity) {
94+
$this->getSpecificity();
95+
}
96+
}
97+
98+
public function getSelector() {
99+
return $this->sSelector;
100+
}
101+
102+
public function setSelector($sSelector) {
103+
$this->sSelector = trim($sSelector);
104+
$this->iSpecificity = null;
105+
}
106+
107+
public function __toString() {
108+
return $this->getSelector();
109+
}
110+
111+
public function getSpecificity() {
112+
if($this->iSpecificity === null) {
113+
$a = 0;
114+
/// @todo should exclude \# as well as "#"
115+
$aMatches;
116+
$b = substr_count($this->sSelector, '#');
117+
$c = preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $this->sSelector, $aMatches);
118+
$d = preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $this->sSelector, $aMatches);
119+
$this->iSpecificity = ($a*1000) + ($b*100) + ($c*10) + $d;
120+
}
121+
return $this->iSpecificity;
122+
}
123+
}
124+

0 commit comments

Comments
 (0)