Build a server-rendered task application in which people register, sign in, create and complete their own tasks, upload an optional attachment, and never read or modify another account’s private data. The project is deliberately small enough to finish and broad enough to exercise production boundaries.
Completion means more than displaying pages: the database enforces integrity, requests are validated and authorized, failures are logged safely, tests protect key rules, quality commands pass, and a documented deployment can be rolled back.
app/
Application/
Domain/
Http/
Infrastructure/
config/
public/index.php
storage/uploads/
templates/
tests/Unit/
tests/Integration/
Only public belongs under the web document root; uploads and application source remain outside it.
Add foreign keys and indexes from actual ownership and feed queries. Keep the original upload name as escaped display metadata only if the feature needs it.
| Table | Essential Data |
|---|---|
| users | id, canonical email, password hash, created timestamp |
| tasks | id, user_id, title, completion timestamp, created timestamp |
| attachments | id, task_id, generated path, MIME type, size |
| audit_events | id, user_id, event type, subject id, timestamp |
| Method and Path | Behavior |
|---|---|
| GET /tasks | List the authenticated owner’s tasks |
| POST /tasks | Validate CSRF and create one task |
| POST /tasks/{id}/complete | Authorize ownership and complete atomically |
| POST /tasks/{id}/attachment | Authorize, validate, and store one PDF |
| POST /logout | Validate CSRF and clear authenticated state |
Make ownership part of the query so another account’s row is not loaded and then accidentally exposed. Authorization still remains an explicit application decision.
<?php
$statement = $pdo->prepare(
'SELECT id, title, completed_at
FROM tasks
WHERE id = :task_id AND user_id = :user_id'
);
$statement->execute(['task_id' => $taskId, 'user_id' => $currentUserId]);
$task = $statement->fetch();
if ($task === false) {
http_response_code(404);
exit('Task not found');
}
The same not-found outcome covers a missing task and a task owned by another account.
Only begin stretch work after the acceptance evidence passes. Add email reminders through an idempotent queue, an API representation with token authentication and rate limiting, or administrator reporting with explicit authorization.
0 of 2 checked
Try this next
0 of 2 completed
Explore 500+ free tutorials across 20+ languages and frameworks.