PHP Built in Server and .Htaccess Mod Rewrites

use .htaccess with php5.4 built-in server

As in my comment mentioned, the current directory is by default your webroot. Also this webserver doesn't support .htaccess.

You'll find a good explanation about your issues here.

Get the Server Running

By default, the current directory is your webroot and you can now
request whatever files are here, and run PHP in the usual way

or

Routing Requests Like Apache Rewrite

One immediate feature that I was looking for was the ability to
redirect all incoming requests to index.php, which I usually do with
an .htaccess file. This webserver doesn't support those (although my
good friend Josh has created something pretty close) but it does
support a routing file.

Does php internal server read .htaccess configuration?

That is true. .htaccess is used and processed by Apache. Apache handles the requests. It just loads PHP as a helper to parse and execute PHP files. When PHP is started as a stand-alone server, it won't use those configurations.

You can check out this GitHub project for an .htaccess parser that can be used with PHP's built in server. Note though, that this project is an undefined state, somewhere between 'alpha' and 'abandoned'. This blog post links to that project too, and provides a little more context.

As a side note, PHP's internal server is not to be used as a production server. It doesn't have the power, flexibility, stability and security of a full fledged server like Apache or Nginx.

Routing PHP 5.4+ Built-in Web Server like .htaccess

<?php
$_matches = array();

/**
* Initialize the rewrite environment.
*/
function initialize() {
set_environment($_SERVER['REQUEST_URI']);
}

/**
* Set important environment variables and re-parse the query string.
* @return boolean
*/
function finalize() {
if (defined('REWRITER_FINALIZED')) return false;

define('REWRITER_FINALIZED', true);

if (\is_file($_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_NAME'])) {
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_NAME'];
}

if (isset($_SERVER['QUERY_STRING'])) {
$_GET = [];
parse_str($_SERVER['QUERY_STRING'], $_GET);
}

$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];

return true;
}

/**
* Adjust the server environment variables to match a given URL.
* @param string $url
*/
function set_environment($url) {
$url = rtrim($url, '&?');
$request_uri = $script_name = $url;
$query_string = null;

if (strpos($url, '?') > 0) {
$script_name = substr($url, 0, strpos($url, '?'));
$query_string = substr($url, 1 + strpos($url, '?'));
}

$_SERVER['REQUEST_URI'] = $request_uri;
$_SERVER['SCRIPT_NAME'] = $script_name;
$_SERVER['QUERY_STRING'] = $query_string;
}

/**
* Parse regular expression matches. eg. $0 or $1
* @param string $url
* @return string
*/
function parse_matches($url) {
$replace = function($bit) {
global $matches;
return isset($matches[$bit[1]])
? $matches[$bit[1]]
: null;
};

return preg_replace_callback('/\$([0-9]+)/', $replace, $url);
}

/**
* Parse Apache style rewrite parameters. eg. %{QUERY_STRING}
* @param string $url
* @return string
*/
function parse_parameters($url) {
$replace = function($bit) {
return isset($_SERVER[$bit[1]])
? $_SERVER[$bit[1]]
: null;
};
return preg_replace_callback('/%\{([A-Z_+]+)\}/', $replace, $url);
}

/**
* Change the internal url to a different url.
* @param string $from Regular expression to match current url, or optional when used in conjunction with `test`.
* @param string $to URL to redirect to.
* @return boolean
*/
function rewrite($from, $to = null) {
if (defined('REWRITER_FINALIZED')) return false;

$url = $_SERVER['SCRIPT_NAME'];

if (isset($to)) {
$url = preg_replace($from, $to, $url);
} else {
$url = parse_matches($from);
}

set_environment(
parse_parameters($url)
);

return true;
}

/**
* Compare a regular expression against the current request, store the matches for later use.
* @return boolean
*/
function test($expression) {
global $matches;
if (defined('REWRITER_FINALIZED')) return false;
return 0 < (integer)preg_match($expression, $_SERVER['SCRIPT_NAME'], $matches);
}

initialize();

// Your rewrite rules here.
test('%/(.*)-(.*)\.htm$%') && rewrite('/?page=$1&sub=$2&%{QUERY_STRING}') && finalize();
test('%^([^/]*)\.htm$%') && rewrite('/?page=$0&%{QUERY_STRING}') && finalize();

echo "<pre>";
var_dump($_SERVER);
// include index.php or something

I've included a bunch of 'helper' functions which will make it easier to write your rewrite rules (borrowed here).

.htaccess: ??/index.php -- ??/, built on a relative path

To serve index.php from the requested directory you use mod_dir's DirectoryIndex directive (which is probably already set in the server config, although defaults to index.html only) - you do not need mod_rewrite for this. For example:

# Serve "index.php" from the requested directory
DirectoryIndex index.php

This instructs Apache to serve index.php from whatever directory is requested. eg. Request /foo/bar/ then /foo/bar/index.php is served via an internal subrequest (no redirect). If index.php is not present in that directory you'll get a 403 Forbidden response (assuming directory listings - as generated by mod_autoindex - are disabled).

To remove index.php from any URL that is requested directly you can use mod_rewrite. For example:

RewriteEngine On

# Remove "index.php" from any URL and redirect back to the "directory"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.+/)?index\.php(/|$) /$1 [R=301,L]

The above will redirect as follows, preserving the requested protocol and hostname:

  • /index.php to /
  • /foo/index.php to /foo/
  • /foo/bar/index.php to /foo/bar/
  • /fooindex.php - NO REDIRECT (404 expected)
  • /foo/index.php/bar (containing path-info) to /foo/ (path-info removed)
  • /foo/index.phpbar - NO REDIRECT (404 expected)

The (optional) capturing group (.*/)? contains the part of the URL-path before index.php. This is then available in the substitution string using the $1 backreference. In the case when /index.php is requested in the document root, this is empty. When a subdirectory is present then this contains a string of the form subdir/, including the trailing slash.

If you have no other directives in your .htaccess file then you don't strictly need the condition that checks against the REDIRECT_STATUS environment variable. This condition ensures that only direct requests are redirected in the case when you have a front-controller pattern later in the file that might rewrite requests to index.php.

If you do have other directives in the file then the order can be important. This rule that removes index.php via an external redirect must go before any existing rewrites, near the top of the file.

Note that this removes index.php from the URL regardless of whether the requested URL actually maps to a real file or whether the preceding URL-path even exists as a physical directory. So, /<something>/index.php is redirected to /<something>/ regardless of whether /<something>/ is a physical directory or not. This check can be implemented at the cost of an additional filesystem check - but it's probably not required.

NB: Test first with a 302 (temporary) redirect to avoid potential caching issues. Only change to a 301 (permanent) redirect once you have tested that it works as intended.


UPDATE#1:

These Questions do not provide an answer:

  • htaccess redirect index.php to root (including subdomains)

    • This does not address subdirectories, only subdomains
    • This redirects example.tld/dir/index.php to example.tld/, but I need example.tld/dir/

Actually, the first question you've linked to does answer your question, with regards to removing index.php from the requested URL. It does address subdirectories and would redirect example.tld/dir/index.php to example.tld/dir/ (not example.tld/ as you've stated).

The part of the question that discusses subdomains is a bit misleading as it doesn't really have anything to do with subdomains specifically.

The solution presented in the linked question basically does the same sort of thing as I've done above, except that it arguably matches too much (and not enough). It would incorrectly/unnecessarily redirect /fooindex.php to /foo (no trailing slash) and would fail to redirect URLs that contained path-info (which could be malicious). eg. /foo/index.php/bar would fail to redirect but still serve the contents of /foo/index.php (unless AcceptPathInfo had been disabled). Although whether these "errors" would actually cause an issue in your case is another matter.


UPDATE#2:

I have the code exactly in the directory for example.tld/dir

The code above assumes the .htaccess file is located in the document root. If the .htaccess file is located in the directory of where the app is installed then you would need to modify the above like so:

# Remove "index.php" from any URL and redirect back to the "directory"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/(.+/)?index\.php
RewriteRule (^|/)index\.php(/|$) /%1 [R=301,L]

The %1 backreference (as opposed to $1) refers to the captured group in the preceding CondPattern. This naturally includes the full URL-path, so avoids having to hardcode the directory in which the .htaccess file is located.

This applies to the directory that contains the .htaccess file and any subdirectories thereof. Note that, by default, this completely overrides any mod_rewrite directives that might be present in the parent .htaccess file (mod_rewrite directives are not inherited by default).

...including subdirectories, which may have their own .htaccess and index.php.

If additional sub-subdirectories have their own .htaccess file then this may or may not work depending on the directives used in these sub-subdirectory .htaccess files and/or how mod_rewrite inheritance is configured.

mod_rewrite directives do not inherit by default. So, if the .htaccess file in the sub-subdirectory enables the rewrite engine then the above mod_rewrite directives in the parent directory will be completely overridden (they are not even processed).

If, on the other hand, the .htaccess file in the sub-subdirectory uses directives from other modules then this may not be an issue.

Create a rewrite rule in .htaccess for php file

Change it to:

RewriteEngine On
RewriteRule ^sabc/(.+)$ upload_pic.php?action=$1

The .+ will capture one or more characters after the / and be captured into $1

Mod Rewrite in PHP

I am not good in english, but here i can try to answer.

look like your blog is on a sub dir.
so,

RewriteRule ^blog/([^/]*)$ /blog/?id=$1 [L]

and try to add

RewriteBase /

if you still have problem,
also..
there are many generator for htaccess
for example
http://www.generateit.net/mod-rewrite/index.php



Related Topics



Leave a reply



Submit