From d3bcd6d7f82f16e8e8063d0583f96dd49e98bf7c Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Mon, 9 Jun 2025 12:36:58 -0600 Subject: [PATCH] Init --- .gitignore | 1 + .htaccess | 10 +++ Makefile | 29 +++++++ Todo.php | 75 ++++++++++++++++++ TodoController.php | 188 +++++++++++++++++++++++++++++++++++++++++++++ index.php | 91 ++++++++++++++++++++++ 6 files changed, 394 insertions(+) create mode 100644 .gitignore create mode 100644 .htaccess create mode 100644 Makefile create mode 100644 Todo.php create mode 100644 TodoController.php create mode 100644 index.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..550c582 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +todos.json \ No newline at end of file diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..339480e --- /dev/null +++ b/.htaccess @@ -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] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..632c1f8 --- /dev/null +++ b/Makefile @@ -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" \ No newline at end of file diff --git a/Todo.php b/Todo.php new file mode 100644 index 0000000..023e82b --- /dev/null +++ b/Todo.php @@ -0,0 +1,75 @@ +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 + ]; + } +} +?> \ No newline at end of file diff --git a/TodoController.php b/TodoController.php new file mode 100644 index 0000000..11edf8d --- /dev/null +++ b/TodoController.php @@ -0,0 +1,188 @@ +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()]); + } + } +} +?> \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..d68dfe7 --- /dev/null +++ b/index.php @@ -0,0 +1,91 @@ + "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']); + } +} +?> \ No newline at end of file