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 .php files.
  • 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.
With the PHPX Tag Support extension, your Prisma PHP experience becomes as seamless as working with Prisma in TypeScript — but optimized for the PHP ecosystem.

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.

isChildren="true"
type="submit"

Reactive Props

PulsePoint signals passed to the client. They must use mustache syntax.

counter="{count}"
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
PulsePoint reactive props always require mustache syntax (e.g., prop="{value}"). Standard PHP props do not.
For more details, examples, and advanced patterns for passing reactive props with PulsePoint, see the official documentation: PulsePoint — Component Props Guide .

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
A component is only recognized if it extends the 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 via PP\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');
}