NSWI142: Web Applications

6. PHP Application II.

Štěpán Stenchlák, KSI
stepan.stenchlak@matfyz.cuni.cz

Preliminaries

Please read this section at least one day prior to the seminar. This section outlines what you are expected to know, be able to do, or prepare in advance. Following these instructions will help you get the most out of the seminar and ensure smooth participation.

Preliminaries

Before the start of the practical, you should be able to:

  • Explain the Front Controller design pattern.
  • Explain the Model-View-Presenter design pattern.
  • include or require a PHP file.

You can use OneCompiler PHP to test yourself before the lecture.

Preliminaries software

This slide applies to you only if you plan to use your own computer for the practical.

Before the start of the practical, make sure that:

Agenda

  • Introduction to semestral project
    • Exercise: Git init
    • Exercise: Working with models
    • Exercise: Front controller
    • Exercise: Templates
    • Model-View-Presenter (MVP)

Semestral project: Event management

A website where users manage and browse events. It can be used as a place to publish information about events in a given city (e.g., gatherings, concerts, competitions, new openings, etc.)

Formal specification is still work in progress.

Key features important for today's labs:

  • An Event has a title and a description.
  • You can find event details by its ID. (Meaning that for each event, there is a dedicated page with all information about it.)
  • You can list all events.

Example events:

Nov 12, 2025 Dean's Sports Day -
Nov 27, 2025 Open Day Information about studies, lectures, booths, and excursions...

Exercise: Git init

Use Git to track your work on the semestral project.

You will be required to use the following GitLab repository:

https://gitlab.mff.cuni.cz/teaching/nswi142/2025-26/name-surname
git@gitlab.mff.cuni.cz:teaching/nswi142/2025-26/name-surname.git
  • Update a README.md file with relevant information, check .gitignore and make yourself comfortable with Git.

Hints:

  • Use your SSH key to sync changes without typing a password.
  • You can commit and push directly from the VS Code interface.

Model-View-Presenter and code project structure

There are other ways to do it, this is just one opinion! You are not required to have exactly this structure, but you are required to have some structure.

  • root directory of your project
    • src - contains all source code
      • model - contains Models
      • view or templates - contains mostly html with little php
      • utils or services - contains helper php files, utils, database wrapper, etc.
      • ...
    • public - contains .js/.css/img files and index.php

The project root should not be accessible. Users should be able to see only the contents of the public directory.

See the next slides for more information.

Exercise: Your first model

The idea is to have a "black-box" that can manage events.

What do we mean by manage? Create, Read, Update, Delete, Find, List

What do we mean by "black-box"? Encapsulation: PHP class with well-defined methods.

Tip: It is a good practice to have numerical unique IDs.

Steps:

  • Create the /src/models/EventModel.php file containing a single class EventModel.
  • Implement functionality for creating an event and retrieving an event by its id.
  • Use a .json database for now (using file_put_contents/file_get_contents).

Exercise: Front Controller

The idea is to have a single file that will handle all requests.

Implement a /index.php file that prints (using var_dump is fine for now) event details based on the URL. For example, /events/{event-id} should show details for an event with the specified id.

We need to properly configure our web server. See the next slides.

Update your README.md with instructions on how to run your server locally.

Not required right now: Add .htaccess files for Apache.

Configuring PHP built-in server

Static files (e.g., .css, .js) are served from the /public/ directory. Everything else is passed to /index.php. You can still access files in ../ via require/include.


php -S 127.0.0.1:8888 -t ./public/

To test it, you can try the following script:


<?php
  var_dump($_SERVER['REQUEST_URI']);

Configuring Apache

The Apache server must be configured to allow for use of .htaccess and rewrites. webik.ms.mff.cuni.cz is configured this way. We need two files to make it work.

The first /.htaccess file is located in the src of your project. It passes all requests to the public directory.


RewriteEngine on
RewriteRule ^$ public/ [L]
RewriteRule (.*) public/$1 [L]

The second /public/.htaccess file is in the public directory. It redirects requests to /public/index.php if the requested file does not exist.


RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

Keep in mind that files starting with a dot (for example .htaccess) are hidden by default on some systems.

If nothing works... 💀

Instead of nice URLs, use query parameters such as ?page=event/42.

Instead of $_SERVER['REQUEST_URI'] use $_GET['page'].

Exercise: Templates and static files

(Consider integrating your PHP Templator from ReCodEx.)

The idea is to have .php files with minimal PHP code, mostly HTML.

  • Implement /src/templates/header.php and /src/templates/footer.php files.
  • Integrate them to your /public/index.php file.
  • Add static files (for example style.css) to your /public/ directory and link them to your templates.

Front Controller vs. Presenters

Front Controller is a single centralized part that does:

  • Request handling: Parses URL and request methods.
  • Routing: Based on URL, calls appropriate Presenter and delegates the work.

Presenter is responsible for handling specific requests:

  • Interacts with Models to get data.
  • Prepares data for the View (template).
  • Renders the View.

Project structure:

    /public/index.php usually only calls the Front Controller and does not contain much logic.
    /src/FrontController.php contains the FrontController class.
    /src/presenters/ contains Presenter classes that handle specific requests.

Exercise: Building better Front Controller with Presenters

Implement a simple Front Controller that delegates work to an EventPresenter based on the URL /events/{event-id}.

Final code example 1/3

src/Container.php

<?php
/**
 * Simple services container. Provides access to services such as Database.
 */
class Container
{
  public function getDatabase() {}
}

/public/index.php

<?php
$fc = new FrontController();

$container = new Container();
$fc->injectContainer($container);

$fc->routeAndDispatch($_SERVER);

Final code example 2/3

/**
 * This exception is thrown when a requested resource is not found.
 */
class NotFoundException extends Exception {}

interface Presenter
{
  /**
   * @param $url array URL chunks after the presenter name
   * @param $method string HTTP method (e.g., 'GET', 'POST')
   * @param $data mixed Data sent with the request (e.g., $_POST)
   * @throws NotFoundException
   */
  function process(array $url, string $method, mixed $data);

  /**
   * Renders the template with data prepared in process().
   */
  function render();

  /**
   * Injects the container with access to services such as Database.
   */
  function injectContainer(Container $container);
}

Final code example 3/3

/**
 * FrontController is responsible for routing requests to appropriate presenters.
 */
class FrontController
{
  private $container;

  /**
   * Injects the container with access to services such as Database.
   */
  public function injectContainer(Container $container)
  {
    $this->container = $container;
  }

  public function routeAndDispatch($serverData)
  {
    $chunks = explode('/', $serverData['REQUEST_URI']);
    $presenter = $this->route($chunks[1]);
    $this->dispatch($presenter, array_slice($chunks, 2), $serverData['REQUEST_METHOD'], $_POST);
  }

  private function route($chunk)
  {
    switch ($chunk) {
      case 'home':
        $presenter = new HomePresenter();
        break;
      case 'about':
        $presenter = new AboutPresenter();
        break;
      default:
        $presenter = new NotFoundPresenter();
        break;
    }
    $presenter->injectContainer($this->container);
    return $presenter;
  }

  private function dispatch($presenter, array $chunks, $method, $data)
  {
    try {
      $presenter->process($chunks, $method, $data);
    } catch (NotFoundException $e) {
      $presenter = new NotFoundPresenter();
      $presenter->injectContainer($this->container);
      $presenter->process([], 'GET', null);
    } catch (Exception $e) {
      http_response_code(500);
      die("Internal server error.");
    }

    $presenter->render();
  }
}

Next steps

Create a template for a 404 page.

Implement an ErrorPresenter that is called when the Front Controller cannot find an appropriate Presenter. Don't forget to return a 404 status code.

Implement support for the /events URL, returning a list of events.

Questions, ideas, or any other feedback?

Please feel free to use the anonymous feedback form.