Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Css Parser Laravel Package

sabberworm/php-css-parser

A PHP library that parses CSS files into a manipulable data structure, allowing programmatic editing of rules and declarations before outputting optimized CSS. Supports custom charsets, strict parsing and optional multi-byte functions. Install via Composer.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require sabberworm/php-css-parser

The entire API surface is three objects: Parser (string → tree), the tree itself (Document / CSSList / RuleSet / Rule / Value), and OutputFormat (tree → string). A minimal working cycle:

use Sabberworm\CSS\Parser;

$document = (new Parser(file_get_contents(public_path('css/app.css')))->parse();
$document->render(); // round-trips back to CSS

Where to look first

  1. README.md — the “Usage → Manipulation” section is the real documentation; the data-model explanation (CSSList vs RuleSet vs Rule vs Value) is the mental model you need before anything else makes sense.
  2. src/Value/ and src/RuleSet/ — the concrete class list tells you what you can instanceof against. The README’s var_dump() example of the parsed tree is worth reproducing once against your own stylesheet.
  3. tests/OutputFormatTest.php — the README explicitly points here because OutputFormat has no prose docs; the tests are the spec for every indentation/space hook.
  4. CHANGELOG.md before bumping — major versions have moved namespaces (OutputFormat only exists from v8 onward), so copy-pasted snippets from old blog posts often won’t compile.

First use case to build: a minifying/rewriting pass. Parse a stylesheet, strip font-* and cursor rules, scope every selector under a container ID, and emit compact CSS. That single exercise exercises parsing, traversal, mutation, and rendering — the four things you’ll do forever with this package.

$parser = new Parser($css);
$doc = $parser->parse();

foreach ($doc->getAllDeclarationBlocks() as $block) {
    foreach ($block->getSelectors() as $selector) {
        $selector->setSelector('#app ' . $selector->getSelector());
    }
    $block->removeRule('font-');   // trailing dash = prefix match
    $block->removeRule('cursor');
}

echo $doc->render(OutputFormat::createCompact());

Implementation Patterns

Traversal: prefer the Document convenience methods over hand-rolled recursion. getAllDeclarationBlocks() (alias getAllSelectors()), getAllRuleSets(), and getAllValues() already walk arbitrarily nested @media/@supports/@keyframes blocks. Manual recursion over getContents() is only needed when you care about where a rule lives (e.g. “only rewrite rules at the top level, leave @media print blocks alone”).

Walk with instanceof, mutate in place. The tree is mutable and traversal returns live objects, so there’s no “save the tree” step:

foreach ($doc->getAllValues() as $value) {
    if ($value instanceof Value\Size && !$value->isRelative()) {
        $value->setSize($value->getSize() / 2);
    }
    if ($value instanceof Value\Color) {
        // $value->getColor()['h'] for hex, ['r']/['g']/['b'] for rgb()
    }
}

Programmatic rule injection (theme/token generation). Build CSS that never existed as source — useful for design tokens, generated utility classes, or injecting a config-driven media query:

$block = new RuleSet\DeclarationBlock();
$block->setSelectors([new Property\Selector('.btn-primary')]);

$rule = new Rule\Rule('background-color');
$rule->setValue(new Value\Color(['r' => 37, 'g' => 99, 'b' => 235]));
$block->addRule($rule);

$doc->append($block);

Note the constructor/setValue split: Rule is constructed with just the property name, and the value is attached separately. This is the most common source of “why is my rule empty?” confusion.

Preserve specificity when re-emitting. Selector::getSpecificity() is computed lazily; if you’re reordering or de-duplicating declarations, sort using the specificity object rather than string comparison, otherwise .a .b will sort before .a.

Laravel integration: wrap it, don’t scatter it. A single CssTransformer service with methods like scope($css, $selector), strip($css, array $prefixes), minify($css) keeps the parser at the edge of your app. In a service provider or via constructor injection:

class CssTransformer
{
    public function transform(string $css, callable $mutator): string
    {
        $doc = (new Parser($css, Settings::create()))->parse();
        $mutator($doc);
        return $doc->render(OutputFormat::createCompact());
    }
}

Cache the output, not the tree. Serializing the parsed tree with serialize() is possible but fragile across package versions and bloats cache entries. Instead cache the rendered string keyed on md5(file contents + config hash + package version), and regenerate in an artisan command or queued job. Never parse in a request cycle — a 500 KB stylesheet costs tens of milliseconds and real memory.

Choose the output format deliberately. createPretty() for anything a human reviews in a PR, createCompact() for anything served. Build one custom OutputFormat per project convention and reuse the instance — these are cheap but the readability of your diffs depends on being consistent.

Pipelines that pay off in practice: RTL flipping (swap left/right rule names), dark-mode variant generation (rewrite color values), CSS-variable substitution for email templates (inline values, since many clients don’t support var()), critical-CSS extraction (filter @media blocks by type), and dead-rule pruning from a whitelist of selectors found in your Blade templates.

Gotchas and Tips

render() is not a round-trip. Comments are dropped, whitespace is regenerated from OutputFormat, and formatting is normalized. If you need to preserve the original file byte-for-byte when nothing changed, compare the rendered output and bail out — otherwise your first commit after adopting this tool will be a 4,000-line whitespace diff.

Compact output is not a minifier. createCompact() removes structural whitespace but does not merge duplicate selectors, drop empty rule sets, shorten hex colors, or reorder declarations. Budget for a real minifier downstream, or accept modest savings.

removeRule('font-') also removes font. The dash suffix means “this name and everything prefixed by name-”. If you want only the shorthand gone, call removeRule('font') exactly. Conversely, passing 'font-' without the dash ('font') will not catch font-size — the semantics surprise people in both directions.

Property names are case-sensitive as stored. CSS is case-insensitive for property names, but the parser stores the key as written. $block->getRule('Background-Color') won’t find background-color. Normalize with strtolower() on both sides if you’re matching against user- or vendor-supplied CSS.

!important survives setValue() but not serialization tricks. When you copy a rule to another block by constructing a new Rule, you must copy the flag explicitly: $new->setValue($old->getValue()); $new->setImportant($old->isImportant());. Silently dropped !important flags are the classic “why did my override stop working” bug.

Strict mode is a debugging tool, not a production default. Settings::create()->beStrict() throws on unknown constructs and disables the legacy IE filter: alpha(opacity=90) unquoted workaround. Turn it on to hunt down why a block vanished; leave it off in production, because real-world CSS contains enough oddity to make strict parsing brittle.

Silent skipping is the default failure mode. In non-strict mode, unparseable constructs are ignored and parsing continues. If a rule is “missing” from your output, the cause is almost always a parse-level skip, not a traversal bug. Diff the input against render() to find what was dropped.

Custom properties (--foo) and modern at-rules are the weak spot. The package’s grammar predates much of the custom-properties era; var() parses as a CSSFunction, but complex fallbacks and some newer at-rule preludes can trip it. Always round-trip a real stylesheet through parse→render in a test before trusting it on your full build.

Multibyte setting is a real speed lever, but only with trusted input. withMultibyteSupport(false) swaps mb_* for plain string functions and is meaningfully faster; it’s safe for UTF-8 because multibyte characters live inside string literals. Don’t enable it for CSS uploaded by users — the README is explicit that it isn’t well covered by tests.

OutputFormat methods mutate and return $this. Chain from OutputFormat::create() and don’t hold a reference to a “template” you then modify elsewhere — you’ll be surprised when a second render() call produces different formatting than the first.

Use line numbers for diagnostics. Every node carries a protected $lineNumber (visible in the README’s var_dump). It’s not a public API on all versions, so treat it as a debugging aid via reflection rather than something to build validation messages on top of.

Pin the major version and read the changelog on every bump. Namespaces have moved between majors (Sabberworm\CSS\Value\Size vs older CSSSize in legacy snippets). A composer update across a major version can silently break a custom instanceof chain — write one integration test that asserts on rendered output so the break is loud.

There is no AST serialization format and no source maps. If you need either, this package is the wrong tool — it’s for transform-and-emit, not for tooling-grade analysis.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator