Štěpán Stenchlák, KSI
stepan.stenchlak@matfyz.cuni.cz
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.
Before the start of the practical, you should be able to:
include or require a PHP file.You can use OneCompiler PHP to test yourself before the lecture.
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:
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:
Example events:
| Nov 12, 2025 | Dean's Sports Day | - |
| Nov 27, 2025 | Open Day | Information about studies, lectures, booths, and excursions... |
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
README.md file with relevant information, check .gitignore and make yourself comfortable with Git.Hints:
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.
src - contains all source code
model - contains Modelsview or templates - contains mostly html with little phputils or services - contains helper php files, utils, database wrapper, etc.public - contains .js/.css/img files and index.phpThe 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.
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:
/src/models/EventModel.php file containing a single class EventModel..json database for now (using file_put_contents/file_get_contents).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.
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']);
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.
Instead of nice URLs, use query parameters such as ?page=event/42.
Instead of $_SERVER['REQUEST_URI'] use $_GET['page'].
(Consider integrating your PHP Templator from ReCodEx.)
The idea is to have .php files with minimal PHP code, mostly HTML.
/src/templates/header.php and /src/templates/footer.php files./public/index.php file.style.css) to your /public/ directory and link them to your templates.Front Controller is a single centralized part that does:
Presenter is responsible for handling specific requests:
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.Implement a simple Front Controller that delegates work to an EventPresenter based on the URL /events/{event-id}.
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);
/**
* 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);
}
/**
* 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();
}
}
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.