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

Phpsass Laravel Package

cartalyst/phpsass

Cartalyst PHPSass is a PHP implementation of the Sass CSS preprocessor, letting you compile SCSS and Sass stylesheets directly in PHP without Ruby. It supports variables, nesting, mixins, and inheritance for building maintainable stylesheets.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the vendor directory before writing any code. The package has no published docs and its metadata is unreliable: the marketing copy calls it a "PHP wrapper for libsass," but this lineage (richthegeek/phpsass) is a pure-PHP port of early Ruby Sass. That means the API is SassParser::parse_file() — not a libsass-style SassCompiler::compile(). Open these three things first:

  1. vendor/cartalyst/phpsass/composer.json — the php constraint (this is what will block you on PHP 8.x), autoload strategy (classmap/PSR-0, not PSR-4), and whether a Laravel provider block exists.
  2. vendor/cartalyst/phpsass/src/ — the entry class and its public methods. That is your API.
  3. LICENSE — confirm MIT and attribution survive; you will likely be vendoring and patching this.

Pin an exact version — never a caret/tilde constraint on an unmaintained package.

composer require cartalyst/phpsass:1.0.4   # exact, no ^ or ~

Smoke-test outside the app first. A throwaway script tells you whether the package even runs on your PHP version before you commit to a service provider:

require __DIR__.'/vendor/autoload.php';

$parser = new SassParser([
    'style'      => 'compressed',
    'load_paths' => [__DIR__.'/resources/scss'],
    'cache'      => false,
]);

file_put_contents(
    __DIR__.'/public/css/app.css',
    $parser->parse_file(__DIR__.'/resources/scss/app.scss')
);

Your first real use case is a parity spike, not an integration. Compile your full production stylesheet set with this package and with Dart Sass, then diff:

npx sass resources/scss/app.scss /tmp/dart.css --style=compressed
php spike.php > /tmp/phpsass.css
diff -u /tmp/dart.css /tmp/phpsass.css | head -100

Cosmetic diffs mean a contained legacy dependency. Structural diffs — missing rules, wrong values, dropped media queries — mean stop and move to scssphp/scssphp. Do this on day one, before any wiring.

Implementation Patterns

Wrap it behind a contract you own. This is the single most valuable thing you can do with an unmaintained parser — it converts a vendor choice into a one-line container binding.

namespace App\Stylesheets;

interface StylesheetCompiler
{
    /** @param string $source Trusted, version-controlled SCSS only. */
    public function compile(string $source, array $options = []): string;
}
final class PhpSassCompiler implements StylesheetCompiler
{
    public function compile(string $source, array $options = []): string
    {
        $parser = new \SassParser([
            'style'      => $options['style'] ?? 'compressed',
            'load_paths' => $options['includePaths'] ?? [resource_path('scss')],
            'cache'      => false,
        ]);

        return $parser->parse_file($source);
    }
}
// AppServiceProvider::register()
$this->app->singleton(StylesheetCompiler::class, PhpSassCompiler::class);

Migrating to scssphp/scssphp later changes only this class. Commands, tests, and Blade helpers stay put.

Drive compilation from an Artisan command, not a request. Compiled CSS is a build artifact.

final class CompileStylesheets extends Command
{
    protected $signature = 'styles:compile {--force : Ignore the cache}';

    public function handle(StylesheetCompiler $compiler): int
    {
        $entrypoints = config('styles.entrypoints'); // ['app' => 'scss/app.scss']

        foreach ($entrypoints as $name => $relative) {
            $source = resource_path($relative);
            $target = public_path("css/{$name}.css");
            $stamp  = public_path("css/.{$name}.hash");

            $hash = md5_file($source);
            if (! $this->option('force') && is_file($stamp) && file_get_contents($stamp) === $hash) {
                $this->line("{$name}: unchanged");
                continue;
            }

            file_put_contents($target, $compiler->compile($source));
            file_put_contents($stamp, $hash);
            $this->info("{$name}: compiled");
        }

        return self::SUCCESS;
    }
}

Call it from your deploy hook — after composer install, before route:cache — and commit the emitted CSS if your deploy target can't run it.

Golden-file tests are your regression net. Silent CSS divergence is the dominant risk: it compiles cleanly and passes every existing test.

public function test_app_stylesheet_matches_snapshot(): void
{
    $css = app(StylesheetCompiler::class)->compile(resource_path('scss/app.scss'));

    $this->assertSame(
        file_get_contents(base_path('tests/fixtures/css/app.css')),
        $css,
        'Compiled CSS diverged. If intentional, regenerate the fixture.'
    );
}

Give yourself an explicit --update-snapshots path so a compiler swap or PHP upgrade produces a reviewable diff instead of a silent one.

Config, not constants. Put include paths, output style, and entrypoint lists in config/styles.php. Make include paths absolute with resource_path()/base_path(); the parser resolves @import relative to its working directory, not your app root.

For dynamic theming, compile at build time and emit CSS custom properties per tenant. Never compile per request.

:root { --brand-primary: #{$brand-primary}; }
<style>:root { --brand-primary: {{ $tenant->brand_primary }}; }</style>

Same white-label capability, none of the runtime cost.

Parallelize per entrypoint if you have many: run one Artisan invocation per entrypoint in your deploy script. The parser is CPU-bound with no parallelism of its own.

Gotchas and Tips

PHP 8.x is the main hazard, not upstream's willingness to fix things. This lineage is from the PHP 5.3–7.x era. Expect deprecation notices (each(), dynamic properties, implicit nullable parameters) and possible fatals from removed functions. The composer.json php constraint may hard-block installation on 8.x — if it does, you are already patching. Test against both ends of your CI matrix.

Autoloading behaves differently in CI. Classmap/PSR-0 packages don't participate in the optimized PSR-4 autoloader the same way, and composer dump-autoload -o can resolve classes differently than in dev. Verify composer install --no-dev -o in a clean container before relying on it — the failure mode is a class-not-found at deploy time that never reproduces locally.

The dialect is early Ruby Sass, not Dart Sass. No @use, no @forward, no math.div, and / is still integer division. Modern-authored stylesheets either fail loudly (fine) or — much worse — compile with different semantics and produce subtly wrong CSS. Audit the surface first:

grep -rnE '@use|@forward|math\.div|@each|@function|#\{' resources/scss | wc -l

Watch for set_error_handler collisions. Pure-PHP Sass parsers typically install their own error handler to convert warnings into SassExceptions. Under APP_DEBUG=true, that can swallow or reshape framework exceptions in confusing ways. If error output looks bizarre, isolate by running the parser in a bare script. Better: keep compilation in a separate process (Artisan, CI) so the handler's lifetime is bounded.

Include-path resolution is working-directory sensitive. @import "variables" resolves against load_paths and the CWD, which differs between php artisan styles:compile (project root), a queue worker, and PHPUnit. Always pass absolute paths, and add a test that compiles from a different CWD.

Disable the parser's internal disk cache. Some builds write parse caches keyed on mtime; in containers or read-only filesystems this silently fails or throws. Set 'cache' => false and roll your own hash-based caching (as in the Artisan example) where the key is explicit and inspectable.

Performance is CPU-bound and materially slower than libsass/Dart Sass. A 200ms build can become 10+ seconds on a large stylesheet set. Keep it out of the request cycle, parallelize per entrypoint, and watch memory_limit — pure-PHP parsers build large ASTs.

Never feed it user-editable or request-time input. An unmaintained parser with no CVE process, handling arbitrary input, is a DoS and correctness risk. Compile only from trusted, version-controlled sources at build/deploy time.

Document the exit. Two artifacts, written the same week you adopt it: (1) a runbook — how to recompile, where fixtures live, how to regenerate them; (2) a "how to replace this" note naming the StylesheetCompiler interface, the swap target (scssphp/scssphp, similar compile() shape), and the validation steps (parity diff + golden-file suite). With 0 stars and 0 dependents, nobody upstream is going to fix this for you — and the person who can replace it in an afternoon is the one who wrote that note. Log it on the tech-debt register with a named owner, a pinned version, and a dated exit criterion.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect