Skip to content

Commit

Permalink
Implement integration test suite for mmamedov#10
Browse files Browse the repository at this point in the history
- Update composer dependencies
- Update test cases for updated phpunit
- Add more logging
- Add default ttl for SimpleCache::set() method call
- Implement integration test suite (guzzlehttp + symfony/process + built-in PHP web-server)
- Add integration test for internal PSR-16 filesystem-based adapter (tests are failing)
- Add PHP7.1 and PHP7.2 to Travis CI config
- Issue-related tests added to group "issue10" and can be selected by "--group issue10"
- Some tests are failing due to unpredictable error (possible race condition)
  • Loading branch information
spotman committed Feb 14, 2018
1 parent ad755cd commit 61f9aa9
Show file tree
Hide file tree
Showing 33 changed files with 4,741 additions and 53 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ language: php
php:
- 5.6
- 7.0
- 7.1
- 7.2

#script:
# - phpunit -v -c phpunit.xml
Expand Down
10 changes: 7 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@
},
"require-dev": {
"mobiledetect/mobiledetectlib": "^2.8",
"monolog/monolog": "^1.19",
"brianium/paratest": "dev-master",
"mikey179/vfsStream": "^1.6"
"monolog/monolog": "^1.23",
"brianium/paratest": "^1.1",
"mikey179/vfsStream": "^1.6",
"guzzlehttp/guzzle": "^6.3",
"cache/integration-tests": "dev-master",
"symfony/cache": "^4.0",
"symfony/process": "^3.4 || ^4.0"
},
"suggest": {
"monolog/monolog": "Allows using custom logging subsystem",
Expand Down
54 changes: 35 additions & 19 deletions src/HttpHeaders.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,28 +121,37 @@ public function send()
$this->itemETagString
);
}
}

// Will exit if conditions for the If-Modified-Since header are met
$this->checkIfNotModified();
/**
* @return array
*/
public function getSentHeaders()
{
/** @link http://php.net/manual/ru/function.headers-list.php#120539 */
return (PHP_SAPI === 'cli' && \function_exists('xdebug_get_headers'))
? xdebug_get_headers()
: headers_list();
}

/**
* Sends HTTP Header
*
* @param string $name Header name
* @param string|null $value Header value
* @param int $http_response_code HTTP response code
* @param string $name Header name
* @param string|null $value Header value
* @param int $httpResponseCode HTTP response code
*/
private function setHeader($name, $value = null, $http_response_code = null)
private function setHeader($name, $value = null, $httpResponseCode = null)
{
header($name.($value ? ': '.$value : ''), true, $http_response_code);
header($name.($value ? ': '.$value : ''), true, $httpResponseCode);
}

/**
* Set Not Modified header, only if HTTP_IF_MODIFIED_SINCE was set or ETag matches
* Content body is not sent when this header is set. Client/browser will use its local copy.
* @return bool
*/
private function checkIfNotModified()
public function checkIfNotModified()
{
$lastModifiedTimestamp = $this->itemLastModified->getTimestamp();
$modifiedSinceTimestamp = $this->getIfModifiedSinceTimestamp();
Expand All @@ -157,14 +166,16 @@ private function checkIfNotModified()
// Client's version older than server's?
// If ETags matched ($notModified=true), we skip this step.
// Because same hash means same file contents, no need to further check if-modified-since header
if ($notModified) {
$notModified = $modifiedSinceTimestamp !== false && $modifiedSinceTimestamp >= $lastModifiedTimestamp;
if (!$notModified && $modifiedSinceTimestamp !== false) {
$notModified = $modifiedSinceTimestamp >= $lastModifiedTimestamp;
}

if ($notModified) {
http_response_code(304);
$this->setHeader(self::HEADER_NOT_MODIFIED);
exit();
}

return $notModified;
}

/**
Expand All @@ -175,13 +186,20 @@ private function checkIfNotModified()
private function getIfModifiedSinceTimestamp()
{
if (!empty($_SERVER[self::HTTP_IF_MODIFIED_SINCE])) {
$mod_time = $_SERVER[self::HTTP_IF_MODIFIED_SINCE];
$modifiedSinceString = $_SERVER[self::HTTP_IF_MODIFIED_SINCE];
} elseif (!empty($_ENV[self::HTTP_IF_MODIFIED_SINCE])) {
$modifiedSinceString = $_ENV[self::HTTP_IF_MODIFIED_SINCE];
} else {
$modifiedSinceString = null;
}

if ($modifiedSinceString) {
// Some versions of IE 6 append "; length=##"
if (($pos = strpos($mod_time, ';')) !== false) {
$mod_time = substr($mod_time, 0, $pos);
if (($pos = strpos($modifiedSinceString, ';')) !== false) {
$modifiedSinceString = substr($modifiedSinceString, 0, $pos);
}

return strtotime($mod_time);
return strtotime($modifiedSinceString);
}

return false;
Expand Down Expand Up @@ -221,6 +239,7 @@ public function detectResponseETagString()

/**
* @param $name
*
* @return mixed|null
*/
private function detectResponseHeaderValue($name)
Expand All @@ -238,10 +257,7 @@ private function detectResponseHeaderValue($name)
private function getResponseHeaders()
{
if (!$this->responseHeaders) {
/** @link http://php.net/manual/ru/function.headers-list.php#120539 */
$rawData = (PHP_SAPI === 'cli') ? xdebug_get_headers() : headers_list();

foreach ($rawData as $item) {
foreach ($this->getSentHeaders() as $item) {
list($key, $value) = explode(':', $item, 2);

$this->responseHeaders[$key] = trim($value);
Expand Down
40 changes: 28 additions & 12 deletions src/PageCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@

namespace PageCache;

use PageCache\Storage\FileSystem\FileSystemCacheAdapter;
use PageCache\Strategy\DefaultStrategy;
use DateTime;
use PageCache\Storage\CacheItem;
use PageCache\Storage\CacheItemInterface;
use PageCache\Storage\CacheItemStorage;
use PageCache\Storage\FileSystem\FileSystemCacheAdapter;
use PageCache\Strategy\DefaultStrategy;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Psr\SimpleCache\CacheInterface;
use DateTime;

/**
* Class PageCache
Expand Down Expand Up @@ -97,9 +97,9 @@ public function __construct($config_file_path = null)
throw new PageCacheException('PageCache already created.');
}

$this->config = new Config($config_file_path);
$this->config = new Config($config_file_path);
$this->httpHeaders = new HttpHeaders();
$this->strategy = new DefaultStrategy();
$this->strategy = new DefaultStrategy();

//Disable Session by default
if (!$this->config->isUseSession()) {
Expand Down Expand Up @@ -129,7 +129,9 @@ public function init()
.'; script:'.$_SERVER['SCRIPT_NAME'].'; query:'.$_SERVER['QUERY_STRING'].'.');

// Search for valid cache item for current request
if ($item = $this->getCurrentItem()) {
$item = $this->getCurrentItem();

if ($item) {
// Display cache item if found
// If cache file not found or not valid, init() continues with cache generation(storePageContent())
$this->displayItem($item);
Expand Down Expand Up @@ -195,11 +197,24 @@ private function displayItem(CacheItemInterface $item)
// Decide if sending headers from Config
// Send headers (if not disabled) and process If-Modified-Since header
if ($this->config->isSendHeaders()) {
// Check if conditions for the If-Modified-Since header are met
$notModified = $this->httpHeaders->checkIfNotModified();

// Send standard caching headers
$this->httpHeaders->send();

$this->log(__METHOD__.' Headers sent: '.PHP_EOL.implode(PHP_EOL, $this->httpHeaders->getSentHeaders()));

if ($notModified) {
$this->log(__METHOD__.' Not modified, exiting with '.http_response_code());
// 304 Not Modified header already sent, exiting without content
exit();
}
}

// Normal flow, show cached content
$this->log(__METHOD__ . ' Cache item found: ' . $this->getCurrentKey());
$this->log(__METHOD__.' Cache item found: '.$this->getCurrentKey());
$this->log(__METHOD__.' Response status: '.http_response_code());

// Echo content and stop execution
echo $item->getContent();
Expand All @@ -222,7 +237,7 @@ private function storePageContent($content)
// When enabled we store original header values with the item
$isHeadersForwardingEnabled = $this->config->isSendHeaders() && $this->config->isForwardHeaders();

$this->log('Header forwarding is '.($isHeadersForwardingEnabled ? 'enabled' : 'disabled'));
$this->log(__METHOD__.' Header forwarding is '.($isHeadersForwardingEnabled ? 'enabled' : 'disabled'));

$expiresAt = $isHeadersForwardingEnabled
? $this->httpHeaders->detectResponseExpires()
Expand Down Expand Up @@ -255,7 +270,8 @@ private function storePageContent($content)
$eTagString = md5($lastModified->getTimestamp());
}

$item->setContent($content)
$item
->setContent($content)
->setLastModified($lastModified)
->setETagString($eTagString);

Expand Down Expand Up @@ -316,7 +332,7 @@ public function clearPageCache(CacheItemInterface $item = null)
*/
public function getPageCache()
{
$key = $this->getCurrentKey();
$key = $this->getCurrentKey();
$item = $this->getItemStorage()->get($key);

return $item ? $item->getContent() : false;
Expand All @@ -332,7 +348,7 @@ public function getPageCache()
public function isCached(CacheItemInterface $item = null)
{
if (!$item) {
$key = $this->getCurrentKey();
$key = $this->getCurrentKey();
$item = $this->getItemStorage()->get($key);
}

Expand Down Expand Up @@ -367,7 +383,7 @@ private function getCurrentItem()
{
// Hack for weird initialization logic
if (!$this->currentItem) {
$key = $this->getCurrentKey();
$key = $this->getCurrentKey();
$this->currentItem = $this->getItemStorage()->get($key);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Storage/CacheItemStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ public function set(CacheItemInterface $item)
{
$key = $item->getKey();

$this->adapter->set($key, $item);
// Add ttl for buggy adapters (double time for correct cache stampede preventing algorithm)
$this->adapter->set($key, $item, $this->cacheExpiresIn * 2);
}

public function delete(CacheItemInterface $item)
Expand Down
4 changes: 2 additions & 2 deletions tests/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use PageCache\PageCache;
use PageCache\PageCacheException;

class ConfigTest extends \PHPUnit_Framework_TestCase
class ConfigTest extends \PHPUnit\Framework\TestCase
{
/**
* @var Config
Expand Down Expand Up @@ -169,4 +169,4 @@ public function testWrongConfigFile()
$this->expectException(PageCacheException::class);
new Config(__DIR__.'/config_wrong_test.php');
}
}
}
32 changes: 32 additions & 0 deletions tests/Integration/IntegrationPsrCacheTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* This file is part of the PageCache package.
*
* @author Denis Terekhov <i.am@spotman.ru>
* @author Muhammed Mamedov <mm@turkmenweb.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace PageCache\Tests\Integration;

use Cache\IntegrationTests\SimpleCacheTest;
use PageCache\Storage\FileSystem\FileSystemCacheAdapter;

/**
* Class IntegrationPsrCacheTest
*
* @package PageCache\Tests\Integration
*/
class IntegrationPsrCacheTest extends SimpleCacheTest
{
public function createSimpleCache()
{
return new FileSystemCacheAdapter(
__DIR__.DIRECTORY_SEPARATOR.'cache',
LOCK_EX | LOCK_NB,
500
);
}
}
Loading

0 comments on commit 61f9aa9

Please sign in to comment.