Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > PHP/MySQL > Moderated Pro PHP
Password Reminder
Register
Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
Moderated Pro PHP This is a moderated forum for discussing advanced, professional level issues with PHP. Your posts will not appear until a moderator approves them. Posts that are not the right level for this forum will be responded to and you'll be asked to post them in the [url="http://p2p.wrox.com/sql-server-2000-20/9"]Beginning PHP[/url] forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the Moderated Pro PHP section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developersí questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Display Modes
  #1 (permalink)  
Old May 26th, 2005, 08:23 PM
richard.york's Avatar
Wrox Author
Points: 5,506, Level: 31
Points: 5,506, Level: 31 Points: 5,506, Level: 31 Points: 5,506, Level: 31
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Camby, IN, USA.
Posts: 1,706
Thanks: 0
Thanked 5 Times in 5 Posts
Default Database driven clean URLs with Apache mod_rewrite

A few months ago I started a pretty ambitious project that involved writing a new PHP framework from scratch. One of the driving prinicipals of the framework was that it was to feature the ability to create "clean URLs", but use a single PHP file to process all requests. The goals of the project were:
  • Only files not found in the normal file system get processed by my PHP framework
  • Query strings must still work and be available
  • The document cannot return a 404 error on successful requests, as this could affect search indexing and the like
After some snooping around I came to the consensus that there were two methods of acheiving this. The first was via 404 error pages and the second via the Apache mod_rewrite module. At first the 404 method seemed like the easiest method, as I had no experience with mod_rewrite. So I began writing my framework using the 404 method.

In Apache it is trivial to specify a custom 404 errror document. In httpd.conf, you can do something like this either globaly or within your virtual host configuration.
Code:
    ErrorDocument 404 /index.php
    ErrorDocument 500 /index.php
    ErrorDocument 401 /index.php
    ErrorDocument 400 /index.php
    ErrorDocument 503 /index.php
    ErrorDocument 403 /index.php
A few things became apparent immediately. Apache does not supply POST or GET data to the 404 error document, this means that in PHP the $_GET and $_POST variables aren't populated when you post to a document that doesn't exist.

You may be asking yourself, well since you're creating clean URLs why do you need query strings? I still want the option to use query strings, if I need them. Certain internal functionality can still be done with query strings, while I can retain the benefit of clean URIs for the majority of my static content.

I found a work-around for GET data. That data can be found in $_SERVER['REQUEST_URI'], no work-around for the POST method though. So for a while I was content with using query strings, when it was required, and posting directly to the index.php page when I needed to send POST data.

Later, after building a couple AJAX-enabled applications, I realized that there were more problems with this method. These problems seemed to center around redirecting to the 404 error document. They persisted even after explicitly setting the HTTP status heading in the response headers of the PHP document as:
Code:
Status: 200 OK HTTP/1.1
My AJAX applications failed in certain browsers. Opera was especially bad, as nothing worked. Explorer was buggier than usual.

Despite these woes, another problem manifested itself. In one of my AJAX applications I included download links to files stored in my Framework's Virtual File System (by creating a clean-url driven application, I am essentially creating my own filesystem). These links failed in Explorer, Opera, and Mozilla 1.8 Beta! It seems that something I wasn't seeing was being sent in the HTTP response headers. Viewing the response headers didn't reveal any new information, and I was stuck at an impasse. Clearly the 404 method wasn't going to work out. Time to revisit Apache mod_rewrite.

I read about Apache's mod_rewrite early on in the development of my framework. Essentially, it exists for the purpose of mapping a URL to another file on the server (hence its name). The mod_rewrite docs were informative, but they didn't shed much light onto the approach that I wanted to take. Through a few Google searches and a few more reviews of the docs, I managed to get a working solution.

mod_rewrite must be enabled in httpd.conf. mod_rewrite is included in the Windows Apache default installation (but must be enabled), and must be compiled with Apache on Linux.

After some tweaking, I came up with the following (which must appear after DOCUMENT_ROOT is set):
Code:
<IfModule mod_rewrite.c>
    RewriteEngine On
        RewriteLog logs/rewrite.log
        RewriteLogLevel 0

        # If the REQUEST_FILENAME does not exist as a file or directory
        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
        RewriteRule ^(.*) /index.php?%{QUERY_STRING} [L]
</IfModule>
RewriteEngine On activates mod_rewrite. You can specify a log for debugging as well to see what URIs fit the critera in the conditions, and which don't. Then comes the real magic; I specify two conditions that decide whether or not the subsequent rule is to be executed. When there are two or more conditions, as I have here, it is the same as binding them together with a logical '&&' or 'AND' operator. Additionally, an implicit [OR] can be supplied after the first condition to alter the logic to '||' or 'OR' (see mod_rewrite docs for syntax). The rule says that if the file in the document root path does not exist and there is no directory that exists by that name either and the request string contains any characters of any length, send the request to index.php. Therefore http://www.example.com/any/directory/anypage.html can be sent to index.php where I can analyze the path and output accordingly.

This will also replace any 404 document, if the request cannot be found within the framework, an explicit
Code:
Status: 404 Not Found
can be supplied since it is now impossible to invoke normal 404 error handling. A small tradeoff, IMO, as I can log my own 404 errors and react appropriately.

With this in place I can now make up any URL I like, and fully create my own database-driven virtual file system using PHP and MySQL.

Regards,
Rich

--
[http://www.smilingsouls.net]
Mail_IMAP: A PHP/C-Client/PEAR solution for webmail
Author: Beginning CSS: Cascading Style Sheets For Web Design
Reply With Quote
  #2 (permalink)  
Old April 15th, 2006, 03:32 PM
Registered User
 
Join Date: Apr 2006
Location: Coral Springs, FL, USA.
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I'm trying to setup a situation where I can create virtual hosts on the fly without restarting apache. as you've stated the documentation is dry and difficult to understand. I'm trying to follow the last example found here:

http://httpd.apache.org/docs/2.0/vhosts/mass.html

this is essentially what I've done (which I only partly understand):

#############################################
# /www/vhosts.map
#############################################
www.testhost1.com /www/test_dynamic/1
www.testhost2.com /www/test_dynamic/2
www.testhost3.com /www/test_dynamic/3

#############################################
# /etc/httpd/conf.d/dynamic_hosts.conf
#############################################

RewriteEngine on

RewriteMap lowercase int:tolower

# define the map file
RewriteMap vhost txt:/www/vhosts.map

# deal with aliases as above
RewriteCond %{REQUEST_URI} !^/icons/
RewriteCond %{REQUEST_URI} !^/cgi-bin/
RewriteCond ${lowercase:%{SERVER_NAME}} ^(.+)$
# this does the file-based remap
RewriteCond ${vhost:%1} ^(/.*)$
RewriteRule ^/(.*)$ %1/docs/$1

RewriteCond %{REQUEST_URI} ^/cgi-bin/
RewriteCond ${lowercase:%{SERVER_NAME}} ^(.+)$
RewriteCond ${vhost:%1} ^(/.*)$
RewriteRule ^/(.*)$ %1/cgi-bin/$1 [T=application/x-httpd-cgi]


can you provide me with your thoughts and some direciton?

Reply With Quote
  #3 (permalink)  
Old April 17th, 2006, 12:28 PM
richard.york's Avatar
Wrox Author
Points: 5,506, Level: 31
Points: 5,506, Level: 31 Points: 5,506, Level: 31 Points: 5,506, Level: 31
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Camby, IN, USA.
Posts: 1,706
Thanks: 0
Thanked 5 Times in 5 Posts
Default

I don't understand why you aren't just creating a new virtual host entry then doing a "soft" restart (aka graceful restart)?

Have you tried this? What was the result?

Regards,
Rich

--
Author,
Beginning CSS: Cascading Style Sheets For Web Design
CSS Instant Results

http://www.catb.org/~esr/faqs/smart-questions.html
Reply With Quote
  #4 (permalink)  
Old April 17th, 2006, 01:30 PM
Registered User
 
Join Date: Apr 2006
Location: Coral Springs, FL, USA.
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I ended up goign with:

<VirtualHost 192.168.0.14:80>

# get the server name from the Host: header
UseCanonicalName Off

# this log format can be split per-virtual-host based on the first field
LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon
CustomLog /www/logs/dynamic.access_log vcommon

# include the server name in the filenames used to satisfy requests
VirtualDocumentRoot /www/hosts/%0/docs
VirtualScriptAlias /www/hosts/%0/cgi-bin
</VirtualHost>

basically I wanted users to be able to create hosts on the fly and just point dns to the box to serve up pages that would be created with a suite of utilities we're going to be publishing in the coming weeks. This was the first obsticle to over come. I'm also looking at mod_vhs which stores virtual host info in a database and can answer requests based on database values.

Normally I would just create a virtual and do a service httpd restart, but with the number of hosts we need to fascilitate and the time frames these webmasters will be working on this, it just doesn't seem like a reasonable proposition .

-Jon

Reply With Quote
  #5 (permalink)  
Old January 15th, 2011, 09:15 AM
zweibieren
Guest
 
Posts: n/a
Default CORRECTION

Apparently mod_rewrite has changed since the original post.
Today, the value of REQUEST_FILENAME already includes the value of DOCUMENT_ROOT.
Also, the slash before index.php is erroneous.
For my own purposes, I've changed the target to dispatcher.php.

The corrected code follows.

Code:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*) dispatcher.php?%{QUERY_STRING}  [L]
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off

Similar Threads
Thread Thread Starter Forum Replies Last Post
Database driven drop down rajneesh Classic ASP Professional 3 May 5th, 2007 01:39 AM
*.do URLs dont work in apache web server+tomcat haomomo Struts 1 August 29th, 2006 09:57 AM
mod_jk + apache + mod_rewrite fanch Apache Tomcat 0 December 5th, 2005 11:34 AM
Apache mod_rewrite() anshul Linux 2 October 14th, 2005 09:09 AM
Database Driven Website w/ META tags Dan Jallits PHP How-To 2 June 16th, 2003 11:42 AM



All times are GMT -4. The time now is 04:21 PM.


Powered by vBulletin®
Copyright ©2000 - 2016, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.