0

I'm trying to work out to create a proper RESTful API in PHP. I've been following some steps from http://coreymaynard.com/blog/creating-a-restful-api-with-php/ and to me it looks like I've done everything exactly the same except when I try and go to http://localhost/api/v1/example I get an internal server error.

In the apache error log I am seeing the following:

[Sat Feb 18 19:30:10.594193 2017] [core:error] [pid 10272:tid 1144] [client ::1:58203] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3747): [client ::1:58203] AH00121: r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/api.php
[Sat Feb 18 19:30:10.594193 2017] [core:debug] [pid 10272:tid 1144] core.c(3753): [client ::1:58203] AH00122: redirected from r->uri = /api/v1/example

My BaseAPI class (what they call API in the example) is below:

abstract class API
{
    protected $method = '';
    protected $endpoint = '';
    protected $verb = '';
    protected $args = Array();

    public function __construct($request)
    {
        header("Access-Control-Allow-Origin: *");
        header("Access-Control-Allow-Method: *");
        header("Content-Type: application/json");

        $this->args = explode('/', rtrim($request, '/'));
        $this->endpoint = array_shift($this->args);
        if (array_key_exists(0, $this->args) && !is_numeric($this->args[0]))
        {
            $this->verb = array_shift($this->args);
        }

        $this->method = $_SERVER["REQUEST_METHOD"];
        if ($this->method === "POST" && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER))
        {
            if ($_SERVER['HTTP_X_HTTP_METHOD'] === "DELETE")
            {
                $this->method = "DELETE";
            }
            else if ($_SERVER["HTTP_X_HTTP_METHOD"] === "PUT")
            {
                $this->method = "PUT";
            }
            else
            {
                throw new Exception("Unexpected header");
            }
        }

        switch ($this->method)
        {
            case 'DELETE':
            case 'POST':
                $this->request = $this->cleanInputs($_POST);
                break;
            case 'GET':
                $this->request = $this->cleanInputs($_GET);
                break;
            case 'PUT':
                $this->request = $this->clearInputs($_GET);
                $this->file = file_get_contents("php://input");
                break;
            default:
                $this->response('Invalid Method', 405);
                break;
        }
    }

    public function processAPI()
    {
        if (method_exists($this, $this->endpoint))
        {
            return $this->response($this->{$this->endpoint}($this->args));
        }
        return $this->response("No Endpoint: $this->endpoint", 404);
    }

    private function response($data, $status = 200)
    {
        header("HTTP/1.1 " . $status . " " . $this->requestStatus($status));
        return json_encode($data);
    }

    private function cleanInputs($data)
    {
        $clean_input = Array();
        if (is_array($data))
        {
            foreach ($data as $key => $value)
            {
                $clean_input[$key] = $this->cleanInputs($value);
            }
        }
        else
        {
            $clean_input = htmlspecialchars($data);
        }
        return $clean_input;
    }

    private function requestStatus($code)
    {
        $status = array(
            200 => 'OK',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            500 => 'Internal Server Error'
        );
        return ($status[$code])?$status[$code]:$status[500];
    }
}

The MyAPI file is as follows:

require_once 'BaseApi.php';

class MyAPI extends API
{
    public function __construct($request)
    {
        parent::__construct($request);
    }

    protected function example()
    {
        if ($this->method === "GET")
        {
            return "Hello to my RESTful API";
        }
        else
        {
            return "Only accepts GET requests";
        }
    }
}

My api.php is as follows:

require_once 'MyAPI.php';

try
{
    $api = new MyAPI($_REQUEST['request']);
    echo $api->processAPI();
}
catch (Exception $ex)
{
    echo $ex->getMessage();
}

My .htaccess file is as follows:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule api/v1/(.*)$ api/v1/api.php?request=$1 [QSA,NC,L]
</IfModule>

It looks like to me everything is exactly the same as in that article, if it makes any difference I'm using Wamp server running on Windows 10.

1
  • I use Slim PHP MVC when I try to create MVC. I looked at the tutorial and it seemed kind of complicated. Maybe give Slim a go? slimframework.com Commented Feb 18, 2017 at 21:41

1 Answer 1

1

I suspect, you have all files, .htaccess and *.php, in the same base directory.

If this is the case, the substitution api/v1/api.php will be seen as just another request for a non-existing file, because api.php is in the current directory and not in api/v1.

Therefore api/v1/example will be rewritten to api/v1/api.php?request=example, and then rewritten to api/v1/api.php?request=api.php, and then rewritten to api/v1/api.php?request=api.php, and so on.


To make this work, you must have .htaccess in the base directory and all PHP files in the subdirectory api/v1.


If you want to keep the files in one directory, the target of the rewrite rule must point to an existing script file api.php?request=$1, e.g.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule api/v1/(.*)$ api.php?request=$1 [QSA,NC,L]
Sign up to request clarification or add additional context in comments.

1 Comment

Ah yea that makes sense, I've given that a try and it works. Thanks for your help

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.