5

I am working on a test project involving the PHP's built-in web server and I am testing some ideas.

I want to implement my own caching mechanism for commonly used resources (png,jpg,json,txt, etc...) to reduce the load on the built-in server in php.

I start the built in server like this:

php -S 127.0.0.1:80 -t public router.php

So, the document root of the built-in server is set to public and it runs the router.php (as I am thinking of implementing a simple rewrite feature also).

Here's the contents of my router.php file:

<?php

// Register request uri
$requestUri = isset($_SERVER['REQUEST_URI']) 
    ? $_SERVER['REQUEST_URI'] 
    : '/';

// Handle app resources with caching
if (preg_match('/\.(?:png|jpg|jpeg|gif|xml|json|css|eot|svg|otf|ttf|woff|woff2|scss|less|txt|ico)$/', $requestUri))
{
    // Generate file name
    $fileName = __DIR__ .'/public'. $requestUri;

    // Parse file data
    $lastModified = filemtime($fileName);
    $etagFile = md5_file($fileName);
    $ifModifiedSince = (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false);
    $etagHeader = (isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false);

    // Set caching header
    header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastModified) .' GMT');
    header('Etag: '. $etagFile);
    header('Cache-Control: public');

    // Check if the requested resource has changed
    if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified || $etagHeader == $etagFile)
    {
        // File has not changed
        header('HTTP/1.1 304 Not Modified');
        exit;
    }
    else
    {
        // Parse requested resource's mime type
        $finfo = new finfo(FILEINFO_MIME);
        $mime_type = $finfo->buffer(
            file_get_contents($fileName, false, null, -1, 64),
            FILEINFO_MIME_TYPE
        );

        // Serve requested resource
        header('Content-Type: '. $mime_type);
        header('Content-Length: '. filesize($fileName));
        @readfile($fileName);
        $finfo = null;
        exit;
    }
}

// Parse requested page & action
list ($page, $action) =
    array_pad(array_values(array_filter(explode('/', $requestUri, 3), 'strlen')), 2, 'index');
if ($page == 'index') $page = 'server';

// Test - to do rest of routing
var_dump('page = '. $page);
var_dump('action = '. $action);
// include 'app/'. $page .'/'. $action .'.php';

?>

I tested the reource (png image) caching by visiting the following url: http://localhost/apple-icon-120x120.png

So, this is the first load of the resource, so serve returns the resource with HTTP 200 response as expected, takes approx 307ms: enter image description here

Now, if I press the F5 to reload the page, the server returns HTTP 304 (not modified) as expected and the request took approx 5ms (great!!): enter image description here

If I press F5 for the third time, the server still returns HTTP 304 (not modified) as expected, however this time the request took approx 306ms again (as if the resource was not cached): enter image description here

If I keep pressing F5, the time to process the request is alternativing randomly between 5m and approx 307ms.

Any ideas why it's behaving like this? Once the resource is cached, shouldn't it constantly return 304 and process the request approx 5ms? Why is the behaviour sporadic?

I do see that the returned content size is 225 bytes (when it knows the data is cahced), I just can't figure out where the bottleneck is with the request processing time. My host machine is running windows with Intel i7 CPU, 6GB RAM & SSD drives.

Latheesan
  • 23,247
  • 32
  • 107
  • 201

1 Answers1

0

Your router file is working fine. I have tested it locally and it behaves as expected: first time HTTP 200 with download, then a HTTP 304 with only headers.

Looking at your timeline it takes 307 ms to serve a response of 11.9 KB which is obviously way too slow.

You do receive HTTP 304 so your script must have been exited without sending the file. However, to send the 304 status code in first instance PHP still has to find the mtime and compute the md5 hash of the file. Accessing the file is probably the bottle-neck.

The response time alternating between 5ms and 300ms could be caused by disk caching. Maybe you have a hard drive or a hybrid drive?

Why not echo the microtime() at the start, before the mtime and after the hash calculation?

Tom
  • 3,281
  • 26
  • 33