I'm trying to alter the way we currently provide a gateway page system within our CMS. What i mean by gateway page is mapping a non-existent URL to a page through a rewrite rule in the .htaccess, e.g.
RewriteRule ^foobar$ page.php?mode=bar&method=foo&id=1
This allows people to create short links to CMS pages for magazine adverts etc. The problem with this method is that it relies on access to the .htaccess. I would prefer a method that sits at code level but it occurs to me that - without a rewrite rule - a 404 error will be called. Is there any way to prevent this or work around this?
You can use a rewritemap within your htaccess. What this does is references an external file/script, passing it the incoming uri and getting back the rewritten uri.
In your .htaccess
RewriteMap shorts prg:/path/to/map.php
In your php file map.php
#!/path/to/php
$keyboard = fopen("php://stdin","r"); // get data from stdin
while (1) {
$line = trim(fgets($keyboard));
// fetch rewrite for line and echo out
}
The php file is passed the short url and returns - based on your logic - the full url
Related
I want to make user-friendly API for my service. I am trying to make it like Discord API - https://example.com/api/accounts/<account ID>/<stuff>/, some expected link examples below:
https://example.com/api/accounts/1234567890/data/
https://example.com/api/accounts/1234567890/inventory/
https://example.com/api/accounts/1234567890/servers/
I tried to make some htaccess tricks, but without any result - website got corrupted (just white page without anything) or account ID was considered as folder (HTTP 404, because I don't want those IDs as folders).
Thank you in advance for your response
Edit 1:
File structure can be really any, I just want it working. But most expected by me would be
api
- accounts
- data
- inventory
- ...
- server (example)
- game (example)
Edit 2:
My API is OOP - so I can call it from any place. I just need to get data and create new object and call some functions. In my bad-looking API, link was like: https://example.com/api/accounts/data.php?id=123456789 and in that script I call $stuff = new StuffObj($id, $other, $stuff, $from, $post); and function like $stuff->execute();. But with new API I can just make GET var, like action and in switch choose what object to use.
In my bad-looking API, link was like: https://example.com/api/accounts/data.php?id=123456789
In that case, to implement a "pretty" URL of the form /api/accounts/<id>/data/, you would do something like the following using mod_rewrite in your /api/.htaccess file:
Options -MultiViews
RewriteEngine On
RewriteRule ^accounts/(\d+)/data/$ accounts/data.php?id=$1 [L]
To make this more "generic", to rewrite a URL of the form /api/accounts/<id>/<action>/ to /api/accounts/<action>.php?id=<id>, assuming <action> would only consist of lowercase letters then you could change the above RewriteRule to read:
RewriteRule ^accounts/(\d+)/([a-z]+)/$ accounts/$2.php?id=$1 [L]
Although in your file structure you appear to have "subdirectories"(?) of the /accounts directory that also match the basename of the .php file? If so, this could be problematic, hence why I have disabled MutliViews above.
But with new API I can just make GET var, like action
Yes, you could - and would perhaps be preferable - everything is managed by a single entry point (eg. index.php - but you can call it anything your like) - However, you need to be specific and decide on this before implementing the "pretty URL" - which is really just cosmetic fluff. In fact, if this is just an API, do you need to implement a "pretty URL" at all?
We currently have a full Angular project running in a sub-dir on our server and a physical "device" using a hardcoded URL to send a user to that page.
I'm looking for some kind of way to "intercept" the request via a PHP script first to (for example, not the real purpose) see if the requested "ID" param for that browser page has enabled the option to view the browser page or if it has been configured by the user to return a 406 HTTP response (for example).
Currently:
- ..com/app/routing-view?id=1234 => Angular view -> fetch info
Idea:
- ..com/app/routing-view?id=1234 => PHP-script -> isValid => forward to angular and do a normal 'webview' -> fetch info
- ..com/app/routing-view?id=2889 => PHP-script -> notValid => HTTP code
I thought about having a .htaccess "intercept" the url and forward it to a .php file. Do the magic and checks there, and then forward the browser to the original url, but to "bypass" the previous interceptor.
It's about that last part that I'm currently having issues. Because it's Angular and it is using paths, I can't just say "okay, redirect to index.html instead of index.php" because it would need to be something like ..com/app/routing-view?id=1234 (and the index.html is located in the /app directory.
I don't want to add PHP code to the original Angular-index file if that could be avoided.
Am I looking at this right or would there be a different, more efficient way to tackle this?
The reason for all this is that I want to (for example) return a different HTTP code or different headers to the device instead of a 200 html header response even if the ID turned out to be disabled or something like that.
Got it to work via a simple .htaccess, a parameter and an "interceptor" script. Quite simple and basic really, not sure why my brain didn't go this route before posting my question.
Added the .htaccess to the root of the Angular application with the following code.
RewriteEngine On
# Check if the request has been intercepted before (via URL parameter)
RewriteCond %{QUERY_STRING} !(^|&)intercepted=true
# .. If not, rewrite the page to use the interceptor script (only if it matches the route).
RewriteRule ^my-route dir-of-script/extra/interceptor.php?url=https://%{HTTP_HOST}%{REQUEST_URI}?%{QUERY_STRING} [L,QSA]
# Other stuff for rewriting the Angular routes etc
...
Added a /dir-of-script/extra/interceptor.php like-script that parses the URL from the GET parameter, fetches the info, does the checks and depending on the result, returns the output or redirects the page and let it through.
This adds a ..&intercepted=true parameter to the original URL so that the .htaccess doesn't intercept it again.
This seems to be working like a charm (for my situation). The only "downside" is that this counts as a redirect and not just a rewrite when it was allowed to go through. Will look further into it, to maybe let the PHP script "serve" the Angular content instead of redirecting to it.
For a "classic" website, one would create a /foldername/index.php for every web page. With WordPress, however, this is not the case. For example, if a page was created with WordPress whose URI was http://myblog.org/some_page, you would not find the folder www/myblog.org/some_page in your web host's FTP.
My question then, is, How can I serve up pages located at http://[MY_WEBSITE].com/[page_name] for any arbitrary page_name, without creating a new folder for every page_name?
One method would be to use the page_name as parameter to a common file and use that to serve the contents of the required page.
That behaviour is handled (in an Apache server) by a .htaccess file, wherein rewrite rules are defined. Rewrite rules basically capture incoming traffic and directs those requests to a file on the server (typically a single page which will act as a router).
The router is then responsible for taking the input URI (usually via $_SERVER["REQUEST_URI"] in PHP) and working out what to do with it, and ultimately what the output will be for that request.
As for a decent router, you could look at klein.php. Also, a brief example:
# htaccess file
RewriteEngine On
RewriteRule ^[^\.]+$ index.php
And the index.php:
$route = $_SERVER["REQUEST_URI"];
if($route === '/home')
{
echo 'This is the homepage';
}
You tell your server to rewrite the URL. Most servers do it in their own way, so to find out how to do it look at your server's documentation.
Wordpress uses templates that make use of the require() function and a foreach loop commonly called "The Loop" to retreive content.
Different pages are called using different templates. If you want to know exactly how this logic is calculated, look into this.
I am working with PHP5.3.6 on Windows 2008R2.
In Wordpress, I can set all kinds of friendly URLs which ultimately route to one PHP page. From the outside, it looks like many pages, but is only one.
In the web.config file (or .htaccess) file, there is only one instruction as opposed to having one entry per page/article.
This means that somewhere PHP is looking at the URL, comparing it to the database (where the article titles exist) and then routing transparently to the page.
All of this is transparent to the end user.
How is this accomplished in a non wordpress PHP site?
Here's a related answer that touches on how to do that. In brief, you'll want to check the $_SERVER['REQUEST_URI'] and just parse that.
Here's a simple example of parsing the request (MVC routers are usually configurable, and can route and match for many different URI structures):
If your format is something like news/article-slig you can do this (example code, there are less rigid ways to do this):
list($section, $slug) = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
At this point your PHP script knows how to interpret the request. If this were a full blown MVC application, the router would load the matching controller and pass the request data to it. If you're just doing a simple single page script, loading some data, then your DB calls would follow.
If the request is invalid, then a simple header() call can notify the browser:
header('HTTP/1.0 404 Not Found');
And any data output would be the content of your 404 page.
I can't vouch for wordpress, one method I have used is to redirect 404's in .htaccess to index.php and then have that file sort by parsing:
$sub = $_SERVER['SERVER_NAME'];
$file = $_SERVER['REQUEST_URI'];
$sub can be used to mask non existant subdomains to a specific file. $file can be used in a switch or if clause to include / redirect based on file name.
Obviously, you need to make sure that the alias' are not actual files in your doc root.
Its called Routing(you can also check info about Front Controller pattern). You can write your own implementation by redirecting all your requests to single file using server settings and parse request in this file. You can also check, for example, Zend_Controller_Router docs and sources to understand how it works.
http://framework.zend.com/manual/en/zend.controller.router.html
http://framework.zend.com/manual/en/zend.controller.html
I have a website that responds on *.domain.com.
Going to x.domain.com or y.domain.com should produce the same web page.
What * is I do not know, but it is and important piece of info since we track things based on it.
When moving to wordpress we ran into a pretty severe problem. It seems to generate links (using get_page_link) with the domain which is set in the admin.
This will not work for us because we can't find a way to tell wordpress to generate links without the domain (why does it do this anyway?!) and every time a link is clicked the browser goes from: x.domain.com to domain.com (since domain.com is what we have in the admin).
Unfortunately WordPress is architected such that it is really hard to get rid of the domain component of URLs. But all is not lost! Read on as the answer to your question requires a bit of background.
The WordPress team made the decision to require the user of a site to hardcode the site domain either in the database via an admin console, which you can see in the following screen shot, of via PHP which we'll discuss below:
You might ask what the difference is between the the two URLs? Even I find it confusing because I almost never need anything other them to have them both set to the root URL and as it's not important for your question I'll just gloss over that detail. If you are interested though you can learn more here:
Changing The Site URL
Editing wp-config.php: WordPress Address (URL)
Giving WordPress Its Own Directory
Moving along, the other option is to hardcode the two PHP constants WP_SITEURL and WP_HOME in the /wp-config.php file which can be found in the root of a WordPress installation. Those two lines might look like this in your /wp-config.php file:
define('WP_HOST','http://domain.com');
define('WP_SITEURL','http://domain.com');
The good news is that you can define both of them dynamically based on the current domain your site is serving from (I'm going to assume you have both your DNS server and your Apache web server configured for wildcard DNS.) You can use the following code to match any subdomain name consisting of letters and numbers:
$root_domain = 'domain.com'; // Be sure to set this to your 2nd level domain!!!
$this_domain = $_SERVER['SERVER_NAME'];
if (!preg_match("#^([a-zA-Z0-9]+\.)?{$root_domain}$#",$this_domain)) {
echo "ERROR: The domain [$this_domain] is not a valid domain for this website.";
die();
} else {
define('WP_HOME',"http://{$this_domain}");
define('WP_SITEURL',"http://{$this_domain}");
}
The bad news is you may have some "artifacts" to deal with after you get it working such as how URLs are handled for image URLs stored in the database content (which may or may not end up being a problem) or for Google Maps API keys, etc. If you have trouble with them let me suggest you post another question here or even better at the new WordPress Answers Exchange also run by the same people as StackOverflow.
As for telling WordPress how to generate links, there are filters you can "hook" but in my quick testing I don't think you need it because WordPress will generate the links for whatever domain happens to be your current domain. Still if you do find you need them you can do it although be prepared to be overwhelmed by all the add_filter() statements required! Each one controls one of the different ways links can be generated in WordPress.
Here is the hook filter function and the 40+ add_filter() calls; you might not need them all but if you do here they are:
function multi_subdomain_permalink($permalink){
$root_domain = 'domain.com';
$this_domain = $_SERVER['SERVER_NAME'];
if (preg_match("#^([a-zA-Z0-9]+)\.?{$root_domain}$#",$this_domain,$match)) {
$permalink = str_replace("http://{$match[1]}.",'http://',$permalink);
}
return $permalink;
}
add_filter('page_link','multi_subdomain_permalink');
add_filter('post_link','multi_subdomain_permalink');
add_filter('term_link','multi_subdomain_permalink');
add_filter('tag_link','multi_subdomain_permalink');
add_filter('category_link','multi_subdomain_permalink');
add_filter('post_type_link','multi_subdomain_permalink');
add_filter('attachment_link','multi_subdomain_permalink');
add_filter('year_link','multi_subdomain_permalink');
add_filter('month_link','multi_subdomain_permalink');
add_filter('day_link','multi_subdomain_permalink');
add_filter('search_link','multi_subdomain_permalink');
add_filter('feed_link','multi_subdomain_permalink');
add_filter('post_comments_feed_link','multi_subdomain_permalink');
add_filter('author_feed_link','multi_subdomain_permalink');
add_filter('category_feed_link','multi_subdomain_permalink');
add_filter('taxonomy_feed_link','multi_subdomain_permalink');
add_filter('search_feed_link','multi_subdomain_permalink');
add_filter('get_edit_tag_link','multi_subdomain_permalink');
add_filter('get_edit_post_link','multi_subdomain_permalink');
add_filter('get_delete_post_link','multi_subdomain_permalink');
add_filter('get_edit_comment_link','multi_subdomain_permalink');
add_filter('get_edit_bookmark_link','multi_subdomain_permalink');
add_filter('index_rel_link','multi_subdomain_permalink');
add_filter('parent_post_rel_link','multi_subdomain_permalink');
add_filter('previous_post_rel_link','multi_subdomain_permalink');
add_filter('next_post_rel_link','multi_subdomain_permalink');
add_filter('start_post_rel_link','multi_subdomain_permalink');
add_filter('end_post_rel_link','multi_subdomain_permalink');
add_filter('previous_post_link','multi_subdomain_permalink');
add_filter('next_post_link','multi_subdomain_permalink');
add_filter('get_pagenum_link','multi_subdomain_permalink');
add_filter('get_comments_pagenum_link','multi_subdomain_permalink');
add_filter('shortcut_link','multi_subdomain_permalink');
add_filter('get_shortlink','multi_subdomain_permalink');
add_filter('home_url','multi_subdomain_permalink');
add_filter('site_url','multi_subdomain_permalink');
add_filter('admin_url','multi_subdomain_permalink');
add_filter('includes_url','multi_subdomain_permalink');
add_filter('content_url','multi_subdomain_permalink');
add_filter('plugins_url','multi_subdomain_permalink');
add_filter('network_site_url','multi_subdomain_permalink');
add_filter('network_home_url','multi_subdomain_permalink');
add_filter('network_admin_url','multi_subdomain_permalink');
While brings us to the final point. There is functionality in WordPress that attempts to ensure every URL that is loaded is served via its canonical URL which in general is a web best practice, especially if you are concerned with optimizing search engine results on Google and other search engines. In your case, however, if you really do not want WordPress to redirect to your canonical URL then you need to add a redirect_canonical filter hook and tell WordPress not to do it.
What follows is the code to make sure any page that serves as "x.domain.com" stays on "x.domain.com" even if all the URLs are filtered to be "domain.com". That may not be the exact logic you need but I'm just showing you the building blocks of WordPress so you'll be able to figure out the logic that you require.
A few final details about this function call; parameters #3 and #4 refer respectively to the priority (10 is standard priority so this hook will not be handled special) and the number of function arguments (the 2 arguments are $redirect_url and $requested_url.) The other thing to note is that returning false instead of a valid URL cancels the canonical redirect:
add_filter('redirect_canonical','multi_subdomain_redirect_canonical',10,2);
function multi_subdomain_redirect_canonical($redirect_url,$requested_url){
$redirect = parse_url($redirect_url);
$requested = parse_url($requested_url);
// If the path+query is the same for both URLs, Requested and Redirect, and
if ($redirect['path']+$redirect['query']==$requested['path']+$requested['query']) {
// If Requested URL is a subdomain of the Redirect URL
if (preg_match("#^([a-zA-Z0-9]+).{$redirect['host']}$#",$requested['host'])) {
$redirect_url = false; // Then cancel the redirect
}
}
return $redirect_url;
}
That's about it. Hope this helps.
-Mike
do you have any control over your hosting? Maybe you can use the rewrite module in apache, if you are using apache.
In httpd.conf add:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^x\.domain\.com
RewriteRule ^(.*)$ http://www.domain.com/x/$1
RewriteCond %{HTTP_HOST} ^y\.domain\.com
RewriteRule ^(.*)$ http://www.domain.com/y/$1
You can change the way you pass the variable "v" too
RewriteRule ^(.*)$ http://www.domain.com/$1&var=v
I didn't try the code, but i'm pretty sure that at least it will open your mind to a new way to deal with this issue --one that doesn't involve so much coding.
Cheers.
A.