From f8411accb4b234d10966ab6738487ca7f0b06327 Mon Sep 17 00:00:00 2001 From: Jaspreet Singh Date: Tue, 28 Jul 2026 22:57:00 +0530 Subject: [PATCH] Initial commit of LightMVC framework files. --- .htaccess | 6 ++ README.md | 97 +++++++++++++++++++++++++++++- app/Controllers/ApiController.php | 14 +++++ app/Controllers/SiteController.php | 20 ++++++ app/Views/_404.php | 1 + app/Views/contact.php | 16 +++++ app/Views/home.php | 2 + app/Views/layouts/main.php | 36 +++++++++++ composer.json | 28 +++++++++ core/Application.php | 28 +++++++++ core/Controller.php | 11 ++++ core/Request.php | 37 ++++++++++++ core/Response.php | 11 ++++ core/Router.php | 53 ++++++++++++++++ core/View.php | 39 ++++++++++++ package.json | 12 ++++ public/index.php | 16 +++++ 17 files changed, 424 insertions(+), 3 deletions(-) create mode 100755 .htaccess mode change 100644 => 100755 README.md create mode 100755 app/Controllers/ApiController.php create mode 100755 app/Controllers/SiteController.php create mode 100755 app/Views/_404.php create mode 100755 app/Views/contact.php create mode 100755 app/Views/home.php create mode 100755 app/Views/layouts/main.php create mode 100755 composer.json create mode 100755 core/Application.php create mode 100755 core/Controller.php create mode 100755 core/Request.php create mode 100755 core/Response.php create mode 100755 core/Router.php create mode 100755 core/View.php create mode 100755 package.json create mode 100755 public/index.php diff --git a/.htaccess b/.htaccess new file mode 100755 index 0000000..be07158 --- /dev/null +++ b/.htaccess @@ -0,0 +1,6 @@ + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ public/index.php [L,QSA] + diff --git a/README.md b/README.md old mode 100644 new mode 100755 index b86b6e3..4c12a0f --- a/README.md +++ b/README.md @@ -1,3 +1,94 @@ -# LightMVC - -A minimal, PSR-4 compliant PHP MVC framework designed for simplicity and scalability. \ No newline at end of file +# LightMVC - Lightweight PHP MVC Framework + +A minimal, PSR-4 compliant PHP MVC framework designed for simplicity and scalability. + +## Directory Structure + +```text +├── app/ +│ ├── Controllers/ # Application logic (Controllers) +│ ├── Models/ # Data structures (Models) +│ └── Views/ # UI templates (Views) +├── core/ # Framework engine +│ ├── Application.php +│ ├── Controller.php +│ ├── Request.php +│ ├── Response.php +│ ├── Router.php +│ └── View.php +├── public/ # Entry point and static assets +│ ├── css/ # Custom CSS files +│ ├── js/ # Custom JS files +│ └── images/ # Static images +├── node_modules/ # Frontend dependencies +├── vendor/ # Composer dependencies +``` + +## Asset Management & npm + +This project uses `npm` for frontend dependencies (e.g., Bootstrap). + +1. Initialize/Install: `npm install` +2. Add custom CSS/JS in `public/css/app.css` and `public/js/app.js`. +3. Reference assets in `app/Views/layouts/main.php`. + +## How It Works + +1. **Request Flow**: Requests hit `public/index.php`. +2. **Initialization**: `Core\Application` initializes `Request`, `Response`, `Router`, and `View`. +3. **Routing**: The `Router` matches the `Request` path against registered routes. +4. **Dispatching**: If a match is found, the `Router` instantiates the controller and invokes the requested method. +5. **Controller Action**: The controller handles business logic and returns a `View` using `$this->render()`. +6. **Response**: `View` loads the layout from `app/Views/layouts/main.php`, injects the view content into the `{{content}}` placeholder, and returns the response. + +## Adding a New Route + +In `public/index.php`, register a route mapping a path to a controller method: + +```php +$app->router->get('/path', [ControllerClass::class, 'methodName']); +``` + +## API Routing + +API routes are registered in `public/index.php` with the `/api/` prefix to keep them separate from regular page routes. + +### Example API Route + +```php +$app->router->get('/api/ping', [App\Controllers\ApiController::class, 'ping']); +``` + +### API Controller + +The `ApiController` class lives in `app/Controllers/ApiController.php` and should contain methods for API endpoints. + +Example: +```php +response->setHeader('Content-Type', 'application/json'); + $this->response->setBody(json_encode(['status' => 'ok', 'time' => microtime(true)])); + } +} +``` + +### Testing the API + +You can test the `/api/ping` endpoint with: +```bash +curl http://localhost/api/ping +``` + +Expected response: +```json +{"status":"ok","time":1718409600.123} +``` diff --git a/app/Controllers/ApiController.php b/app/Controllers/ApiController.php new file mode 100755 index 0000000..f0450e6 --- /dev/null +++ b/app/Controllers/ApiController.php @@ -0,0 +1,14 @@ +response->setHeader('Content-Type', 'application/json'); + $this->response->setBody(json_encode(['status' => 'ok', 'time' => microtime(true)])); + } +} \ No newline at end of file diff --git a/app/Controllers/SiteController.php b/app/Controllers/SiteController.php new file mode 100755 index 0000000..2f0d92d --- /dev/null +++ b/app/Controllers/SiteController.php @@ -0,0 +1,20 @@ +render('home', [ + 'name' => 'My PHP MVC Framework' + ]); + } + + public function contact() + { + return $this->render('contact'); + } +} diff --git a/app/Views/_404.php b/app/Views/_404.php new file mode 100755 index 0000000..46ab38a --- /dev/null +++ b/app/Views/_404.php @@ -0,0 +1 @@ +

404 - Page Not Found

diff --git a/app/Views/contact.php b/app/Views/contact.php new file mode 100755 index 0000000..b3ce707 --- /dev/null +++ b/app/Views/contact.php @@ -0,0 +1,16 @@ +

Contact

+
+
+ + +
+
+ + +
+
+ + +
+ +
diff --git a/app/Views/home.php b/app/Views/home.php new file mode 100755 index 0000000..f036d37 --- /dev/null +++ b/app/Views/home.php @@ -0,0 +1,2 @@ +

Home

+

Welcome to {{name}}

diff --git a/app/Views/layouts/main.php b/app/Views/layouts/main.php new file mode 100755 index 0000000..51530fb --- /dev/null +++ b/app/Views/layouts/main.php @@ -0,0 +1,36 @@ + + + + + + PHP MVC Framework + + + + + + + + +
+ {{content}} +
+ + + + + diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..cbd2a45 --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "name": "jaspreet/lightmvc", + "description": "A lightweight PHP MVC framework", + "type": "project", + "license": "MIT", + "autoload": { + "psr-4": { + "App\\": "app/", + "Core\\": "core/" + } + }, + "authors": [ + { + "name": "Jaspreet Singh", + "email": "jaspreet.online2012@gmail.com", + "role": "developer" + }, + { + "name": "Gemini CLI", + "role": "Artificial Intelligence" + }, + { + "name": "OpenClaw", + "role": "Artificial Intelligence" + } + ], + "require": {} +} diff --git a/core/Application.php b/core/Application.php new file mode 100755 index 0000000..c077264 --- /dev/null +++ b/core/Application.php @@ -0,0 +1,28 @@ +request = new Request(); + $this->response = new Response(); + $this->view = new View(); + $this->router = new Router($this->request, $this->response); + } + + public function run() + { + echo $this->router->resolve(); + } +} diff --git a/core/Controller.php b/core/Controller.php new file mode 100755 index 0000000..849e562 --- /dev/null +++ b/core/Controller.php @@ -0,0 +1,11 @@ +router->renderView($view, $params); + } +} diff --git a/core/Request.php b/core/Request.php new file mode 100755 index 0000000..7508076 --- /dev/null +++ b/core/Request.php @@ -0,0 +1,37 @@ +getMethod() === 'get') { + foreach ($_GET as $key => $value) { + $body[$key] = filter_input(INPUT_GET, $key, FILTER_SANITIZE_SPECIAL_CHARS); + } + } + if ($this->getMethod() === 'post') { + foreach ($_POST as $key => $value) { + $body[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS); + } + } + return $body; + } +} diff --git a/core/Response.php b/core/Response.php new file mode 100755 index 0000000..1a61dd9 --- /dev/null +++ b/core/Response.php @@ -0,0 +1,11 @@ +request = $request; + $this->response = $response; + } + + public function get($path, $callback) + { + $this->routes['get'][$path] = $callback; + } + + public function post($path, $callback) + { + $this->routes['post'][$path] = $callback; + } + + public function resolve() + { + $path = $this->request->getPath(); + $method = $this->request->getMethod(); + $callback = $this->routes[$method][$path] ?? false; + + if ($callback === false) { + $this->response->setStatusCode(404); + return $this->renderView("_404"); + } + + if (is_string($callback)) { + return $this->renderView($callback); + } + + if (is_array($callback)) { + $callback[0] = new $callback[0](); + } + + return call_user_func($callback, $this->request); + } + + public function renderView($view, $params = []) + { + return Application::$app->view->renderView($view, $params); + } +} diff --git a/core/View.php b/core/View.php new file mode 100755 index 0000000..3702d08 --- /dev/null +++ b/core/View.php @@ -0,0 +1,39 @@ +renderOnlyView($view, $params); + $layoutContent = $this->layoutContent(); + return str_replace('{{content}}', $viewContent, $layoutContent); + } + + protected function layoutContent() + { + ob_start(); + include_once Application::$ROOT_DIR . "/app/Views/layouts/main.php"; + return ob_get_clean(); + } + + protected function renderOnlyView($view, $params) + { + foreach ($params as $key => $value) { + $$key = $value; + } + ob_start(); + include_once Application::$ROOT_DIR . "/app/Views/$view.php"; + $content = ob_get_clean(); + + // Simple {{variable}} replacement + foreach ($params as $key => $value) { + if (is_scalar($value)) { + $content = str_replace('{{'.$key.'}}', $value, $content); + } + } + + return $content; + } +} diff --git a/package.json b/package.json new file mode 100755 index 0000000..fe330af --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "php-mvc-framework", + "version": "1.0.0", + "description": "Asset management for PHP MVC Framework", + "scripts": { + "dev": "echo 'Build assets for development'", + "build": "echo 'Build assets for production'" + }, + "dependencies": { + "bootstrap": "^5.3.0" + } +} diff --git a/public/index.php b/public/index.php new file mode 100755 index 0000000..5071a1f --- /dev/null +++ b/public/index.php @@ -0,0 +1,16 @@ +router->get('/', [SiteController::class, 'home']); +$app->router->get('/contact', [SiteController::class, 'contact']); + +// API routes +$app->router->get('/api/ping', [App\Controllers\ApiController::class, 'ping']); + +$app->run();