2
\$\begingroup\$

I am using PHP to build a REST API. I want to receive requests from the URL in the following format:

http://mydomain.com/api/format/method?app_id=value&call=encrypted_request

So, I have modified my .htaccess as:

Options +FollowSymLinks 
IndexIgnore */* 
RewriteEngine On 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php

Now, I receive the request URI and extract the name of the method and the parameters from it:

$params = explode('/',$_SERVER['REQUEST_URI']);
$format = $params[count($params)-2];
$method = substr($params[count($params)-1],0,strpos($params[count($params)-1],'?'));
$app_id = $_REQUEST['app_id'];
$enc_request = $_REQUEST['call'];

My question here is, whether it is ok to use substr and strpos on the array value, simultaneously ? Also, whether this approach of forming the URL and extracting the paramters from it is correct ?

Reference : substr strpos

\$\endgroup\$
  • \$\begingroup\$ If you want to fetch the GET variables, they are stored in $_GET array. \$\endgroup\$ – hjpotter92 Mar 30 '13 at 8:57
  • \$\begingroup\$ If I were you, I'd store $params[count($params)-1] in a variable to make things easier to read. Then, I'd consider the situation where what you are looking for does not exist : what is no '?' can be found ? At the moment, I think your substr would return the empty string but you might prefer the whole string. \$\endgroup\$ – Josay Mar 30 '13 at 8:59
  • \$\begingroup\$ @Josay: Valid point. If I add some checks for various possible case or use a try...catch block, will this approach and code be ok then ? \$\endgroup\$ – gentrobot Mar 30 '13 at 9:17
1
\$\begingroup\$

Is it ok to use substr and strpos on the array value, simultaneously?

Well to answer this question we can just look up the manual and see what each returned and passed value means. As for the types we are perfectly fine because the substr function will accept an int (or something that can be valued as one) and we are returning an int from strpos.

As per the logical meaning, assuming $params[count($params)-1] actually exists, we are getting a position of the first occurrence of ? in the string.

But we have to notice that strpos might return false if no occurrence of the symbol is found, therefore leading to a problem in your script.

I suggest you to check for that option:

$e = strpos($params[count($params)-1],'?');
if ($e === false) $e = 0;

And then call:

$method = substr($params[count($params)-1],0,$e);

Is this approach of forming the URL and extracting the paramters from it correct?

Well it has few bugs we should discuss.

First, every occurrence in the form of $params[count($params)-x] may trigger an undefined index error if the count of $params is actually 0 (or 1). Therefore I suggest you to check for the option where the string is way shorter than you expect:

if (count($params) < 2) // what do we do?

On a side note, you probably want to store the value of count($params) instead of calling it every time (it's not likely gonna change). ($i = count($params)).

Second, assuming we are expecting a well formed string in the form of ([x] means x is optional):

a[/b[/c[/...]]][?x=x1[&y=y1[&z=z1[&...]]]]

You might want to take a look at $_SERVER['PATH_INFO'] which will remove the query string for you:

Contains any client-provided pathname information trailing the actual script filename but preceding the query string, if available. For instance, if the current script was accessed via the URL http://www.example.com/php/path_info.php/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain /some/stuff.

Notice: it will work also with URL rewriting because the actual filename will be inserted anyway into the request.

\$\endgroup\$
1
\$\begingroup\$

Instead of raw string manipulation, I would use parse_url and parse_str:

<?php

// Note: parse_url is not intended for relative URLs
// (in other words, bare $_SERVER['REQUEST_URI'] will not work -- you'll have to build a full URL)
$url = 'http://mydomain.com/api/format/method?app_id=value&call=encrypted_request';

$path = explode('/', ltrim(parse_url($url, PHP_URL_PATH), '/'));
parse_str(parse_url($url, PHP_URL_QUERY), $query);

$api = array_shift($path);
$format = array_shift($path);
$method = array_shift($path);

$app_id = (isset($query['app_id'])) ? $query['app_id'] : null;
$enc_request = (isset($query['call'])) ? $query['call'] : null;

Yes, this code is a bit longer, but it's more readable to me. Also, raw string manipulation is hazard to failing border cases. If you use a well tested parser, problems less likely.

And, as an added bonus, this routing isn't tied to any globals. Off the top of my head I can't think of a reason, but at some point, it might be useful to be able to route a fake request. It definitely makes unit testing easier.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.