Notes PHP (1/2)
Backend
Tehdään PHP:lla toimiva backend joka toimii notesdemon kanssa. Lisää kansio phpback notesdemon alapuolelle.
Laadi kansiorakenne
phpback index.php phpback / helpers helpers.php phpback / models connection.php note.php user.php phpback / controlelrs notesManagement.php usersManagement.php
helpers.php
<?php
function getRoute($uri) {
$segments = explode('/', trim($uri, '/'));
$route = null;
if (count($segments) > 1) {
$route = "/" . strtolower($segments[0]) . "/" . strtolower($segments[1]); // /api/notes
}
else {
$route = "/" . strtolower($segments[0]);
}
return $route;
}
function getId($uri) {
$segments = explode('/', trim($uri, '/'));
$id = $segments[2] ?? null;
return $id;
}
function respond($statusCode, $data = null)
{
$allowedOrigins = [
"http://localhost:5173",
"https://alidomain.tunnus.treok.io"
];
if (isset($_SERVER['HTTP_ORIGIN']) && in_array($_SERVER['HTTP_ORIGIN'], $allowedOrigins)) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header("Access-Control-Allow-Credentials: true");
}
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
// 200 = OK, 201 = Created, 204 = No content
// 400 = Bad request, 401 = Unauthorized, 403 = Forbidden, 404 = Not found
// 500 = Internal server error
http_response_code($statusCode);
// Kaikissa muissa paitsi 204-tilanteissa lähetetään JSON
if ($statusCode !== 204) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
exit;
}
models
connection.php
<?php
function connectDB(){
static $connection;
if(!isset($connection)) {
$connection = connect();
}
return $connection;
}
function connect() {
$host = getenv('DB_HOST', true) ?: "localhost";
$port = getenv('DB_PORT', true) ?: 3306;
$dbname = getenv('DB_NAME', true) ?: "notesdemo_db";
$user = getenv('DB_USERNAME', true) ?: "root";
$password = getenv('DB_PASSWORD', true) ?: "mypass123";
$connectionString = "mysql:host=$host;dbname=$dbname;port=$port;charset=utf8";
try {
$pdo = new PDO($connectionString, $user, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e){
echo "Virhe tietokantayhteydessä: " . $e->getMessage();
die();
}
}
note.php
<?php
require_once "connection.php";
function addNote(string $content, $date, $important, int $userid): int
{
$pdo = connectDB();
$important = $important ? 1 : 0; // mysql boolean
$stmt = $pdo->prepare('INSERT INTO notes (content, `date`, important, user_id) VALUES (?, ?, ?, ?)');
$stmt->execute([$content, $date, $important, $userid]);
$last_id = $pdo->lastInsertId();
return $last_id;
}
function getNotes(int $userid): array
{
$pdo = connectDB();
$sql = "SELECT * FROM notes WHERE user_id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$userid]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
function deleteNote(int $id): void
{
$pdo = connectDB();
$stmt = $pdo->prepare('DELETE FROM notes WHERE id = ?');
$stmt->execute([$id]);
}
function updateNote(int $id, string $content, bool $important): void
{
$pdo = connectDB();
$important = $important ? 1 : 0; // mysql boolean
$stmt = $pdo->prepare('UPDATE notes SET content = ?,important = ? WHERE id = ?');
$stmt->execute([$content, $important, $id]);
}
user.php
<?php
require_once "connection.php";
function isEmailTaken(string $email) {
$pdo = connectDB();
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
function registerUser(string $username, string $email, string $password) {
$pdo = connectDB();
if (isEmailTaken($email)) {
throw new Exception('Email already exists.');
}
$stmt = $pdo->prepare('INSERT INTO users (username, email, password) VALUES (?, ?, ?)');
$stmt->execute([$username, $email, password_hash($password, PASSWORD_DEFAULT)]);
}
function login(string $username, string $password) {
$pdo = connectDB();
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?');
$stmt->execute([$username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
return false;
}
else if (!password_verify($password, $user['password'])) {
return false;
}
else {
return $user;
}
}
controllers
notesManagement.php
<?php
require_once "./models/note.php";
function updateNoteController()
{
if (!isset($_SESSION['userid'])) {
respond(401, ['message' => 'Not logged in']);
}
// viesti request bodyssa:
$note = json_decode(file_get_contents('php://input'));
if (isset($note->id, $note->content, $note->important) && !is_null($note->id) && !is_null($note->content) && !is_null($note->important)) {
$id = $note->id;
$content = $note->content;
$important = $note->important;
try {
updateNote($id, $content, $important);
respond(204); // 204 no content
} catch (Exception $e) {
respond(500, ['message' => $e->getMessage()]);
}
} else {
respond(400, ['message' => 'Provided JSON is invalid.']);
}
}
function deleteNoteController(int $id)
{
if (!isset($_SESSION['userid'])) {
respond(401, ['message' => 'Not logged in']);
}
if (isset($id) && !is_null($id)) {
try {
deleteNote($id);
respond(204); // 204 no content
} catch (Exception $e) {
respond(500, ['message' => $e->getMessage()]);
}
} else {
respond(400, ['message' => 'Provided JSON is invalid.']);
}
}
function addNoteController()
{
if (!isset($_SESSION['userid'])) {
respond(401, ['message' => 'Not logged in']);
}
try {
$note = json_decode(file_get_contents('php://input'));
if (isset($note->content,$note->important) && !is_null($note->content) && !is_null($note->important)) {
$content = $note->content;
$important = $note->important;
$date = date("Y-m-d");
$userid = $_SESSION['userid'];
try {
$id = addNote($content,$date, $important, $userid);
$data = array('content' => $content,'date' => $date, 'important' => $important, 'id' => $id);
respond(201, $data); // 201 created
} catch (Exception $e) {
respond(500, ['message' => $e->getMessage()]);
}
}
} catch (Exception $e) {
respond(500, ['message' => $e->getMessage()]);
}
}
function getNotesController()
{
if (!isset($_SESSION['userid'])) {
respond(401, ['message' => 'Not logged in']);
}
try {
$userid = $_SESSION['userid'];
$notes = getNotes($userid);
if (is_null($notes)) {
respond(500, ['message' => 'Internal server error, please contact the administrator.']);
} else {
respond(200, $notes);
}
} catch (Exception $e) {
respond(500, ['message' => $e->getMessage()]);
}
}
usersManagement.php
<?php
require_once "./models/user.php";
function registerController() {
$user = json_decode(file_get_contents('php://input'));
if (isset($user->username, $user->email, $user->password) && !is_null($user->username) && !is_null($user->email) && !is_null($user->password)) {
$username = $user->username;
$password = $user->password;
$email = $user->email;
try {
registerUser($username, $email, $password);
respond(201, ['user' => $user]);
} catch (Exception $e) {
respond(500, ['message' => $e->getMessage()]);
}
} else {
respond(400, ['message' => 'Provided JSON is invalid.']);
}
}
function loginController() {
$user = json_decode(file_get_contents('php://input'));
//echo json_encode(['useri' => $user]);
if (isset($user->username, $user->password) && !is_null($user->username) && !is_null($user->password)) {
$username = $user->username;
$password = $user->password;
try {
$user = login($username, $password);
if ($user === false) {
respond(401, ['message' => 'Invalid credentials.']);
return;
}
$_SESSION['username'] = $user['username'];
$_SESSION['userid'] = $user['id'];
$_SESSION['session_id'] = session_id();
respond(200, ['user' => $user]);
} catch (Exception $e) {
respond(500, ['message' => $e->getMessage()]);
}
} else {
respond(400, ['message' => 'Provided JSON is invalid.']);
}
}
function logoutController() {
session_unset();
session_destroy();
respond(200,['message' => 'Logged out']);
}
function meController() {
if (isset($_SESSION['userid'])) {
respond(200, [
'id' => $_SESSION['userid'],
'username' => $_SESSION['username']
]);
} else {
// toinen tapa olisi lähettää 401
// siitä virheilmoitus frontin konsolissa
respond(200, [
'id' => null,
'username' => null
]);
}
}
index.php
Sijoita index.php juurihakemistoon. Täällä käsitellään reititys ja ohjataan pyyntö oikeaan paikkaan.
<?php
session_start();
require_once "./helpers/helpers.php";
require_once './controllers/notesManagement.php';
require_once './controllers/usersManagement.php';
// GET, POST, DELETE, OPTIONS, PUT
$method = $_SERVER['REQUEST_METHOD'];
$uri = explode("?", $_SERVER["REQUEST_URI"])[0];
$route = getRoute($uri); // /api/notes
$id = getId($uri); // /api/notes/1
if ($method === 'OPTIONS') {
respond(200);
exit();
}
switch ($route) {
case "/api/me":
meController();
break;
case "/api/register":
registerController();
break;
case "/api/login":
loginController();
break;
case "/api/logout":
logoutController();
break;
case "/api/notes":
switch ($method) {
case 'GET':
getNotesController();
break;
case 'POST':
addNoteController();
break;
case 'DELETE':
if ($id) deleteNoteController($id);
break;
case 'PUT':
if ($id) updateNoteController($id);
break;
}
break;
default:
respond(404);
}