PHP Security Advisory: Read our guidance →

πŸ“š Documentation · v0.0.3

PHPue Documentation

Introduction

PHPue is a PHP framework that brings the simplicity of Vue's component model to server-side rendering. Build modern web applications without the complexity of build tools or client-side hydration.

What is PHPue?

A PHP framework that uses Vue-like syntax for server-side rendering. Write components with familiar directives, but get pure HTML from the server. Zero JavaScript required for core functionality.

It looks like Vue. It isn't Vue.

No virtual DOM, no reactivity, no props and no hydration.
Everything inside {{ }}, p-if and p-for runs once, on the server.
Raw PHP is always allowed. If a directive can't express something, write <?php if … ?> and move on.
Need the browser? Add plain JavaScript in <cscript>, or AlpineJS.

What's New in 0.0.3 0.0.3

0.0.3 is a bug-fix release for the compiler. Most 0.0.2 workarounds are no longer needed, and existing apps keep working: replace index.php and conversion.php together. Sections below marked 0.0.3 are new or changed.

Templates

  • Smarter directives - p-if / p-for work on one line, across lines, on nested tags and together
  • Smarter {{ }} - function calls like count($items) now work
  • Nested <template> - AlpineJS x-for / x-if templates compile
  • Safer blocks - block names in comments or strings no longer break a file

Server

  • Safer @AJAX - the declared method is enforced, and only @AJAX functions can be called
  • Sessions - PHPUE_SESSION = 'lazy' keeps public pages cacheable
  • HTML language - phpue_set_lang() sets <html lang> per request
  • Locked dev tools - ?build, ?compile= and ?hot-reload switch off in production

Upgrading from 0.0.2?

Swap in the new index.php and conversion.php, check you're on PHP 8.2+, then rebuild (php index.php build). Any @AJAX call that used the wrong HTTP method now gets a 405, so test your forms. Full notes: release page.

Tutorials

Learn PHPue through step-by-step tutorials designed for developers of all skill levels. Our tutorials cover everything from basic setup to advanced features.

πŸŽ“ Interactive Learning Platform

PHPue includes a complete tutorial system built with PHPue itself. Each tutorial provides hands-on examples and real-world scenarios.

Getting Started

  • β€’ Setting up your development environment
  • β€’ Creating your first PHPue application
  • β€’ Understanding the file structure

What's Included

  • β€’ Step-by-step video guides
  • β€’ Interactive code examples
  • β€’ OS-specific installation guides
πŸ“š Explore PHPue Tutorials

All tutorials are built with PHPue and demonstrate real-world usage patterns.

Tutorial Structure

Beginner-Friendly: Start from zero knowledge and progress to advanced topics
Practical Examples: Every concept is demonstrated with working code
Self-Contained: Each tutorial can be followed independently
Built with PHPue: The tutorial system itself is a PHPue application

Get Started

Start building with PHPue in minutes. No complex setup, no build process, just pure PHP power.

Terminal
# Clone the starter template
git clone https://github.com/PHPue/PHPue my-app
cd my-app

# Start the development server (recommended: Docker, PHP + NGINX)
docker compose up
# Open http://localhost:3000

# No Docker? PHP's built-in server works too
cd www && php -S localhost:3000

πŸ–₯️ Choose Your Setup

Docker (recommended): docker compose up gives you NGINX + PHP-FPM on port 3000. Hot reload and AJAX work together, just like production.
PHP built-in server: cd www && php -S localhost:3000 (any free port, e.g. 8080). Great for a first look, but it handles one request at a time, so hot reload (?live) can block your AJAX calls.
Apache / XAMPP: copy the contents of www/ into htdocs/. The included .htaccess routes every page through index.php.
Step-by-step guides for each OS: Setting up a PHP server. Then open views/index.pvue and start building.

Installation

PHPue Framework requires PHP 8.2+ and works with any web server. No additional extensions, Composer or npm needed.

Manual Installation

  1. Download PHPue core files | PHPue - GitHub
  2. Copy index.php and conversion.php into your web root.
  3. You may need to configure your server to point to the index.php file
    as the entry point. - View .htaccess example
  4. Start building views and components.
  5. Syntax Highlighting: Download PHPue Syntax Highlighter |
    VSCode Extension - GitHub

Requirements

  • PHP 8.2+ - 0.0.3 uses PHP 8 functions, and 8.2 is the oldest PHP still getting security fixes 0.0.3
  • Any web server - Apache, NGINX, or PHP's built-in server
  • Shared hosting - works over plain FTP
  • Optional - Docker for local development, Tailwind CLI for styling

File Structure

PHPue follows a convention-based structure that makes organising your application intuitive and scalable.

Project Structure

# Root Directory
www/
App.pvue
index.php
conversion.php
assets/
css/
js/
img/
components/
dashboards/
forms/
Navbar.pvue
views/
index.pvue
about.pvue
docs.pvue
features.pvue
backend/ # optional: PHP classes, auto-loaded
httpReqs/ # optional: custom 404 page
.dist/
# Auto-generated by the build
App.php
pages/
components/
ajax/
backend/
httpReqs/
assets/

Core Files

  • App.pvue - Main application layout and routing
  • index.php - Application server / builder
  • conversion.php - Build engine for PVue files

Directories

  • views/ - Page components with routing
  • components/ - Reusable UI components
  • assets/ - Static files (CSS, JS, images)
  • backend/ - PHP classes and vendor libraries (optional)
  • httpReqs/ - Custom error pages (optional)
  • .dist/ - Compiled PHP files (auto-generated). While it exists, the server serves it instead of your .pvue files

Naming Conventions

Views: Use descriptive names (about.pvue, docs.pvue). The file name is the route: about.pvue β†’ /about
Components: Use PascalCase (Navbar.pvue, Sidebar.pvue), in any folder depth
Routes: Views sit at the top level of views/ (no nested paths). Use query strings for parameters: /post?id=42
Terminal
[skira24@SK14 PHPue]$ cd www
[skira24@SK14 www]$ ls
App.pvue backend conversion.php index.php views
assets components httpReqs tailwind.config.js
[skira24@SK14 www]$ cd assets
[skira24@SK14 assets]$ ls
css img js
[skira24@SK14 assets]$ cd ../components/
[skira24@SK14 components]$ ls
codeBlocks dashboards forms Navbar.pvue navigation
[skira24@SK14 components]$ cd ../views/
[skira24@SK14 views]$ ls
about.pvue docs.pvue features.pvue index.pvue

Deployment

PHPue offers two deployment strategies - choose the one that fits your workflow and security requirements.

Option 1: Build Locally

Recommended for production - Compile locally and deploy only the optimised files.

# Build locally
cd www/
php index.php build
# or visit: localhost:3000?build=1
# Deploy to server via FTP:
πŸ“ .dist/
πŸ“ httpReqs/
πŸ“ assets/
πŸ“„ index.php
πŸ“„ conversion.php
# OR: copy the contents of .dist/ into your web root
πŸ“ ajax/
πŸ“ components/
πŸ“ pages/
πŸ“ httpReqs/
πŸ“ assets/
πŸ“„ App.php
πŸ“„ index.php
πŸ“„ conversion.php
πŸ“„ .htaccess

βœ… Advantages

  • β€’ No source code on production server
  • β€’ Better performance (pre-compiled)
  • β€’ No security risks from exposed .pvue files
  • β€’ Smaller deployment footprint
  • β€’ (Optional:) Block access to .dist/ (OR) any business logic directories.
  • β€’ Rebuild after every change: compiled files are served instead of your .pvue files

Option 2: Deploy Source

For development or when you need instant builds on the server.

# Deploy all files via FTP:
πŸ“ components/
πŸ“ views/
πŸ“ assets/
πŸ“„ *.pvue files
πŸ“„ index.php
πŸ“„ conversion.php
# Build on server:
Visit: yourdomain.com?build=1

⚠️ Security Required

  • β€’ Must block .pvue files in .htaccess/NGINX
  • β€’ Protect App.pvue, components/, views/, backend/ and other exposed directories
  • β€’ Source files expose business logic if accessible

πŸ”’ Essential Security Configuration

If you choose Option 2, you MUST add these security rules to prevent source code exposure.

Apache (.htaccess)

# BLOCK access to all .pvue files (anywhere)
<FilesMatch "\.pvue$">
    Require all denied
</FilesMatch>

# BLOCK access to source directories
RewriteRule ^(views|components|backend|httpReqs)(/.*)?$ - [F,L]

βœ… Blocks all .pvue files and protects source directories

NGINX

# BLOCK .pvue files
location ~ \.pvue$ {
    deny all;
    return 404;
}

# BLOCK source directories
location ~ /(views|components|backend|httpReqs)/ {
    deny all;
    return 404;
}

βœ… Equivalent protection for NGINX servers

πŸ›‘οΈ Production Safety Feature 0.0.3

PHPue automatically switches off its developer tools once your app is compiled, so nobody can rebuild or inspect your live site from a browser.

# If .dist/App.php (or App.php) exists (production):
Visit: yourdomain.com?build=1
β†’ Ignored: you are sent back to the page
β†’ ?compile= and ?hot-reload are switched off too
# Intentional rebuild on the server:
php index.php build

This prevents accidental rebuilds that could break your live site. For intentional rebuilds, use the CLI command. Need the tools on a compiled site (e.g. staging)? Add define('PHPUE_DEV_TOOLS', true); to index.php, never in production.

πŸš€ Quick Start Guide

1

Develop Locally

Build your app with docker compose up (or php -S localhost:3000)

2

Build & Test

Run php index.php build (or ?build=1) to compile to .dist/

3

Deploy Safely

Upload only .dist/, assets/, httpReqs/, index.php, and conversion.php

πŸ’‘ Pro Tip: Always build locally and deploy the compiled .dist/ directory for maximum security and performance. For zero-downtime switches and instant rollbacks, see Painless Deployment.

@require & #require

Import components and views using simple require statements.

Component Require

@require ComponentName 'components/componentName.pvue';

Import a component for use in your template, then write <ComponentName/>, <ComponentName /> or <ComponentName></ComponentName>. Single or double quotes both work. 0.0.3

View Require

#require 'views/viewName.pvue';

Include a view template with its own logic. Register every view in App.pvue.

Example: App.pvue

Here's how you might structure your main application file:

<script>
    @require Navbar 'components/Navbar.pvue';
    #require 'views/index.pvue';
    #require 'views/about.pvue';
</script>

<header>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="assets/css/styles.css">
    <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
    <script src="assets/js/jquery-3.7.1.min.js"></script>
</header>

<template>
    <Navbar></Navbar>
    <View></View>
</template>

Key Points:

  • @require imports reusable components
  • #require imports views for routing
  • <header> block for global assets and meta tags
  • <View> component automatically handles current page rendering (only in App.pvue)
  • Scope: App variables are not visible inside views. Share data with backend/ classes, $_SESSION or define()

No Props, By Design

Components are inlined into their parent, so they see every variable the parent has - just like a PHP include. Set a variable before the tag, and give it a default inside the component.

<!-- views/index.pvue -->
<?php $cardTitle = 'Pricing'; ?>
<Card/>

<!-- 0.0.3: render a component once per item -->
<ProductCard p-for="$product in $products" />

<!-- components/Card.pvue -->
<script>
    $cardTitle = $cardTitle ?? 'Untitled';
</script>

πŸ’‘ Tip: Don't declare named PHP functions or classes inside a component (using it twice declares them twice). Put them in backend/.

App.pvue & Routing

App.pvue is the shell for every page. <View/> (only allowed in App) is replaced by the current route's view.

File-Based Routes

  • views/index.pvue β†’ /
  • views/ue-insights.pvue β†’ /ue-insights
  • No nested paths: use query strings like ?id=42
  • phpue_navigation() returns every route with its title, handy for menus

Good to Know

  • β€’ Register every view with #require so production builds include its @AJAX handlers
  • β€’ $_GET['page'] is reserved for the route: paginate with ?p=
  • β€’ App variables are not visible inside views: share data through backend/, $_SESSION, define() or $GLOBALS

Template Blocks

PHPue components are structured using familiar blocks for different concerns.

<script>
// PHP logic here
$title = "Hello World";
$users = $db->query("SELECT * FROM users")->fetchAll();
</script>

<header>
    <title>Hello World | My PHPue App</title>
    <meta name="description" content="My PHPue app">
</header>

<template>
    <!-- Your HTML template -->
    <h1>{{ $title }}</h1>
    <div p-for="$user in $users">
        {{ $user['name'] }}
    </div>
</template>

<cscript>
// Client-side JavaScript (optional)
console.log('Component mounted');
</cscript>

The Four Blocks

  • <script> - PHP, runs on the server (not JavaScript)
  • <header> - Head tags: title, meta, CSS
  • <template> - HTML with directives and {{ }}
  • <cscript> - JavaScript, runs in the browser

Good to Know

  • β€’ Each block is used at most once, in this order
  • β€’ A view's <header> is static: PHP and {{ }} don't run there (App.pvue's header can print variables)
  • β€’ Block names inside comments or strings no longer break a file 0.0.3

Directives

Use Vue-like directives for common tasks. All processing happens on the server.

p-if

<div p-if="$user->isAdmin">Admin Content</div>

Conditionally render elements based on PHP expressions. There is no p-else: use a second p-if with the opposite condition, or plain PHP.

p-for

<li p-for="$item in $items"></li>

Loop through arrays and objects to render lists. Use $key => $value in $items when you need the key.

What Directives Can Do Now 0.0.3

<!-- Loop over a function call, then filter each item -->
<li p-for="$n in range(1, 5)" p-if="$n % 2">{{ $n }}</li>

<!-- Same-tag children, all on one line -->
<div p-for="$row in $rows"><div class="cell">{{ $row }}</div></div>

<!-- {{ }} in attributes, and void elements -->
<span p-if="$bNew" class="badge {{ $tone }}">New</span>
<input p-if="$bEdit" name="title" value="{{ $title }}">
  • β€’ One line or many: no more special line breaks around p-if
  • β€’ Loop over arrays, function calls or database results (PDOStatement). Anything else renders nothing
  • β€’ p-for + p-if on one element: loops first, then checks each item
  • β€’ Always close the element, or only its opening tag is wrapped

{{ }} Output

<p>{{ $user['name'] }} has {{ count($orders) }} orders</p>
<p>{{ $bVip ? 'VIP' : 'Member' }}</p>
  • β€’ Prints a PHP expression, always escaped. For trusted HTML use <?= $html ?>
  • β€’ Starts with $ or, since 0.0.3, a function call like count($orders)
  • β€’ Use PHP syntax: $user['name'], never $user.name
  • β€’ Anything else ({{ item.name }}) is left alone for AlpineJS

Form Handling Philosophy

Privacy-First Form Design

PHPue respects user privacy by avoiding automatic form tracking. Instead of two-way data binding that monitors every keystroke, we encourage explicit form submissions with clear user intent.

// views/contact.pvue - Complete form example
<script>
    @AJAX('POST')
    function ajaxFormSubmit($input) {
        $formData = $input['formData'] ?? [];
        $email = trim($formData['email'] ?? '');

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            echo json_encode(['success' => false, 'message' => 'Please enter a valid email.']);
            return;
        }

        // Store in session to use later
        $_SESSION['subForm_email'] = $email;
        echo json_encode(['success' => true, 'message' => 'Thanks, you are subscribed!']);
    }

    $email = $_SESSION['subForm_email'] ?? '';
</script>

<template>
    <form onsubmit="handleSubmit(event, 'ajaxFormSubmit')">
        <input name="email" type="email" value="{{ $email }}">
        <button type="submit">Submit</button>
        <p id="formMessage"></p>
    </form>
</template>

<cscript>
    async function handleSubmit(event, methodName) {
        event.preventDefault();
        const formData = new FormData(event.target);

        const response = await fetch(window.location.pathname, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                action: methodName,
                formData: Object.fromEntries(formData)
            })
        });

        const result = await response.json();
        document.getElementById('formMessage').textContent = result.message;
    }
</cscript>

πŸ’‘ Note: We use explicit form submission instead of automatic tracking. Users must intentionally click "Submit" - no passive data collection. That's why PHPue has no p-model, by design. Pre-fill fields with value="{{ $email }}". Want live previews while typing? Use AlpineJS x-model in the browser: nothing reaches the server until the user sends it.

Respect user privacy with explicit form handling patterns.

Hot Reload

Instant browser refreshes when you edit your .pvue or PHP files. See changes in real-time without manual reloading.

πŸš€ Quick Start

1

Start Development Server

cd www && php -S localhost:3000
2

Enable Hot Reload

Visit any page with ?live parameter:

localhost:3000/your-page?live
3

Start Coding

Edit any .pvue file and watch the browser refresh automatically! πŸ”₯

🐳 Docker Compose Advantage

Using Docker? Hot reload works even better with full AJAX support!

# Start with Docker (recommended)
docker compose up
# Hot reload is automatically enabled
# AJAX works perfectly with NGINX

⚠️ Important Notes

PHP Built-in Server: Hot reload works. However, AJAX may not function properly
NGINX (Docker): Hot reload + AJAX work perfectly together
Watches: App.pvue, views/ and components/, including nested component folders 0.0.3
Production: Hot reload is automatically disabled once your app is compiled

πŸ’‘ Pro Tip: Use Docker Compose for the best development experience with full hot reload and AJAX support!

AJAX Functions

Create server-side AJAX handlers with simple annotations.

@AJAX('POST')
function saveUser($input) {
    // Handle form submission ($input is the JSON body)
    echo json_encode(['success' => true]);
}

// Browser: call it by name
fetch(window.location.pathname, {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({ action: 'saveUser', name: 'Ada' })
});
@AJAX('GET')
function updateSession() {
    if(isset($_GET['token'])) {
        $_SESSION['userToken'] = $_GET['token'];
    }
    echo json_encode(['saved' => isset($_GET['token'])]);
}

// Browser: fetch('?action=updateSession&token=abc123')

How It Works

Put @AJAX('POST') or @AJAX('GET') right above a function in a view, App.pvue or a component
The action field picks the function; POST data arrives as $input
Echo your response (usually json_encode): return values are discarded
Function names are global, so keep them unique across your app

Safer in 0.0.3 0.0.3

Only @AJAX functions can be called. Other functions (and ?action=phpinfo) just load the page
The declared method is enforced: a GET to a POST handler gets 405
Double quotes, typed parameters and return types all work
Handlers run before App.pvue, so read shared data from backend/ or the session, not App variables

HTTP Error Pages

PHPue automatically handles 404 errors with support of customisable error pages. Create beautifully styled 404 pages that match your application's design.

(Optional) File Structure for 404 Pages

# Create these files in your httpReqs directory
httpReqs/
http404.php (Optional or will fall back to Framework 404 Handler
http404Head.php (Optional or will fall back to Framework 404 Header)

Current Support: Only 404 pages are supported in this version.

Example: Custom 404 Page

Create a user-friendly 404 page that helps users navigate back to your application:

httpReqs/http404Head.php
<?php
    global $phpue_header;
    $phpue_header = <<<HTML
        <script src="https://cdn.tailwindcss.com"></script>
        <title>404 - Page Not Found</title>
        <meta name="description" content="The page you're looking for doesn't exist">
        <meta name="keywords" content="404, page not found">
        <meta name="author" content="Edward Patch">
    HTML;

    $GLOBALS['phpue_http404_header'] = $phpue_header;
?>
httpReqs/http404.php
<!-- APP WRAPPER -->
<div class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 flex items-center justify-center p-4">
    <div class="max-w-md w-full text-center">
        <div class="text-6xl mb-4">πŸ”</div>
        <h1 class="text-2xl font-bold text-gray-900 mb-2">Page Not Found</h1>
        <p class="text-gray-600 mb-6">
            Sorry, we couldn't find the page you're looking for. 
            The page might have been moved or deleted.
        </p>
        <div class="space-y-3">
            <a href="/" class="block w-full bg-emerald-600 hover:bg-emerald-700 text-white font-medium py-3 px-6 rounded-lg transition duration-200">
                Return to Homepage
            </a>
            <button onclick="history.back()" class="w-full border border-gray-300 hover:bg-gray-50 text-gray-700 font-medium py-3 px-6 rounded-lg transition duration-200">
                Go Back
            </button>
        </div>
    </div>
</div>
<!-- END OF APP WRAPPER -->
Live Preview
πŸ”

Page Not Found

Sorry, we couldn't find the page you're looking for. The page might have been moved or deleted.

Best Practices for 404 Pages

Keep it simple: Clear messaging with minimal technical jargon
Provide clear actions: Include prominent buttons to go home or back
Use friendly visuals: Add icons or illustrations to soften the error
Search functionality: Consider adding a search box for content discovery
Be helpful: Suggest what the user can do next
Note: Your app's navigation and branding are automatically included!

Backend Directory

Extend PHPue with custom PHP classes and vendor libraries using the backend/ directory. All classes are auto-loaded when the framework starts, making them available throughout your application. PHPue fully supports Composer vendor directories and automatically detects autoload.php files.

Directory Structure

# Backend Directory
backend/
DBLoader.php
AuthManager.php
EmailService.php
vendor/

Auto-loading: All PHP files in backend/ are automatically included when the framework initialises. Vendor directories with autoload.php are fully supported. You can also create a custom autoload.php to control load order.

How Backend Auto-loading Works

1
Framework Initialisation

PHPue scans the backend/ directory on startup, in the same sorted order on every server 0.0.3

2
Auto-include Classes

All .php files in the backend directory are automatically included.

3
Vendor Support

If backend/vendor/autoload.php exists, PHPue automatically requires it, enabling full Composer vendor support (PSR-4, classmap, and files).

4
Optional Autoload Ordering

Create a custom autoload.php in any directory and add your require statements to control class load order. PHPue will load classes in the specified order and skip the brute-force scan for that directory.

5
Ready to Use

Your classes are now available throughout the application via singleton patterns or direct instantiation.

Example: Database Loader Class

Create a singleton database manager that's available throughout your application:

// backend/DBLoader.php

<?php
    class SimpleDB {
        private static ?self $instance = null;

        public static function getInstance(): self
        {
            if(self::$instance === null)
                self::$instance = new self();

            return self::$instance;
        }

        public function getMessage(): string
        {
            return "Hello from backend class!";
        }

        public function getUsers(): array
        {
            return [
                ['id' => 1, 'name' => 'John'],
                ['id' => 2, 'name' => 'Jane'],
            ];
        }
    }
?>
Usage in Components
// In any .pvue file script section
<script>
  $db = SimpleDB::getInstance();
  $users = $db->getUsers();
</script>

Vendor Libraries (Experimental)

PHPue fully supports Composer vendor directories and automatically detects autoload.php. Simple libraries like Parsedown work out of the box. Complex dependencies may still require manual testing or a custom autoload.php.

Current Support Level

βœ… Fully Supported: Composer vendor directories
πŸ”§ Needs Testing: Large multi-package ecosystems

Manual Vendor Setup (Optional)
# Download vendor library manually

cd backend

mkdir -p vendor

wget https://github.com/erusev/parsedown/archive/1.7.4.zip

unzip 1.7.4.zip -d vendor/

# Result: backend/vendor/parsedown-1.7.4/Parsedown.php

PHPue will automatically find and load Parsedown.php or any autoload.php in backend/vendor/.

πŸ’‘
Important Usage Guidance
  • β€’ Load dependencies before initialising singletons
  • β€’ If dependencies are in the same file, call Class::getInstance() immediately
  • β€’ Otherwise, initialise classes in App.pvue, views/, or components/
  • β€’ Use a custom autoload.php to control load order and prevent brute-force scanning

Complex libraries may still require testing, but following this sequence ensures best results.

Best Practices

Use Singleton Pattern for resource-heavy classes
Implement proper exception handling
Use environment variables for sensitive configuration
Organise classes with namespaces
Use PHP 8+ type hints
Include PHPDoc for IDE support

Common Backend Classes

Database Managers
  • β€’ DBLoader - Database connections
  • β€’ UserRepository - User data access
  • β€’ CacheManager - Caching layer
Service Classes
  • β€’ EmailService - Email delivery
  • β€’ AuthManager - Authentication
  • β€’ FileUploader - File handling
Utility Classes
  • β€’ Logger - Application logging
  • β€’ Validator - Data validation
  • β€’ Formatter - Data formatting

Sessions 0.0.3

By default PHPue starts a session on every request, just like 0.0.2. That's simple, but it adds a cookie and a no-store header to every page, so NGINX or a CDN can't cache it. 0.0.3 adds a lazy option.

index.php / any .pvue
// index.php: edit the define near the top
define('PHPUE_SESSION', 'lazy');   // 'auto' (default) or 'lazy'

// Need to write to the session? (login, cart, preferences)
phpue_session(true);               // starts one if needed
$_SESSION['cart'][] = $sku;

Auto vs Lazy

'auto': a session on every request. Easiest; pages can't be cached
'lazy': an existing session is resumed, a new one only starts when you call phpue_session(true). Public pages stay cacheable
@AJAX POST handlers always get a session; GET handlers don't (in lazy mode)
Calling session_start() yourself in App.pvue still turns PHPue's session handling off

Setting <html lang> 0.0.3

The <html lang> attribute is worked out on every request (0.0.2 fixed it at build time). Pick a fixed language per page, or set it in code for a language switcher.

Static: One Language per Page

Put it in the view's <header>. Either form works:

<header>
    <meta name="lang" content="fr">
    <title>Ma Page en FranΓ§ais</title>
</header>

<header>
    <!-- lang: es -->
</header>

Write the meta exactly as name="lang" then content="…", with double quotes. <meta lang="fr"> is not recognised.

Dynamic: A Language Switcher

Call phpue_set_lang() in App.pvue's <script>, or in a backend/ file when it loads:

<!-- App.pvue -->
<script>
    $lang = $_GET['lang'] ?? null;
    if (in_array($lang, ['en', 'cy'], true)) {
        $_SESSION['PHPue_Lang'] = $lang;
    }
    phpue_set_lang($_SESSION['PHPue_Lang'] ?? null);
</script>

null means no override, so the view's meta and PHPUE_LANG still apply. PHPue_Lang is your own session key: PHPue doesn't set one.

Example: A Welsh Contact Page

The rest of the site stays English (PHPUE_LANG is 'en'). This one page sets <html lang="cy"> from its header, no PHP needed:

views/welsh-contact.pvue
<header>
    <meta name="lang" content="cy">
    <title>Cysylltwch Γ’ Ni | PHPue</title>
</header>

<template>
    <h1>Cysylltwch Γ’ ni</h1>
    <p>Anfonwch neges atom a byddwn yn ateb cyn gynted Γ’ phosib.</p>
    <form method="post">
        <label>E-bost <input name="email" type="email"></label>
        <button type="submit">Anfon</button>
    </form>
</template>

Result on /welsh-contact: <html lang="cy">. Every other page keeps <html lang="en">. Remember to #require 'views/welsh-contact.pvue'; in App.pvue.

⚠️ A View's Script Is Too Late

  • β€’ phpue_set_lang() in a view's or component's <script> does nothing: <html lang> is printed before the view renders. Use the view's <header> instead.
  • β€’ phpue_set_lang() sets the language. phpue_html_lang() only reads what will be printed.
  • β€’ 404 pages get the chosen language too, and the value is HTML-escaped.

Order (first match wins):

phpue_set_lang() <meta name="lang"> / <!-- lang: xx --> PHPUE_LANG 'en'

cscript & AlpineJS

PHP renders the page; the browser layer is optional. Use <cscript> for plain JavaScript, and AlpineJS when you want reactivity.

cscript: the data bridge
<cscript>
    const fruits = {{ $fruits }};             // ["Banana","Apple"]
    const theme  = {{ $settings['theme'] }};  // "dark" (0.0.3: any $ expression)
    document.querySelector('#count').textContent = fruits.length;
</cscript>

The Data Bridge

<cscript> becomes a script tag after your markup, so the page is ready when it runs
{{ $anything }} inside it is turned into JSON: arrays and objects arrive ready to use
Don't wrap it in quotes: JSON already adds them
AlpineJS + x-ssr
<!-- App.pvue header -->
<script defer src="/assets/js/fworks/alpine.min.js"></script>
<script src="/assets/js/phpue-ext-csr/alpine.ext.min.ssr.js"></script>

<!-- any view -->
<div x-data="{ count: {{ $initial }} }">
    <button @click="count++">Clicked <span x-text="count">{{ $initial }}</span></button>
</div>

AlpineJS Tips

Put the server value inside x-text elements too, so the page is right before Alpine starts
<template x-for> now compiles 0.0.3, but it builds the list in the browser
Want the list rendered by PHP first (SEO, fast first paint)? Use the x-ssr extension: it takes over the server-rendered items
This site's tutorials browser and Insights filters are built this way. See the AlpineJS tutorial

Configuration & Helpers

Everything you can set in index.php, and the helper functions available in any .pvue file.

Constants (index.php)

  • PHPUE_VERSION - Framework version
  • PHPUE_LANG - Fallback <html lang> (default 'en')
  • PHPUE_SESSION - 'auto' or 'lazy' 0.0.3
  • PHPUE_DEV_TOOLS - Force dev tools on/off 0.0.3

Helpers

  • phpue_navigation() - Every route with its URL and title
  • phpue_current_meta() - The current page's meta (title, lang…)
  • phpue_session($write) - Resume or start a session 0.0.3
  • phpue_set_lang($lang) - Set <html lang> 0.0.3
  • phpue_html_lang() - Reads the language that will be printed (doesn't set it) 0.0.3

Troubleshooting

The most common surprises, and the fix for each.

My .pvue edits don't show up. A .dist/ folder exists, so compiled files are served. Rebuild or delete it
My @AJAX call returns the whole page. The action name doesn't match an @AJAX function, or the view isn't #required in App.pvue. A 405 means the method is wrong
AJAX hangs on the built-in server. ?live keeps the single PHP worker busy. Test without it, or use Docker
A view's header shows $title literally. View headers are static. Move dynamic tags to App.pvue
A file path works in dev but not after building. __DIR__ points into .dist/ once compiled. Use getcwd()
The script block ends early. A closing script tag inside a PHP string ends the block. Split it: '</scr' . 'ipt>'
Pages can't be cached. The automatic session sends no-store headers. Switch to PHPUE_SESSION = 'lazy'