sabberworm/php-css-parser

Parser for CSS Files written in PHP

Package info

github.com/MyIntervals/PHP-CSS-Parser

Homepage

pkg:composer/sabberworm/php-css-parser

Statistics

Installs: 208 050 950

Dependents: 82

Suggesters: 2

Stars: 1 820

Open Issues: 156

v9.3.0 2026-03-03 17:31 UTC

Requires

Suggests

Provides

None

Conflicts

None

Replaces

None

MIT 88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949

  • Raphael Schweikert
  • Oliver Klee <github.woop@oliverklee.de>
  • Jake Hotson <jake.github.woop@qzdesign.co.uk>

cssparserstylesheet

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, @charset or @media. A common example for this is @font-face.
  • DeclarationBlock – a RuleSet constrained by a Selector; 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 numeric size value 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 in URL("") 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 like font: bold 12px/3 Helvetica, Verdana, sans-serif; (where the value would be a whitespace-separated list of the primitive value bold, 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 like filter: 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 as getAllSelectors().
  • getAllRuleSets() – does what it says; no matter how deeply nested the rule sets are.
  • getAllValues() – finds all Value objects inside Rules.

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 Color instead 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 @namespace at-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.