YWP is ZPE’s server-side webpage format. It lets you place YASS inside ordinary
HTML in much the same way PHP works: HTML is sent to the browser as written, while
code inside <ywp> … </ywp> blocks runs on the server.
ZPE has supported web development through Velocity Web Server since version 1.8.6. From ZPE 1.14.9, YWP can also define an optional router for cleaner application-style URLs. Traditional YWP pages remain fully supported and do not need a router.
A first YWP page
Save the following as index.ywp. It writes HTML normally, then uses
YASS to print a value from the URL and a short number sequence.
<!doctype html>
<html>
<head>
<title>Simple ZPE/YASS webpage</title>
</head>
<body>
<h1>Welcome to ZPE/YASS!</h1>
<ywp>
if (is_set(REQUEST->get('name'))
print("Hello, " & REQUEST->get('name'))
else
print("Hello, visitor")
end if
for($i = 0 to 10)
print($i & "<br>")
end for
</ywp>
</body>
</html>
Opening /index.ywp?name=Jamie displays Hello, Jamie.
The REQUEST object
Every YWP page receives a public REQUEST object. It is the supported
way to read information supplied by the browser. Internal variables beginning with
@@ are deliberately private implementation details and cannot not be
used by webpages. These can be accessed through the REQUEST or RESPONSE
objects.
| Feature | Example | Purpose |
|---|---|---|
| Query-string value | REQUEST->get['q'] |
Reads ?q=value from the URL. |
| Submitted form value | REQUEST->post['email'] |
Reads a parsed POST field. |
| Raw submitted data | REQUEST->post_raw['body'] |
Reads raw POST content where available. |
| Request header | REQUEST->get_header("User-Agent") |
Reads an HTTP request header. |
| Server information | REQUEST->server_data("REQUEST_METHOD") |
Reads server data such as request method and path. |
| Session value | REQUEST->session("user") |
Reads a previously stored session value. |
Reading GET values
<ywp> $query = REQUEST->get('q') if (is_set($query)) print("Searching for: " & $query) else print("Enter a search term.") end if </ywp>
Reading a submitted form
<form method="post">
<label>
Name
<input name="name">
</label>
<button type="submit">Send</button>
</form>
<ywp>
if REQUEST->post('name')
print("Welcome, " & REQUEST->post('name'))
end if
</ywp>
The RESPONSE object
The RESPONSE object controls the HTTP response sent back to the
browser. It can return JSON, set headers, redirect the browser, choose a status
code, and store session values.
| Feature | Example |
|---|---|
| Return JSON | return RESPONSE->json(["name" => "Jamie"]) |
| Set a header | RESPONSE->set_header("Content-Type", "text/plain; charset=utf-8") |
| Read a response header | RESPONSE->get_header("Content-Type") |
| Choose a status code | RESPONSE->set_http_response("404") |
| Redirect | return RESPONSE->redirect("/login.ywp") |
| Store a session value | RESPONSE->set_session_variable("user", $username) |
Returning JSON
<ywp> $pupil = [ "name" => "Jamie", "role" => "Teacher" ] return RESPONSE->json($pupil) </ywp>
Rendering reusable YWP pages
Use render "page.ywp"
to render another YWP page while preserving a copy of the active request,
route variables and page context:
<ywp> render "/partials/navigation.ywp" </ywp>
The requested file must be a .ywp page inside the website's
document root. render is available only while processing a YWP page.
Ordinary include keeps its existing behaviour. Use
include_context when the included YWP page needs access to the
current request, route variables, and other page context.
<ywp>
include_context "/pages/search.ywp"
</ywp>
The included page can then read, for example,
REQUEST->get['q']. Context includes are restricted to YWP files
inside the website’s document root.
Web routing in YASS and ZPE
Routing is optional. A site only becomes a routed site when its root
index.ywp or index.yep contains the
@router annotation. Without it, pages continue to be served in the
traditional way.
Routes are declared with @get, @post,
@put and @delete. Path values
such as {id} are passed directly to the function. Query-string
values are not part of the route pattern; ZPE supplies matching GET or POST values
to function parameters by name.
<ywp> @router @get("/products/{id}") function product($id) return RESPONSE->json([ "id" => $id ]) end function @post("/search") function submitSearch($q) return "Searching for " & $q end function @get("/search") function showSearch($q) include_context "/pages/search.ywp" end function </ywp>
The router above handles the following requests:
/products/42— callsproduct($id)with$idset to42.POST /searchwith a field namedq— callssubmitSearch($q)./search?q=books— callsshowSearch($q)with$qset tobooks.
Route paths match the URL path only. Therefore write
@get("/search"), not @get("/search?q={q}").
Query-string values remain available through REQUEST->get['q']
and can also be injected into a route function parameter with the same name.
Local development server
ZPE includes a lightweight local web server for testing YWP, YEP, HTML, CSS and JavaScript during development. It is intended for local development, not as a replacement for a production HTTPS web server.
zpe -z /path/to/your/site zpe -z /path/to/your/site --port 8080
Start with an index.ywp in the selected directory, then open the
displayed local address in your browser.
Legacy YWP syntax
Current YWP pages should use <ywp> ... </ywp>. The older
<?ywp ... ywp?> delimiters remain supported for existing pages.
The shorter paired tags were made available in ZPE 1.14.9
(Spurriergate, September 2026).

There are no comments on this page.
Comments are welcome and encouraged, including disagreement and critique. However, this is not a space for abuse. Disagreement is welcome; personal attacks, harassment, or hate will be removed instantly. This site reflects personal opinions, not universal truths. If you can’t distinguish between the two, this probably isn’t the place for you. The system temporarily stores IP addresses and browser user agents for the purposes of spam prevention, moderation, and safeguarding. This data is automatically removed after fourteen days. Your email address is stored so that replies can be sent to your email address.
Comments powered by BalfComment