ZF-2052: base url not detected when it has urlencoded characters

Description

When the base url has a space in it (or whatever urlencoded character I guess) the Zend_Controller_Request_Http object does not detect it properly.

A simple urldecode() inside Zend_Controller_Request_Http::setRequestUri() will fix that

Comments

Scheduling for 1.0.3

Patched in trunk and release-1.0 branch.

Changing this behaviour actually breaks many applications, particularly those that pull from getPathInfo(). I am reverting this change.

If you need this functionality, subclass the request object as follows:


class My_Request_Http extends Zend_Controller_Request_Http
{
    public function setRequestUri($requestUri = null)
    {
        parent::setRequestUri($requestUri);
        $this->_requestUri = urldecode($this->_requestUri);
        return $this;
    }
}

Will not fix, as it breaks other behaviours.

I'm suggesting another fix that won't change {{Zend_Controller_Request_Http::$_requestUri}}.

Context: I access my development Apache server via the following URL (a VirtualHost): http://devprojects.myself.local My multiple Zend-powered application are accessible from there. For example: http://devprojects.myself.local/myGreatZfApp/… But, for some, there are spaces in the name of the folder that contains the application. Hence they appears the URL as "%20" (eg. [http://devprojects.myself.local/another%20Zf App/public/]).

Problem: My problem is that, on theses projects, baseUrl is not correctly set: it stays empty (an empty string). By analysing the code of {{Zend_Controller_Request_Http::setBaseUrl()}} I found out that base URL is somehow firstly correctly detected (either via {{$_SERVER['SCRIPT_NAME']}} or {{$_SERVER['PHP_SELF']}}), stored in local variable {{$baseUrl}} but gets compared to {{Zend_Controller_Request_Http::_requestUri}}:


// Does the baseUrl have anything in common with the request_uri?
$requestUri = $this->getRequestUri();

if (0 === strpos($requestUri, $baseUrl)) {
    // full $baseUrl matches
    $this->_baseUrl = $baseUrl;
    return $this;
}

if (0 === strpos($requestUri, dirname($baseUrl))) {
    // directory portion of $baseUrl matches
    $this->_baseUrl = rtrim(dirname($baseUrl), '/');
    return $this;
}

$truncatedRequestUri = $requestUri;
if (($pos = strpos($requestUri, '?')) !== false) {
    $truncatedRequestUri = substr($requestUri, 0, $pos);
}

$basename = basename($baseUrl);
if (empty($basename) || !strpos($truncatedRequestUri, $basename)) {
    // no match whatsoever; set it blank
    $this->_baseUrl = '';
    return $this;
}

// If using mod_rewrite or ISAPI_Rewrite strip the script filename
// out of baseUrl. $pos !== 0 makes sure it is not matching a value
// from PATH_INFO or QUERY_STRING
if ((strlen($requestUri) >= strlen($baseUrl))
    && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0)))
{
    $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
}

My opinion is the code doesn't make the difference between a file path on the server and an URL (that have it's own set of constraints): the two may be similar then have some small differences.

Steps to reproduce: - Create a blank new Zend application - Make it accessible via an URL that contains one or more spaces (eg. [http://127.0.0.1/my projects rep/The Application/]) - Print the base URL via {{Zend_Controller_Front::getInstance()->getBaseUrl()}} or {{Zend_View_Helper_BaseUrl::getBaseUrl()}} - Notice it's empty (should be "/my projects rep/The Application/")

Fix proposal: The simple following fix seems to do the trick (making the {{Zend_Controller_Request_Http::_requestUri}} going through {{urldecode()}}):


// Does the baseUrl have anything in common with the request_uri?
$requestUri = urldecode($this->getRequestUri());

But, as I said, file paths and URLs shouldn't be considered as equals: thus I suggest to make {{$baseUrl}} (which is actually constructed from file paths) go through {{urlencode()}} instead:


// Convert the paths found into an URL (handling specials characters such as spaces)
$baseUrl = urlencode($baseUrl);

// Does the baseUrl have anything in common with the request_uri?
$requestUri = $this->getRequestUri();

[...]

Please ignore my second proposal (applying {{urlencode()}} on {{$baseUrl}}) as ({{urlencode()}} hasn't the proper effect on base URL (spaces are changed to "+").

Additionally, I've noticed that with my first fix proposal (applying {{urldecode()}} to the request URI) when accessing to [http://127.0.0.1/my%20projects%20rep/…] (note the tailing "index.php"), base URL takes the wrong value "/my%20projects%20rep/The%20Application/public/index.php" (because, in that case, {{$baseUrl}} directly fully matches the request URI).