I have a problem with PHP 8 routing. I'm creating my MVC "framework" to learn more about OOP. I did routing, controller and view - it works, I'm happy. But I found out that I would test my creation. This is where the problem begins, namely if the path is empty (""), it returns the "Home" view as asked in the routing, but if I enter "/ test" in the URL, for example, I have a return message "404 Not Found - The requested URL was not found on this server. " even though the given route is added to the table. What is wrong?
.htaccess file
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ index.php [QSA,L]
index.php with routes
<?php
/*
* Require Composer
*/
require dirname(__DIR__) . '/vendor/autoload.php';
/*
* Routing
*/
$routes = new Core\Router();
$routes->new('', ['controller' => 'HomeController', 'method' => 'index']);
$routes->new('/test', ['controller' => 'HomeController', 'method' => 'test']);
$routes->redirectToController($_SERVER['QUERY_STRING']);
Router.php file
<?php
namespace Core;
class Router
{
/*
* Routes and parameters array
*/
protected $routes = [];
protected $params = [];
/*
* Add a route to the route board
*/
public function new($route, $params = [])
{
/*
* Converting routes strings to regular expressions
*/
$route = preg_replace('/\//', '\\/', $route);
$route = preg_replace('/\{([a-z]+)\}/', '(?P<\1>[a-z-]+)', $route);
$route = '/^' . $route . '$/i';
$this->routes[$route] = $params;
}
/*
* Get all routes from table
*/
public function getRoutesTable() {
return $this->routes;
}
/*
* Checking if the specified path exists in the route table
* and completing the parameters table
*/
public function match($url)
{
foreach($this->routes as $route => $params) {
if(preg_match($route, $url, $matches)) {
foreach($matches as $key => $match) {
if(is_string($key)) {
$params[$key] = $match;
}
}
$this->params = $params;
return true;
}
}
return false;
}
/*
* Get all params from table
*/
public function getParamsTable() {
return $this->params;
}
/*
* Redirection to the appropriate controller action
*/
public function redirectToController($requestUrl) {
$requestUrl = explode('?', $requestUrl);
$url = $requestUrl[0];
if ($this->match($url)) {
$controller = $this->params['controller'];
$controller = "App\\Controllers\\$controller";
if(class_exists($controller)) {
$controller_obj = new $controller($this->params);
$method = $this->params['method'];
if(method_exists($controller, $method)) {
$controller_obj->$method();
} else {
echo "Method $method() in controller $controller does not exists!";
}
} else {
echo "Controller $controller not found!";
}
} else {
echo "No routes matched!";
}
}
}
View.php file
<?php
namespace Core;
class View {
public static function view($file) {
$filename = "../App/Views/$file.php";
if(file_exists($filename)) {
require_once $filename;
} else {
echo "View $file is not exist!";
}
}
}
HomeController file
<?php
namespace App\Controllers;
use Core\Controller;
use Core\View;
class HomeController extends Controller {
public function index() {
return View::view('Home');
}
public function test() {
return View::view('Test');
}
}
Folder structure
Routes in web browser
I'm using XAMPP with PHP 8.0 and Windows 10.
First of all, choose one of the following two options to proceed.
Option 1:
If the document root is set to be the same as the project root, e.g. path/to/htdocs, in the config file of the web server (probably httpd.conf), similar to this:
DocumentRoot "/path/to/htdocs"
<Directory "/path/to/htdocs">
AllowOverride All
Require all granted
</Directory>
then create the following .htaccess in the project root, remove any .htaccess file from the directory path/to/htdocs/Public and restart the web server:
<IfModule dir_module>
DirectoryIndex /Public/index.php /Public/index.html
</IfModule>
Options FollowSymLinks
RewriteEngine On
RewriteBase /Public/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Option 2:
If the document root is set to be the directory path/to/htdocs/Public in the config file of the web server (probably httpd.conf), similar to this:
DocumentRoot "/path/to/htdocs/Public"
<Directory "/path/to/htdocs/Public">
AllowOverride All
Require all granted
</Directory>
then create the following .htaccess in the directory path/to/htdocs/Public, remove any other .htaccess file from the project root (unless it, maybe, contains some relevant settings, other than DirectoryIndex) and restart the web server::
Options FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
After you decided and proceeded with one of the options above, edit the page index.php as shown bellow:
<?php
//...
/*
* NOTE: A route pattern can not be an empty string.
* If only the host is given as URL address, the slash
* character (e.g. "/") will be set as value of the
* URI path, e.g. of $_SERVER['REQUEST_URI']. Therefore,
* the route pattern should be set to "/" as well.
*/
$routes->new('/', [
'controller' => 'HomeController',
'method' => 'index',
]);
//...
/*
* In order to get the URI path, you must use the server
* parameter "REQUEST_URI" instead of "QUERY_STRING".
*/
$routes->redirectToController(
$_SERVER['REQUEST_URI']
);
Notes:
In regard of changing $_SERVER['QUERY_STRING'] to $_SERVER['REQUEST_URI'], the credits go to #MagnusEriksson, of course.
There can't be an "MVC framework", but a "web framework for MVC-based application(s)". This is, because a framework is just a collection of libraries (not at all correlated with MVC), which, in turn, is used by one or more applications implementing the MVC pattern.
For any further questions, don't hesitate to ask us.
I tried to use the AltoRouter on my webspace in a subfolder. But the matches always false!
I have this structure on my webspace
domain.com
--/2 --> subfolder
----index.php
----.htacces
My .htaccess file looks like this:
DirectoryIndex index.php
RewriteEngine on
RewriteBase /2/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
So I setup up the router like this:
<?php
session_start();
require_once 'vendor/autoload.php';
use LemWebPortal\Config;
$latte = new Latte\Engine;
$router = new AltoRouter();
$router->setBasePath('/2/');
$router->map('GET', '/', function () {
require __DIR__ . '/src/Init/index.php';
});
$router->map('GET', '/schulungsbedarf', function () {
require __DIR__ . '/src/Init/courseRequest.php';
});
$match = $router->match();
if ($match && is_callable($match['target'])) {
call_user_func_array($match['target'], $match['params']);
} else {
// no route was matched
header($_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
}
echo '<pre>';
var_dump($match);
Now, when I call domain.com/2/or domain.com/2/schulungsbedarfI see an empty white page with this message:
bool(false)
What did I miss here?
I am using Altorouter in a basic PHP App(No framework) but somehow it's not working. Below are details:
index.php
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
require_once __DIR__ . '/vendor/autoload.php';
$router = new AltoRouter();
$router->map( 'GET', '/', function() {
include __DIR__ . 'home.php';
});
print "Done";
It prints Done and no error in php log.
htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
I am access it as `http://localhost/home/myapp/
Ok I figured out the issue. The URL I want to access is:
http://localhost/home/myapp/
Altorouter does not know about root URL so basePath needs to be set. it is done as:
$router->setBasePath('/home/myapp');
Do note that there's no trailing / should be put in setBasePath because we will put that in our map function like that:
$router->map('GET', '/', 'home.php', 'home');
$match = $router->match();
if ($match) {
require $match['target'];
} else {
header("HTTP/1.0 404 Not Found");
require '404.html';
}
I want to run my activeIndex() function in GuestsController.php file under a subdirectory as the first default page when the user enters the url.
For this reason, I have written such a script in my .htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/controllers/GuestsController.php/.*
RewriteRule ^/mvc/(.*) /controllers/GuestsController.php?method=$actionList?page=$1 [QSA,L]
And my GuestsController.php file is:
<?php
class GuestsController
{
public function actionIndex()
{
echo "start";
sleep(1);
include(dirname(__FILE__) . '/../views/guests/index.php' );
}
public function actionList()
{
$guests = Guests::findAll();
echo json_encode( $guests );
}
}
I still could not manage to achieve my goal.
How I should modify .htaccess file to run actionIndex() function directly?
Thanks,
i am new to MVC i have created a routing class below, it is working fine but when i go to the index page the navigation anchor href are correct. but when i move to other controller the url first string which is url-0 , is still the previous controller, which change all navigation href address base+previous controller, for e.g if i am on indexController/index which will display all the pages froom database. and when i want to call logincontroller through navigation anchor the logincontroller href change it becomes indecontroller/loginController/login. the correct login href address is loginController/login. my htaccess and routing class is below.and folder structure.
mvc app controllers indexcontroller.php userController.php
model user.php page.php
lib
core App.php Controller.php
includes navigation.php
style style.css
images
js javscript
index.php
I hope some one can help me, i tried but so success yet Please Help Thanks in advance.
RewriteEngine On
RewriteBase /socialNetwork
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^([a-zA-Z0-9\/\-_]+)\.?([a-zA-Z]+)?$ index.php?url=$1&extension=$2 [QSA,L]
class App
{
protected $controller = 'indexController';
protected $method = 'index';
protected $params = array();
public function __construct()
{
$url = $this->parseUrl();
//print_r($url);
if (isset($url[0]))
{
if (file_exists('app/controllers/'.$url[0].'.php'))
{
//$url = ('../app/controllers/'.$url[0].'.php');
$this->controller = $url[0];
//echo ($this->controller);
unset($url[0]);
}
}
require_once('app/controllers/'.$this->controller.'.php');
$this->controller = new $this->controller;
if (isset($url[1]))
{
if (method_exists($this->controller,$url[1]))
{
$this->method = $url[1];
unset($url[1]);
}
}
$this->params = $url ? array_values($url) : array();
call_user_func_array(array($this->controller,$this->method),$this->params);
}
public function parseUrl()
{
if (isset($_GET['url']))
{
return $url =explode('/',filter_var(rtrim($_GET['url'],'/'),FILTER_SANITIZE_URL));
}
}
}
this is my index page.
<header>
<img width="960" height="100" src="http://localhost/socialNetwork/images/bgheader.png">
</header>
<?php
include_once("include/navigation.php");?>
<section class="content">
<?php
require_once('app/init.php');
$app = new App;
?>
</section>
You should not have relative url's in your view files (html-files). Check out some common MVC-like PHP frameworks like Laravel or Symfony. They all have methods which provides you the full qualified url for you vew-files like: http://domain.tld/controller/param/... instead of /cobntroller/..
Implementing this should solve your problem.
For example check out Laravel. It's very easy to understand and pretty flexible. There you have access to all your route with functions like Redirect::route('your-route-name'). The routing engine will then translate this into the full url!
EDIT: Just to be more precise, laravel is not a real mvc by definition. But in some parts it acts like a mvc.
Hope this helps