Init
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
todos.json
|
10
.htaccess
Normal file
10
.htaccess
Normal file
@ -0,0 +1,10 @@
|
||||
RewriteEngine On
|
||||
|
||||
# Handle CORS preflight requests
|
||||
RewriteCond %{REQUEST_METHOD} OPTIONS
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||
|
||||
# Route all requests to index.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
29
Makefile
Normal file
29
Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
.PHONY: start clean install
|
||||
|
||||
# Default target
|
||||
.DEFAULT_GOAL := start
|
||||
|
||||
# Variables
|
||||
PHP = php
|
||||
HOST = localhost
|
||||
PORT = 8000
|
||||
SERVER_URL = http://$(HOST):$(PORT)
|
||||
|
||||
# Start the PHP development server
|
||||
start:
|
||||
@echo "Starting PHP TODO API server at $(SERVER_URL)"
|
||||
@echo "Available endpoints:"
|
||||
@echo " GET $(SERVER_URL)/ - API info and available endpoints"
|
||||
@echo " GET $(SERVER_URL)/todos - Get all todos"
|
||||
@echo " POST $(SERVER_URL)/todos - Create todo"
|
||||
@echo " PUT $(SERVER_URL)/todos/:id - Update todo"
|
||||
@echo " DELETE $(SERVER_URL)/todos/:id - Delete todo"
|
||||
@echo ""
|
||||
@echo "Press Ctrl+C to stop the server"
|
||||
$(PHP) -S $(HOST):$(PORT)
|
||||
|
||||
# Clean up data file
|
||||
clean:
|
||||
@echo "Cleaning up data..."
|
||||
@rm -f todos.json
|
||||
@echo "Data file removed"
|
75
Todo.php
Normal file
75
Todo.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
class Todo {
|
||||
private $id;
|
||||
private $title;
|
||||
private $description;
|
||||
private $completed;
|
||||
private $createdAt;
|
||||
private $updatedAt;
|
||||
|
||||
public function __construct($id, $title, $description = '', $completed = false) {
|
||||
$this->id = $id;
|
||||
$this->title = $title;
|
||||
$this->description = $description;
|
||||
$this->completed = $completed;
|
||||
$this->createdAt = date('Y-m-d H:i:s');
|
||||
$this->updatedAt = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// Getters
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function isCompleted() {
|
||||
return $this->completed;
|
||||
}
|
||||
|
||||
public function getCreatedAt() {
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function getUpdatedAt() {
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
// Setters
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
$this->updateTimestamp();
|
||||
}
|
||||
|
||||
public function setDescription($description) {
|
||||
$this->description = $description;
|
||||
$this->updateTimestamp();
|
||||
}
|
||||
|
||||
public function setCompleted($completed) {
|
||||
$this->completed = $completed;
|
||||
$this->updateTimestamp();
|
||||
}
|
||||
|
||||
private function updateTimestamp() {
|
||||
$this->updatedAt = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'description' => $this->description,
|
||||
'completed' => $this->completed,
|
||||
'created_at' => $this->createdAt,
|
||||
'updated_at' => $this->updatedAt
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
188
TodoController.php
Normal file
188
TodoController.php
Normal file
@ -0,0 +1,188 @@
|
||||
<?php
|
||||
require_once 'Todo.php';
|
||||
|
||||
class TodoController {
|
||||
private $dataFile = 'todos.json';
|
||||
|
||||
public function __construct() {
|
||||
// Create data file if it doesn't exist
|
||||
if (!file_exists($this->dataFile)) {
|
||||
file_put_contents($this->dataFile, json_encode([]));
|
||||
}
|
||||
}
|
||||
|
||||
private function loadTodos() {
|
||||
$data = file_get_contents($this->dataFile);
|
||||
$todosData = json_decode($data, true) ?: [];
|
||||
|
||||
$todos = [];
|
||||
foreach ($todosData as $todoData) {
|
||||
$todos[] = new Todo(
|
||||
$todoData['id'],
|
||||
$todoData['title'],
|
||||
$todoData['description'] ?? '',
|
||||
$todoData['completed'] ?? false
|
||||
);
|
||||
}
|
||||
|
||||
return $todos;
|
||||
}
|
||||
|
||||
private function saveTodos($todos) {
|
||||
$todosData = [];
|
||||
foreach ($todos as $todo) {
|
||||
$todosData[] = $todo->toArray();
|
||||
}
|
||||
|
||||
file_put_contents($this->dataFile, json_encode($todosData, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
private function getNextId() {
|
||||
$todos = $this->loadTodos();
|
||||
if (empty($todos)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$maxId = 0;
|
||||
foreach ($todos as $todo) {
|
||||
if ($todo->getId() > $maxId) {
|
||||
$maxId = $todo->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return $maxId + 1;
|
||||
}
|
||||
|
||||
public function getAllTodos() {
|
||||
try {
|
||||
$todos = $this->loadTodos();
|
||||
$todosArray = [];
|
||||
|
||||
foreach ($todos as $todo) {
|
||||
$todosArray[] = $todo->toArray();
|
||||
}
|
||||
|
||||
echo json_encode($todosArray);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Failed to fetch todos: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function createTodo() {
|
||||
try {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$input || !isset($input['title']) || empty(trim($input['title']))) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Title is required']);
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $this->getNextId();
|
||||
$title = trim($input['title']);
|
||||
$description = isset($input['description']) ? trim($input['description']) : '';
|
||||
$completed = isset($input['completed']) ? (bool)$input['completed'] : false;
|
||||
|
||||
$todo = new Todo($id, $title, $description, $completed);
|
||||
|
||||
$todos = $this->loadTodos();
|
||||
$todos[] = $todo;
|
||||
$this->saveTodos($todos);
|
||||
|
||||
http_response_code(201);
|
||||
echo json_encode($todo->toArray());
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Failed to create todo: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateTodo($id) {
|
||||
try {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if (!$input) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Invalid JSON input']);
|
||||
return;
|
||||
}
|
||||
|
||||
$todos = $this->loadTodos();
|
||||
$todoIndex = -1;
|
||||
|
||||
for ($i = 0; $i < count($todos); $i++) {
|
||||
if ($todos[$i]->getId() === $id) {
|
||||
$todoIndex = $i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($todoIndex === -1) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Todo not found']);
|
||||
return;
|
||||
}
|
||||
|
||||
$todo = $todos[$todoIndex];
|
||||
|
||||
// Update fields if provided
|
||||
if (isset($input['title'])) {
|
||||
if (empty(trim($input['title']))) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Title cannot be empty']);
|
||||
return;
|
||||
}
|
||||
$todo->setTitle(trim($input['title']));
|
||||
}
|
||||
|
||||
if (isset($input['description'])) {
|
||||
$todo->setDescription(trim($input['description']));
|
||||
}
|
||||
|
||||
if (isset($input['completed'])) {
|
||||
$todo->setCompleted((bool)$input['completed']);
|
||||
}
|
||||
|
||||
$this->saveTodos($todos);
|
||||
|
||||
echo json_encode($todo->toArray());
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Failed to update todo: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteTodo($id) {
|
||||
try {
|
||||
$todos = $this->loadTodos();
|
||||
$todoIndex = -1;
|
||||
|
||||
for ($i = 0; $i < count($todos); $i++) {
|
||||
if ($todos[$i]->getId() === $id) {
|
||||
$todoIndex = $i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($todoIndex === -1) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Todo not found']);
|
||||
return;
|
||||
}
|
||||
|
||||
$deletedTodo = $todos[$todoIndex];
|
||||
array_splice($todos, $todoIndex, 1);
|
||||
$this->saveTodos($todos);
|
||||
|
||||
echo json_encode([
|
||||
'message' => 'Todo deleted successfully',
|
||||
'deleted_todo' => $deletedTodo->toArray()
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Failed to delete todo: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
91
index.php
Normal file
91
index.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||
|
||||
// Handle preflight OPTIONS requests
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once 'TodoController.php';
|
||||
|
||||
$controller = new TodoController();
|
||||
|
||||
// Get the request URI and method
|
||||
$request_uri = $_SERVER['REQUEST_URI'];
|
||||
$request_method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// Remove query string if present
|
||||
$path = strtok($request_uri, '?');
|
||||
|
||||
// Remove trailing slash if present
|
||||
$path = rtrim($path, '/');
|
||||
|
||||
// If path is empty, set to root
|
||||
if (empty($path)) {
|
||||
$path = '/';
|
||||
}
|
||||
|
||||
// Route handling
|
||||
switch ($path) {
|
||||
case '/':
|
||||
// Base route - show available endpoints
|
||||
if ($request_method === 'GET') {
|
||||
$host = $_SERVER['HTTP_HOST'];
|
||||
$port = $_SERVER['SERVER_PORT'];
|
||||
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
|
||||
|
||||
echo json_encode([
|
||||
'message' => "Todo API running at {$protocol}://{$host}:{$port}/",
|
||||
'available_endpoints' => [
|
||||
'GET /todos - Get all todos',
|
||||
'POST /todos - Create todo',
|
||||
'PUT /todos/:id - Update todo',
|
||||
'DELETE /todos/:id - Delete todo'
|
||||
]
|
||||
], JSON_PRETTY_PRINT);
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
case '/todos':
|
||||
switch ($request_method) {
|
||||
case 'GET':
|
||||
$controller->getAllTodos();
|
||||
break;
|
||||
case 'POST':
|
||||
$controller->createTodo();
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Handle /todos/:id routes
|
||||
if (preg_match('/^\/todos\/(\d+)$/', $path, $matches)) {
|
||||
$id = (int)$matches[1];
|
||||
|
||||
switch ($request_method) {
|
||||
case 'PUT':
|
||||
$controller->updateTodo($id);
|
||||
break;
|
||||
case 'DELETE':
|
||||
$controller->deleteTodo($id);
|
||||
break;
|
||||
default:
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
}
|
||||
} else {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Endpoint not found']);
|
||||
}
|
||||
}
|
||||
?>
|
Reference in New Issue
Block a user