Forum Moderators: phranque

Message Too Old, No Replies

Possible to increment environment variables under mod rewrite?

Using mod_rewrite to determine current, previous and last year

         

cschults

5:30 pm on Mar 20, 2008 (gmt 0)

10+ Year Member



In addition to the current year, I need to know the previous and next years.

Is something like this possible?

RewriteCond %{REQUEST_URI} ^/foo/bar.html$
RewriteRule .* - [E=curr_year:%{TIME_YEAR}] [C]
RewriteRule .* - [E=next_year:%{ENV:curr_year}++] [C]
RewriteRule .* - [E=prev_year:%{ENV:curr_year}--]

So I can later do something like this:

RewriteCond /path/to/root/foo/%{ENV:next_year}/bar.html -f
RewriteRule ^.*$ /foo/%{ENV:next_year}/bar.html [L]
RewriteCond /path/to/root/foo/%{ENV:curr_year}/bar.html -f
RewriteRule ^.*$ /foo/%{ENV:curr_year}/bar.html [L]
RewriteCond /path/to/root/foo/%{ENV:last_year}/bar.html -f
RewriteRule ^.*$ /foo/%{ENV:last_year}/bar.html [L]

In this example, what I'm trying to do is basically this:

If next year's file is ready early, use that.
Else if this year's file exists, use that.
Else if last year's file exists, use that.

This is actually a simplified version of my actual need, but an answer to this will help.

Thanks.

jdMorgan

7:08 pm on Mar 20, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



No, it's not possible to do this with mod_rewrite. Mod_rewrite is not a general-purpose scripting language.
If your scope is limited to just a few years, however, it is possible to use a "lookup table" method, as described in this recent thread: [webmasterworld.com...]

If you have access to httpd.conf or conf.d, you can also define a RewriteMap to call a PERL script or similar to do any date calculations you like. RewriteMaps can be called from, but not defined in, .htaccess files.

Jim

jdMorgan

7:11 pm on Mar 20, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Also note that if you do use the lookup table method, you'll want to look up both the previous and next year at the same time, based on the current year. This means, in relation to code in the cited thread, that each RewriteCond should create two back-references; One to the previous year and one to the current year.

Jim

cschults

8:36 pm on Mar 20, 2008 (gmt 0)

10+ Year Member



Thanks Jim. I'm actually using something similar to a lookup table for months (not included in the example above), but I'd prefer the years to be variable so I don't have to hardcode them. I'll consider using the RewriteMap with script option.

What I have now should only require a person to update an .htaccess once a year, but I would prefer to set it up and forget about it. :o)

Chris

cschults

8:40 pm on Mar 20, 2008 (gmt 0)

10+ Year Member



Also, what impact (if any) do dummy RewriteRules whose sole purpose is to set environment variables have?

For example:

RewriteRule .* - [E=foo:bar] [C]
RewriteRule .* - [E=abc:123] [C]
RewriteRule .* - [E=var:val]

Can you set multiple variables at a time?

jdMorgan

9:37 pm on Mar 20, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Not sure what you mean by "what effect" but the following is valid:

RewriteRule .* - [E=foo:bar,E=abc:123,E=var:val]

Here's a "lookup" example that may be closer to what you're doing. This was from a "calendar page" project I did awhile ago (without the benefit of using RewriteMap), but updated for current years:


# CALENDAR PAGE REWRITES

# Set current calendar path variable
RewriteCond %{TIME_YEAR}>%{TIME_MON} ^20([0-9]{2})>([0-9]{2})$
RewriteRule \.html$ - [E=CurrentCal:/calendar/cal%1%2.html]

# Look up previous and next calendar-page month
RewriteCond %{TIME_MON}>12>02 ^01>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>01>03 ^02>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>02>04 ^03>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>03>05 ^04>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>04>06 ^05>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>05>07 ^06>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>06>08 ^07>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>07>09 ^08>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>08>10 ^09>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>09>11 ^10>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>10>12 ^11>([^>]+)>([^>]+)$ [OR]
RewriteCond %{TIME_MON}>11>13 ^12>([^>]+)>([^>]+)$
RewriteRule \.html$ - [E=PrevMonth=%1,E=NextMonth:%2]

# Default next and previous calendar-pages' year to current year
RewriteCond %{TIME_YEAR} ^20([0-9]{2})$
RewriteRule \.html$ - [E=NextCal:/calendar/cal%1%{ENV:NextMonth}.html,E=PrevCal:/calendar/cal%1%{ENV:PrevMonth}.html]

# If current month is December, look up next calendar-page year
RewriteCond %{TIME_MON} ^12$
RewriteCond %{TIME_YEAR}>09 ^2008>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>10 ^2009>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>11 ^2010>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>12 ^2011>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>13 ^2012>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>14 ^2013>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>15 ^2014>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>16 ^2015>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>17 ^2016>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>18 ^2017>([^>]+)$
RewriteRule \.html$ - [E=NextCal:/calendar/cal%1%{ENV:NextMonth}.html]

# If current month is January, look up previous calendar-page year
RewriteCond %{TIME_MON} ^01$
RewriteCond %{TIME_YEAR}>07 ^2008>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>08 ^2009>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>09 ^2010>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>10 ^2011>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>11 ^2012>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>12 ^2013>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>13 ^2014>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>14 ^2015>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>15 ^2016>([^>]+)$ [OR]
RewriteCond %{TIME_YEAR}>16 ^2017>([^>]+)$
RewriteRule \.html$ - [E=PrevCal:/calendar/cal%1%{ENV:PrevMonth}.html]


Jim

cschults

11:53 pm on Mar 20, 2008 (gmt 0)

10+ Year Member



Wow, thanks Jim. My appreciation of mod_rewrite just grew immensely.

By impact/effect, I meant, is there a performance hit? I assuming not by the looks of your rules.

Also, I used some of your techniques, so I shouldn't need to update my .htaccess file for many years.

jdMorgan

12:28 am on Mar 21, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Yes, there's a performance hit, but no more than say, the same number of lines of PHP or SSI code. If you've got a very busy server or share a server with a lot of other Web sites, you might notice a performance decrease, while on a super-fast dedicated server with a moderate number of visitors, you likely would not notice at all. On a niche site hosted on a decent shared server, you could have a couple thousand lines of mod_rewrite code and not notice. Precisely because mod_rewrite is purpose-built rather than being a general scripting language, it's very efficient. So, the performance hit depends on many factors, but usually isn't large unless your code is very poorly implemented.

Nevertheless, it's always a good idea to limit the scope of your rules, so that they are only executed when necessary. For example, in the code above, note that the RewriteRule patterns specify that the rules only apply to .html files. Therefore, the RewriteConds won't even be parsed for non-html files (See mod_rewrite documentation for details).

Similarly, it's always a good idea to place very-CPU-intensive or inefficient RewriteConds last --after all the other RewriteConds that apply to a rule-- so they are not executed unless all the previous RewriteConds 'pass.' Two examples of RewriteConds which should be deferred when possible are the 'file exists' checks and (if available) reverse-DNS lookups -- i.e.

RewriteCond %{REQUEST_FILENAME} -f
or
RewriteCond %{REMOTE_HOST} ^blah\.blah\.com

On the one hand, the shorter and more efficient your code is, the better. On the other, the computers work for us, not the other way 'round... :)

Jim

cschults

6:48 pm on Mar 21, 2008 (gmt 0)

10+ Year Member



Thanks Jim. While my code has grown, I've reduced the number of file exist checks and limited the scope.

gckorn

4:42 pm on Apr 3, 2008 (gmt 0)

10+ Year Member



Another follow-up question. This should not be difficult, but I cannot find an answer.

I have a calendar of music events. The URL that shows is always the current month (something done in scripting). Hence, if it is April, the URL that shows is http://www.example.com/events/music/april.php

Now, I want to enable people to link to, say, http://www.example.com/events/music/index.php , but I want that link to always be redirected by .htaccess to the CURRENT month, i.e., which is currently as noted above, http://www.example.com/events/music/april.php

Does anyone know the proper command?

Thanks,
gckorn

[edited by: jdMorgan at 7:25 pm (utc) on May 6, 2008]
[edit reason] example.com [/edit]

jdMorgan

4:05 am on Apr 4, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Yes, but we don't write your code here, you do... :)

You can use


RewriteCond %{TIME_MON} (.+)

to get and create a back-reference containing the current month for use in your rewriterule.

This will get the month as a two-digit number. If you want to use named months, then you can use the lookup technique shown above.

Jim

gckorn

11:23 am on Apr 4, 2008 (gmt 0)

10+ Year Member



Jim, thanks. The following code works too. Perhaps part can be replaced with your (.+):

Options +FollowSymLinks
RewriteEngine On
RewriteCond %{TIME_MON}january ^01(january)$ [OR]
RewriteCond %{TIME_MON}february ^02(february)$ [OR]
RewriteCond %{TIME_MON}march ^03(march)$ [OR]
RewriteCond %{TIME_MON}april ^04(april)$ [OR]
RewriteCond %{TIME_MON}may ^05(may)$ [OR]
RewriteCond %{TIME_MON}june ^06(june)$ [OR]
RewriteCond %{TIME_MON}july ^07(july)$ [OR]
RewriteCond %{TIME_MON}august ^08(august)$ [OR]
RewriteCond %{TIME_MON}september ^09(september)$ [OR]
RewriteCond %{TIME_MON}october ^10(october)$ [OR]
RewriteCond %{TIME_MON}november ^11(november)$ [OR]
RewriteCond %{TIME_MON}december ^12(december)$
RewriteRule ^events/music\.php$ /events/music/%1.php [QSA,L]

jdMorgan

2:06 pm on Apr 4, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



> Perhaps part can be replaced with your (.+)

I just used the (.+) to prevent a future typo from breaking the code in a subtle way. For example, if you typed
"RewriteCond %{TIME_MON}january ^01(janaury)$ [OR]"
then all january pages would fail, whereas it would still work with (.+) -- or fail more obviously if the typo was in the left side.

It's more a matter of style, really. There are many right ways to do things when coding... Never mind that there are far more wrong ways... :)

Jim