30

I have a index.php which handle all the routing index.php?page=controller (simplified) just to split up the logic with the view.

Options +FollowSymlinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([\w\d~%.:_\-]+)$ index.php?page=$1 [NC]

Which basically: http://localhost/index.php?page=controller To

http://localhost/controller/

Can anyone help me add the Rewrite for

http://localhost/controller/param/value/param/value (And soforth)

That would be:

http://localhost/controller/?param=value&param=value

I can't get it to work with the Rewriterule.

A controller could look like this:

    <?php
if (isset($_GET['action'])) {
 if ($_GET['action'] == 'delete') {
do_Delete_stuff_here();
}
}
?>

And also:

    <?php
if (isset($_GET['action']) && isset($_GET['x'])) {
 if ($_GET['action'] == 'delete') {
do_Delete_stuff_here();
}
}
?>
Tim Stone
  • 19,119
  • 6
  • 56
  • 66
John
  • 2,900
  • 8
  • 36
  • 65
  • 2
    See also [Everything You Ever Wanted to Know about Mod_Rewrite Rules but Were Afraid to Ask?](http://serverfault.com/questions/214512/everything-you-ever-wanted-to-know-about-mod-rewrite-rules-but-were-afraid-to-ask) – mario Oct 06 '11 at 16:27
  • What you want to do doesn't make much sense imho. I mean, replacing & and = isn't really much of a gain in readability. Additionally it will not allow you to use valueless query strings. – NikiC Oct 27 '11 at 18:10

8 Answers8

54

Basically what people try to say is, you can make a rewrite rule like so:

RewriteRule ^(.*)$ index.php?params=$1 [NC, QSA]

This will make your actual php file like so:

index.php?params=param/value/param/value

And your actual URL would be like so:

http://url.com/params/param/value/param/value

And in your PHP file you could access your params by exploding this like so:

<?php

$params = explode( "/", $_GET['params'] );
for($i = 0; $i < count($params); $i+=2) {

  echo $params[$i] ." has value: ". $params[$i+1] ."<br />";

}

?>
Wesley
  • 2,190
  • 17
  • 26
  • The .htaccess didn't seem to work. I was redirected 404 when I tried to reach localhost/params/param/value. – John Oct 31 '11 at 21:00
  • What happens when you only try to put localhost/params ? – Wesley Oct 31 '11 at 21:39
  • 5
    This is old, but for future reference, I tested this and it DIDN'T WORK (`params` gave me `'index.php'`). I solved it by adding the [QSA flag](https://httpd.apache.org/docs/2.4/rewrite/flags.html#flag_qsa), so, instead of just `[NC]`, I have `[NC,QSA]`. Also, it looked like this: `index.php?params=params/param/value/param/value`, so it would be ok to remove `params` and just keep it like so: `http://url.com/param/value/param/value`. – FirstOne Jan 14 '16 at 22:07
15

I think it's better if you redirect all requests to the index.php file and then extract the controller name and any other parameters using php. Same as any other frameworks such as Zend Framework.

Here is simple class that can do what you are after.

class HttpRequest
{
    /**
     * default controller class
     */
    const CONTROLLER_CLASSNAME = 'Index';

    /**
     * position of controller
     */
    protected $controllerkey = 0;

    /**
     * site base url
     */
    protected $baseUrl;

    /**
     * current controller class name
     */
    protected $controllerClassName;

    /**
     * list of all parameters $_GET and $_POST
     */
    protected $parameters;

    public function __construct()
    {
        // set defaults
        $this->controllerClassName = self::CONTROLLER_CLASSNAME;
    }

    public function setBaseUrl($url)
    {
        $this->baseUrl = $url;
        return $this;
    }

    public function setParameters($params)
    {
        $this->parameters = $params;
        return $this;
    }

    public function getParameters()
    {
        if ($this->parameters == null) {
            $this->parameters = array();
        }
        return $this->parameters;
    }

    public function getControllerClassName()
    {
        return $this->controllerClassName;
    }

    /**
     * get value of $_GET or $_POST. $_POST override the same parameter in $_GET
     * 
     * @param type $name
     * @param type $default
     * @param type $filter
     * @return type 
     */
    public function getParam($name, $default = null)
    {
        if (isset($this->parameters[$name])) {
            return $this->parameters[$name];
        }
        return $default;
    }

    public function getRequestUri()
    {
        if (!isset($_SERVER['REQUEST_URI'])) {
            return '';
        }

        $uri = $_SERVER['REQUEST_URI'];
        $uri = trim(str_replace($this->baseUrl, '', $uri), '/');

        return $uri;
    }

    public function createRequest()
    {
        $uri = $this->getRequestUri();

        // Uri parts
        $uriParts = explode('/', $uri);

        // if we are in index page
        if (!isset($uriParts[$this->controllerkey])) {
            return $this;
        }

        // format the controller class name
        $this->controllerClassName = $this->formatControllerName($uriParts[$this->controllerkey]);

        // remove controller name from uri
        unset($uriParts[$this->controllerkey]);

        // if there are no parameters left
        if (empty($uriParts)) {
            return $this;
        }

        // find and setup parameters starting from $_GET to $_POST
        $i = 0;
        $keyName = '';
        foreach ($uriParts as $key => $value) {
            if ($i == 0) {
                $this->parameters[$value] = '';
                $keyName = $value;
                $i = 1;
            } else {
                $this->parameters[$keyName] = $value;
                $i = 0;
            }
        }

        // now add $_POST data
        if ($_POST) {
            foreach ($_POST as $postKey => $postData) {
                $this->parameters[$postKey] = $postData;
            }
        }

        return $this;
    }

    /**
     * word seperator is '-'
     * convert the string from dash seperator to camel case
     * 
     * @param type $unformatted
     * @return type 
     */
    protected function formatControllerName($unformatted)
    {
        if (strpos($unformatted, '-') !== false) {
            $formattedName = array_map('ucwords', explode('-', $unformatted));
            $formattedName = join('', $formattedName);
        } else {
            // string is one word
            $formattedName = ucwords($unformatted);
        }

        // if the string starts with number
        if (is_numeric(substr($formattedName, 0, 1))) {
            $part = $part == $this->controllerkey ? 'controller' : 'action';
            throw new Exception('Incorrect ' . $part . ' name "' . $formattedName . '".');
        }
        return ltrim($formattedName, '_');
    }
}

How to use it:

$request = new HttpRequest();
$request->setBaseUrl('/your/base/url/');
$request->createRequest();

echo $request->getControllerClassName(); // return controller name. Controller name separated by '-' is going to be converted to camel case.
var_dump ($request->getParameters());    // print all other parameters $_GET & $_POST

.htaccess file:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
satrun77
  • 3,202
  • 17
  • 20
  • This is what I'd recommend as well. Works brilliantly too. – Coreus Feb 21 '13 at 11:44
  • Hi. I've used your solution and it works perfect on local-host. But when I try to deploy my application on in-motion hosting server it doesn't work at all. Could you possibly tell me what does mean each line of .htaccess directives? Maybe I can solve the problem if I understand the meaning of those Rewrite directives. Thanks. – a.toraby Nov 11 '13 at 21:05
  • 1 - Enable rewrite engine 2 - If the request is for existing file OR 3 - If the request is for existing symlink OR 4 - If the request is for existing directory. 5 - If any of these conditions is true, then serve the request as normal. ELSE 6 - redirect the request to the index.php – satrun77 Nov 15 '13 at 09:06
10

Your rewrite rule would pass the entire URL:

RewriteRule ^(.*)$ index.php?params=$1 [NC]

Your index.php would interpret that full path as controller/param/value/param/value for you (my PHP is a little rusty):

$params = explode("/", $_GET['params']);
if (count($params) % 2 != 1) die("Invalid path length!");

$controller = $params[0];
$my_params = array();
for ($i = 1; $i < count($params); $i += 2) {
  $my_params[$params[$i]] = $params[$i + 1];
}
Kevin Stricker
  • 17,178
  • 5
  • 45
  • 71
6

For some reason, the selected solution did not work for me. It would constantly only return "index.php" as value of params.

After some trial and error, I found the following rules to work well. Assuming you want yoursite.com/somewhere/var1/var2/var3 to point to yoursite.com/somewhere/index.php?params=var1/var2/var3, then place the following rule in a .htaccess file in the "somewhere" directory:

Options +FollowSymLinks
RewriteEngine On
# The first 2 conditions may or may not be relevant for your needs
# If the request is not for a valid file
RewriteCond %{REQUEST_FILENAME} !-d
# If the request is not for a valid directory
RewriteCond %{REQUEST_FILENAME} !-f
# This rule converts your flat link to a query
RewriteRule ^(.*)$ index.php?params=$1 [L,NC,NE]

Then, in PHP or whichever language of your choice, simply separate the values using the explode command as pointed out by @Wesso.

For testing purposes, this should suffice in your index.php file:

if (isset($_GET['params']))
{
    $params = explode( "/", $_GET['params'] );
    print_r($params);
    exit("YUP!");
}
Nick
  • 2,573
  • 19
  • 21
6

How about redirect to index.php?params=param/value/param/value, and let php split the whole $_GET['params']? I think this is the way wordpress handling it.

OpenGG
  • 4,345
  • 2
  • 24
  • 31
  • Creating a rewrite rule for this would be possible, but it's so much simpler to just have the php code handle it. – Jeff Day Oct 06 '11 at 16:19
5

Is this what your looking for?

This example demonstrates how to easily hide query string parameters using loop flag. Suppose you have URL like http://www.mysite.com/foo.asp?a=A&b=B&c=C and you want to access it as http://www.myhost.com/foo.asp/a/A/b/B/c/C

Try the following rule to achieve desired result:

RewriteRule ^(.*?\.php)/([^/]*)/([^/]*)(/.+)? $1$4?$2=$3 [NC,N,QSA]

Kevin Stricker
  • 17,178
  • 5
  • 45
  • 71
Doug Chamberlain
  • 11,192
  • 9
  • 51
  • 91
  • So, I just attempted to actually test this rule, and my shared host immediately froze up. I don't want to D/V due to it being bad advice without confirmation that it's a very dangerous rule (could have been something else, or even my other rules contributing to the problem) but at the same time I don't really want to try again and potentially irritate my hosting provider more. – Kevin Stricker Nov 03 '11 at 14:55
  • It was definitely this rule. Tried on a clean WAMP install. It does seem like a solid idea, but far too dangerous in current form. – Kevin Stricker Nov 03 '11 at 15:24
1

Are you sure you are using apache server,.htaccess works only on apache server. If you are using IIS then web.config is reqired. In that case:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
        <rule name="Homepage">
                    <match url="Homepage"/>
                    <action type="Rewrite" url="index.php" appendQueryString="true"/>
                </rule>
</rules>
        </rewrite>


        <httpErrors errorMode="Detailed"/>
        <handlers>
            <add name="php" path="*.php" verb="*" modules="IsapiModule" scriptProcessor="C:\Program Files\Parallels\Plesk\Additional\PleskPHP5\php5isapi.dll" resourceType="Unspecified"/>
        </handlers>




    </system.webServer>
</configuration>
Sibu
  • 4,609
  • 2
  • 26
  • 38
-1

use QSA

blabla.php?originialquery=333 RewriteRule ^blabla.php index.php?addnewquery1=111&addnewquery2=222 [L,NC,QSA] you will get all 111,222,333

Hakan
  • 240
  • 3
  • 4