History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: ZF-262
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: Michal Minicki
Reporter: Laurent Taupiac.
Votes: 3
Watchers: 2
Operations

If you were logged in you would be able to see more operations.
Google issue summary
Zend Framework

RewriteRouter: weird rewriteBase

Created: 17/Jul/06 02:12 AM   Updated: 05/Jul/07 02:43 PM
Component/s: Zend_Controller
Affects Version/s: 0.9.0
Fix Version/s: 0.2.0

Time Tracking:
Not Specified

Issue Links:
Duplicate
 

Tags:
Participants: Gavin, Laurent Taupiac., Michal Minicki, Mislav Marohni?, Sergey Belov and Simon Mundy


 Description  « Hide
According to documentation, rewrite base (RB later ) should be set to the directory from where the index.php is loaded
I did my tests with rewriting rules in httpd.conf and .htaccess, any url starting with /zf is rewrote to /zf/index.php unless file exist

I did my test in subdir called /zf

for url = http://instdev2.dev.fdj.fr/zf/ctl/action/
RB is null, should be /zf

for url = http://instdev2.dev.fdj.fr/zf/ctl/action/param
RB is null, should be /zf

for url = http://instdev2.dev.fdj.fr/zf/ctl/action/param?bingo
RB is null, should be /zf

for url = http://instdev2.dev.fdj.fr/zf/ctl/action/param/index.php (index.php doesn't exist here)
RB is http://instdev2.dev.fdj.fr/zf/ctl/action/param/index.php, should be /zf

for url = http://instdev2.dev.fdj.fr/zf/ctl/action?test=index.php (index.php doesn't exist here)
RB is http://instdev2.dev.fdj.fr/zf/ctl/action, should be /zf

for url = http://instdev2.dev.fdj.fr/zf/ctl/action/index.phpold (index.php doesn't exist here)
RB is http://instdev2.dev.fdj.fr/zf/ctl/action/index.phpold, should be /zf

http://instdev2.dev.fdj.fr/zf/ctl/action/rolindex.php
RB is http://instdev2.dev.fdj.fr/zf/ctl/action/rolindex.php, should be /zf

Unless i misunderstood the rewriteBase, it could be set to

$_rewriteBase = dirname(str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']));



 All   Comments   Work Log   Change History   FishEye   Crucible      Sort Order: Ascending order - Click to sort in descending order
Michal Minicki - 17/Jul/06 05:15 AM
Let's begin with your rewriteBase detection - it triggers 9 unit test failures.

First three should work while they don't on your configuration. What's your PHP version? What is your operating system and what is your webserver?

For 4-7 I will prepare a fix but let's resolve your problem first.


Michal Minicki - 17/Jul/06 06:08 AM
Ok, failed test are becouse DOCUMENT_ROOT is not set in tests. I'll look into this.

Michal Minicki - 17/Jul/06 07:09 AM
Could you test something for me, Laurent? Test if it works - change rewrite detection in RewriteRouter constructor to this:
$base = dirname(str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']));
$base = rtrim(str_replace('\\', '/', $base), '/'); // windows and unix dirname
$filename = basename($_SERVER['SCRIPT_FILENAME']);
if (strpos($_SERVER['REQUEST_URI'], $filename) !== false) {
    $base .= '/' . $filename;
}
$this->_rewriteBase = rtrim($base, '/');

Checking for filename is there for testing if RewriteEngine is off (it may be replaced by different code). Of course 4-7 will still not work. I will givie it a try on my Windows machine in a couple of hours - on Apache and IIS.


Michal Minicki - 17/Jul/06 07:34 AM
I have optimized it a bit:
$base = str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']);
if (strpos($_SERVER['REQUEST_URI'], basename($_SERVER['SCRIPT_FILENAME'])) === false) {
     $base = str_replace('\\', '/', dirname($base)); // windows and unix dirname
}
$this->_rewriteBase = rtrim($base, '/');

Laurent Taupiac. - 17/Jul/06 07:52 AM
http://instdev2.dev.fdj.fr/zf/ctl/action/
RB=/zf OK but Exception raised

http://instdev2.dev.fdj.fr/zf/ctl/action/param
RB=/zf OK but Exception raised

http://instdev2.dev.fdj.fr/zf/ctl/action/param/index.php
RB=/zf/index.php NOK - Should be /zf

http://instdev2.dev.fdj.fr/zf/ctl/action/param?bingo
RB=/zf OK but Exception raised

http://instdev2.dev.fdj.fr/zf/ctl/action/param/index.php
RB=/zf/index.php NOK - Should be /zf

http://instdev2.dev.fdj.fr/zf/ctl/action?test=index.php
RB=/zf/index.php NOK - Should be /zf + Exception raised

http://instdev2.dev.fdj.fr/zf/ctl/action/index.phpold
RB=/zf/index.php NOK - Should be /zf

http://instdev2.dev.fdj.fr/zf/ctl/action/rolindex.php
RB=/zf/index.php NOK - Should be /zf
-----------------------------------------------------------------------------------------------------
When Exception was raised, it was
/sg2/instdev2/htdocs/libfdj/classes/Zend/Controller/RewriteRouter.php : ligne 138
Request could not be mapped to a route.
-----------------------------------------------------------------------------------------------------
// This is the code where i have in front to add my route
$router->addRoute('tit',':controller/:action/:param',array('param'=>'default param value'));
$C->setRouter($router);


Laurent Taupiac. - 17/Jul/06 08:09 AM
Forget exception... It come from a type in url test.

Michal Minicki - 17/Jul/06 08:28 AM
May I ask for another test?
$base = str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']);
if (!strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME'])) // for Apache config rewrite
    || strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === false) {
    $base = str_replace('\\', '/', dirname($base)); // windows and unix dirname
}
$this->_rewriteBase = rtrim($base, '/');

Laurent Taupiac. - 17/Jul/06 08:47 AM
Following url still have RB set to /zf/index.php and then are misrouting to Index Controller instead of ctl Controleur

http://instdev2.dev.fdj.fr/zf/ctl/action/param/index.php
http://instdev2.dev.fdj.fr/zf/ctl/action/index.phpold
http://instdev2.dev.fdj.fr/zf/ctl/action/rolindex.php


Michal Minicki - 19/Jul/06 07:13 AM
Where do you keep your RewriteRule directive? I know you said you have tested it in .htaccess and config files but I mean where you have kept it while testing the above code? I'll try to reproduce.

The problem is when you place RewriteRule in <VirtualHost> because $_SERVER['SCRIPT_NAME'] is initialized incorrectly. But when you use it in .htaccess or <Directory> all is well. Could you confirm it?


Michal Minicki - 19/Jul/06 07:38 AM
And hopefully last code modification:
$base = str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']);
if (isset($_SERVER['SCRIPT_URL']) // for Apache config <VirtualHost> rewrite compatibility
    || strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === false) {
    $base = str_replace('\\', '/', dirname($base)); // windows and unix dirname
}
$this->_rewriteBase = rtrim($base, '/');

Laurent Taupiac. - 19/Jul/06 07:51 AM
Indeed, i did my first tests on both .htaccess and httpd.conf with a virtual host.

But, after, i was testing only in virtual host wich is my production configuration...
My appologies for this mis-understood

I did test again on both..
Your right, the problem is only on virtual host. and $_SERVER['SCRIPT_NAME'] is initialized to the user query instead of to rewriting query

I dont have time to finish this comment that you posted some new code...
I tested it, and it s seem to work on both .htacess and virtual host now.

Nice job


Michal Minicki - 21/Jul/06 05:06 AM
Please leave it open, I still have to commit it to svn. But first I have to test it on different setups.

Sergey Belov - 04/Aug/06 02:11 PM
what did you do with detectRewriteBase() ?
public function detectRewriteBase() {
    $base = '';
    if (!isset($_SERVER['PATH_INFO'])) $base = $_SERVER['REQUEST_URI'];
    else if ($pos = strpos($_SERVER['REQUEST_URI'], $_SERVER['PATH_INFO'])) {
        $base = substr($_SERVER['REQUEST_URI'], 0, $pos);
    };
    return rtrim($base, '/');
}

this line is terrible, now rewrite base alway equals to REQUEST_URI

if (!isset($_SERVER['PATH_INFO'])) $base = $_SERVER['REQUEST_URI'];

Michal Minicki - 04/Aug/06 02:18 PM
If there is no PATH_INFO then you're at root application directory (on index.php) and then Request URI is your rewrite base.

If you're not on your root, then you've hit a bug - try moving RewriteRule from <VirtualHost> to <Directory> or .htaccess.


Sergey Belov - 04/Aug/06 05:10 PM
Michael Minicki, you are not right, $_SERVER['REQUEST_URI'] contains GET request string

detectRewriteBase() must be like this, it fixes the problem

public function detectRewriteBase() {
    $base = '';
    if (!isset($_SERVER['PATH_INFO'])) {
        $filename = basename($_SERVER['SCRIPT_FILENAME']);
        $base = $_SERVER['SCRIPT_NAME'];
        if (strpos($_SERVER['REQUEST_URI'], $filename) === false) {
            // Default of '' for cases when SCRIPT_NAME doesn't contain a filename (ZF-205)
            $base = (strpos($base, $filename) !== false) ? dirname($base) : '';
        }
    }
    else if ($pos = strpos($_SERVER['REQUEST_URI'], $_SERVER['PATH_INFO'])) {
        $base = substr($_SERVER['REQUEST_URI'], 0, $pos);
    }
    return rtrim($base, '/\\');
}

Michal Minicki - 05/Aug/06 02:29 AM
Of course it contains GET URI. And for URL of 'http://localhost/test/index.php/archive/2006/05' and 'http://localhost/test/archive/2006/05' request URI is set to '/test/index.php/archive/2006/05' or '/test/index.php/archive/2006/05' and path info SHOULD be set to '/archive/2006/05'. It's not empty and is just subtracted from request uri leaving '/test/index.php' or '/test' as rewrite base.

If you request application root - 'http://localhost/test/index.php' and 'http://localhost/test/' then path info is not present and your reqest URIs are your rewrite bases - '/test/index.php' and '/test/'.

BUT! There is a bug (probably in php) which makes path_info unreliable on certain conditions. It doesn't get initialized when it should. See:

http://bugs.php.net/bug.php?id=38141
http://issues.apache.org/bugzilla/show_bug.cgi?id=40102

And check if this is your case.

PS. As I told you before - move your RewriteRules to htaccess and you'll see.



Simon Mundy - 05/Aug/06 10:57 PM
I'm getting unexpected results, too! The method 'detectRewriteBase()' is always returning '/activities/ssc' from the address 'http://www.client.dev/activities/ssc'

This is the current $_SERVER values:-

[SERVER_SOFTWARE] => Apache/2.2.0 (Fedora)
    [SERVER_NAME] => www.client.dev
    [SERVER_ADDR] => 10.0.1.250
    [SERVER_PORT] => 80
    [REMOTE_ADDR] => 10.0.1.250
    [DOCUMENT_ROOT] => /home/aaa/bbb/www.client.dev/docs
    [SERVER_ADMIN] => root@localhost
    [SCRIPT_FILENAME] => /home/aaa/bbb/www.client.dev/docs/index.php
    [REDIRECT_URL] => /activities/ssc
    [GATEWAY_INTERFACE] => CGI/1.1
    [SERVER_PROTOCOL] => HTTP/1.0
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => 
    [REQUEST_URI] => /activities/ssc
    [SCRIPT_NAME] => /index.php
    [PHP_SELF] => /index.php
    [REQUEST_TIME] => 1154834871

The paths are set up as follows:-

$router = new Zend_Controller_RewriteRouter();
$router->addRoute('activities',
   new Zend_Controller_Router_Route('activities/:action/:id/:sid',
      array('controller' => 'activities', 'action' => 'index', 'id' => 'index', 'sid' => ''),
      array('action' => '^\w+$', 'id' => '^\w+$', 'sid' => '^\w+$')));
$router->addRoute('training',
   new Zend_Controller_Router_Route('training/:id/:sid',
      array('controller' => 'training', 'action' => 'index', 'id' => 'index', 'sid' => ''),
      array('id' => '^\w+$', 'sid' => '^\w+$')));

$front = Zend_Controller_Front::getInstance();
$front->setRouter($router);
$front->setControllerDirectory(PeptoCMS::path('app') . '/controllers');
$front->dispatch();

I've noticed a few ways of determing the auto base url from above but these seem to give inconsistent results. For example $_SERVER['DOCUMENT_ROOT'] will only be set on an Apache webserver, not IIS.

What I can't understand is how useful this feature is - surely if you've set your rewriting for ANYTHING to resolve to /test/index.php and your URL is http://www.client.dev/test/my/test then the request URI will still always be /my/test - what sort of circumstances would provide different results?


Simon Mundy - 05/Aug/06 11:09 PM
Sorry - forgot to add the patch
public function detectRewriteBase()
    {
        $base = '';
        if (!empty($_SERVER['PATH_INFO'])) {
            if (($pos = strpos($_SERVER['REQUEST_URI'], $_SERVER['PATH_INFO'])) !== false) {
                $base = substr($_SERVER['REQUEST_URI'], 0, $pos);
            }
        }
        return rtrim($base, '/');
    }

Sergey Belov - 06/Aug/06 03:25 AM
Michael Minicki, it seems to be a problem with apache 1.3.x only (i have this one on my dev server). $_SERVER doesn't have PATH_INFO key in all cases. But, it has key ORIG_PATH_INFO.

Here is my situation:

php runs as cgi

.htaccess contents

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !\.(swf)|(jpg)|(png)|(gif)$
RewriteRule ^(.*) index.php5

DirectoryIndex index.php5

doc_root=/home/site/www/

bootstrap file in /home/site/www/somedir/index.php5

get request: http://site/somedir/aaa/bbb/

$_SERVER contents

array(40) {
  ["DOCUMENT_ROOT"] => string(21) "w:/home/site/www"
  ["HTTP_HOST"] => string(9) "site"
  ["REDIRECT_REDIRECT_STATUS"] => string(3) "200"
  ["REDIRECT_STATUS"] => string(3) "200"
  ["REDIRECT_URL"] => string(15) "/somedir/index.php5"
  ["SCRIPT_FILENAME"] => string(36) "w:\home\site\www\somedir\index.php5"
  ["REQUEST_URI"] => string(18) "/somedir/aaa/bbb/"
  ["SCRIPT_NAME"] => string(15) "/somedir/index.php5"
  ["ORIG_PATH_TRANSLATED"] => string(36) "w:\home\site\www\somedir\index.php5"
  ["ORIG_PATH_INFO"] => string(15) "/somedir/index.php5"
  ["ORIG_SCRIPT_NAME"] => string(18) "/_php5/php-cgi.exe"
  ["ORIG_SCRIPT_FILENAME"] => string(27) "/usr/local/php5/php-cgi.exe"
  ["PHP_SELF"] => string(15) "/somedir/index.php5"
}

Michal Minicki - 06/Aug/06 04:15 AM
Simon, I'll comment your example. If you set your rewriting for ANYTHING to resolve to /test/index.php and your URL is http://www.client.dev/test/my/test then you SHOULD have those variables set to:

REQUEST_URI: /test/my/test
PATH_INFO: /my/test

I know it's not like that in your case but it's how it should be according to CGI specification. It's an Apache/PHP bug.

Guys, move your rules please. If you have set them like that:

<VirtualHost *:80>

        ServerName test.intranet
        DocumentRoot /var/www/test/htdocs

        RewriteEngine on
        RewriteCond %{SCRIPT_FILENAME} !-f
        RewriteCond %{SCRIPT_FILENAME} !-d
        RewriteRule ^(.*)$ index.php/$1

</VirtualHost>

Just set them this way:

<VirtualHost *:80>

        ServerName test.intranet
        DocumentRoot /var/www/test/htdocs

        <Directory /var/www/test/htdocs>
                RewriteEngine on
                RewriteCond %{SCRIPT_FILENAME} !-f
                RewriteCond %{SCRIPT_FILENAME} !-d
                RewriteRule ^(.*)$ index.php/$1
        </Directory>

</VirtualHost>

It works with Apache 2 this way. If placed directly under VirtualHost you have a problem with variables initialized incorrectly.

I would like to avoid hacking for avoiding other software bugs. If you insist on keeping your RewriteRules in the old place, then just use setRewriteBase for now. Set your bases accordingly - in your example, Simon, it should be set to '/test'.

I am waiting for PHP and Apache devs to come up with a solution. If they won't resolve it quickly, I will contact Zend and ask for their decision.


Michal Minicki - 06/Aug/06 04:18 AM
And for the inconsistent results and DOCUMENT_ROOT not set on IIS. That's why I want to stick only to vars available in CGI specification. They should be supported by any web server which complies with it.

Simon Mundy - 06/Aug/06 05:39 AM
Hi Martel - I'll give that a try and confirm that works.

However wouldn't the patch I provided still work in either case? If PATH_INFO is not set, don't apply the base_url. If it is, remove it from the REQUEST_URI and it resolves correctly.

I don't have problems modifying my Apache configs, but perhaps there are some (albeit unlucky) users who are restricted by how much they can play with their config files.


Simon Mundy - 06/Aug/06 05:54 AM
OK - Have just checked and (must have slipped my mind) but the Rewrite directives ARE already in the directory scope, not the virtualhost. Are the 'multiviews' likely to cause trouble with the PATH_INFO? AcceptPathInfo is not set in either the global nor virtualhost scope.

Here's the config for clarity:

DocumentRoot "/home/aaa/bbb/www.client.dev/docs"
ServerName www.client.dev  
ServerAdmin webmaster@peptolab.com
DirectoryIndex index.php index.html
<Directory "/home/aaa/bbb/www.client.dev/docs">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
AllowOverride None
Order allow,deny
Allow from all
</Directory>

Michal Minicki - 07/Aug/06 03:36 AM
Simon, you can't ignore the rewrite base if path_info is empty. If you have a file of /test/index.php and a URL of http://localhost/test/ then CGI vars should be:

REQUEST_URI: '/test'
PATH_INFO: '' (I guess Apache does not set it if it's empty)

So the rewrite base has to be '/test' and not '/'.

The problem is, whichever approch to detect rewritebase you choose you hit some problems. The code I have proposed to Laurent won't work with CGI version of PHP for instance (I mean CGI under Apache).

The current solution at least uses a standard and well documented variables.

I think it would be best if you could provide some feedback on PHP and Apache bug trackers. If the devs see the problem touches more than one person they may be motivated better:

http://bugs.php.net/bug.php?id=38141
http://issues.apache.org/bugzilla/show_bug.cgi?id=40102


Mislav Marohni? - 04/Sep/06 10:09 AM
I've just moved a site from a host that had mod_php to a one that runs it as FCGI, and have been hit by this issue. My routing was broken, and I don't even have a rewrite base because the site is in document root. The problem was that, instead of figuring out that rewrite base is an empty string, RewriteRouter thought rewrite base was the whole REQUEST_URI because of this line:

if (empty($_SERVER['PATH_INFO'])) $base = $_SERVER['REQUEST_URI'];

Now, I don't quite understand the whole background and complexity of this problem in different environments, but it should be obvious that the primary goal of detectRewriteBase() should be positively detecting that there is no rewrite base when the site is in root. Detecting the rewrite base when there really is a prefix only comes in second. What good is automatic detection of base when the same feature breaks routing if site is in root?


Gavin - 06/Sep/06 01:01 PM
I recommend we move this issue completely out of the core ZF components. Instead, I suggest using an empty string as the default rewriteBase, if no default is given in the ZF global "INI" (proposal coming soon).

Then, I propose moving all code (also see ZF-288) involved in detectRewriteBase() to an external ZF "configuration" script. The script would use Zend_Environment, perform some tests, make a best guess for rewriteBase, store the guess into the ZF global "INI", and ask permission to submit the test results back to our new community server (more information on that server soon).

The test should be interactive, such the the user immediately sees and corrects any problems. Thus, test results submitted back to our data collection process on the community server should have a reasonable degree of accuracy.

Lastly, I suggest asking for volunteers to work together in creating the test script, data collection process, and work on analyzing the results and improving the test script.


Gavin - 06/Sep/06 05:18 PM
One alternative to consider:

Use Zend_Cache instead to seed the default value in the registry. If the rewriteBase has not been set, or the "signature" of the key $_SERVER variables has changed, then load the source code for calculating rewriteBase, make the calculation, and store the result to Zend_Cache. This would add Zend_Cache as a required component for many "Hello World!" programs using the rewriteRouter, adding weight to even the simplest of ZF applications.

Another variation of the alternative above:

Use a global Zend "INI" file for caching a small number of super simple auto-generated things, like a calculated rewriteBase, thus avoiding requiring Zend_Cache.


Michal Minicki - 08/Nov/06 08:37 AM
Rewrite base detection (and handling) was recently moved to Request class (with a major rewrite to whole Controller module). At the time of writing modified code is evaluated in the incubator.