Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does ::$1 mean in an htaccess?

I've been browsing the symfony2 framework source. In the htaccess file for their example website, I found the %{REQUEST_URI}::$1 written as follows:

RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
RewriteRule ^(.*) - [E=BASE:%1]

The comment above that rule explains

The following rewrites all other queries to the front controller. The condition ensures that if you are using Apache aliases to do mass virtual hosting, the base path will be prepended to allow proper resolution of the app.php file; it will work in non-aliased environments as well, providing a safe, one-size fits all solution.

However, that doesn't explain the ::$1 or ::\2.

Are they backreferences? If not, what are they? What is their purpose?

like image 667
Christopher Thomas Avatar asked May 26 '13 17:05

Christopher Thomas


2 Answers

I have encountered almost the same htaccess file in my Zend project, and here are my thoughts and hope it helps.

The htaccess file (located at the Zend project directory, same as index.php) says

RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
RewriteRule ^(.*)$ - [E=BASE:%1]
RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]

Suppose Zend is installed at http://mydomain.tld/zend (let's call it yourdomain later on) and we are requesting yourdomain/mycontroller/myaction

Therefore %{REQUEST_URI} will be /zend/mycontroller/myaction.

Note that $1, which is the pattern in the RewriteRule directive in the htaccess context [1], "will initially be matched against the filesystem path, after removing the prefix that led the server to the current RewriteRule (e.g. app1/index.html or index.html depending on where the directives are defined)".

Therefore $1 will be mycontroller/myaction.

And %{REQUEST_URI}::$1 will be /zend/mycontroller/myaction::mycontroller/myaction.

The above string will be matched against ^(/.+)(.+)::\2$. Note that for the two capturing groups in round braces i.e., (/.+)(.+) before :: many combinations can match that. For example:

Group 1: /z

Group 2: end/mycontroller/myaction

or

Group 1: /zend/mycontroller/myactio

Group 2: n

and anything in between is a valid match. In fact, the most interesting one would be

Group 1: /zend/

Group 2: mycontroller/myaction

which (is the only case that) makes backreference \2 (after ::) to the second group a match.

In this case, /zend/ will be stored in the environment variable BASE which is what the first RewriteRule does. The %1 refers to the first matched string in RewriteCond which is /zend/.

Looking at the second RewriteRule, it is clear that why there is a need for this. As index.php can only be found in /zend/index.php, we need to add /zend/ in front of index.php.

Here we assume to use the URL-path as Substitution for the second RewriteRule directive. Refer to [1] and search for "A DocumentRoot-relative path to the resource to be served" under the RewriteRule Directive section.

All the above leave the query string unchanged/untouched. It is up to index.php how to parse the query string (as well as the URI).

Lastly goes the case where Zend is installed at the domain root.

  • %{REQUEST_URI} will be /mycontroller/myaction.
  • $1 will be mycontroller/myaction.

The string to be matched by RewriteCond will be /mycontroller/myaction::mycontroller/myaction.

This time the second group in (/.+)(.+) will never match mycontroller/myaction as there needs to be at least one letter following the initial backslash for the first group, making the second group as close as ycontroller/myaction but not exactly mycontroller/myaction so there cannot be a match.

As a result, the first RewriteRule is not used. The BASE enviornment variable will not be set, and when the second RewriteRule uses it, it will simply be empty.

References

[1] http://httpd.apache.org/docs/current/mod/mod_rewrite.html

like image 184
realwecan Avatar answered Oct 13 '22 08:10

realwecan


The $1 in %{REQUEST_URI}::$1 references the matched string of the RewriteRule directive, i.e., the matched string of .* in ^(.*). So %{REQUEST_URI}::$1 is expanded to the requested URI path as supplied by the user, and the current internal URI path and query, separated by ::.

The pattern ^(/.+)(.+)::\2$ is used to find a prefix (first capturing group) which makes the remaining part match the part behind the :: (\2 is a back reference to the matched string of the second capturing group of the pattern).

If such a match is found, the prefix is stored in the environment variable BASE ([E=BASE:%1], where %1 references the matched string of the previous successful RewriteCond pattern match).

like image 22
Gumbo Avatar answered Oct 13 '22 08:10

Gumbo