Routing issues with Slim Framework running on AppFog - php

I've been successfully running Slim apps on a couple different servers and tried setting one up on AppFog today using the same structure, but it isn't running normally.
I'll start with my directory structure:
.htaccess
/public
.htaccess
index.php
/routes
/Slim
The root .htaccess file contains the DocumentRoot code from the AppFog docs.
RewriteEngine on
RewriteCond %{HTTP_HOST} ^brs.aws.af.cm$ [NC,OR]
RewriteCond %{HTTP_HOST} ^www.brs.aws.af.cm$
RewriteCond %{REQUEST_URI} !public/
RewriteRule (.*) /public/$1 [L]
The /public directory is where my api code will go, and the Slim index.php and .htaccess files currently are. The index.php file contains two simple routes:
require '../Slim/Slim.php';
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
// Default GET route
$app->get('/', function () {
echo "Default GET route";
});
// Hello World route
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
The server is setup at http://brs.aws.af.cm/ and I've listed the main routes below:
/ => uses the default GET route
/hello/john => 404 Error
/public/hello/john => works, but requires "/public" in the url
And here's some extra weirdness. Seven-character routes result in a 404 error, six or less end up using the default GET route.
/123456 => shouldn't work, but uses the default GET route
/1234567 => 404 error
I'm completely stumped. I figure it has something to do with the DocumentRoot code, but I'm not sure what exactly. I've also tried setting
RewriteBase /public/
in /public/.htaccess but it doesn't seem to affect anything.
Any help would be greatly appreciated. Thanks!

There is a bug in the Slim PHP framework in Environment.php line 143. In particular, it assumes that the $_SERVER['SCRIPT_NAME'] path is compatible with $_SERVER['REQUEST_URI'] variable. In most cases this is probably true, however not when using MOD_REWRITE to hide an intermediate directory (as is happening in the .htaccess you quoted).
What's happening is $_SERVER['SCRIPT_NAME'] looks something like "/public/something..." but (because it is hidden), $_SERVER['REQUEST_URI'] looks like "/something...".
Slim is assuming the request URI is based on the script name, which is not the case here. I plan on notifying the Slim authors of the bug, but wanted to make note of it here as well.
You can fix/work around this by modifying Slim/Environment.php line 143 to this:
if (strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === 0) {
$env['SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME']; //Without URL rewrite
$env['PATH_INFO'] = substr_replace($_SERVER['REQUEST_URI'], '', 0, strlen($env['SCRIPT_NAME']));
} else {
$env['SCRIPT_NAME'] = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])); //With URL rewrite
$env['PATH_INFO'] = $_SERVER['REQUEST_URI'];
}
// $env['PATH_INFO'] = substr_replace($_SERVER['REQUEST_URI'], '', 0, strlen($env['SCRIPT_NAME']));
At least that seems to work fine in my case. I believe the intention was to remove the path from the request uri, but that seems a pretty horrible way of doing it. If you need subdirectories to keep working you may need to do a bit more thinking. :)

Related

Is there a function or way to do in PHP similar how this works in Node JS?

In Node we can get an url address with a structure like this /example/:page/:id and we can take the page and id params. Is there a possibility to do something similar using PHP? Or is it only possible using the "?" with all the wanted params after the interrogation point?
I searched for a while and I tried some configurations in the htaccess file. All of them gave some kind of error like 403, 404 or in one of the configurations the intentioned page was loaded but it didn't find the css, js and images files.
Thanks
Edit:
I will put the solution I found here because maybe it can be useful for someone someday. After looking for some routers packages, I saw them instructing to put these lines in the htaccess file:
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L,QSA]
I've tried something like this before and it was the one that I mentioned in the question that loaded the page but it didn't find the files like css, js, etc.
So I've decide to check the base url and I saw this was the point where the error was coming. After I changed it, the page loaded as the expected and now it's possible to get the value where the users can put a number and redirect to the page that they want (it's something like a magazine).
You can achieve it many ways.
In Laravel (see Documentation). I think every framework now has routing implemented.
Route::get('example/{page}/{id}', function ($page, $id) {
//
})->where(['page' => '[0-9]+', 'id' => '[a-z]+']);
With Mod-rewrite and then with access through $_GET parameters.
RewriteEngine on
RewriteRule ^example/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ index.php?page=$1&id=$2 [NC]
You can also redirect everything to index.php and there implement your own router. See: Redirect all to index.php using htaccess
RewriteEngine on
RewriteRule ^(.*)$ /index.php?path=$1 [NC,L,QSA]
Something like this could work
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = explode( '/', $uri );
// all of our endpoints start with /person
// everything else results in a 404 Not Found
if ($uri[1] !== 'page') {
header("HTTP/1.1 404 Not Found");
exit();
}
For more reference visit this url
https://developer.okta.com/blog/2019/03/08/simple-rest-api-php
have you tried parse_url()?
it will return and associative array which has all the components in your URL

How to make website pages accessed by code

It's pretty hard to explain so if somebody have a better title then be my guest..
Basically I made a website with 5 pages.
1) index.html
2) page.html
3) footer.html
4) menu.html
5) contact.html
In order to access the pages you have to type the page name at the end of the domain (I bet you knew that..)
I wanted to access the pages with a code..
for example -> mywebsite.com\?page=contact
How can I do this ?
Kind Regards,
Kobi.
why don't make a index.php with following code:
<?php
include($_GET['page'].'.html');
?>
The result will be:
calling mywebsite.com/?page=contact will open mywebsite.com/index.php?page=contact because this is default
the url will stay mywebsite.com/?page=contact
the script load the file contact.html and show it
You only need to configure whatever web server you have to look for a file called index.php whenever you don't specify any. That has been a pretty standard feature of all web servers since the early 1990s. In Apache you'll use the DirectoryIndex directive; this is what mine looks like:
<IfModule dir_module>
DirectoryIndex index.php index.html
</IfModule>
Then, write PHP code in such index.php to act as router. You should check Variables From External Sources and learn about $_GET.
However, that's probably not the best layout. Friendly URLs have been around for years:
http://example.com/contact
... and it's again mostly a web server feature. In Apache you'd use the mod_rewrite module. Here's a sample rule used by some PHP frameworks:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
Requested path can be parsed out of $_SERVER['REQUEST_URI'].
Once you put your hands in the index.php file there're a lot of design patterns you can use (modern frameworks often use a third-party router and template engine) but if you're to learn from scratch and just want to get something done quickly from static HTML you can use a combination of switch statements (to create a route white list) and readfile() to inject each file into the output. (Beware that PHP include construct family will handle files as PHP code, which is not what you want.)
<?php
define('INC_PATH', __DIR__ . '/../wherever/includes/are');
switch ($_GET['page']) {
case 'contact':
case 'help':
case 'whatever':
$page = $_GET['page'];
break;
default:
$page = 'error';
}
readfile(INC_PATH . '/index.html');
readfile(INC_PATH . '/page.html');
readfile(INC_PATH . '/footer.html');
readfile(INC_PATH . '/menu.html');
readfile(INC_PATH . "/$page.html");

Using the url query parameter with a controller

I am attempting to implement an oembed provider using the Silverstripe framework but have come across an issue.
I have a controller routed from the url /omebed.json and it works fine if I call something like /omebed.json?mediaurl=mymovie.mp4.
However the Oembed standard states it should be /omebed.json?url=mymovie.mp4
But Silverstripe internally checks the $_GET['url'] variable and will attempt to route to that page/controller.
So SilverStripe is trying to route to /mymovie.mp4 skipping my controller and hitting the ErrorPage_Controller creating a 404.
I'm thinking im going to have to extend the ErrorPage_Controller and rejig it if the url is oembed.json, but this seems a little hackish.
Any suggestions?
Cheers
Extending on #Stephen's answer, here is a way to get around that issue without duplicating main.php and without modifying it directly.
What I did was create a _ss_environment.php file which is added early on in the loading process of Silverstripe.
_ss_environment.php
global $url;
$url = $_GET['raw_url'];
if (isset($_GET['url']))
{
unset($_GET['url']);
}
// IIS includes get variables in url
$i = strpos($url, '?');
if($i !== false)
{
$url = substr($url, 0, $i);
}
.htaccess
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule .* framework/main.php?raw_url=%1 [QSA]
So here is what is happening:
The .htaccess is now using raw_url instead of url
_ss_environment.php is being called early in the loading process, setting the global $url variable that main.php normally sets. This is set with raw_url rather than url.
To prevent main.php to just override it again when it sees your url query string parameter, it is unset (Silverstripe seems to reset this later as far as my test is concerned).
Lastly is a little block of code that main.php would normally run if $_GET['url'] is set, copied as-is for apparent support in IIS. (If you don't use IIS, you likely won't need it.)
This has a few benefits:
No update to main.php allows upgrading Silverstripe slightly easier in the future
Runs the minimal amount of code needed to "trick" Silverstripe into thinking it is running normally.
The one obvious drawback to any solution for changing away form the url query string parameter is if anything looks at the parameter directly. With how Silverstripe works, it is more likely that code uses the $url global variable or the Director class rather than looking at the query string for the current URL.
I tested this on a 3.1 site by doing the changes I mentioned and:
Creating a controller called TestController
In the init function of the controller, I am running the following:
var_dump($_GET['url']);
var_dump($this->getRequest()->getVars());
Visited /TestController?url=abc123, saw the value of both dumps have "abc123" as the value for the URL parameter.
Navigated to a few other custom pages on the site to make sure they were still working (no issues that I saw)
Unfortunately, I haven't been able to find documentation for the order of inclusion in regards to _config.php and _ss_environment.php. However, after browsing through the code, I have worked out it is this:
main.php runs, first main task is to require core/Constants.php
Constants.php's first task is to search for _ss_environment.php in the base folder and potential parent folders. If it finds it, it will be included.
Going back to main.php (and after the $_GET['url'] check is done in main.php), it will start an ErrorControlChain which it internally does another require for core/Core.php
Inside Core.php, it performs calls for the config manifest
ConfigManifest.php exposes the functions to actually add _config.php files and for them to be required.
I could probably go on however I think this gives a pretty good picture of what is going on. I don't really see a way around not using the _ss_environment.php file. Nothing else gets included early enough that you can hook into without modifying core code.
I had a quick play with this the other day. And looking at what main.php does it might be best to hack away at it rather than ErrorPage_controller.
For startes SS's default .htaccess file does this:
<IfModule mod_rewrite.c>
SetEnv HTTP_MOD_REWRITE On
RewriteEngine On
# RewriteBase /silverstripe
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* framework/main.php?url=%1&%{QUERY_STRING} [L]
</IfModule>
Note the ?url changing that to something else and then changing main.php's usage as well may/should help or will cause a heap of extra errors and sadness.
To avoid hacking the core/framework, you could change the .htaccess to target a copy of main.php in mysite (with appropriate include changes).

php get method unreachable in xampp

I've been trying to figure this out for hours, but haven't been able to get anywhere. There are so many questions asked in stackoverflow, and I've tried almost all of them, but still haven't been able to figure out what the problem is that I'm having.
I'm working on a project for my Master's thesis for which I have to perform some data analysis. I'm building a site using php, backbone, and mongodb. I'm using xampp, and this is my directory structure:
htdocs
|-MyProject
|------API
|---Slim
|---index.php
|------scripts
|-------App
|----Collections
|----Models
|----Views
|-------lib
|-index.html
The index.html is the boilerplate html stuff and calls backbone collections and views. The index.php within the directory API instantiates slim and has GET method.
Here's the index.php
<?php
echo 'test';
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();
use Slim\Slim;
$app = new Slim();
$app->get('/trends', 'getTrends');
and my backbone collections
App.Collections.TrendsCollection = Backbone.Collection.extend({
model : App.Models.TrendModel,
url : "API/trends",
initialize: function(){
console.log('collections');
}
});
I read that I may need to set RewriteRule, so I tried this:
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /API/index.php [R,NC]
but this didn't find the method I'm trying to reach either. I've been trying to figure this out for too long, and I'm pressing for time. Can someone please give me some guidance?
EDIT:
function getTrends(){
echo 'Hello';
}
I think your problem is in getTrends function. Show me that function.
After hours of trying to figure it out, I finally realized that the rewrite rules should be in .htaccess in the same directory as index.php. It finally worked.

How can I quickly set up a RESTful site using PHP without a Framework?

I would like to quickly set up a RESTful site using PHP without learning a PHP Framework. I would like to use a single .htaccess-file in Apache or a single rule using Nginx, so I easyli can change web server without changing my code.
So I want to direct all requests to a single PHP-file, and that file takes care of the RESTful-handling and call the right PHP-file.
In example:
The user request http://mysite.com/test
The server sends all requests to rest.php
The rest.php call test.php (maybe with a querystring).
If this can be done, is there a free PHP-script that works like my rest.php? or how can I do this PHP-script?
Using Apache Mod Rewrite:
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^test$ rest.php [nc]
Yes, make a 404 Redirect to a page called rest.php. Use $url = $_SERVER['REQUEST_URI']; to examine the url. In rest.php you can redirect to wherever you want.
This only requires one rule (404 in your .htaccess file).
You have to be careful and make sure that if the page requested (e.g. mysite.com/test1 doesn't have a test1.php) errors out with a 404 you don't get yourself caught in a loop.
Modified slightly from the way that Drupal does it. This redirects everything to rest.php and puts the original requested URL into $_GET['q'] for you do take the appropriate action on. You can put this in the apache config, or in your .htaccess. Make sure mod_rewrite is enabled.
RewriteEngine on
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ rest.php?q=$1 [L,QSA]
If all you then want to do is include the requested file, you can do something like this
<?php
if (!empty($_GET['q'])) {
$source = $_GET['q'] . '.php';
if (is_file($source)) {
include $source;
} else {
echo "Source missing.";
}
}
?>
You really don't want to do that, however; if someone were to request '/../../../etc/passwd', for example, you might end up serving up something you don't want to.
Something more like this is safer, I suppose.
<?php
if (!empty($_GET['q'])) {
$request = $_GET['q'];
switch ($request) {
case 'test1':
include 'test1.php';
break;
default:
echo 'Unrecognised request.';
break;
}
}
?>

Categories