I am redirecting certain urls with path to get variables like the following:
localhost2/post/myTitle => localhost2/post.php?title=myTitle
localhost2/post/123 => localhost2/post.php?id=123
So In my htaccess file, I use
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^post/(\d ) post.php?id=$1
RewriteRule ^post/(.*) post.php?title=$1
</IfModule>
This works no problem. But I want to learn how to write negative of ^post/(\d )
, that is ^post/(NEGATE-ONLY-NUMBERS)
. In other words I want a regex that matches the whole input sting if there is not only numbers after post/
. So post/abc
, post/a23
, post/ab3
, post/12c
and post/a2c
should all pass but not post/123
. I refered to this post, which suggest using:
(?!^\d $)^. $
I can't use ^post/(?!^\d $)^. $
, because there can be only one ^
and one $
. I don't know what regex anchor specifies first position in a substring. My best guess is
post\/(?!\d ).*
I think (?!\d )
, with the
would eat all characters followig and check if all are digits. But this fails at post/1ab
.
Another guess is:
post\/(?![\d,\/] $).*
The works the best but it allows: post/3455/X
.
Secondly, eventually I need to convert localhost2/post/myTitle/123 => localhost2/post.php?title=myTitle&repeat=123
as well. I ave come up with the following:
^post/(?!\d ($|/))(. ?($|/))(\d $)?
Note: ?
to use lazy quantifier, otherwise multiple slashes will be matched by .
and
^post/(?!\d ($|/))([^/\n\r] ($|/))(\d $)?
Here I use [^/\n\r]
instead of . ?
CodePudding user response:
Patterns inside zero-width assertions like (?!\d )
are non-consuming, they do not "eat" chars, they only check the context while keeping the regex index at the same location as before matching the zero-width assertion pattern.
You can use any of the following:
^post/(?!\d (?:/|$)).*
^post/(?!\d (?=/|$)).*
^post/(?!\d (?![^/])).*
See the regex demo. Details:
^post/
- start of input,post/
literal string(?!\d (?=/|$))
- a negative lookahead that fails the match if, immediately to the right of the current location, there are one or more digits followed with/
or end of string.*
- the rest of the input.
CodePudding user response:
Do not over complicate things when you can keep things simple by keeping 3 separate rewrite rules and since your query parameters are named differently you will need 3 separate rewrite rules anyway.
Consider:
Options -MultiViews
RewriteEngine On
RewriteRule ^post/(\d ) post.php?id=$1 [L,QSA,NC]
RewriteRule ^post/(.*) post.php?title=$1 [L,QSA,NC]
RewriteRule ^post/([\w-] )/(\d ) localhost2/post.php?title=$1&repeat=$2 [L,QSA,NC]
Take note of Options -MultiViews
. If this is not enabled in Apache config you must have it here otherwise it will keep all $_GET
parameters empty in your php file.
Option MultiViews
(see http://httpd.apache.org/docs/2.4/content-negotiation.html) is used by Apache's content negotiation module
that runs before mod_rewrite
and makes Apache server match extensions of files. So if /file
is the URL then Apache will serve /file.html
.