changed dir structure to handle second version of the book
This commit is contained in:
161
Vanilla/src/Controller/AddressBookAdminController.php
Normal file
161
Vanilla/src/Controller/AddressBookAdminController.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Service\Router;
|
||||
use App\Service\Template;
|
||||
use App\Repository\UserRepository;
|
||||
|
||||
class AddressBookAdminController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Template $template,
|
||||
private readonly User $user,
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly Router $router
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
private function adminCheck(): void
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
$this->template->render(templateName: 'status/403.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function admin(): never
|
||||
{
|
||||
$this->adminCheck();
|
||||
$this->template->render(templateName: 'admin/index.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function adminUser(): never
|
||||
{
|
||||
$this->adminCheck();
|
||||
|
||||
$users = $this->userRepository->findAll();
|
||||
|
||||
$this->template->render(templateName: 'admin/users.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'users' => $users,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
public function adminUserEdit(array $parameters): never
|
||||
{
|
||||
$this->adminCheck();
|
||||
|
||||
if (!empty($_POST)) {
|
||||
if (!empty($_POST['is_admin'])) {
|
||||
$isAdmin = 1;
|
||||
} else {
|
||||
$isAdmin = 0;
|
||||
}
|
||||
|
||||
if (empty($_POST['new_password'])) {
|
||||
$current = $this->userRepository->findByID(id: $_POST['id']);
|
||||
$password = $current->getPassword();
|
||||
$updateUser = new User(nick: $_POST['nick'], password: $password, first: $_POST['first'], last: $_POST['last'], id: $_POST['id'], isAdmin: $isAdmin);
|
||||
} else {
|
||||
$password = $_POST['new_password'];
|
||||
$updateUser = new User(nick: $_POST['nick'], newPassword: $password, first: $_POST['first'], last: $_POST['last'], id: $_POST['id'], isAdmin: $isAdmin);
|
||||
}
|
||||
|
||||
$this->userRepository->update(user: $updateUser);
|
||||
|
||||
$users = $this->userRepository->findAll();
|
||||
|
||||
$this->template->render(templateName: 'admin/users.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'users' => $users,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
$editUser = $this->userRepository->findByNick(nick: $parameters['nick']);
|
||||
|
||||
|
||||
$this->template->render(templateName: 'admin/users_edit.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'editUser' => $editUser,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
public function adminUserAdd(): never
|
||||
{
|
||||
$this->adminCheck();
|
||||
|
||||
$nick = $_POST['nick'];
|
||||
|
||||
if ($this->userRepository->findByNick(nick: $nick)) {
|
||||
die("User: $nick already exists");
|
||||
}
|
||||
if (!empty($_POST)) {
|
||||
$isAdmin = empty($_POST['is_admin']) ? 0 : 1;
|
||||
$user = new User(nick: $_POST['nick'], newPassword: $_POST['new_password'], first: $_POST['first'], last: $_POST['last'], isAdmin: $isAdmin);
|
||||
|
||||
if ($this->userRepository->insert(user: $user)) {
|
||||
$users = $this->userRepository->findAll();
|
||||
|
||||
$this->template->render(templateName: 'admin/users.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'users' => $users,
|
||||
'router' => $this->router
|
||||
]);
|
||||
} else {
|
||||
die("Error inserting user");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->template->render(templateName: 'admin/users_add.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
public function adminUserDelete(array $parameters): never
|
||||
{
|
||||
$this->adminCheck();
|
||||
|
||||
$nick = $parameters['nick'];
|
||||
if ($user = $this->userRepository->findByNick(nick: $nick)) {
|
||||
if ($this->userRepository->delete(user: $user)) {
|
||||
$users = $this->userRepository->findAll();
|
||||
|
||||
$this->template->render(templateName: 'admin/users.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'users' => $users,
|
||||
'router' => $this->router
|
||||
]);
|
||||
} else {
|
||||
die("Error deleting user");
|
||||
}
|
||||
} else {
|
||||
$this->template->render(templateName: 'status/404.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
133
Vanilla/src/Controller/AddressBookController.php
Normal file
133
Vanilla/src/Controller/AddressBookController.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Entity\AddressBookEntry;
|
||||
use App\Enums\StatusCode;
|
||||
use App\Enums\UserAuth;
|
||||
use App\Service\Router;
|
||||
use App\Service\Template;
|
||||
use App\Repository\AddressRepository;
|
||||
|
||||
class AddressBookController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Template $template,
|
||||
private readonly User $user,
|
||||
private readonly AddressRepository $addressRepository,
|
||||
private readonly Router $router
|
||||
)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
public function main(): never
|
||||
{
|
||||
if ($this->user->getAuth() != UserAuth::AUTH_ANONYMOUS) {
|
||||
$addresses = $this->addressRepository->findAll();
|
||||
}
|
||||
|
||||
$this->template->render(templateName: 'index.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router,
|
||||
'addresses' => $addresses ?? []
|
||||
]);
|
||||
}
|
||||
|
||||
public function addAddress(): never
|
||||
{
|
||||
if (!empty($_POST)) {
|
||||
$address = new AddressBookEntry(owner: $_POST['owner'], first: $_POST['first'], last: $_POST['last'], street: $_POST['street'], zip: $_POST['zip'], city: $_POST['city'], phone: $_POST['phone']);
|
||||
|
||||
if ($this->addressRepository->insert(address: $address)) {
|
||||
$addresses = $this->addressRepository->findAll();
|
||||
|
||||
$this->template->render(templateName: 'index.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'addresses' => $addresses,
|
||||
'router' => $this->router
|
||||
]);
|
||||
} else {
|
||||
die("Error inserting user");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->template->render(templateName: 'addressbook/add_address.html.php', vars: [
|
||||
'user' => $this->user,
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function updateAddress(): void
|
||||
{
|
||||
$_POST = json_decode(json: file_get_contents(filename: "php://input"), associative: true);
|
||||
|
||||
if (empty($_POST)) {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($address = new AddressBookEntry(owner: $_POST['owner'], first: $_POST['first'], last: $_POST['last'], street: $_POST['street'], zip: $_POST['zip'], city: $_POST['city'], phone: $_POST['phone'], id: $_POST['id'])) {
|
||||
if ($this->addressRepository->update(address: $address)) {
|
||||
$status = 200;
|
||||
$message = 'OK';
|
||||
} else {
|
||||
$status = 400;
|
||||
$message = 'BAD_REQUEST';
|
||||
}
|
||||
} else {
|
||||
$status = 400;
|
||||
$message = "BAD REQUEST";
|
||||
}
|
||||
|
||||
$this->template->renderJson(results: [
|
||||
'status' => $status,
|
||||
'message' => $message
|
||||
]);
|
||||
}
|
||||
|
||||
public function deleteAddress(): void
|
||||
{
|
||||
$_POST = json_decode(json: file_get_contents(filename: "php://input"), associative: true);
|
||||
|
||||
if (empty($_POST)) {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($address = $this->addressRepository->findByID(id: $_POST['id'])) {
|
||||
if ($this->addressRepository->delete(addressBookEntry: $address)) {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 200,
|
||||
'message' => 'OK'
|
||||
]);
|
||||
} else {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
|
||||
}
|
||||
} else {
|
||||
$this->template->renderJson(results: [
|
||||
'status' => 400,
|
||||
'message' => 'BAD REQUEST'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
65
Vanilla/src/Controller/SecurityController.php
Normal file
65
Vanilla/src/Controller/SecurityController.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Service\Router;
|
||||
use App\Service\Template;
|
||||
|
||||
class SecurityController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Template $template,
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly Router $router
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function login(): never
|
||||
{
|
||||
if (!empty($_POST)) {
|
||||
$nick = $_POST['nick'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($nick && $password) {
|
||||
$nick = strtolower(string: $nick);
|
||||
if ($user = $this->userRepository->findbyNick(nick: $nick)) {
|
||||
if (password_verify(password: $password, hash: $user->getPassword())) {
|
||||
$_SESSION['user_id'] = $user->getId();
|
||||
header(header: 'Location: /');
|
||||
exit(0);
|
||||
} else {
|
||||
$message = "Wrong credentials.";
|
||||
}
|
||||
} else {
|
||||
$message = "User not found.";
|
||||
}
|
||||
} else {
|
||||
$message = 'You need to enter your credentials.';
|
||||
}
|
||||
}
|
||||
|
||||
$this->template->render(templateName: 'security/login.html.php', vars: [
|
||||
'user' => $user ?? new User(),
|
||||
'message' => $message ?? '',
|
||||
'router' => $this->router
|
||||
]);
|
||||
}
|
||||
|
||||
function logout(): void
|
||||
{
|
||||
session_unset();
|
||||
header(header: 'Location: /');
|
||||
}
|
||||
|
||||
}
|
107
Vanilla/src/Entity/AddressBookEntry.php
Normal file
107
Vanilla/src/Entity/AddressBookEntry.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
class AddressBookEntry
|
||||
{
|
||||
public function __construct(
|
||||
private int $owner,
|
||||
private string $first,
|
||||
private string $last,
|
||||
private string $street,
|
||||
private string $zip,
|
||||
private string $city,
|
||||
private string $phone,
|
||||
private int $id = 0,
|
||||
)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
public function getOwner(): int
|
||||
{
|
||||
return $this->owner;
|
||||
}
|
||||
|
||||
public function setOwner(int $owner): void
|
||||
{
|
||||
$this->owner = $owner;
|
||||
}
|
||||
|
||||
public function getStreet(): string
|
||||
{
|
||||
return $this->street;
|
||||
}
|
||||
|
||||
public function setStreet(string $street): void
|
||||
{
|
||||
$this->street = $street;
|
||||
}
|
||||
|
||||
public function getZip(): string
|
||||
{
|
||||
return $this->zip;
|
||||
}
|
||||
|
||||
public function setZip(string $zip): void
|
||||
{
|
||||
$this->zip = $zip;
|
||||
}
|
||||
|
||||
public function getCity(): string
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
public function setCity(string $city): void
|
||||
{
|
||||
$this->city = $city;
|
||||
}
|
||||
|
||||
public function getPhone(): string
|
||||
{
|
||||
return $this->phone;
|
||||
}
|
||||
|
||||
public function setPhone(string $phone): void
|
||||
{
|
||||
$this->phone = $phone;
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(int $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getFirst(): string
|
||||
{
|
||||
return $this->first;
|
||||
}
|
||||
|
||||
public function setFirst(string $first): void
|
||||
{
|
||||
$this->first = $first;
|
||||
}
|
||||
|
||||
public function getLast(): string
|
||||
{
|
||||
return $this->last;
|
||||
}
|
||||
|
||||
public function setLast(string $last): void
|
||||
{
|
||||
$this->last = $last;
|
||||
}
|
||||
}
|
77
Vanilla/src/Entity/Route.php
Normal file
77
Vanilla/src/Entity/Route.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Closure;
|
||||
|
||||
class Route
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private string $route,
|
||||
private string $regEx,
|
||||
private array $parameters,
|
||||
private Closure $callback
|
||||
)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getRoute(): string
|
||||
{
|
||||
return $this->route;
|
||||
}
|
||||
|
||||
public function setRoute(string $route): void
|
||||
{
|
||||
$this->route = $route;
|
||||
}
|
||||
|
||||
public function getRegEx(): string
|
||||
{
|
||||
return $this->regEx;
|
||||
}
|
||||
|
||||
public function setRegEx(string $regEx): void
|
||||
{
|
||||
$this->regEx = $regEx;
|
||||
}
|
||||
|
||||
public function getParameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function setParameters(array $parameters): void
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
public function getCallback(): Closure
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
public function setCallback(Closure $callback): void
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
}
|
114
Vanilla/src/Entity/User.php
Normal file
114
Vanilla/src/Entity/User.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Enums\UserAuth;
|
||||
|
||||
class User
|
||||
{
|
||||
public function __construct(
|
||||
private string $nick = '',
|
||||
private string $password = '',
|
||||
private readonly string $newPassword = '',
|
||||
private string $first = '',
|
||||
private string $last = '',
|
||||
private int $id = 0,
|
||||
private bool $isAdmin = false,
|
||||
private UserAuth $userAuth = UserAuth::AUTH_ANONYMOUS
|
||||
)
|
||||
{
|
||||
if (!empty($this->newPassword)) {
|
||||
echo "password";
|
||||
$this->password = password_hash(password: $this->newPassword, algo: PASSWORD_ARGON2I);
|
||||
}
|
||||
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
// ANONYMOUS has id 0
|
||||
if ($this->id != 0) {
|
||||
if ($this->isAdmin) {
|
||||
$this->userAuth = UserAuth::AUTH_ADMIN;
|
||||
} else {
|
||||
$this->userAuth = UserAuth::AUTH_USER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getNick(): string
|
||||
{
|
||||
return $this->nick;
|
||||
}
|
||||
|
||||
public function setNick(string $nick): void
|
||||
{
|
||||
$this->nick = $nick;
|
||||
}
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function setPassword(string $password): void
|
||||
{
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
public function getFirst(): string
|
||||
{
|
||||
return $this->first;
|
||||
}
|
||||
|
||||
public function setFirst(string $first): void
|
||||
{
|
||||
$this->first = $first;
|
||||
}
|
||||
|
||||
public function getLast(): string
|
||||
{
|
||||
return $this->last;
|
||||
}
|
||||
|
||||
public function setLast(string $last): void
|
||||
{
|
||||
$this->last = $last;
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(int $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return $this->isAdmin;
|
||||
}
|
||||
|
||||
public function setIsAdmin(bool $isAdmin): void
|
||||
{
|
||||
$this->isAdmin = $isAdmin;
|
||||
}
|
||||
|
||||
public function getAuth(): UserAuth
|
||||
{
|
||||
return $this->userAuth;
|
||||
}
|
||||
|
||||
public function setAuth(UserAuth $userAuth): void
|
||||
{
|
||||
$this->userAuth = $userAuth;
|
||||
}
|
||||
|
||||
}
|
17
Vanilla/src/Enums/UserAuth.php
Normal file
17
Vanilla/src/Enums/UserAuth.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum UserAuth
|
||||
{
|
||||
case AUTH_ANONYMOUS;
|
||||
case AUTH_USER;
|
||||
case AUTH_ADMIN;
|
||||
}
|
174
Vanilla/src/Repository/AddressRepository.php
Normal file
174
Vanilla/src/Repository/AddressRepository.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\AddressBookEntry;
|
||||
use App\Service\DatabaseConnection;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Handles CRUD of Addresses class.
|
||||
*/
|
||||
class AddressRepository
|
||||
{
|
||||
public function __construct(private readonly DatabaseConnection $databaseConnection)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
public function findAll(string $orderBy = 'last'): array
|
||||
{
|
||||
$sql = "
|
||||
SELECT id, owner, first, last, street, zip, city, phone
|
||||
FROM " . DatabaseConnection::TABLE_ADDRESSES . "
|
||||
ORDER BY :order";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: ':order', var: $orderBy);
|
||||
|
||||
$statement->execute();
|
||||
$addresses = [];
|
||||
while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
$address = new AddressBookEntry(
|
||||
owner: htmlspecialchars(string: $result['owner']),
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
street: htmlspecialchars(string: $result['street']),
|
||||
zip: htmlspecialchars(string: $result['zip']),
|
||||
city: htmlspecialchars(string: $result['city']),
|
||||
phone: htmlspecialchars(string: $result['phone']),
|
||||
id: htmlspecialchars(string: $result['id']));
|
||||
$addresses[] = $address;
|
||||
}
|
||||
return $addresses;
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function findByID(int $id): ?AddressBookEntry
|
||||
{
|
||||
$sql = "
|
||||
SELECT id, owner, first, last, street, zip, city, phone
|
||||
FROM " . DatabaseConnection::TABLE_ADDRESSES . "
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: ':id', var: $id);
|
||||
$statement->execute();
|
||||
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
return new AddressBookEntry(
|
||||
owner: htmlspecialchars(string: $result['owner']),
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
street: htmlspecialchars(string: $result['street']),
|
||||
zip: htmlspecialchars(string: $result['zip']),
|
||||
city: htmlspecialchars(string: $result['city']),
|
||||
phone: htmlspecialchars(string: $result['phone']),
|
||||
id: htmlspecialchars(string: $result['id']));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function insert(AddressBookEntry $address): bool|string
|
||||
{
|
||||
$sql = "
|
||||
INSERT INTO " . DatabaseConnection::TABLE_ADDRESSES . " (owner, first, last, city, zip, street, phone)
|
||||
VALUES (:owner, :first, :last, :city, :zip, :street, :phone)";
|
||||
|
||||
try {
|
||||
$owner = $address->getOwner();
|
||||
$first = $address->getFirst();
|
||||
$last = $address->getLast();
|
||||
$city = $address->getCity();
|
||||
$zip = $address->getZip();
|
||||
$street = $address->getStreet();
|
||||
$phone = $address->getPhone();
|
||||
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: ':owner', var: $owner);
|
||||
$statement->bindParam(param: ':first', var: $first);
|
||||
$statement->bindParam(param: ':last', var: $last);
|
||||
$statement->bindParam(param: ':city', var: $city);
|
||||
$statement->bindParam(param: ':zip', var: $zip);
|
||||
$statement->bindParam(param: ':street', var: $street);
|
||||
$statement->bindParam(param: ':phone', var: $phone);
|
||||
$statement->execute();
|
||||
|
||||
return $this->databaseConnection->getConnection()->lastInsertId();
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function update(AddressBookEntry $address): bool|int
|
||||
{
|
||||
$id = $address->getId();
|
||||
$first = $address->getFirst();
|
||||
$last = $address->getLast();
|
||||
$street = $address->getStreet();
|
||||
$zip = $address->getZip();
|
||||
$city = $address->getCity();
|
||||
$phone = $address->getPhone();
|
||||
|
||||
$sql = "
|
||||
UPDATE " . DatabaseConnection::TABLE_ADDRESSES . " SET
|
||||
first = :first,
|
||||
last = :last,
|
||||
street = :street,
|
||||
zip = :zip,
|
||||
city = :city,
|
||||
phone = :phone
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: 'id', var: $id);
|
||||
$statement->bindParam(param: 'first', var: $first);
|
||||
$statement->bindParam(param: 'last', var: $last);
|
||||
$statement->bindParam(param: 'street', var: $street);
|
||||
$statement->bindParam(param: 'zip', var: $zip);
|
||||
$statement->bindParam(param: 'city', var: $city);
|
||||
$statement->bindParam(param: 'phone', var: $phone);
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function delete(AddressBookEntry $addressBookEntry): int
|
||||
{
|
||||
$sql = "
|
||||
DELETE FROM " . DatabaseConnection::TABLE_ADDRESSES . "
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$id = $addressBookEntry->getId();
|
||||
$statement->bindParam(param: 'id', var: $id);
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
196
Vanilla/src/Repository/UserRepository.php
Normal file
196
Vanilla/src/Repository/UserRepository.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Service\DatabaseConnection;
|
||||
use App\Entity\User;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Handles CRUD of User class.
|
||||
*/
|
||||
class UserRepository
|
||||
{
|
||||
public function __construct(private readonly DatabaseConnection $databaseConnection)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
public function findAll(string $orderBy = 'nick'): array
|
||||
{
|
||||
$users = [];
|
||||
$sql = "
|
||||
SELECT id, nick, password, first, last, is_admin
|
||||
FROM " . DatabaseConnection::TABLE_USERS . "
|
||||
ORDER BY :order";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: ':order', var: $orderBy);
|
||||
|
||||
$statement->execute();
|
||||
while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
$user = new User(
|
||||
nick: htmlspecialchars(string: $result['nick']),
|
||||
password: $result['password'],
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
id: $result['id'],
|
||||
isAdmin: $result['is_admin']);
|
||||
$users[] = $user;
|
||||
}
|
||||
return $users;
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function findByID(int $id): ?User
|
||||
{
|
||||
$sql = "
|
||||
SELECT id, nick, password, first, last, is_admin
|
||||
FROM " . DatabaseConnection::TABLE_USERS . "
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: ':id', var: $id);
|
||||
$statement->execute();
|
||||
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
return new User(
|
||||
nick: htmlspecialchars(string: $result['nick']),
|
||||
password: $result['password'],
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
id: $result['id'],
|
||||
isAdmin: $result['is_admin']);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function findByNick(string $nick): ?User
|
||||
{
|
||||
$nick = strtolower(string: $nick);
|
||||
|
||||
$sql = "
|
||||
SELECT id, nick, password, first, last, is_admin
|
||||
FROM " . DatabaseConnection::TABLE_USERS . "
|
||||
WHERE nick = :nick";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: ':nick', var: $nick);
|
||||
$statement->execute();
|
||||
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
|
||||
return new User(
|
||||
nick: htmlspecialchars(string: $result['nick']),
|
||||
password: $result['password'],
|
||||
first: htmlspecialchars(string: $result['first']),
|
||||
last: htmlspecialchars(string: $result['last']),
|
||||
id: $result['id'],
|
||||
isAdmin: $result['is_admin']);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function insert(User $user): bool|string
|
||||
{
|
||||
$sql = "
|
||||
INSERT INTO " . DatabaseConnection::TABLE_USERS . " (nick, password, first, last, is_admin)
|
||||
VALUES (:nick, :password, :first, :last, :is_admin)";
|
||||
|
||||
try {
|
||||
$nick = $user->getNick();
|
||||
$password = $user->getPassword();
|
||||
$first = $user->getFirst();
|
||||
$last = $user->getLast();
|
||||
$isAdmin = $user->isAdmin() ? 1 : 0;
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: ':nick', var: $nick);
|
||||
$statement->bindParam(param: ':password', var: $password);
|
||||
$statement->bindParam(param: ':first', var: $first);
|
||||
$statement->bindParam(param: ':last', var: $last);
|
||||
$statement->bindParam(param: ':is_admin', var: $isAdmin);
|
||||
$statement->execute();
|
||||
|
||||
return $this->databaseConnection->getConnection()->lastInsertId();
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function update(User $user): bool
|
||||
{
|
||||
$id = $user->getId();
|
||||
$nick = $user->getNick();
|
||||
$first = $user->getFirst();
|
||||
$last = $user->getLast();
|
||||
$isAdmin = $user->isAdmin() ? 1 : 0;
|
||||
|
||||
if ($user->getPassword()) {
|
||||
$password = $user->getPassword();
|
||||
} else {
|
||||
$current = $this->findByID(id: $id);
|
||||
$password = $current->getPassword();
|
||||
}
|
||||
|
||||
$sql = "
|
||||
UPDATE " . DatabaseConnection::TABLE_USERS . " SET
|
||||
nick = :nick,
|
||||
password = :password,
|
||||
first = :first,
|
||||
last = :last,
|
||||
is_admin = :is_admin
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$statement->bindParam(param: 'id', var: $id);
|
||||
$statement->bindParam(param: 'nick', var: $nick);
|
||||
$statement->bindParam(param: 'password', var: $password);
|
||||
$statement->bindParam(param: 'first', var: $first);
|
||||
$statement->bindParam(param: 'last', var: $last);
|
||||
$statement->bindParam(param: 'is_admin', var: $isAdmin);
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function delete(User $user): bool
|
||||
{
|
||||
$sql = "
|
||||
DELETE FROM " . DatabaseConnection::TABLE_USERS . "
|
||||
WHERE id = :id";
|
||||
|
||||
try {
|
||||
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
|
||||
$id = $user->getId();
|
||||
$statement->bindParam(param: 'id', var: $id);
|
||||
return $statement->execute();
|
||||
} catch (PDOException $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
43
Vanilla/src/Service/Config.php
Normal file
43
Vanilla/src/Service/Config.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
private array $config;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Check for either config.json.local or config.json.
|
||||
$configFile = dirname(path: __DIR__, levels: 2) . "/config.json.local";
|
||||
if (!file_exists(filename: $configFile)) {
|
||||
$configFile = dirname(path: __DIR__, levels: 2) . "/config.json";
|
||||
}
|
||||
|
||||
if (!file_exists(filename: $configFile)) {
|
||||
die('Missing config file');
|
||||
}
|
||||
$configJSON = file_get_contents(filename: $configFile);
|
||||
|
||||
if (json_decode(json: $configJSON) === null) {
|
||||
die('Config file is not valid JSON.');
|
||||
}
|
||||
|
||||
$this->config = json_decode(json: $configJSON, associative: true);
|
||||
}
|
||||
|
||||
public function getConfig(string $configKey): string
|
||||
{
|
||||
return $this->config[$configKey];
|
||||
}
|
||||
}
|
68
Vanilla/src/Service/Container.php
Normal file
68
Vanilla/src/Service/Container.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Controller\AddressBookAdminController;
|
||||
use App\Controller\AddressBookController;
|
||||
use App\Controller\SecurityController;
|
||||
use App\Entity\User;
|
||||
use App\Repository\AddressRepository;
|
||||
use App\Repository\UserRepository;
|
||||
|
||||
/*
|
||||
* A quick and dirty class container for DI.
|
||||
* Caveat: Classes are always instantiated
|
||||
* No autowiring (yet, maybe later, but it might fit for a demo)
|
||||
*/
|
||||
|
||||
class Container
|
||||
{
|
||||
|
||||
private AddressBookController $addressBook;
|
||||
private AddressBookAdminController $addressBookAdmin;
|
||||
private AddressRepository $addressRepository;
|
||||
private Config $config;
|
||||
private DatabaseConnection $databaseConnection;
|
||||
private Router $router;
|
||||
private SecurityController $securityController;
|
||||
private Template $template;
|
||||
private User $user;
|
||||
private UserRepository $userRepository;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = new Config();
|
||||
$this->databaseConnection = new DatabaseConnection(config: $this->config);
|
||||
$this->template = new Template(templateDir: dirname(path: __DIR__, levels: 2) . '/templates/');
|
||||
$this->router = new Router(template: $this->template);
|
||||
$this->userRepository = new UserRepository(databaseConnection: $this->databaseConnection);
|
||||
$this->addressRepository = new AddressRepository(databaseConnection: $this->databaseConnection);
|
||||
$this->securityController = new SecurityController(template: $this->template, userRepository: $this->userRepository, router: $this->router);
|
||||
if (empty($_SESSION['user_id'])) {
|
||||
$this->user = new User(); // ANONYMOUS
|
||||
} else {
|
||||
$this->user = $this->userRepository->findByID(id: $_SESSION['user_id']);
|
||||
}
|
||||
$this->addressBook = new AddressBookController(template: $this->template, user: $this->user, addressRepository: $this->addressRepository, router: $this->router);
|
||||
$this->addressBookAdmin = new AddressBookAdminController(template: $this->template, user: $this->user, userRepository: $this->userRepository, router: $this->router);
|
||||
}
|
||||
|
||||
public function get(string $className): object
|
||||
{
|
||||
return match ($className) {
|
||||
'App\Controller\AddressBookController' => $this->addressBook,
|
||||
'App\Controller\AddressBookAdminController' => $this->addressBookAdmin,
|
||||
'App\Controller\SecurityController' => $this->securityController,
|
||||
'App\Service\Router' => $this->router,
|
||||
//default => throw new Exception(message: "Missing class definition: $class")
|
||||
default => die("Missing class definition: $className")
|
||||
};
|
||||
}
|
||||
}
|
46
Vanilla/src/Service/DatabaseConnection.php
Normal file
46
Vanilla/src/Service/DatabaseConnection.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Take care of the PDO object.
|
||||
*/
|
||||
class DatabaseConnection
|
||||
{
|
||||
private PDO $dbConnection;
|
||||
|
||||
// Currently no prefixes are used, but could be easily added to config.json.
|
||||
const TABLE_PREFIX = '';
|
||||
const TABLE_USERS = self::TABLE_PREFIX . "users";
|
||||
const TABLE_ADDRESSES = self::TABLE_PREFIX . "addresses";
|
||||
|
||||
public function __construct(private readonly Config $config)
|
||||
{
|
||||
$dbHost = $this->config->getConfig(configKey: 'dbHost');
|
||||
$dbPort = $this->config->getConfig(configKey: 'dbPort');
|
||||
$dbDatabase = $this->config->getConfig(configKey: 'dbDatabase');
|
||||
$dbUser = $this->config->getConfig(configKey: 'dbUser');
|
||||
$dbPassword = $this->config->getConfig(configKey: 'dbPassword');
|
||||
|
||||
$this->dbConnection = new PDO(
|
||||
dsn: "mysql:host=$dbHost;port=$dbPort;charset=utf8mb4;dbname=$dbDatabase",
|
||||
username: $dbUser,
|
||||
password: $dbPassword
|
||||
);
|
||||
$this->dbConnection->setAttribute(attribute: PDO::ATTR_ERRMODE, value: PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
public function getConnection(): PDO
|
||||
{
|
||||
return $this->dbConnection;
|
||||
}
|
||||
}
|
124
Vanilla/src/Service/Router.php
Normal file
124
Vanilla/src/Service/Router.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022. Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
|
||||
use App\Entity\Route;
|
||||
use Closure;
|
||||
|
||||
/*
|
||||
* A small router implementation for the address book demo.
|
||||
* Currently it doesn't handle GET requests, as not needed.
|
||||
* But if I reuse the code in my bindApi I'll maybe support GET as well.
|
||||
*/
|
||||
|
||||
class Router
|
||||
{
|
||||
/*
|
||||
* The easiest way to differentiate between static and dynamic routes is using
|
||||
* two arrays, no need to pollute the class Route with that information
|
||||
*/
|
||||
private array $staticRoutes = [];
|
||||
private array $dynamicRoutes = [];
|
||||
|
||||
public function __construct(private readonly Template $template)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
/*
|
||||
* This method takes a route like /admin/users/{user} and creates a regex to match on call
|
||||
* More complex routes like /posts/{thread}/show/{page} are supported as well.
|
||||
*/
|
||||
function addRoute(string $name, string $route, Closure $callback): void
|
||||
{
|
||||
// check for parameters
|
||||
preg_match_all(pattern: "/(?<={).+?(?=})/", subject: $route, matches: $matches);
|
||||
$parameters = $matches[0];
|
||||
|
||||
// create regex for route:
|
||||
$regex = preg_replace(pattern: '/{.+?}/', replacement: '([a-zA-Z0-9]*)', subject: $route);
|
||||
// escape \ in regex
|
||||
$regex = '/^' . str_replace(search: "/", replace: '\\/', subject: $regex) . '$/i';
|
||||
|
||||
$route = new Route(name: $name, route: $route, regEx: $regex, parameters: $parameters, callback: $callback);
|
||||
|
||||
if ($parameters) {
|
||||
$this->dynamicRoutes[] = $route;
|
||||
} else {
|
||||
$this->staticRoutes[] = $route;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if there is a known route and executes the callback.
|
||||
*/
|
||||
public function handleRouting(): void
|
||||
{
|
||||
$requestUri = $_SERVER['REQUEST_URI'];
|
||||
|
||||
/*
|
||||
* Static routes have precedence over dynamic ones, so
|
||||
* /admin/user/add to add and
|
||||
* /admin/user/{name} to edit is possible.
|
||||
* A user named "add" of course not :)
|
||||
*
|
||||
* But who wants to call their users "add" or "delete"?
|
||||
* That's as weird as Little Bobby Tables … (https://xkcd.com/327/)
|
||||
*/
|
||||
foreach ($this->staticRoutes as $route) {
|
||||
if (preg_match(pattern: $route->getRegex(), subject: $requestUri, matches: $matches)) {
|
||||
call_user_func(callback: $route->getCallback());
|
||||
|
||||
// We've found our route, bail out.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->dynamicRoutes as $route) {
|
||||
// PHPStorm doesn't know that $parameters are always available,
|
||||
// (as these are dynamic routes) so I init the array just to make PHPStorm happy.
|
||||
$parameters = [];
|
||||
if (preg_match(pattern: $route->getRegex(), subject: $requestUri, matches: $matches)) {
|
||||
foreach ($route->getParameters() as $id => $parameter) {
|
||||
$parameters[$parameter] = $matches[$id + 1];
|
||||
}
|
||||
// PHP is mad about named parameters in call_user_func when adding parameters.
|
||||
// Uncaught Error: Unknown named parameter $args in <sourceFile>
|
||||
// But PHPStorm seems happy without them. So what?
|
||||
call_user_func($route->getCallback(), $parameters);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if no route is matched, throw a 404
|
||||
$this->template->render(templateName: 'status/404.html.php');
|
||||
}
|
||||
|
||||
public function path(string $routeName, array $vars = [])
|
||||
{
|
||||
foreach (array_merge($this->dynamicRoutes, $this->staticRoutes) as $route) {
|
||||
if ($route->getName() == $routeName) {
|
||||
if ($vars) {
|
||||
// build route for dynamic routes
|
||||
$route = $route->getRoute();
|
||||
// replace placeholder with current values
|
||||
foreach ($vars as $key => $value) {
|
||||
$route = str_replace(search: '{' . $key . '}', replace: $value, subject: $route);
|
||||
}
|
||||
return $route;
|
||||
} else {
|
||||
return $route->getRoute();
|
||||
}
|
||||
}
|
||||
}
|
||||
// no 404, this is reached only if the code is buggy
|
||||
die("Missing Route: $routeName");
|
||||
}
|
||||
}
|
54
Vanilla/src/Service/Template.php
Normal file
54
Vanilla/src/Service/Template.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Micha Espey <tracer@24unix.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
/*
|
||||
* As I'm not allowed to use 3rd party code like Twig or Smarty I ended up
|
||||
* using PHP as a templating engine.
|
||||
*/
|
||||
|
||||
class Template
|
||||
{
|
||||
/*
|
||||
* Just store the information about the template base dir.
|
||||
*/
|
||||
public function __construct(private readonly string $templateDir)
|
||||
{
|
||||
// empty body
|
||||
}
|
||||
|
||||
/*
|
||||
* Add variables to template and throw it out
|
||||
*/
|
||||
public function render(string $templateName, array $vars = []): never
|
||||
{
|
||||
// assign template vars
|
||||
foreach ($vars as $name => $value) {
|
||||
$$name = $value;
|
||||
}
|
||||
|
||||
$templateFile = $this->templateDir . $templateName;
|
||||
if (file_exists(filename: $templateFile)) {
|
||||
include $this->templateDir . $templateName;
|
||||
} else {
|
||||
die("Missing template: $templateFile");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* For AJAX calls, return json
|
||||
*/
|
||||
public function renderJson(array $results): never
|
||||
{
|
||||
http_response_code(response_code: $results['status']);
|
||||
echo json_encode(value: $results);
|
||||
exit(0);
|
||||
}
|
||||
}
|
18
Vanilla/src/bootstrap.php
Normal file
18
Vanilla/src/bootstrap.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
spl_autoload_register(callback: function ($className) {
|
||||
$prefix = 'App';
|
||||
$baseDir = __DIR__;
|
||||
|
||||
$prefixLen = strlen(string: $prefix);
|
||||
if (strncmp(string1: $prefix, string2: $className, length: $prefixLen) !== 0) {
|
||||
die("Invalid class: $className");
|
||||
}
|
||||
$realClassNamePSRpath = substr(string: $className, offset: $prefixLen);
|
||||
$classLocation = $baseDir . str_replace(search: '\\', replace: '/', subject: $realClassNamePSRpath) . '.php';
|
||||
if (file_exists(filename: $classLocation)) {
|
||||
require $classLocation;
|
||||
} else {
|
||||
die("Invalid class: $className");
|
||||
}
|
||||
});
|
Reference in New Issue
Block a user