Your First Page
Drop a file, get a URL. The filesystem is the router.
You will learn
- Create a page by adding a file to public/
- How implicit routing maps files to URLs
- Add dynamic content with PHP
- Handle query parameters
The problem
In most frameworks, adding a new page means editing a routing table, creating a controller, registering it somewhere, and maybe writing a view. That's a lot of ceremony for "show some HTML."
ZealPHP takes a different approach: the filesystem is the router.
Step 1: Create a file
Create a file at public/greeting.php:
<?php
echo "<h1>Hello, World!</h1>";
echo "<p>This page was served by ZealPHP.</p>";
Step 2: Visit the URL
Open http://localhost:8080/greeting in your browser. That's it — no routing
config, no controller registration. ZealPHP saw a file at public/greeting.php and
mapped it to GET /greeting automatically.
How implicit routing works
Think of the public/ folder as a filing cabinet. Folders are drawers,
files are documents. The URL is the path you'd describe to find a document:
public/index.php → GET /
public/about.php → GET /about
public/blog/post.php → GET /blog/post
public/css/site.css → GET /css/site.css (static files too)
No configuration. No routing table. The file's location is its URL. If you rename the file, the URL changes. If you delete the file, the URL returns 404.
Adding dynamic content
These are PHP files, so you can use any PHP you want. Let's make the greeting personal:
<?php
$name = htmlspecialchars($_GET['name'] ?? 'World');
echo "<h1>Hello, {$name}!</h1>";
echo "<p>The time is " . date('H:i:s') . "</p>";
Visit /greeting?name=Alice and the page greets Alice by name.
This very site uses implicit routing. The page you're reading right now is served from public/learn.php. Every page in the docs — /routing, /streaming, /ws — is a file in public/.
Try it: Open the greeting demo →
What about messy URLs?
"But I don't want .php in my URLs!" — you won't see them. ZealPHP strips the
extension automatically. public/about.php responds at /about, not
/about.php. Requesting /about.php directly returns 403.
Using a layout
Right now, your page outputs raw HTML with no <head>, no stylesheet, no navigation.
Every real page needs a shared layout. The next lesson teaches you how to wrap pages in a layout
template using App::render().
Here's a preview — this is how most pages in ZealPHP apps look:
<?php use ZealPHP\App;
App::render('/_master', [
'title' => 'About',
'page' => 'about',
]);
That's the entire file. Three lines. The master template handles the HTML shell, nav, footer, and CSS — your page template only has the content.
If you create a file at public/blog/post.php, what URL will serve it?
Key Takeaways
- Files in
public/automatically become URLs — no routing config needed - The URL mirrors the file path:
public/blog/post.php→/blog/post - Use
$_GETfor query parameters, standard PHP for dynamic content .phpextensions are stripped — clean URLs by default