I asked a question like this before but since i still can't find an answer to this i'll ask it again :-s.
I'm using this very basic 'templating' script:
require_once 'core/init.php';
if(empty($_GET['page'])){
header('Location: home');
die();
}
$basePath = dirname(__FILE__) . '/';
$viewPath = $basePath . 'view/';
$view = scandir($viewPath);
foreach($view as $file)
{
if (!is_dir($viewPath . $file))
{
$pages[] = substr($file, 0, strpos($file, '.'));
}
}
if(in_array($_GET['page'], $pages)){
include($viewPath . $_GET['page'] . '.php');
} else{
include($basePath . '404.php');
}
and i'm rewriting my url from /base/index.php?page=somepage to /base/somepage(somepage is a .php file in my template folder) using this htaccess file
RewriteEngine On
RewriteBase /base/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^?]*) index.php?page=$1 [L,QSA]
With 1 parameter it works just fine but my problem is that i don't know how to rewrite a second param /base/profile?user=username (with no htaccess file this would have look like this /base/index.php?page=profile?user=username) and i want it to look like this /base/profile/username.
I hope that this question is understandable :-s
Routing is a real issue and I can't be exhaustive in one comment, but I'll try to do my best. Please forgive my aproximative english and let me know if you don't understand. I still have a lot to learn so I'll try to explain through something I made, but it is probably fully improvable.
Today's PHP Standard Recommendation about routing and interpreting request should implement PSR7.
I personnaly use it through a FrontController Design Pattern in a MVC framework I'm building to understand these concepts. My folders are organized like this :
Public :
Where I launch my web server, where you can find JavaScript/CSS. There's an index.php which just contains
require_once('../index.php');
App :
Where there's the router and mostly all generic code
Src :
Where there's the specific code to the app. That means controllers and entities for now.
Vendor :
Composer dependencies such as GuzzleHTTP to have a class between the actual request and the code.
Here's the code in my root's index.php :
<?php
require_once 'vendor/autoload.php';
use Namespace\FrontController;
use \GuzzleHttp\Psr7\ServerRequest;
use function Http\Response\send;
$front_ctrl = new FrontController(ServerRequest::fromGlobals());
send($front_ctrl->getResponse());
The main point of it is that I interpret the request within an instance of a class implementing PSR7.
In my FrontController, my request travel through some methods (such as removing trailing slash) to finnaly be sent in a Router class to be handled.
The purpouse of my Router class is to check if the URI exist in the array where I stocked all my routes under this format in a json file :
{
"/": [
"AppController",
"indexAction",
["GET", "POST"]
],...
}
This is where I use regex to match variable inside the URI (/article/:id for example) too.
This Class can be resumed as "Does this URI exists in my app?".
At this point, I instantiate a new Route class with all the array as parameter. From here, I have to answer questions such as "Is it attached to a method in a controller ?", "Does the method in which it is asked is handled ?" ...
To summarize, at this point, I have an Instance of a Class that represents the Request, another one that represents all my routes. I confront them to get ONE Route which I'm gonna manipulate through an Instance of a Class Route.
If it passed all those tests, then I can instantiate the right Controller, where there will be the logical part specific of the app, requiring some action to get data, that I will send in my views to generate a HTML output which I will send back all the way back to my function send so the output is displayed when you ask for a specific URI.
The main point of this long answer is to show you something that is almost completely independent from the server. It's also useful if your app gets bigger and has to handle more specific rules for routing. It forces you to separates all the bundles of your app : A Controller is not a Model neither a Router...
Try to find some good tutorials to learn Oriented Object Programmation in PHP, which would avoid easy security issues and give you much more comfort when developping an app :)
Hope it was understandable and helpful
Related
First of all, I'm a beginner to PHP. And have posted a question here:
Refactoring require_once file in a project
. I've tried to read about Front controller as much as I can, but can't get how it works or even what's all about.
Can somebody explain in brief how it works and what's all about?
Front Controller refers to a design pattern where a single component in your application is responsible for handling all requests to other parts of an application. It centralizes common functionality needed by the rest of your application. Templating, routing, and security are common examples of Front Controller functionality. The benefit to using this design pattern is that when the behavior of these functions need to change, only a small part of the application needs to be modified.
In web terms, all requests for a domain are handled by a single point of entry (the front controller).
An extremely simple example of only the routing functionality of a front-controller. Using PHP served by Apache would look something like this. Most important step is to redirect all requests to the front controller:
.htaccess
RewriteEngine On
RewriteRule . /front-controller.php [L]
front-controller.php
<?php
switch ($_SERVER['REQUEST_URI']) {
case '/help':
include 'help.php';
break;
case '/calendar':
include 'calendar.php';
break;
default:
include 'notfound.php';
break;
}
I'm trying to learn MVC design pattern for web applications so I decided to write my own PHP MVC framework. Before writing this post I read a lot of tutorials and forums about MVC. Now I pretty well understanding the MVC idea, and how communicate controller-model-view. I have write router and few modules (login, categories, ...) - seems it's working.
Now I'm confused a bit:
If I call localhost/LogIn I get only login form, if I call localhost/categories I get category list. Everything OK, but I want to create index controller and when calling localhost/index I want see login form, categories and a lot more modules.
Should I call controllers (login, categories) from indexController.php?
I need advice how to concatenate needed modules in one page.
No, controllers shouldn't be calling each other's functions. Some frameworks introduce "helpers" to implement what you need.
Controllers can use the same models, and views anyway are going to be different, so you can use your Categories model to provide you categories to display (e.g. $categories->getCategoriesList()) and then using it in category controller view and also in index controller view.
A legitimate method of calling one controller from another is by forming an HTTP request - e.g. receiving an HTML snippet (another controller rendered view) to display in your view via AJAX or using an iframe with a source pointing to your another controller (which is a clumsy solution, mostly for idea illustration).
You need several things:
You need a .htaccess file that will cause all requests to go through your index file, here is a simple one:
RewriteEngine On
RewriteBase /demo
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
In the index.php file you need to set the include path, so you won't have to explicitly include the modules/controllers/views or any other class you choose:
define("APPLICATION_PATH", realpath('.'));
$paths = array(
APPLICATION_PATH.'/controllers',
APPLICATION_PATH.'/models',
APPLICATION_PATH.'/views',
APPLICATION_PATH.'/libs',
APPLICATION_PATH.'/includes',
get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $paths));
now add the 'magic method' for autoloading classes (called automatically) and initialize your Bootsrap class:
function __autoload($className){
$fileName = str_replace('\\','/', $className);
require_once "$fileName.php";
}
new Bootstrap();
Bootstrap.php:
<?php
class Bootstrap {
public function __construct() {
$url = $_GET['url'];
$params = explode('/', $url);
//if controller exist - call it, else call login controller
if (isset($params[0]) && $params[0]){
$controller = new $params[0]();
}
else{
$controller = new login();
}
//if method exist - call it, else call index method
if (isset($params[1]) && $params[1]){
//if parameter exit - call method with param, else call witout param
if (isset($params[2]) && $params[2]){
$controller->$params[1]($params[2]);
}
else{
$controller->$params[1]();
}
}
else{
$controller->index();
}
}
}
That should give you a basic MVC Framework.
Use your controller (index.php) to centralized code that would be used on every page (request validators, error handles, exception handlers, session stuff).
Create a Router class to get the correct models. Allow the models to get the correct views. I have included some UML diagrams from my other answer (https://stackoverflow.com/questions/42172228/is-this-how-an-mvc-router-class-typically-works) to help out. Remember, try to program to an abstract interface, not to a concrete implementation.
I was wondering how one could make codeigniter style url segments on a project.
Aside from an htaccess rule to capture the segments, how can this be done in PHP?
After snooping around the codeigniter source code, i could not find any reference to an htaccess file that captures the usage.
Any idea on how this can be done?
Assuming you are passing ALL requests to index.php via Apache mod_rewrite or Nginx:
// Get the current URL path (only) and convert encoded characters back to unicode
$path = rawurldecode(trim(parse_url(getenv('REQUEST_URI'), PHP_URL_PATH), '/')));
// array merge with defaults
$segments = explode('/', $path) + array('home', 'index');
//First two segments are controller/method
$controller = array_shift($segments);
$method = array_shift($segments);
// #todo serious security checking here!
// Finally, load and call
$controller = new $controller;
$controller->$method($segments);
Your controller would look like this
class Home
{
public function index($params)
{
print_r($params);
}
}
What you do is set up one single url param in htaccess, and then use a string splitting method to retrieve a model, controller, and view from that string which will then call a model class, a controller class, and then render the data into a view. the transformation would be something like the following:
mysite.com/index.php?url=/tasks/all => mysite.com/tasks/all
which calls the task model, which then calls the function called "all()" inside of the tasks controller.
As soon as this site goes back online, do look at the htaccess portion of the tutorial -- he did a good job of showing how its done http://www.henriquebarroso.com/how-to-create-a-simple-mvc-framework-in-php/
You still need something to "forward" all virtual requests to a physical file.
The idea is that any URI that doesn't match a physical file or folder on disk is rewritten (usually through mod_rewrite) to your index.php file (it's usually your index file so a direct call to the index works too), and it appends the URI to the path or as a query string parameter:
RewriteCond %{REQUEST_FILENAME} !-f # Not a real file
RewriteCond %{REQUEST_FILENAME} !-d # Not a real folder
RewriteRule ^(.*)$ index.php/$1 [L] # Rewrite to index.php, with a leading slash, followed by the URI
Alternatively, you can use a standard error document handler (still in .htaccess or apache config, but no need for mod_rewrite!):
<IfModule !mod_rewrite.c>
ErrorDocument 404 /index.php
</IfModule>
Now that control is passed to uniformly to an index.php file, you need a router mechanism to match the route to the correct controller. Ideally, you'd have a list of static and dynamic routes. Static routes are direct matches to the URI, and dynamic would be regular expression matches. Check your static routes first, as it's as simple as a single hash lookup, while you'll have to loop through all the dynamic routes.
For performance, it's nice to put your more common dynamic routes at the beginning of the list, and the obscure ones at the end.
Using the .htaccess file to employ mod_rewrite to rewrite the base of the url is essential to this. Otherwise, the browser will treat each segment as a supposed folder or file (depending on whether or not you have a trailing /)
once you have that, however, you can simply use the method described in this post:
how do i parse url php
First of all, I'm a beginner to PHP. And have posted a question here:
Refactoring require_once file in a project
. I've tried to read about Front controller as much as I can, but can't get how it works or even what's all about.
Can somebody explain in brief how it works and what's all about?
Front Controller refers to a design pattern where a single component in your application is responsible for handling all requests to other parts of an application. It centralizes common functionality needed by the rest of your application. Templating, routing, and security are common examples of Front Controller functionality. The benefit to using this design pattern is that when the behavior of these functions need to change, only a small part of the application needs to be modified.
In web terms, all requests for a domain are handled by a single point of entry (the front controller).
An extremely simple example of only the routing functionality of a front-controller. Using PHP served by Apache would look something like this. Most important step is to redirect all requests to the front controller:
.htaccess
RewriteEngine On
RewriteRule . /front-controller.php [L]
front-controller.php
<?php
switch ($_SERVER['REQUEST_URI']) {
case '/help':
include 'help.php';
break;
case '/calendar':
include 'calendar.php';
break;
default:
include 'notfound.php';
break;
}
The controller in a Java EE
application may be represented by a
servlet, which may be currently
implemented using JavaServer Faces
(JSF).
But in PHP there is no such servlet,so I guess is it implemented by url rewrite?
So that every request is directed to that controller?
It can be done with mod_rewrite but mostly in php there is a front-controller mechanisim which does all controlling through a single file. In this way, all controllers are specified in the url. See this for more explanation about controllers and mvc in php.
I think that's called the Front Controller pattern http://en.wikipedia.org/wiki/Front_controller and usually is achieved via mod_rewrite rules that any requests for phisically nonexisting files is redirected to index.php which then decides what to do.
MVC in PHP typically makes use of a front controller, which serves as the only entry point into the application.
This is usually implemented by using mod_rewrite to point all requests to a php file containing your bootstrap code.
This bootstrap code will contain your front controller.
The front controller takes it from there, redirecting the request based on the input parameters to the appropriate controller. Your target controller is usually specified as one of the parameters.
So, when you hit:
http://yourdomain.com/blog/new
The application would redirect the request to your bootstrap code with your front controller, which will interpret this input to mean 'blog' is the requested controller, 'new' is the action. It would then instantiate the 'blog' controller, pass it the 'new' action, and the rest is standard MVC.
As most of the other answers have shown, usually mod_rewrite is the way to do it. But, if you don't want to use mod_rewrite, you can let your bootstrap file actually configure variables from the URL itself.
I use a couple of my own functions which creates an array from a URL, so:
site.com/page/welcome/param1/param2
becomes
$url[0] = 'page'
$url[1] = 'welcome'
$url[2] = 'param1'
$url[3] = 'param2'
and then I can pass the $url array to my Router and it decides which parts of the Controller/Action/Param call each element belongs to.
The same URL request shown above creates the Controller/Action/Param call:
// http request for site.com/page/welcome/param1/param2
$controller = new Page_Controller('param1', 'param2');
$controller->welcomeAction();
while, depending on the settings in my Router object, I can create subdirectories, such as for calls to 'admin/':
// http request for site.com/admin/page/welcome/param1/param2
$controller = new Admin_Page_Controller('param1', 'param2');
$controller->welcomeAction();
With url_rewrite I think it would be harder (still possible though) to add those reroutes in, and with my method (Some frameworks might also do it, not too sure) it allows you to customize it more, ie you can edit the $url array if needed before passing it to the Router object.
I'm not sure what the negatives are in using this method, but it works pretty well for me!
You would use mod_rewrite to redirect everything to the index.php file. So you use mod_rewrite if you want this:
http://example.com/page/welcome
and not
http://example.com/index.php?page/welcome
or
http://example.com/index.php?controller=page&action=welcome