sabberworm/php-css-parser
Parser for CSS Files written in PHP
Maintainers
Requires
- php: ^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0
- ext-iconv: *
- thecodingmachine/safe: ^1.3 || ^2.5 || ^3.4
Requires (Dev)
- php-parallel-lint/php-parallel-lint: 1.4.0
- phpstan/extension-installer: 1.4.3
- phpstan/phpstan: 1.12.32 || 2.1.32
- phpstan/phpstan-phpunit: 1.4.2 || 2.0.8
- phpstan/phpstan-strict-rules: 1.6.2 || 2.0.7
- phpunit/phpunit: 8.5.52
- rawr/phpunit-data-provider: 3.3.1
- rector/rector: 1.2.10 || 2.2.8
- rector/type-perfect: 1.0.0 || 2.1.0
- squizlabs/php_codesniffer: 4.0.1
- thecodingmachine/phpstan-safe-rule: 1.2.0 || 1.4.1
Suggests
- ext-mbstring: for parsing UTF-8 CSS
Provides
None
Conflicts
None
Replaces
None
MIT 88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949
- Raphael Schweikert
- Oliver Klee <github.woop@oliverklee.de>
- Jake Hotson <jake.github.woop@qzdesign.co.uk>
This package is auto-updated.
Last update: 2026-06-18 07:17:20 UTC
README
π Build Status
π Coverage Status
A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output as (optimized) CSS.
Usage
Installation using Composer
composer require sabberworm/php-css-parser
Extraction
To use the CSS Parser, create a new instance. The constructor takes the following form:
new \Sabberworm\CSS\Parser($css);
To read a file, for example, youβd do the following:
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); $cssDocument = $parser->parse();
The resulting CSS document structure can be manipulated prior to being output.
Options
Charset
The charset option will only be used if the CSS file does not contain an @charset declaration. UTF-8 is the default, so you wonβt have to create a settings object at all if you donβt intend to change that.
$settings = \Sabberworm\CSS\Settings::create() ->withDefaultCharset('windows-1252'); $parser = new \Sabberworm\CSS\Parser($css, $settings);
Strict parsing
To have the parser throw an exception when encountering invalid/unknown constructs (as opposed to trying to ignore them and carry on parsing), supply a thusly configured \Sabberworm\CSS\Settings object:
$parser = new \Sabberworm\CSS\Parser( file_get_contents('somefile.css'), \Sabberworm\CSS\Settings::create()->beStrict() );
Note that this will also disable a workaround for parsing the unquoted variant of the legacy IE-specific filter rule.
Disable multibyte functions
To achieve faster parsing, you can choose to have PHP-CSS-Parser use regular string functions instead of mb_* functions. This should work fine in most cases, even for UTF-8 files, as all the multibyte characters are in string literals. Still itβs not recommended using this with input you have no control over as itβs not thoroughly covered by test cases.
$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false); $parser = new \Sabberworm\CSS\Parser($css, $settings);
Manipulation
The resulting data structure consists mainly of five basic types: CSSList, RuleSet, Rule, Selector and Value. There are two additional types used: Import and Charset, which you wonβt use often.
CSSList
CSSList represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc.
To access the items stored in a CSSList β like the document you got back when calling $parser->parse() β, use getContents(), then iterate over that collection and use instanceof to check whether youβre dealing with another CSSList, a RuleSet, a Import or a Charset.
To append a new item (selector, media query, etc.) to an existing CSSList, construct it using the constructor for this class and use the append($oItem) method.
RuleSet
RuleSet is a container for individual rules. The most common form of a rule set is one constrained by a selector. The following concrete subtypes exist:
AtRuleSetβ for generic at-rules for generic at-rules which are not covered by specific classes, i.e., not@import,@charsetor@media. A common example for this is@font-face.DeclarationBlockβ aRuleSetconstrained by aSelector; contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the matching elements.
Note: A CSSList can contain other CSSLists (and Imports as well as a Charset), while a RuleSet can only contain Rules.
If you want to manipulate a RuleSet, use the methods addRule(Rule $rule), getRules() and removeRule($rule) (which accepts either a Rule or a rule name; optionally suffixed by a dash to remove all related rules).
Rule
Rules just have a string key (the rule) and a Value.
Value
Value is an abstract class that only defines the render method. The concrete subclasses for atomic value types are:
Sizeβ consists of a numericsizevalue and a unit.Colorβ colors can be input in the form #rrggbb, #rgb or schema(val1, val2, β¦) but are always stored as an array of ('s' => val1, 'c' => val2, 'h' => val3, β¦) and output in the second form.CSSStringβ this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes.URLβ URLs in CSS; always output inURL("")notation.
There is another abstract subclass of Value, ValueList: A ValueList represents a lists of Values, separated by some separation character (mostly ,, whitespace, or /).
There are two types of ValueLists:
RuleValueListβ The default type, used to represent all multivalued rules likefont: bold 12px/3 Helvetica, Verdana, sans-serif;(where the value would be a whitespace-separated list of the primitive valuebold, a slash-separated list and a comma-separated list).CSSFunctionβ A special kind of value that also contains a function name and where the values are the functionβs arguments. Also handles equals-sign-separated argument lists likefilter: alpha(opacity=90);.
Convenience methods
There are a few convenience methods on Document to ease finding, manipulating and deleting rules:
getAllDeclarationBlocks()β does what it says; no matter how deeply nested the selectors are. Aliased asgetAllSelectors().getAllRuleSets()β does what it says; no matter how deeply nested the rule sets are.getAllValues()β finds allValueobjects insideRules.
To-Do
- More convenience methods (like
selectorsWithElement($sId/Class/TagName),attributesOfType($type),removeAttributesOfType($type)) - Real multibyte support. Currently, only multibyte charsets whose first 255 code points take up only one byte and are identical with ASCII are supported (yes, UTF-8 fits this description).
- Named color support (using
Colorinstead of an anonymous string literal)
Use cases
Use Parser to prepend an ID to all selectors
$myId = "#my_id"; $parser = new \Sabberworm\CSS\Parser($css); $cssDocument = $parser->parse(); foreach ($cssDocument->getAllDeclarationBlocks() as $block) { foreach ($block->getSelectors() as $selector) { // Loop over all selector parts (the comma-separated strings in a // selector) and prepend the ID. $selector->setSelector($myId.''.$selector->getSelector()); } }
Shrink all absolute sizes to half
$parser = new \Sabberworm\CSS\Parser($css); $cssDocument = $parser->parse(); foreach ($cssDocument->getAllValues() as $value) { if ($value instanceof CSSSize && !$value->isRelative()) { $value->setSize($value->getSize() / 2); } }
Remove unwanted rules
$parser = new \Sabberworm\CSS\Parser($css); $cssDocument = $parser->parse(); foreach($cssDocument->getAllRuleSets() as $oRuleSet) { // Note that the added dash will make this remove all rules starting with // `font-` (like `font-size`, `font-weight`, etc.) as well as a potential // `font` rule. $oRuleSet->removeRule('font-'); $oRuleSet->removeRule('cursor'); }
Output
To output the entire CSS document into a variable, just use ->render():
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); $cssDocument = $parser->parse(); print $cssDocument->render();
If you want to format the output, pass an instance of type \Sabberworm\CSS\OutputFormat:
$format = \Sabberworm\CSS\OutputFormat::create() ->indentWithSpaces(4)->setSpaceBetweenRules("\n"); print $cssDocument->render($format);
Or use one of the predefined formats:
print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty()); print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact());
To see what you can do with output formatting, look at the tests in tests/OutputFormatTest.php.
Examples
Example 1 (At-Rules)
Input
@charset "utf-8"; @font-face { font-family: "CrassRoots"; src: url("../media/cr.ttf"); } html, body { font-size: 1.6em; } @keyframes mymove { from { top: 0px; } to { top: 200px; } }
Output (render())
@charset "utf-8"; @font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");} html, body {font-size: 1.6em;} @keyframes mymove {from {top: 0px;} to {top: 200px;}}
Example 2 (Values)
Input
#header { margin: 10px 2em 1cm 2%; font-family: Verdana, Helvetica, "Gill Sans", sans-serif; color: red !important; }
Output (render())
#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;}
Class diagram
classDiagram
direction LR
class Anchor {
}
class AtRule {
<<interface>>
}
class AtRuleBlockList {
}
class AtRuleSet {
}
class CSSBlockList {
<<abstract>>
}
class CSSElement {
<<interface>>
}
class CSSFunction {
}
class CSSList {
<<abstract>>
}
class CSSListItem {
<<interface>>
}
class CSSNamespace {
}
class CSSString {
}
class CalcFunction {
}
class CalcRuleValueList {
}
class Charset {
}
class Color {
}
class Comment {
}
class Commentable {
<<interface>>
}
class DeclarationBlock {
}
class Document {
}
class Import {
}
class KeyFrame {
}
class KeyframeSelector {
}
class LineName {
}
class OutputException {
}
class OutputFormat {
}
class OutputFormatter {
}
class Parser {
}
class ParserState {
}
class Positionable {
<<interface>>
}
class PrimitiveValue {
<<abstract>>
}
class Renderable {
<<interface>>
}
class Rule {
}
class RuleContainer {
<<interface>>
}
class RuleSet {
}
class RuleValueList {
}
class Selector {
}
class Settings {
}
class Size {
}
class SourceException {
}
class SpecificityCalculator {
}
class URL {
}
class UnexpectedEOFException {
}
class UnexpectedTokenException {
}
class Value {
<<abstract>>
}
class ValueList {
<<abstract>>
}
Anchor ..> ParserState: dependency
CSSListItem <|-- AtRule: inheritance
AtRule <|.. AtRuleBlockList: realization
CSSBlockList <|-- AtRuleBlockList: inheritance
AtRule <|.. AtRuleSet: realization
RuleSet <|-- AtRuleSet: inheritance
CSSList <|-- CSSBlockList: inheritance
Renderable <|-- CSSElement: inheritance
ValueList <|-- CSSFunction: inheritance
CSSElement <|.. CSSList: realization
CSSListItem <|.. CSSList: realization
CSSList ..> Charset: dependency
CSSList ..> Import: dependency
Positionable <|.. CSSList: realization
Commentable <|-- CSSListItem: inheritance
Renderable <|-- CSSListItem: inheritance
AtRule <|.. CSSNamespace: realization
Positionable <|.. CSSNamespace: realization
PrimitiveValue <|-- CSSString: inheritance
CSSFunction <|-- CalcFunction: inheritance
RuleValueList <|-- CalcRuleValueList: inheritance
AtRule <|.. Charset: realization
Charset ..> CSSString: dependency
Positionable <|.. Charset: realization
CSSFunction <|-- Color: inheritance
Positionable <|.. Comment: realization
Renderable <|.. Comment: realization
CSSElement <|.. DeclarationBlock: realization
CSSListItem <|.. DeclarationBlock: realization
Positionable <|.. DeclarationBlock: realization
RuleContainer <|.. DeclarationBlock: realization
DeclarationBlock ..> RuleSet : dependency
DeclarationBlock ..> Selector: dependency
CSSBlockList <|-- Document: inheritance
AtRule <|.. Import: realization
Positionable <|.. Import: realization
AtRule <|.. KeyFrame: realization
CSSList <|-- KeyFrame: inheritance
Selector <|-- KeyframeSelector: inheritance
ValueList <|-- LineName: inheritance
SourceException <|-- OutputException: inheritance
OutputFormat ..> OutputFormatter: dependency
OutputFormatter ..> OutputFormat: dependency
Parser ..> ParserState: dependency
ParserState ..> Settings: dependency
Value <|-- PrimitiveValue: inheritance
CSSElement <|.. Rule: realization
Commentable <|.. Rule: realization
Positionable <|.. Rule: realization
Rule ..> RuleValueList: dependency
CSSElement <|.. RuleSet: realization
CSSListItem <|.. RuleSet: realization
Positionable <|.. RuleSet: realization
RuleSet ..> Rule: dependency
RuleContainer <|.. RuleSet: realization
ValueList <|-- RuleValueList: inheritance
Renderable <|.. Selector: realization
PrimitiveValue <|-- Size: inheritance
Exception <|-- SourceException: inheritance
Positionable <|.. SourceException: realization
URL ..> CSSString: dependency
PrimitiveValue <|-- URL: inheritance
UnexpectedTokenException <|-- UnexpectedEOFException: inheritance
SourceException <|-- UnexpectedTokenException: inheritance
CSSElement <|.. Value: realization
Positionable <|.. Value: realization
Value <|-- ValueList: inheritance
CSSList ..> CSSList: dependency
CSSList ..> Comment: dependency
CSSList ..> RuleSet: dependency
CSSNamespace ..> Comment: dependency
Charset ..> Comment: dependency
Import ..> Comment: dependency
OutputFormat ..> OutputFormat: dependency
Rule ..> Comment: dependency
RuleSet ..> Comment: dependency
ValueList ..> Value: dependency
Loading
API and deprecation policy
Please have a look at our API and deprecation policy.
Contributing
Contributions in the form of bug reports, feature requests, or pull requests are more than welcome. π Please have a look at our contribution guidelines to learn more about how to contribute to PHP-CSS-Parser.
Contributors/Thanks to
- oliverklee for lots of refactorings, code modernizations and CI integrations
- raxbg for contributions to parse
calc, grid lines, and various bugfixes. - westonruter for bugfixes and improvements.
- FMCorz for many patches and suggestions, for being able to parse comments and IE hacks (in lenient mode).
- Lullabot for a patch that allows to know the line number for each parsed token.
- ju1ius for the specificity parsing code and the ability to expand/compact shorthand properties.
- ossinkine for a 150 time performance boost.
- GaryJones for lots of input and https://css-specificity.info/.
- docteurklein for output formatting and
CSSList->remove()inspiration. - nicolopignatelli for PSR-0 compatibility.
- diegoembarcadero for keyframe at-rule parsing.
- goetas for
@namespaceat-rule support. - ziegenberg for general housekeeping and cleanup.
- View full list
Misc
Legacy Support
The latest pre-PSR-0 version of this project can be checked with the 0.9.0 tag.
