ImportComponent
ImportComponent
turns a PHP partial into a real PulsePoint component boundary:
it executes the file in an isolated namespace (preventing function redeclare collisions),
parses the produced HTML,
enforces exactly one root element,
injects pp-component + serialized props as attributes,
and returns clean, hydratable markup.
Concept
The core idea is simple: every imported PHP file becomes a single-root component that PulsePoint can target later. The root node is automatically decorated with:
- pp-component — a deterministic ID derived from the file path (CRC32 → base36), stable for that path.
-
Serialized props as attributes — scalar values become strings,
booleans become
true/false, arrays/objects become JSON. -
PulsePoint mustache compatibility — when a prop contains mustache
(ex:
{count}), the prop key is converted from camelCase → kebab-case, keeping HTML attribute rules while preserving JS naming conventions.
How it Works Internally
-
Execute component in an isolated namespace
Props areextract()'d into local scope, then file source is wrapped in a unique namespace andeval()'d. This prevents global function redeclare collisions. -
Capture HTML output
Output is buffered and returned as a string. -
Parse as XML fragment
UsesTemplateCompiler::convertToXml()to safely parse the HTML fragment. -
Enforce exactly one root element
If multiple root elements are found, an exception is thrown. -
Inject pp-component + props
Root getspp-component="s..."and prop attributes. -
Serialize and echo final HTML
The resulting HTML is echoed and stored inImportComponent::sections().
During isolated execution, ImportComponent detects any newly defined functions
in the sandbox namespace that have the @Exposed attribute and registers them
into ExposedRegistry.
This enables PulsePoint to call PHP-exposed server functions (via your existing Exposed tooling) without
requiring global function names in the main namespace.
// Inside a component file:
use PP\Attributes\Exposed;
#[Exposed]
function saveDraft(array $payload): array {
// ...
return ['ok' => true];
}
// ImportComponent will automatically register it
// under its short name: "saveDraft".
API Reference
static render(string $filePath, array $props = []): void
Executes the PHP file, enforces single-root output, injects attributes, stores a snapshot in
ImportComponent::sections(), and echoes the final HTML.
static import(string $filePath, array $props = []): void
Alias of render(). Use whichever reads better in templates.
static sections(): array
Returns an array snapshot of imported sections keyed by $filePath, containing:
path, html, and props.
Useful for debugging, devtools, or server-side inspection.
Props, Attributes, and Serialization Rules
-
Null props are skipped — if a value is
null, no attribute is emitted. -
Booleans become
"true"/"false". - Scalars (string/int/float) are cast to string.
-
Arrays/objects become JSON using:
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES. -
Mustache-aware attribute naming — if the serialized value contains mustache syntax
(
{...}), the prop key is converted fromcamelCase→kebab-case.
// Example:
ImportComponent::render(APP_PATH . '/inc/counter.php', [
'count' => '{count}', // becomes count="{count}"
'setCount' => '{setCount}', // becomes set-count="{setCount}"
'config' => ['step' => 2], // becomes config='{"step":2}'
]);
Single Root Element is Mandatory
ImportComponent throws an exception unless the imported file renders
exactly one root element.
Wrap your template in a single container (or a semantic tag like section, article, etc.).
Usage Examples
1. Basic Import
Import a PHP component file and automatically generate a stable pp-component id.
<?php
use PP\ImportComponent;
ImportComponent::render(APP_PATH . '/inc/Hero.php');
?>
2. Passing Props (Server Data → Attributes)
<?php
use PP\ImportComponent;
$user = ['id' => 7, 'name' => 'Jefferson'];
ImportComponent::render(APP_PATH . '/inc/UserCard.php', [
'user' => $user,
'variant' => 'compact',
'isOnline' => true,
]);
?>
<div
pp-component="s1x..."
user='{"id":7,"name":"Jefferson"}'
variant="compact"
isOnline="true"
>
<!-- component output... -->
</div>
3. PulsePoint Reactive Bindings (Mustache → kebab-case)
Any prop value containing mustache ({...})
triggers camelCase → kebab-case conversion for the attribute name.
This is ideal for passing signals/setters into components.
<?php
use PP\ImportComponent;
?>
<div class="space-y-4">
<?php ImportComponent::render(APP_PATH . '/inc/SearchBox.php', [
'query' => '{query}',
'setQuery' => '{setQuery}', // becomes set-query="{setQuery}"
]); ?>
<script type="text/pp">
const [query, setQuery] = pp.state('');
</script>
</div>
4. Component File Template (Single Root + Icon Style)
Your component file must output a single root element. Below is a pattern-friendly template that matches Tailwind + Shadcn tokens and demonstrates the Lucide icon style inside the markup.
<?php
// File: /inc/SearchBox.php
declare(strict_types=1);
// props are available as local variables if passed:
// $query, $setQuery
?>
<div class="rounded-lg border bg-card p-4">
<div class="flex items-center gap-2">
<!-- Lucide style (shown as example) -->
<!-- <Search class="size-4" /> -->
<span class="text-sm font-medium text-foreground">Search</span>
</div>
<div class="mt-3">
<input
class="w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground"
placeholder="Type to search..."
value="{query}"
oninput="{(e) => setQuery(e.target.value)}"
/>
</div>
</div>
5. Debugging: Inspect Imported Sections
sections() keeps a record of everything imported during the request.
This is useful for debugging, devtools overlays, or server logs.
<?php
use PP\ImportComponent;
ImportComponent::render(APP_PATH . '/inc/Hero.php', ['title' => 'Hello']);
ImportComponent::render(APP_PATH . '/inc/UserCard.php', ['user' => ['id' => 1]]);
$sections = ImportComponent::sections();
// Example: var_dump($sections[APP_PATH . '/inc/Hero.php']['html']);
?>
Notes & Best Practices
-
Keep component files deterministic — since the ID is path-based,
moving/renaming files changes
pp-component. - Avoid multiple roots — wrap everything in a single container. If you need multiple siblings, nest them inside one root node.
-
Strict types handling — a leading
declare(strict_types=1);is stripped before eval to keep sandbox execution safe. - Security posture — only import trusted files from your own codebase. This is a server-side render tool; do not pass user-supplied file paths.
- PulsePoint-friendly attributes — use mustache bindings for signals/setters; camelCase keys will become valid HTML attributes automatically.