Skip to content

Php Project

DodaTech 5 min read

title: PHP Project — Complete Guide to Building a Full PHP Application description: 'Learn building a complete PHP project: planning, MVC structure, database design, REST API, authentication, testing, docker deployment, and production launch.' date: 2026-06-28 lastmod: 2026-06-28 weight: 45 tags: [backend, php]


This project walks through building a complete PHP application from planning to production: a task management API with user authentication, CRUD operations, testing, and Docker deployment.

## What You'll Learn

By the end of this project, you'll plan and architect a PHP application, implement MVC with routing, build RESTful endpoints, add JWT authentication, write PHPUnit tests, and deploy with Docker.

## Why This Project Matters

Building a complete application integrates all the skills you've learned: PHP basics, MVC, databases, security, testing, and deployment. It's the capstone that prepares you for real-world PHP development.

## Real-World Use

This task management API follows the same patterns as Trello, Asana, or Jira. The architecture scales from a small team tool to an enterprise project management system.

## Project Learning Path

```mermaid
flowchart LR
  A[Docker/Deploy] --> B[Project]
  B --> C[Next Steps]
  B --> D{You Are Here}
  style D fill:#f90,color:#fff

Project Structure

task-manager/
  api/
    index.php          # Entry point
    src/
      Controllers/
        AuthController.php
        TaskController.php
      Models/
        User.php
        Task.php
      Middleware/
        AuthMiddleware.php
      Core/
        Router.php
        Database.php
    config/
      database.php
    public/
      index.php        # Front controller
    tests/
      Unit/
        TaskTest.php
      Feature/
        AuthTest.php
    Dockerfile
    docker-compose.yml

Database Schema

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    status ENUM('todo', 'in_progress', 'done') DEFAULT 'todo',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX idx_tasks_user_id ON tasks(user_id);
CREATE INDEX idx_tasks_status ON tasks(status);

Router

<?php
// src/Core/Router.php
namespace App\Core;
class Router {
    private array $routes = [];
    public function get(string $pattern, array $handler): void {
        $this->addRoute("GET", $pattern, $handler);
    }
    public function post(string $pattern, array $handler): void {
        $this->addRoute("POST", $pattern, $handler);
    }
    private function addRoute(string $method, string $pattern, array $handler): void {
        $regex = preg_replace("/\{(\w+)\}/", "(?P<$1>[^/]+)", $pattern);
        $this->routes[] = ["method" => $method, "pattern" => "#^$regex$#", "handler" => $handler];
    }
    public function dispatch(string $method, string $uri): void {
        foreach ($this->routes as $route) {
            if ($route["method"] !== $method) continue;
            if (preg_match($route["pattern"], $uri, $matches)) {
                [$class, $action] = $route["handler"];
                $params = array_filter($matches, "is_string", ARRAY_FILTER_USE_KEY);
                echo (new $class())->$action($params);
                return;
            }
        }
        http_response_code(404);
        echo json_encode(["error" => "Not found"]);
    }
}

Task Controller

<?php
// src/Controllers/TaskController.php
namespace App\Controllers;
use App\Models\Task;
class TaskController {
    public function __construct(private Task $taskModel) {}
    public function index(array $params): string {
        $userId = $_REQUEST["user_id"];
        $status = $_GET["status"] ?? null;
        $tasks = $status
            ? $this->taskModel->findByUserAndStatus($userId, $status)
            : $this->taskModel->findByUser($userId);
        return json_encode($tasks);
    }
    public function store(array $params): string {
        $data = json_decode(file_get_contents("php://input"), true);
        $id = $this->taskModel->create([
            "user_id" => $_REQUEST["user_id"],
            "title" => $data["title"],
            "description" => $data["description"] ?? "",
        ]);
        http_response_code(201);
        return json_encode(["id" => $id]);
    }
    public function update(array $params): string {
        $data = json_decode(file_get_contents("php://input"), true);
        $this->taskModel->update((int)$params["id"], $data);
        return json_encode(["updated" => true]);
    }
    public function destroy(array $params): string {
        $this->taskModel->delete((int)$params["id"]);
        return json_encode(["deleted" => true]);
    }
}

Entry Point

<?php
// api/public/index.php
require __DIR__ . "/../vendor/autoload.php";
header("Content-Type: application/json");
$db = \App\Core\Database::getConnection();
$router = new \App\Core\Router();
$router->post("/auth/login", [\App\Controllers\AuthController::class, "login"]);
$router->post("/auth/register", [\App\Controllers\AuthController::class, "register"]);
$router->get("/tasks", [\App\Controllers\TaskController::class, "index"]);
$router->post("/tasks", [\App\Controllers\TaskController::class, "store"]);
$router->get("/tasks/{id}", [\App\Controllers\TaskController::class, "show"]);
$router->put("/tasks/{id}", [\App\Controllers\TaskController::class, "update"]);
$router->delete("/tasks/{id}", [\App\Controllers\TaskController::class, "destroy"]);
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$uri = preg_replace("#^/api#", "", $uri);
$router->dispatch($_SERVER["REQUEST_METHOD"], $uri);

Common Mistakes

1. Skipping Planning

Jumping to code without planning leads to messy architecture. Design your database, routes, and components first.

2. No Error Handling

Production apps need global exception handlers, logging, and proper HTTP status codes for errors.

3. Ignoring Security

Add authentication, input validation, prepared statements, and CSRF protection from day one.

4. Not Writing Tests

Without tests, refactoring is risky. Start with unit tests for critical business logic.

5. No Deployment Automation

Manual deployment leads to errors. Use Docker and CI/CD from the start.

Practice Questions

1. What is the first step in building a PHP project?

Requirements gathering and database design. Plan your data model before writing any code.

2. How do you structure a PHP application for maintainability?

Use MVC pattern with separate directories for Controllers, Models, Middleware, and Core logic.

3. What security measures should every PHP project include?

Prepared statements, input validation, password hashing, HTTPS, CORS headers, and authentication.

4. How do you handle errors in production?

Use try/catch blocks, a global exception handler, and log errors to files (not the browser).

5. Challenge: Add a complete test suite and Docker deployment to any PHP application.

Write functional tests for each endpoint and create Dockerfile + docker-compose.yml for deployment.

FAQ

Should I use a framework for PHP projects?

Frameworks (Laravel, Symfony) provide structure, but building from scratch teaches core concepts. Use frameworks for professional projects.

How do I manage dependencies in a PHP project?

Use Composer. Define dependencies in composer.json, run composer install, and include vendor/autoload.php.

What is the best way to handle file uploads in a project?

Validate file type/size, store outside web root, use randomized names, and serve through a controlled access script.

How do I version my PHP API?

Prefix routes with /api/v1/. Support multiple versions in parallel. Deprecate old versions gradually.

What is the next step after this project?

Learn a PHP framework (Laravel or Symfony), study design patterns, contribute to open source, or build a portfolio project.

Mini Project: Complete Task Manager API

Build a full REST API with authentication, CRUD, testing, and Docker.

# Project setup
composer init
composer require firebase/php-jwt
composer require --dev phpunit/phpunit
# Run tests
./vendor/bin/phpunit
# Run with Docker
docker compose up -d
curl -X POST http://localhost:8080/api/auth/register -H 'Content-Type: application/json' -d '{"name":"Test","email":"test@test.com","password":"secret123"}'
curl -X POST http://localhost:8080/api/auth/login -H 'Content-Type: application/json' -d '{"email":"test@test.com","password":"secret123"}'
curl -H 'Authorization: Bearer <token>' http://localhost:8080/api/tasks

What's Next

PHP Project PHP Reference

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro