Initial commit of LightMVC framework files.

This commit is contained in:
2026-07-28 22:57:00 +05:30
parent 6beffc9fe9
commit f8411accb4
17 changed files with 424 additions and 3 deletions
Executable
+6
View File
@@ -0,0 +1,6 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ public/index.php [L,QSA]
</IfModule>
Regular → Executable
+92 -1
View File
@@ -1,3 +1,94 @@
# LightMVC # LightMVC - Lightweight PHP MVC Framework
A minimal, PSR-4 compliant PHP MVC framework designed for simplicity and scalability. 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
<?php
namespace App\Controllers;
use Core\Controller;
class ApiController extends Controller
{
public function ping()
{
// Simple health-check endpoint
$this->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}
```
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Controllers;
use Core\Controller;
class ApiController extends Controller
{
public function ping()
{
// Simple health-check endpoint
$this->response->setHeader('Content-Type', 'application/json');
$this->response->setBody(json_encode(['status' => 'ok', 'time' => microtime(true)]));
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace App\Controllers;
use Core\Controller;
class SiteController extends Controller
{
public function home()
{
return $this->render('home', [
'name' => 'My PHP MVC Framework'
]);
}
public function contact()
{
return $this->render('contact');
}
}
+1
View File
@@ -0,0 +1 @@
<h1>404 - Page Not Found</h1>
+16
View File
@@ -0,0 +1,16 @@
<h1>Contact</h1>
<form action="" method="post">
<div class="mb-3">
<label class="form-label">Subject</label>
<input type="text" name="subject" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">Body</label>
<textarea name="body" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
+2
View File
@@ -0,0 +1,2 @@
<h1>Home</h1>
<h3>Welcome to {{name}}</h3>
+36
View File
@@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PHP MVC Framework</title>
<!-- Bootstrap from node_modules (needs mapping) -->
<link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="/css/app.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="/">Navbar</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/contact">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
{{content}}
</div>
<script src="/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="/js/app.js"></script>
</body>
</html>
Executable
+28
View File
@@ -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": {}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Core;
class Application
{
public static string $ROOT_DIR;
public Router $router;
public Request $request;
public Response $response;
public View $view;
public static Application $app;
public function __construct($rootPath)
{
self::$ROOT_DIR = $rootPath;
self::$app = $this;
$this->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();
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Core;
class Controller
{
public function render($view, $params = [])
{
return Application::$app->router->renderView($view, $params);
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace Core;
class Request
{
public function getPath()
{
$path = $_SERVER['REQUEST_URI'] ?? '/';
$position = strpos($path, '?');
if ($position === false) {
return $path;
}
return substr($path, 0, $position);
}
public function getMethod()
{
return strtolower($_SERVER['REQUEST_METHOD']);
}
public function getBody()
{
$body = [];
if ($this->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;
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Core;
class Response
{
public function setStatusCode(int $code)
{
http_response_code($code);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace Core;
class Router
{
protected array $routes = [];
public Request $request;
public Response $response;
public function __construct(Request $request, Response $response)
{
$this->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);
}
}
Executable
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Core;
class View
{
public function renderView($view, $params = [])
{
$viewContent = $this->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;
}
}
Executable
+12
View File
@@ -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"
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use App\Controllers\SiteController;
use Core\Application;
$app = new Application(dirname(__DIR__));
$app->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();