Simple MVC built in PHP
I tried to make a MVC in PHP with OOP approach. Code Review this so I can understand whether am I using the best practices and can I legally call it a MVC framework or not. Also, I am going with 1 controller for each action.
This blockquote might help you review my code better.
The default thing is actually works like this:
http://localhost/coolcodes/something/test/ (This is bring up the TestController)
http://localhost/coolcodes/something/ (This will bring up the default controller for something).
There is a function called getDefaultRouteName.
getDefaultRouteName => Returns the key for which the default controller is located in the $routesTree variable.
My code:
index.php (The page the client will go to):
<?php
include_once("App.php");
$app = new App();
$app->init();
$app->execute();
?>
App.php (The main application starter):
<?php
include_once("RoutesTree.php");
include_once("Config.php");
class App {
private $controller;
public function init() {
$routesTree = new RoutesTree;
if (Config::isUnderDevelopment()) {
$this->controller = $routesTree->getUnderDevelopmentController();
} else {
$pageURL = str_replace("/coolcodes/api/", "", $_SERVER['REQUEST_URI']);
$path = explode("/", $pageURL);
if ($_SERVER['REQUEST_URI']{strlen($_SERVER['REQUEST_URI']) - 1} == "/") {
$path = array_slice($path, 0, count($path) - 1);
}
$this->controller = $routesTree->getController($path);
}
}
public function execute() {
$this->controller->execute();
}
}
?>
Routes.php (The routes for MVC):
<?php
include_once("Config.php");
class RoutesTree {
private $routesTree;
private $notFoundController;
function __construct() {
$this->initRoutesTree();
}
public function initRoutesTree() {
$this->routesTree = Config::getPreparedRoutesTree();
$this->notFoundController = Config::getNotFoundController();
}
public function getRoutesTree() {
return $this->routesTree;
}
public function getUnderDevelopmentController() {
return Config::getUnderDevelopmentController();
}
public function getController($path) {
$route = $this->routesTree;
foreach ($path as $pathSegments) {
if (array_key_exists($pathSegments, $route)) {
$route = $route[$pathSegments];
} else {
return $this->notFoundController;
}
}
if (is_array($route)) {
return $route[Config::getDefaultRouteName()];
}
return $route;
}
}
?>
Controller.php (MVC Controller base class):
<?php
abstract class Controller {
abstract public function execute();
}
?>
Some Controllers:
TestController.php:
<?php
include_once("Controller.php");
class TestController extends Controller {
public function execute() {
echo json_encode(["value" => "Hello, World!!!!"]);
}
}
?>
SomethingController.php:
<?php
include_once("Controller.php");
class SomethingController extends Controller {
public function execute() {
echo json_encode(["value" => "From something controller."]);
}
}
?>
Controllers that are required:
NotFoundController.php (The Error 404 Controller)
<?php
include_once("Controller.php");
class NotFoundController extends Controller {
public function execute() {
echo "<h1>Sorry, Page not found.</h1>";
}
}
?>
UnderDevelopementController (Controller shown if the website is under construction):
<?php
include_once("Controller.php");
class UnderDevelopmentController extends Controller {
public function execute() {
echo json_encode(["value" => "This website is under construction. See you soon."]);
}
}
?>
Config.php (Settings for MVC (inspired by settings.py in Django))
I made functions instead of variables because I thought that it will be more OOP oriented in PHP (Because I do not want to make variables outside of any classes).
<?php
include_once("allclasses.php");
class Config {
public static function isUnderDevelopment() {
return false;
}
public static function getUnderDevelopmentController() {
return new UnderDevelopmentController;
}
public static function getNotFoundController() {
return new NotFoundController;
}
public static function getDefaultRouteName() {
return "default";
}
public static function getPreparedRoutesTree() {
return [
"something" => [
"default" => new SomethingController,
"test" => new TestController
]
];
}
}
?>
addclasses.php (To Group all the controllers)
<?php
include_once("controllers/TestController.php");
include_once("controllers/SomethingController.php");
include_once("controllers/NotFoundController.php");
include_once("controllers/UnderDevelopmentController.php");
?>
The .htaccess file:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
php mvc url-routing
New contributor
add a comment |
I tried to make a MVC in PHP with OOP approach. Code Review this so I can understand whether am I using the best practices and can I legally call it a MVC framework or not. Also, I am going with 1 controller for each action.
This blockquote might help you review my code better.
The default thing is actually works like this:
http://localhost/coolcodes/something/test/ (This is bring up the TestController)
http://localhost/coolcodes/something/ (This will bring up the default controller for something).
There is a function called getDefaultRouteName.
getDefaultRouteName => Returns the key for which the default controller is located in the $routesTree variable.
My code:
index.php (The page the client will go to):
<?php
include_once("App.php");
$app = new App();
$app->init();
$app->execute();
?>
App.php (The main application starter):
<?php
include_once("RoutesTree.php");
include_once("Config.php");
class App {
private $controller;
public function init() {
$routesTree = new RoutesTree;
if (Config::isUnderDevelopment()) {
$this->controller = $routesTree->getUnderDevelopmentController();
} else {
$pageURL = str_replace("/coolcodes/api/", "", $_SERVER['REQUEST_URI']);
$path = explode("/", $pageURL);
if ($_SERVER['REQUEST_URI']{strlen($_SERVER['REQUEST_URI']) - 1} == "/") {
$path = array_slice($path, 0, count($path) - 1);
}
$this->controller = $routesTree->getController($path);
}
}
public function execute() {
$this->controller->execute();
}
}
?>
Routes.php (The routes for MVC):
<?php
include_once("Config.php");
class RoutesTree {
private $routesTree;
private $notFoundController;
function __construct() {
$this->initRoutesTree();
}
public function initRoutesTree() {
$this->routesTree = Config::getPreparedRoutesTree();
$this->notFoundController = Config::getNotFoundController();
}
public function getRoutesTree() {
return $this->routesTree;
}
public function getUnderDevelopmentController() {
return Config::getUnderDevelopmentController();
}
public function getController($path) {
$route = $this->routesTree;
foreach ($path as $pathSegments) {
if (array_key_exists($pathSegments, $route)) {
$route = $route[$pathSegments];
} else {
return $this->notFoundController;
}
}
if (is_array($route)) {
return $route[Config::getDefaultRouteName()];
}
return $route;
}
}
?>
Controller.php (MVC Controller base class):
<?php
abstract class Controller {
abstract public function execute();
}
?>
Some Controllers:
TestController.php:
<?php
include_once("Controller.php");
class TestController extends Controller {
public function execute() {
echo json_encode(["value" => "Hello, World!!!!"]);
}
}
?>
SomethingController.php:
<?php
include_once("Controller.php");
class SomethingController extends Controller {
public function execute() {
echo json_encode(["value" => "From something controller."]);
}
}
?>
Controllers that are required:
NotFoundController.php (The Error 404 Controller)
<?php
include_once("Controller.php");
class NotFoundController extends Controller {
public function execute() {
echo "<h1>Sorry, Page not found.</h1>";
}
}
?>
UnderDevelopementController (Controller shown if the website is under construction):
<?php
include_once("Controller.php");
class UnderDevelopmentController extends Controller {
public function execute() {
echo json_encode(["value" => "This website is under construction. See you soon."]);
}
}
?>
Config.php (Settings for MVC (inspired by settings.py in Django))
I made functions instead of variables because I thought that it will be more OOP oriented in PHP (Because I do not want to make variables outside of any classes).
<?php
include_once("allclasses.php");
class Config {
public static function isUnderDevelopment() {
return false;
}
public static function getUnderDevelopmentController() {
return new UnderDevelopmentController;
}
public static function getNotFoundController() {
return new NotFoundController;
}
public static function getDefaultRouteName() {
return "default";
}
public static function getPreparedRoutesTree() {
return [
"something" => [
"default" => new SomethingController,
"test" => new TestController
]
];
}
}
?>
addclasses.php (To Group all the controllers)
<?php
include_once("controllers/TestController.php");
include_once("controllers/SomethingController.php");
include_once("controllers/NotFoundController.php");
include_once("controllers/UnderDevelopmentController.php");
?>
The .htaccess file:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
php mvc url-routing
New contributor
1
Apparently the biggest improvement would be implementation of autoload
– Your Common Sense
Dec 17 at 15:04
1
@YourCommonSense Please do not answer in comments. All suggestions for improvements should be posted as answers.
– 200_success
Dec 17 at 17:44
add a comment |
I tried to make a MVC in PHP with OOP approach. Code Review this so I can understand whether am I using the best practices and can I legally call it a MVC framework or not. Also, I am going with 1 controller for each action.
This blockquote might help you review my code better.
The default thing is actually works like this:
http://localhost/coolcodes/something/test/ (This is bring up the TestController)
http://localhost/coolcodes/something/ (This will bring up the default controller for something).
There is a function called getDefaultRouteName.
getDefaultRouteName => Returns the key for which the default controller is located in the $routesTree variable.
My code:
index.php (The page the client will go to):
<?php
include_once("App.php");
$app = new App();
$app->init();
$app->execute();
?>
App.php (The main application starter):
<?php
include_once("RoutesTree.php");
include_once("Config.php");
class App {
private $controller;
public function init() {
$routesTree = new RoutesTree;
if (Config::isUnderDevelopment()) {
$this->controller = $routesTree->getUnderDevelopmentController();
} else {
$pageURL = str_replace("/coolcodes/api/", "", $_SERVER['REQUEST_URI']);
$path = explode("/", $pageURL);
if ($_SERVER['REQUEST_URI']{strlen($_SERVER['REQUEST_URI']) - 1} == "/") {
$path = array_slice($path, 0, count($path) - 1);
}
$this->controller = $routesTree->getController($path);
}
}
public function execute() {
$this->controller->execute();
}
}
?>
Routes.php (The routes for MVC):
<?php
include_once("Config.php");
class RoutesTree {
private $routesTree;
private $notFoundController;
function __construct() {
$this->initRoutesTree();
}
public function initRoutesTree() {
$this->routesTree = Config::getPreparedRoutesTree();
$this->notFoundController = Config::getNotFoundController();
}
public function getRoutesTree() {
return $this->routesTree;
}
public function getUnderDevelopmentController() {
return Config::getUnderDevelopmentController();
}
public function getController($path) {
$route = $this->routesTree;
foreach ($path as $pathSegments) {
if (array_key_exists($pathSegments, $route)) {
$route = $route[$pathSegments];
} else {
return $this->notFoundController;
}
}
if (is_array($route)) {
return $route[Config::getDefaultRouteName()];
}
return $route;
}
}
?>
Controller.php (MVC Controller base class):
<?php
abstract class Controller {
abstract public function execute();
}
?>
Some Controllers:
TestController.php:
<?php
include_once("Controller.php");
class TestController extends Controller {
public function execute() {
echo json_encode(["value" => "Hello, World!!!!"]);
}
}
?>
SomethingController.php:
<?php
include_once("Controller.php");
class SomethingController extends Controller {
public function execute() {
echo json_encode(["value" => "From something controller."]);
}
}
?>
Controllers that are required:
NotFoundController.php (The Error 404 Controller)
<?php
include_once("Controller.php");
class NotFoundController extends Controller {
public function execute() {
echo "<h1>Sorry, Page not found.</h1>";
}
}
?>
UnderDevelopementController (Controller shown if the website is under construction):
<?php
include_once("Controller.php");
class UnderDevelopmentController extends Controller {
public function execute() {
echo json_encode(["value" => "This website is under construction. See you soon."]);
}
}
?>
Config.php (Settings for MVC (inspired by settings.py in Django))
I made functions instead of variables because I thought that it will be more OOP oriented in PHP (Because I do not want to make variables outside of any classes).
<?php
include_once("allclasses.php");
class Config {
public static function isUnderDevelopment() {
return false;
}
public static function getUnderDevelopmentController() {
return new UnderDevelopmentController;
}
public static function getNotFoundController() {
return new NotFoundController;
}
public static function getDefaultRouteName() {
return "default";
}
public static function getPreparedRoutesTree() {
return [
"something" => [
"default" => new SomethingController,
"test" => new TestController
]
];
}
}
?>
addclasses.php (To Group all the controllers)
<?php
include_once("controllers/TestController.php");
include_once("controllers/SomethingController.php");
include_once("controllers/NotFoundController.php");
include_once("controllers/UnderDevelopmentController.php");
?>
The .htaccess file:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
php mvc url-routing
New contributor
I tried to make a MVC in PHP with OOP approach. Code Review this so I can understand whether am I using the best practices and can I legally call it a MVC framework or not. Also, I am going with 1 controller for each action.
This blockquote might help you review my code better.
The default thing is actually works like this:
http://localhost/coolcodes/something/test/ (This is bring up the TestController)
http://localhost/coolcodes/something/ (This will bring up the default controller for something).
There is a function called getDefaultRouteName.
getDefaultRouteName => Returns the key for which the default controller is located in the $routesTree variable.
My code:
index.php (The page the client will go to):
<?php
include_once("App.php");
$app = new App();
$app->init();
$app->execute();
?>
App.php (The main application starter):
<?php
include_once("RoutesTree.php");
include_once("Config.php");
class App {
private $controller;
public function init() {
$routesTree = new RoutesTree;
if (Config::isUnderDevelopment()) {
$this->controller = $routesTree->getUnderDevelopmentController();
} else {
$pageURL = str_replace("/coolcodes/api/", "", $_SERVER['REQUEST_URI']);
$path = explode("/", $pageURL);
if ($_SERVER['REQUEST_URI']{strlen($_SERVER['REQUEST_URI']) - 1} == "/") {
$path = array_slice($path, 0, count($path) - 1);
}
$this->controller = $routesTree->getController($path);
}
}
public function execute() {
$this->controller->execute();
}
}
?>
Routes.php (The routes for MVC):
<?php
include_once("Config.php");
class RoutesTree {
private $routesTree;
private $notFoundController;
function __construct() {
$this->initRoutesTree();
}
public function initRoutesTree() {
$this->routesTree = Config::getPreparedRoutesTree();
$this->notFoundController = Config::getNotFoundController();
}
public function getRoutesTree() {
return $this->routesTree;
}
public function getUnderDevelopmentController() {
return Config::getUnderDevelopmentController();
}
public function getController($path) {
$route = $this->routesTree;
foreach ($path as $pathSegments) {
if (array_key_exists($pathSegments, $route)) {
$route = $route[$pathSegments];
} else {
return $this->notFoundController;
}
}
if (is_array($route)) {
return $route[Config::getDefaultRouteName()];
}
return $route;
}
}
?>
Controller.php (MVC Controller base class):
<?php
abstract class Controller {
abstract public function execute();
}
?>
Some Controllers:
TestController.php:
<?php
include_once("Controller.php");
class TestController extends Controller {
public function execute() {
echo json_encode(["value" => "Hello, World!!!!"]);
}
}
?>
SomethingController.php:
<?php
include_once("Controller.php");
class SomethingController extends Controller {
public function execute() {
echo json_encode(["value" => "From something controller."]);
}
}
?>
Controllers that are required:
NotFoundController.php (The Error 404 Controller)
<?php
include_once("Controller.php");
class NotFoundController extends Controller {
public function execute() {
echo "<h1>Sorry, Page not found.</h1>";
}
}
?>
UnderDevelopementController (Controller shown if the website is under construction):
<?php
include_once("Controller.php");
class UnderDevelopmentController extends Controller {
public function execute() {
echo json_encode(["value" => "This website is under construction. See you soon."]);
}
}
?>
Config.php (Settings for MVC (inspired by settings.py in Django))
I made functions instead of variables because I thought that it will be more OOP oriented in PHP (Because I do not want to make variables outside of any classes).
<?php
include_once("allclasses.php");
class Config {
public static function isUnderDevelopment() {
return false;
}
public static function getUnderDevelopmentController() {
return new UnderDevelopmentController;
}
public static function getNotFoundController() {
return new NotFoundController;
}
public static function getDefaultRouteName() {
return "default";
}
public static function getPreparedRoutesTree() {
return [
"something" => [
"default" => new SomethingController,
"test" => new TestController
]
];
}
}
?>
addclasses.php (To Group all the controllers)
<?php
include_once("controllers/TestController.php");
include_once("controllers/SomethingController.php");
include_once("controllers/NotFoundController.php");
include_once("controllers/UnderDevelopmentController.php");
?>
The .htaccess file:
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
php mvc url-routing
php mvc url-routing
New contributor
New contributor
edited Dec 17 at 17:43
200_success
128k15150412
128k15150412
New contributor
asked Dec 17 at 14:58
Siddharth Bose
133
133
New contributor
New contributor
1
Apparently the biggest improvement would be implementation of autoload
– Your Common Sense
Dec 17 at 15:04
1
@YourCommonSense Please do not answer in comments. All suggestions for improvements should be posted as answers.
– 200_success
Dec 17 at 17:44
add a comment |
1
Apparently the biggest improvement would be implementation of autoload
– Your Common Sense
Dec 17 at 15:04
1
@YourCommonSense Please do not answer in comments. All suggestions for improvements should be posted as answers.
– 200_success
Dec 17 at 17:44
1
1
Apparently the biggest improvement would be implementation of autoload
– Your Common Sense
Dec 17 at 15:04
Apparently the biggest improvement would be implementation of autoload
– Your Common Sense
Dec 17 at 15:04
1
1
@YourCommonSense Please do not answer in comments. All suggestions for improvements should be posted as answers.
– 200_success
Dec 17 at 17:44
@YourCommonSense Please do not answer in comments. All suggestions for improvements should be posted as answers.
– 200_success
Dec 17 at 17:44
add a comment |
1 Answer
1
active
oldest
votes
As Your Common Sense noted in the comments, leveraging auto loading classes in PHP can help clean up your includes for the controller files (and model classes too).
Some additional improvements:
Tight Coupling to Config class: Create an instance of your Config class, and pass it to the constructor of your App class, instead of using static methods.
$app = new App(new Config());
Then pass this the instance of
Config
into yourRoutesTree
class:
$routesTree = new RoutesTree($this->config);
This gives you loose coupling between the App, RoutesTree and Config classes by way of Dependency Injection.
Separate controllers from ouput: I think this is the area where you are violating the "V" in MVC. Your example controller
echo
's directly to the standard output. A controller should produce a result, but something else should be responsible for sending this back to the client. Most frameworks opt for an object that encompasses the data to be rendered, and the name of a view used to render it.
Convention based URL mappings: Instead of keeping an array of controller names to controllers, use the controller name from the URL to return the right controller:
// http://localhost/app/test maps to TestController
$controller = ucfirst('test') . 'Controller';
return new $controller();
You can still build in a way to do custom routing, but you'll find a pattern to your URLs and the controllers that respond to them
No restriction on HTTP methods: Your controller actions appear to respond to both GET and POST requests. This can be a security flaw when you allow a GET request to modify data. Consider this request:
HTTP/1.1 POST http://localhost/app/createComment
comment=Hacked&post_id=3
That's all fine and dandy until someone puts this HTML on a random page on the internet, and tricks your users into visiting it:
<script src="http://localhost/app/comments?comment=Hacked&post_id=3"></script>
Upon visiting the page, if you are logged in to your site the browser will issue a GET request to
http://localhost/app/comments?comment=Hacked&post_id=3
. Since your application does not check if the client has issued a GET or POST request, blog post #3 gets a new comment each time you visit the page.
Sure the browser can't understand the result of this HTTP request as JavaScript, but that doesn't stop the browser from sending the request, along with your session cookies.
I guess that I will make two different execute functions, one for GET request and another for POST request. So, will it be fine?
– Siddharth Bose
Dec 19 at 12:39
@SiddharthBose: Since you have one controller per request, the controller itself could define a method called something likecanExecuteRequest('GET')
. If it returnsfalse
then theApp
object would throw an exception, or return a400 bad request
response back to the client.
– Greg Burghardt
Dec 19 at 12:54
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Siddharth Bose is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209835%2fsimple-mvc-built-in-php%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
As Your Common Sense noted in the comments, leveraging auto loading classes in PHP can help clean up your includes for the controller files (and model classes too).
Some additional improvements:
Tight Coupling to Config class: Create an instance of your Config class, and pass it to the constructor of your App class, instead of using static methods.
$app = new App(new Config());
Then pass this the instance of
Config
into yourRoutesTree
class:
$routesTree = new RoutesTree($this->config);
This gives you loose coupling between the App, RoutesTree and Config classes by way of Dependency Injection.
Separate controllers from ouput: I think this is the area where you are violating the "V" in MVC. Your example controller
echo
's directly to the standard output. A controller should produce a result, but something else should be responsible for sending this back to the client. Most frameworks opt for an object that encompasses the data to be rendered, and the name of a view used to render it.
Convention based URL mappings: Instead of keeping an array of controller names to controllers, use the controller name from the URL to return the right controller:
// http://localhost/app/test maps to TestController
$controller = ucfirst('test') . 'Controller';
return new $controller();
You can still build in a way to do custom routing, but you'll find a pattern to your URLs and the controllers that respond to them
No restriction on HTTP methods: Your controller actions appear to respond to both GET and POST requests. This can be a security flaw when you allow a GET request to modify data. Consider this request:
HTTP/1.1 POST http://localhost/app/createComment
comment=Hacked&post_id=3
That's all fine and dandy until someone puts this HTML on a random page on the internet, and tricks your users into visiting it:
<script src="http://localhost/app/comments?comment=Hacked&post_id=3"></script>
Upon visiting the page, if you are logged in to your site the browser will issue a GET request to
http://localhost/app/comments?comment=Hacked&post_id=3
. Since your application does not check if the client has issued a GET or POST request, blog post #3 gets a new comment each time you visit the page.
Sure the browser can't understand the result of this HTTP request as JavaScript, but that doesn't stop the browser from sending the request, along with your session cookies.
I guess that I will make two different execute functions, one for GET request and another for POST request. So, will it be fine?
– Siddharth Bose
Dec 19 at 12:39
@SiddharthBose: Since you have one controller per request, the controller itself could define a method called something likecanExecuteRequest('GET')
. If it returnsfalse
then theApp
object would throw an exception, or return a400 bad request
response back to the client.
– Greg Burghardt
Dec 19 at 12:54
add a comment |
As Your Common Sense noted in the comments, leveraging auto loading classes in PHP can help clean up your includes for the controller files (and model classes too).
Some additional improvements:
Tight Coupling to Config class: Create an instance of your Config class, and pass it to the constructor of your App class, instead of using static methods.
$app = new App(new Config());
Then pass this the instance of
Config
into yourRoutesTree
class:
$routesTree = new RoutesTree($this->config);
This gives you loose coupling between the App, RoutesTree and Config classes by way of Dependency Injection.
Separate controllers from ouput: I think this is the area where you are violating the "V" in MVC. Your example controller
echo
's directly to the standard output. A controller should produce a result, but something else should be responsible for sending this back to the client. Most frameworks opt for an object that encompasses the data to be rendered, and the name of a view used to render it.
Convention based URL mappings: Instead of keeping an array of controller names to controllers, use the controller name from the URL to return the right controller:
// http://localhost/app/test maps to TestController
$controller = ucfirst('test') . 'Controller';
return new $controller();
You can still build in a way to do custom routing, but you'll find a pattern to your URLs and the controllers that respond to them
No restriction on HTTP methods: Your controller actions appear to respond to both GET and POST requests. This can be a security flaw when you allow a GET request to modify data. Consider this request:
HTTP/1.1 POST http://localhost/app/createComment
comment=Hacked&post_id=3
That's all fine and dandy until someone puts this HTML on a random page on the internet, and tricks your users into visiting it:
<script src="http://localhost/app/comments?comment=Hacked&post_id=3"></script>
Upon visiting the page, if you are logged in to your site the browser will issue a GET request to
http://localhost/app/comments?comment=Hacked&post_id=3
. Since your application does not check if the client has issued a GET or POST request, blog post #3 gets a new comment each time you visit the page.
Sure the browser can't understand the result of this HTTP request as JavaScript, but that doesn't stop the browser from sending the request, along with your session cookies.
I guess that I will make two different execute functions, one for GET request and another for POST request. So, will it be fine?
– Siddharth Bose
Dec 19 at 12:39
@SiddharthBose: Since you have one controller per request, the controller itself could define a method called something likecanExecuteRequest('GET')
. If it returnsfalse
then theApp
object would throw an exception, or return a400 bad request
response back to the client.
– Greg Burghardt
Dec 19 at 12:54
add a comment |
As Your Common Sense noted in the comments, leveraging auto loading classes in PHP can help clean up your includes for the controller files (and model classes too).
Some additional improvements:
Tight Coupling to Config class: Create an instance of your Config class, and pass it to the constructor of your App class, instead of using static methods.
$app = new App(new Config());
Then pass this the instance of
Config
into yourRoutesTree
class:
$routesTree = new RoutesTree($this->config);
This gives you loose coupling between the App, RoutesTree and Config classes by way of Dependency Injection.
Separate controllers from ouput: I think this is the area where you are violating the "V" in MVC. Your example controller
echo
's directly to the standard output. A controller should produce a result, but something else should be responsible for sending this back to the client. Most frameworks opt for an object that encompasses the data to be rendered, and the name of a view used to render it.
Convention based URL mappings: Instead of keeping an array of controller names to controllers, use the controller name from the URL to return the right controller:
// http://localhost/app/test maps to TestController
$controller = ucfirst('test') . 'Controller';
return new $controller();
You can still build in a way to do custom routing, but you'll find a pattern to your URLs and the controllers that respond to them
No restriction on HTTP methods: Your controller actions appear to respond to both GET and POST requests. This can be a security flaw when you allow a GET request to modify data. Consider this request:
HTTP/1.1 POST http://localhost/app/createComment
comment=Hacked&post_id=3
That's all fine and dandy until someone puts this HTML on a random page on the internet, and tricks your users into visiting it:
<script src="http://localhost/app/comments?comment=Hacked&post_id=3"></script>
Upon visiting the page, if you are logged in to your site the browser will issue a GET request to
http://localhost/app/comments?comment=Hacked&post_id=3
. Since your application does not check if the client has issued a GET or POST request, blog post #3 gets a new comment each time you visit the page.
Sure the browser can't understand the result of this HTTP request as JavaScript, but that doesn't stop the browser from sending the request, along with your session cookies.
As Your Common Sense noted in the comments, leveraging auto loading classes in PHP can help clean up your includes for the controller files (and model classes too).
Some additional improvements:
Tight Coupling to Config class: Create an instance of your Config class, and pass it to the constructor of your App class, instead of using static methods.
$app = new App(new Config());
Then pass this the instance of
Config
into yourRoutesTree
class:
$routesTree = new RoutesTree($this->config);
This gives you loose coupling between the App, RoutesTree and Config classes by way of Dependency Injection.
Separate controllers from ouput: I think this is the area where you are violating the "V" in MVC. Your example controller
echo
's directly to the standard output. A controller should produce a result, but something else should be responsible for sending this back to the client. Most frameworks opt for an object that encompasses the data to be rendered, and the name of a view used to render it.
Convention based URL mappings: Instead of keeping an array of controller names to controllers, use the controller name from the URL to return the right controller:
// http://localhost/app/test maps to TestController
$controller = ucfirst('test') . 'Controller';
return new $controller();
You can still build in a way to do custom routing, but you'll find a pattern to your URLs and the controllers that respond to them
No restriction on HTTP methods: Your controller actions appear to respond to both GET and POST requests. This can be a security flaw when you allow a GET request to modify data. Consider this request:
HTTP/1.1 POST http://localhost/app/createComment
comment=Hacked&post_id=3
That's all fine and dandy until someone puts this HTML on a random page on the internet, and tricks your users into visiting it:
<script src="http://localhost/app/comments?comment=Hacked&post_id=3"></script>
Upon visiting the page, if you are logged in to your site the browser will issue a GET request to
http://localhost/app/comments?comment=Hacked&post_id=3
. Since your application does not check if the client has issued a GET or POST request, blog post #3 gets a new comment each time you visit the page.
Sure the browser can't understand the result of this HTTP request as JavaScript, but that doesn't stop the browser from sending the request, along with your session cookies.
answered Dec 18 at 14:31
Greg Burghardt
4,936620
4,936620
I guess that I will make two different execute functions, one for GET request and another for POST request. So, will it be fine?
– Siddharth Bose
Dec 19 at 12:39
@SiddharthBose: Since you have one controller per request, the controller itself could define a method called something likecanExecuteRequest('GET')
. If it returnsfalse
then theApp
object would throw an exception, or return a400 bad request
response back to the client.
– Greg Burghardt
Dec 19 at 12:54
add a comment |
I guess that I will make two different execute functions, one for GET request and another for POST request. So, will it be fine?
– Siddharth Bose
Dec 19 at 12:39
@SiddharthBose: Since you have one controller per request, the controller itself could define a method called something likecanExecuteRequest('GET')
. If it returnsfalse
then theApp
object would throw an exception, or return a400 bad request
response back to the client.
– Greg Burghardt
Dec 19 at 12:54
I guess that I will make two different execute functions, one for GET request and another for POST request. So, will it be fine?
– Siddharth Bose
Dec 19 at 12:39
I guess that I will make two different execute functions, one for GET request and another for POST request. So, will it be fine?
– Siddharth Bose
Dec 19 at 12:39
@SiddharthBose: Since you have one controller per request, the controller itself could define a method called something like
canExecuteRequest('GET')
. If it returns false
then the App
object would throw an exception, or return a 400 bad request
response back to the client.– Greg Burghardt
Dec 19 at 12:54
@SiddharthBose: Since you have one controller per request, the controller itself could define a method called something like
canExecuteRequest('GET')
. If it returns false
then the App
object would throw an exception, or return a 400 bad request
response back to the client.– Greg Burghardt
Dec 19 at 12:54
add a comment |
Siddharth Bose is a new contributor. Be nice, and check out our Code of Conduct.
Siddharth Bose is a new contributor. Be nice, and check out our Code of Conduct.
Siddharth Bose is a new contributor. Be nice, and check out our Code of Conduct.
Siddharth Bose is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209835%2fsimple-mvc-built-in-php%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Apparently the biggest improvement would be implementation of autoload
– Your Common Sense
Dec 17 at 15:04
1
@YourCommonSense Please do not answer in comments. All suggestions for improvements should be posted as answers.
– 200_success
Dec 17 at 17:44