Skip to content

Commit 88e4fb8

Browse files
committed
mvc demo
1 parent 7f9fc2f commit 88e4fb8

File tree

11 files changed

+291
-0
lines changed

11 files changed

+291
-0
lines changed

App/Controllers/IndexController.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace App\Controllers;
4+
5+
use Zero\Mvc\Controller;
6+
7+
class IndexController extends Controller
8+
{
9+
public function index()
10+
{
11+
$this->view('index');
12+
}
13+
14+
public function contact()
15+
{
16+
$this->view('contact');
17+
}
18+
}

App/views/contact.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Contact

App/views/index.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
HELLO
2+
3+
qwefw

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,32 @@ Autoloading: 1 function
2727

2828
Future possibility:
2929
PHP: Node.js EventLoop - for websockets and non blocking behaviour in PHP.
30+
31+
32+
33+
Zero MVC
34+
--------
35+
36+
/your-project
37+
38+
├── /App
39+
│ ├── /Global
40+
│ │ ├── /Controllers
41+
│ │ ├── /Models
42+
│ │ └── /Views
43+
│ └── /TenantSpecific
44+
│ ├── /Tenant1
45+
│ │ ├── /Controllers
46+
│ │ ├── /Models
47+
│ │ └── /Views
48+
│ └── /Tenant2
49+
│ ├── /Controllers
50+
│ ├── /Models
51+
│ └── /Views
52+
├── /Zero
53+
| └── /Mvc
54+
│ ├── App.php
55+
│ ├── Controller.php
56+
│ └── Model.php
57+
├── autoload.php
58+
└── index.php

Zero/Mvc/App.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
namespace Zero\Mvc;
3+
/**
4+
* App class
5+
*
6+
* This class is responsible for parsing the URL, determining the appropriate controller,
7+
* method, and parameters, and then executing the controller method with the parameters.
8+
*/
9+
class App
10+
{
11+
protected $defaultApp = 'App';
12+
// The App
13+
protected $app;
14+
// Default controller
15+
protected string $defaultController = 'Index';
16+
17+
// The controller instance
18+
protected Controller $controller;
19+
20+
// The controller method that will serve the request
21+
protected string $defaultMethod = 'index';
22+
// Parameters to be passed to the method
23+
protected $params = [];
24+
// Singleton instance
25+
private static $instance = null;
26+
27+
public Route $route;
28+
29+
private function __construct()
30+
{
31+
// Parse the URL to get controller, method, and parameters
32+
$this->route = $this->parseUrl();
33+
$this->controller = $this->loadController($this->route);
34+
35+
// Remove the controller from the URL array
36+
37+
// Check if the method exists in the controller
38+
if (!method_exists($this->controller, $this->route->method)) {
39+
throw new \Exception("Controller method not found: {$this->route->method}");
40+
}
41+
42+
// Remaining parts of the URL are parameters
43+
// $this->params = $url ? array_values($url) : [];
44+
}
45+
46+
public function loadController(Route $route) : Controller
47+
{
48+
// Check controller file exists
49+
$controllerFile = DIR_ROOT.'/'.$route->app.'/Controllers/'.ucfirst($route->controller).'Controller.php';
50+
if (!file_exists($controllerFile))
51+
throw new \Exception("Controller file not found: $controllerFile");
52+
53+
// Instantiate the controller class
54+
$controllerClass = $route->app . '\Controllers\\' . $route->controller . 'Controller';
55+
return new $controllerClass;
56+
}
57+
58+
public function getController()
59+
{
60+
$this->controller;
61+
}
62+
63+
public function getApp() {
64+
return $this->app;
65+
}
66+
67+
/**
68+
* Get the singleton instance of the App
69+
*
70+
* @return App The singleton instance
71+
*/
72+
public static function getInstance()
73+
{
74+
if (self::$instance === null) {
75+
self::$instance = new self();
76+
}
77+
return self::$instance;
78+
}
79+
80+
/**
81+
* Run the application
82+
*
83+
* This method executes the controller method with the parameters.
84+
*/
85+
public function run() : void
86+
{
87+
// Execute the controller method with the parameters
88+
call_user_func_array([$this->controller, $this->route->method], $this->params);
89+
}
90+
91+
/**
92+
* Parse the URL
93+
*
94+
* This method parses the URL from the GET request, sanitizes it, and returns it as an array containing
95+
* the App namespace, Controller name, and method.
96+
*
97+
* @return array Parsed URL segments
98+
*/
99+
public function parseUrl($url=null) : Route
100+
{
101+
$url = $url ? $url : (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/');
102+
$url = explode('/', filter_var(trim($url, '/'), FILTER_SANITIZE_URL));
103+
// If URL is empty, return default App, Controller, and Method
104+
if (empty($url[0])) {
105+
return new Route($this->defaultApp, $this->defaultController, $this->defaultMethod);
106+
}
107+
108+
$appNamespace = isset($url[0]) ? $url[0] : $this->defaultApp;
109+
$controller = isset($url[1]) ? $url[1] : $this->defaultController;
110+
$method = isset($url[2]) ? $url[2] : $this->defaultMethod;
111+
112+
// Check if the App namespace exists
113+
$appPath = DIR_ROOT . "/" . ucwords($appNamespace);
114+
if (!is_dir($appPath)) {
115+
// If App namespace does not exist
116+
// Then /defaultApp/controller/method
117+
$appNamespace = $this->defaultApp;
118+
$controller = isset($url[0]) ? $url[0] : $this->defaultController;
119+
$method = isset($url[1]) ? $url[1] : $this->defaultMethod;
120+
return new Route($appNamespace, $controller, $method);
121+
}
122+
123+
// else if the app exists then assume app/controller/method
124+
return new Route($appNamespace, $controller, $method);
125+
}
126+
}

Zero/Mvc/Controller.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Zero\Mvc;
4+
5+
class Controller
6+
{
7+
public function view($view, $data = [])
8+
{
9+
require_once DIR_ROOT . '/App/Views/' . $view . '.php';
10+
}
11+
}

Zero/Mvc/Model.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
namespace Core;
3+
4+
use PDO;
5+
6+
class Model
7+
{
8+
protected $db;
9+
protected $tenant;
10+
11+
public function setTenant($tenant)
12+
{
13+
$this->tenant = $tenant;
14+
$this->connectDatabase();
15+
}
16+
17+
private function connectDatabase()
18+
{
19+
// Database connection parameters
20+
$host = 'localhost';
21+
$username = 'root';
22+
$password = '';
23+
$dbName = 'tenant_' . $this->tenant; // Database name per tenant
24+
25+
try {
26+
$this->db = new PDO("mysql:host=$host;dbname=$dbName", $username, $password);
27+
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
28+
} catch (\PDOException $e) {
29+
die('Database connection failed: ' . $e->getMessage());
30+
}
31+
}
32+
}

Zero/Mvc/Route.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Zero\Mvc;
4+
5+
class Route
6+
{
7+
public string $app;
8+
public string $controller;
9+
public string $method;
10+
11+
public function __construct(string $app, string $controller, string $method)
12+
{
13+
$this->app = $app;
14+
$this->controller = $controller;
15+
$this->method = $method;
16+
}
17+
}

autoload.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
// Define the root directory
3+
define('DIR_ROOT', __DIR__);
4+
5+
/**
6+
* Autoload function to automatically include PHP class files based on their namespace.
7+
*
8+
* This function registers an autoloader that converts a fully-qualified class name
9+
* into a file path and includes the file if it exists. The base directory for the
10+
* namespace prefix is set to the current directory.
11+
*
12+
* Example:
13+
* If a class named 'App\Controller\HomeController' is requested, this autoloader
14+
* will look for a file at 'App/Controller/HomeController.php' in the base directory.
15+
*
16+
* @param string $class The fully-qualified class name.
17+
* @return void
18+
*/
19+
spl_autoload_register(function ($class) {
20+
// Base directory for the namespace prefix (adjust as needed)
21+
$base_dir = __DIR__ . '/';
22+
23+
// Replace the namespace separator with the directory separator, append with .php
24+
$file = $base_dir . str_replace('\\', '/', $class) . '.php';
25+
26+
// Require the file if it exists
27+
if (file_exists($file)) {
28+
require $file;
29+
}
30+
});
31+
32+
// load in helper functions
33+
require_once 'helpers.php';

helpers.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
use \Zero\Mvc\App;
3+
4+
function dd(...$args) {
5+
var_dump(...$args);
6+
die();
7+
}
8+
/**
9+
* Helper function to get the App instance
10+
*
11+
* @return App The singleton instance of the App
12+
*/
13+
function app()
14+
{
15+
return App::getInstance();
16+
}

public/index.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
require_once '../autoload.php';
3+
4+
$app = \Zero\Mvc\App::getInstance();
5+
$app->run();

0 commit comments

Comments
 (0)