Prisma PHPX
Prisma PHPX is a template system for Prisma PHP that is inspired by JSX and React.
For the best developer experience, it is highly recommended to install the PHPX Tag Support extension in VS Code. This extension provides out-of-the-box features such as fast auto-import of components, props validation, intelligent auto-completion suggestions, and much more to speed up your development workflow.
This extension is built specifically for .php files and Prisma PHP’s component ecosystem. It dramatically improves productivity and reduces bugs by offering:
- ✓ Auto-import of components as you type JSX-style tags in
.phpfiles. - ✓ Model column auto-completion based on your schema when accessing fields like
{user.email}. - ✓ Filtered suggestions only relevant to the model or tag context.
- ✓ Quick fixes (Ctrl + .) to import missing components or resolve mismatches.
- ✓ Command auto-completion for your ORM operations and CLI usage.
- ✓ PHPX-specific diagnostics for invalid props, tag mismatches, and unused imports.
- ✓ Hover previews, definition jumping (F12), and intelligent path linking for models and views.
File Structure and Component Naming
To maintain order and consistency, create a directory in
src/app/Lib/Components/ClassName.php.
This convention helps in organizing and managing your components effectively.
For example, to create a
SearchIcon component,
you should create a file named
SearchIcon.php in the
src/app/Lib/GoogleIcons/SearchIcon.php directory. The class name should match the file name.
Related Components Naming
It is important to note that you can have one initializer file for all your components. For example, you can follow a naming convention like
Accordion for the main component. The related components should follow the same naming convention, such as
AccordionItem,
AccordionTrigger, and
AccordionContent. These classes should be in the same file and start with the main component name.
The file can be located in any directory you desire, but following the naming convention is important. For example:
src/app/Lib/PHPX/Components/Accordion.php.
Prop Management
By default, the class constructor's props will receive the
children property,
which represents the inner content of the component. You can also pass other props to the component, such as
class.
The PHPX class provides three primary properties:
$props: The properties or attributes passed to the component.$children: The children elements or content to be rendered within the component.$class: The CSS class for custom styling.$attributesArray: An array of attributes to be rendered as HTML attributes.
Reactive vs. Static Props
Prisma PHPX distinguishes between Static PHP Props (Server-Side) and Reactive PulsePoint Props (Client-Side).
Static Props
Standard PHP properties. They are strictly typed and processed on the server.
type="submit"
Reactive Props
PulsePoint signals passed to the client. They must use mustache syntax.
setCounter="{setCount}"
When defining a component, standard props behave like normal PHP class properties with type safety. However, when you want to pass a reactive signal (state) from a parent to a child for PulsePoint to handle on the client side, you must use the {variable} syntax.
The Mustache Rule
prop="{value}"). Standard PHP props do not.
Example: Reactive Component
In this example, $counter is a reactive prop. Note how we define a default string value '{0}' in the PHP class to represent the initial JS state.
<?php
declare(strict_types=1);
namespace app\test;
use PP\PHPX\PHPX;
class TestComponent extends PHPX
{
// Normal PHP Prop (Styling)
public ?string $class = '';
// Reactive PulsePoint Props (Signals)
public ?string $counter = '{0}';
public ?string $setCounter = '() => {}';
public function __construct(array $props = [])
{
parent::__construct($props);
}
public function render(): string
{
$class = $this->getMergeClasses($this->class);
$attributes = $this->getAttributes([
'class' => $class,
'counter' => $this->counter, // Reactive Prop if you want to have default value
'setCounter' => $this->setCounter, // Reactive Prop if you want to have default value
]);
return <<<HTML
<div {$attributes}>
{$this->children}
<!-- PulsePoint logic using the props -->
<button onclick="setCounter(counter + 1)">Update Counter</button>
<script>
pp.effect(() => {
console.log('Counter changed: ', counter);
}, [counter]);
</script>
</div>
HTML;
}
}
Example: Usage
Here we pass the count signal to the child using mustache syntax, while passing a static class prop normally.
<?php
use app\test\TestComponent;
?>
<!-- Passing Reactive Props (Mustache) -->
<TestComponent counter="{count}" setCounter="{setCount}" class="p-4 border" />
<p>Parent Count: {count}</p>
<script>
const [count, setCount] = pp.state(0);
</script>
Overridable Methods
PHPX provides several methods that you can override to customize your components.
| Method | Description |
|---|---|
| __construct(array $props) | Constructor to initialize the component with properties. |
| init(array $props) | Registers or initializes any necessary components or settings. |
| getMergeClasses() | Combines and returns CSS classes (Tailwind optimized). |
| getAttributes() | Generates a string of HTML attributes from props. |
| render() | Renders the component as an HTML string. |
| __toString() | Converts the object to its string representation. |
Component Registration
PHPX class or implements the IPHPX interface. Components are registered in settings/class-log.json. Restart the server to re-register them automatically.
PHPX System Files
PHPX now ships as a standalone package at
vendor/tsnc/prisma-php/src/PHPX\PHPX.
Import it like other Prisma PHP core packages using the PP namespace:
use PP\PHPX\PHPX;.
-
TemplateCompiler.php: The main compiler for the template system (located in the package). -
PHPX.php: The base class that your components should extend (accessible viaPP\PHPX\PHPX). -
IPHPX.php: Interface that components may implement (optional when extending the base class). -
TwMerge.php: Helper for merging Tailwind classes (packaged alongside the core files).
Importing Components
Standard import:
use Lib\GoogleIcons\Search;
Usage:
<Search />
Resolving Conflicts: If you have components with the same name from different libraries, use an alias:
use Lib\GoogleIcons\Search as GoogleSearch; use Lib\LucideIcons\Search as LucideSearch; // Usage: <GoogleSearch /> <LucideSearch />
Execution Order
The execution order starts from the outermost parent and proceeds to the innermost child. This allows the parent to share props with its children.
<?php
use Lib\PHPXUI\{Accordion, AccordionItem, AccordionTrigger, AccordionContent};
?>
<Accordion>
<AccordionItem>
<AccordionTrigger>Prisma PHP is awesome!</AccordionTrigger>
<AccordionContent>Prisma PHPX is inspired by JSX and React.</AccordionContent>
</AccordionItem>
</Accordion>
- Accordion initialized
- AccordionItem initialized
- AccordionTrigger initialized
- AccordionContent initialized
JSX-style Closing Tags
All tags must follow strict XML closing rules.
- ✓
<hr /> - ✕
<hr> - ✓
<input type="text" /> - ✕
<input type="text">
XML-style Attributes
Attributes must be enclosed in double quotes.
- ✓
<input required="true" /> - ✕
<input required /> - ✓
<input id="input" /> - ✕
<input id=input />
Handling Complex Data
CDATA Sections
When working with complex strings that need to be escaped from XML processing, use <![CDATA[ ... ]]>.
If you have the PHPX Tag Support VS Code extension installed, typing <! will surface autocomplete suggestions (including CDATA snippets and XML-style tags) to speed up authoring.
<![CDATA[
// Your complex code here
]]>
JSON Encoding
To prevent XSS attacks when passing data to JS, use json_encode($data, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT).
| Constant | Description |
|---|---|
| JSON_HEX_TAG | Escapes < and > to prevent HTML injection. |
| JSON_HEX_AMP | Escapes & to prevent breaking attributes. |
| JSON_HEX_APOS | Escapes ' (single quote) for JS safety. |
| JSON_HEX_QUOT | Escapes " (double quote) for JSON inside attributes. |
JavaScript Injection
Use MainLayout::addFooterScript($script); to add scripts to the footer dynamically.
Use <<<'HTML' (nowdoc) for raw JS, preventing variable parsing.
public function __construct(array $props = []) {
parent::__construct($props);
$accordionScript = <<<'HTML'
<script>
// Your JavaScript code here
</script>
HTML;
MainLayout::addFooterScript($accordionScript);
}
Creating a Component
If you install the PHPX Tag Support VS Code extension, you can simply type
phpxclass
to trigger a snippet that scaffolds a component class, inserts the correct namespace and imports, and suggests the conventional file name (for example ClassName.php) and directory. The extension also provides auto-imports and prop/type suggestions as you type — make sure the file path matches the namespace so the auto-import works seamlessly.
<?php
declare(strict_types=1);
namespace Lib\PHPX\Components;
use PP\PHPX\PHPX;
class ClassName extends PHPX
{
public ?string $class = '';
public mixed $children = null;
public function __construct(array $props = [])
{
parent::__construct($props);
}
public function render(): string
{
$class = $this->getMergeClasses($this->class);
$attributes = $this->getAttributes([
'class' => $class,
]);
return <<<HTML
<div {$attributes}>
{$this->children}
</div>
HTML;
}
}
State Sharing (Parent ↔ Child)
Use the StateManager class for seamless information sharing.
class Parent extends PHPX {
public function __construct(array $props = []) {
parent::__construct($props);
StateManager::setState('fromParent', 'Hello from Parent!');
}
// ... render accessing StateManager::getState('fromChild');
}
class ParentChild extends PHPX {
public function __construct(array $props = []) {
parent::__construct($props);
StateManager::setState('fromChild', 'Hello from Child!');
}
// ... render accessing StateManager::getState('fromParent');
}