dmvdigital/phpsfc

PHP Single File Components: CSS, JS, and HTML sections in one PHP file, Vue-SFC style.

Maintainers

Package info

github.com/dmvdigital/phpsfc

pkg:composer/dmvdigital/phpsfc

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.0.0 2026-07-19 21:03 UTC

This package is auto-updated.

Last update: 2026-07-20 03:12:39 UTC


README

tests Packagist Version License

PHP Single File Components: CSS, JS, and HTML sections in one .php file, Vue-SFC style, no build step.

<!--section::style-->
<style>.__self { padding: 1rem; }</style>

<!--section::output-->
<div class="__self">Hello, <?= htmlspecialchars($name) ?></div>
$engine = new Phpsfc\Engine($componentsDir, $cacheDir);
echo $engine->render('widgets/hello', ['name' => 'World']);

Install

composer require dmvdigital/phpsfc

Requires PHP 8.1+.

For the full method reference, file-format grammar, caching internals, and security model, see DOCUMENTATION.md. Using an AI coding agent to write components? Point it at AGENTS.md first.

Quickstart

A component is referenced by its path, extension omitted, no separately invented name:

$engine->render('sections/hero-animated', ['title' => 'Hello']);
$engine->component('widgets/card')->set('title', 'Getting Started');

A page needs a layout's shell but also needs to hand its own sections into that shell, that's what extends/load and slot()/block()/files() are for:

<!--extends::layout-->
<!--section::content-->
<div class="__self">page body</div>
<!--section::output-->
<head><?= $this->block('style') ?></head>
<body><?= $this->slot('content') ?></body>

The setup section declares props once, and that single declaration drives both the runtime default and Editor Mode's control panel, no separate schema to keep in sync:

<!--section::setup-->
<?php
$setup->metadata('Hero Section')
    ->variable('title', ['default' => 'Welcome', 'control' => 'text']);

Then open a standalone visual viewer (with a Monaco source editor a click away) for every component:

$engine->editor(); // dispatches based on $_GET

Examples

Runnable, in examples/. Clone the repo, then:

php -S localhost:8000 -t examples/01-basic

...and open http://localhost:8000. Same pattern for any other example directory.

  • 01-basic, render/load/extends, a setup schema, and a non-strict missing-component error.
  • 02-layout, a page extending a layout and loading a widget component.
  • 03-modifier, a custom modifier (CSS minification) and a custom OutputStrategy, both registered under a key.
  • 04-vue-sfc, Vue x-template mode: two views compiled to <script> templates, swapped by a hash router.
  • 05-php-logic, loops, conditionals, other plain PHP running inside sections, and __DIR__ reaching a sibling data file.
  • 06-css-file-cache, a modifier that writes bundled CSS to a file instead of inlining it.
  • 07-auto-load, components discovered by being rendered from inside another component's output, not declared upfront.
  • 08-content-blocks, CMS-style blocks in random order per request, with per-block styles injected into <head> regardless of load order.
  • 09-file-modifiers, bundling both CSS and JS out to public files.
  • 10-editor, Editor Mode's standalone visual viewer and Monaco source editor.
  • 11-clear-cache, cacheBuild()/cacheDelete() and their opt-in GET-triggered variants.
  • 12-conditional-slot, hasSlot() picking a sidebar layout depending on what the page defines.
  • 13-autoload, explicit <!--load::--> markers for shared components.
  • 14-component-and-assets, the component()->set() OOP wrapper and asset()'s copy-if-stale + cache-busted URL.
  • 15-strict-props, strict mode (the default) throwing MissingPropException/ComponentNotFoundException instead of soft-erroring.
  • 16-slot-content, passing pre-rendered markup into a component as a prop, ob_start()/ob_get_clean() capturing a nested render() call.

License

MIT