dmvdigital / phpsfc
PHP Single File Components: CSS, JS, and HTML sections in one PHP file, Vue-SFC style.
Requires
- php: ^8.1
Requires (Dev)
- phpunit/phpunit: ^10.0
README
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 andasset()'s copy-if-stale + cache-busted URL. - 15-strict-props, strict mode (the default) throwing
MissingPropException/ComponentNotFoundExceptioninstead of soft-erroring. - 16-slot-content, passing pre-rendered markup into a component as a prop,
ob_start()/ob_get_clean()capturing a nestedrender()call.
License
MIT