Jamie Balfour

Welcome to my personal website.

Find out more about me, my personal projects, reviews, courses and much more here.

Official ZPE/YASS documentationZPE & Web

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.

YWP
<!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
<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

HTML and YWP
<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
<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
<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
<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 — index.ywp
<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 — calls product($id) with $id set to 42.
  • POST /search with a field named q — calls submitSearch($q).
  • /search?q=books — calls showSearch($q) with $q set to books.

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.

Terminal

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).

Comments

There are no comments on this page.

New comment

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

Feedback 👍
Comments are sent via email to me.