Stop clean URLs from affecting relative paths in page HTML

I have implemented clean URLs using the following in my .htaccess

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

And in my index.php the $_SERVER['PATH_INFO'] is parsed to determine the requested page and any parameters.

This all works fine, however any requests for a url with more than one segment, ie

www.example.com/page/foo/bar

cause all linked assets (css, js, images) and hyperlinks on the page to break because they use relative paths. A absolute path with an implied domain (aka prepending all paths with "/") is not viable because the site has to be accessible and working properly as a subdirectory of a parent website in addition to its own root URL. I can not hardcode the fully qualified absolute path for all links either, as I do not have control over some of the content that will be on the page. Using the <base> tag is also not an possibility.

Do I have any other options here?


Does every request to PHP fall on the same index.php file? In that case, the file itself knows where it is, and can calculate what directory it's in.

This isn't the neatest way to do it, but it might give you some ideas to work from.

define('DR', $_SERVER['DOCUMENT_ROOT');
define('BD', str_replace(DR, '', dirname($_SERVER['SCRIPT_FILENAME'] . '/'); // base dir

And then prepend all links with BD .


Do what you normally do when you create a php router... create a link generation facility for assets and URLs.

Since youre using a front controller you can define all your directories up front based on configuration and then generate the paths to things like:

function asset_url($filename, $type = 'img') {
   global $config;
   if(isset($config['paths'][$type])) {
      return $config['paths'][$type] . '/' . $filename;
   }
}

Normally id register this configuration with the Registry pattern implemntation instead of using global but i dunno your set up, so this is just a simple example.


It's a tricky solution. The easiest thing to do is introduce a baseUrl parameter to your application. It would be empty if the site is running at the root directory, and would be something like /corp/site is running out of domain.com/corp/site .

This way, when you display URLs, you would use <?php echo $app_base_url . '/css/file.css' ?> <?php echo $app_base_url . '/css/file.css' ?>

I use Zend Framework a lot and they have a class called Zend_Controller_Request_Http which has a method called getBaseUrl that goes through some complex logic examining the various $_SERVER variables about the script path and the request filename which ultimately figures out what your base Url actually is.

If you use that specific piece of Zend Framework, you can do something like this:

$request = new Zend_Controller_Request_Http();
$baseUrl = $request->getBaseUrl();

Since Zend Framework is modular, you don't need to include the whole library, I've used just this class before (and its dependencies) so I could easily calculate the base URL in a small application without needing the whole ZF library.

To give you an idea of what logic is involved in determining this, here is the code that does most of the work (note, don't try to use this directly, instead use the Zend Framework classes as shown above).

/**
 * Set the base URL of the request; i.e., the segment leading to the script name
 *
 * E.g.:
 * - /admin
 * - /myapp
 * - /subdir/index.php
 *
 * Do not use the full URI when providing the base. The following are
 * examples of what not to use:
 * - http://example.com/admin (should be just /admin)
 * - http://example.com/subdir/index.php (should be just /subdir/index.php)
 *
 * If no $baseUrl is provided, attempts to determine the base URL from the
 * environment, using SCRIPT_FILENAME, SCRIPT_NAME, PHP_SELF, and
 * ORIG_SCRIPT_NAME in its determination.
 *
 * @param mixed $baseUrl
 * @return Zend_Controller_Request_Http
 */
public function setBaseUrl($baseUrl = null)
{
    if ((null !== $baseUrl) && !is_string($baseUrl)) {
        return $this;
    }

    if ($baseUrl === null) {
        $filename = (isset($_SERVER['SCRIPT_FILENAME'])) ? basename($_SERVER['SCRIPT_FILENAME']) : '';

        if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $filename) {
            $baseUrl = $_SERVER['SCRIPT_NAME'];
        } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $filename) {
            $baseUrl = $_SERVER['PHP_SELF'];
        } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
            $baseUrl = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
        } else {
            // Backtrack up the script_filename to find the portion matching
            // php_self
            $path    = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '';
            $file    = isset($_SERVER['SCRIPT_FILENAME']) ? $_SERVER['SCRIPT_FILENAME'] : '';
            $segs    = explode('/', trim($file, '/'));
            $segs    = array_reverse($segs);
            $index   = 0;
            $last    = count($segs);
            $baseUrl = '';
            do {
                $seg     = $segs[$index];
                $baseUrl = '/' . $seg . $baseUrl;
                ++$index;
            } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
        }

        // 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));
        }
    }

    $this->_baseUrl = rtrim($baseUrl, '/');
    return $this;
}
链接地址: http://www.djcxy.com/p/34854.html

上一篇: 访问rails资源的相对路径

下一篇: 停止干净的URL影响页面HTML中的相对路径