Skip to content

Commit

Permalink
Changed configuration cacheKeyFn to work via call_user_func
Browse files Browse the repository at this point in the history
Added tests
  • Loading branch information
serbanghita committed Dec 8, 2024
1 parent c90ae36 commit 807f5ee
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/Cache/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Psr\SimpleCache\CacheInterface;

/**
* Generic implementation of a cache system using an associative array.
*/
class Cache implements CacheInterface
{
/**
Expand Down
15 changes: 11 additions & 4 deletions src/MobileDetect.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ class MobileDetect
// Maximum HTTP User-Agent value allowed.
// @var int
'maximumUserAgentLength' => 500,
// Function that creates the cache key. e.g. (base64, sha1, custom fn).
'cacheKeyFn' => 'base64_encode',
// Cache TTL
// @var null|int|\DateInterval
'cacheTtl' => 86400,
Expand Down Expand Up @@ -1700,18 +1702,23 @@ public function getCache(): Cache
return $this->cache;
}

/**
* @throws CacheException
*/
protected function createCacheKey(string $key): string
{
$userAgentKey = $this->hasUserAgent() ? $this->userAgent : '';
$httpHeadersKey = $this->hasHttpHeaders() ? static::flattenHeaders($this->httpHeaders) : '';

$cacheKey = "$key:$userAgentKey:$httpHeadersKey";

$cacheKeyUsing = $this->config['cacheKeyUsing'] ?? function (string $cacheKey) {
return base64_encode($cacheKey);
};
$cacheKeyFn = $this->config['cacheKeyFn'];

if (!is_callable($cacheKeyFn)) {
throw new CacheException('cacheKeyFn is not a function.');
}

return call_user_func($cacheKeyUsing, $cacheKey);
return call_user_func($cacheKeyFn, $cacheKey);
}

public static function flattenHeaders(array $httpHeaders): string
Expand Down
52 changes: 52 additions & 0 deletions tests/MobileDetectWithCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace DetectionTests;

use Detection\Cache\Cache;
use Detection\Cache\CacheItem;
use Detection\Exception\MobileDetectException;
use Detection\MobileDetect;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -112,4 +114,54 @@ public function testDefaultCacheClassCreatesMultipleCacheRecordsForAllCalls()
base64_decode($detect->getCache()->getKeys()[3])
);
}

/**
* @throws MobileDetectException
*/
public function testCustomCacheWithInvalidFnThrowsException()
{
$this->expectException(MobileDetectException::class);
$this->expectExceptionMessage('Cache problem in isMobile(): cacheKeyFn is not a function.');
$cache = new Cache();

$detect = new MobileDetect($cache, ['cacheKeyFn' => 'not a function']);
$detect->setUserAgent('iPad; AppleWebKit/533.17.9 Version/5.0.2 Mobile/8C148 Safari/6533.18.5');
$detect->isMobile();
}

public function testCustomCacheForConsecutiveCalls()
{
$cache = new Cache();

$detect = new MobileDetect($cache, ['cacheKeyFn' => fn ($key) => base64_encode($key)]);
$detect->setUserAgent('iPad; AppleWebKit/533.17.9 Version/5.0.2 Mobile/8C148 Safari/6533.18.5');

$detect->isMobile();
$this->assertCount(1, $cache->getKeys());

$detect->isMobile();
$this->assertCount(1, $cache->getKeys());
}

public function testGetCacheKeyIsUsedInConsecutiveCallsIfFoundIn()
{
$cache = $this->getMockBuilder(Cache::class)
->onlyMethods(["get", "set"])
->getMock();
$cache->method('get')->withAnyParameters()->willReturn(new CacheItem('name', 'value'));
$cache->method('set')->withAnyParameters()->willReturn(true);


$cache->expects($spy = $this->exactly(2))->method('get');
$cache->expects($spy = $this->never())->method('set');



$detect = new MobileDetect($cache);
$detect->setUserAgent('iPad; AppleWebKit/533.17.9 Version/5.0.2 Mobile/8C148 Safari/6533.18.5');

$detect->isMobile();
$detect->isMobile();

}
}
21 changes: 21 additions & 0 deletions tests/benchmark/MobileDetectBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,25 @@ public function benchIsSamsungTablet(): void
$detect->setUserAgent('Mozilla/5.0 (Linux; Android 12; SM-X906C Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36');
$detect->isSamsungTablet();
}

public function benchIsMobileCacheKeyFnBase64AgainstBestMatch(): void
{
$detect = new MobileDetect(null, ['cacheKeyFn' => 'base64_encode']);
$detect->setUserAgent('iPhone');
$detect->isMobile();
}

public function benchIsMobileCacheKeyFnSha1AgainstBestMatch(): void
{
$detect = new MobileDetect(null, ['cacheKeyFn' => 'sha1']);
$detect->setUserAgent('iPhone');
$detect->isMobile();
}

public function benchIsMobileCacheKeyFnCustomCryptFnAgainstBestMatch(): void
{
$detect = new MobileDetect(null, ['cacheKeyFn' => fn ($key) => crypt($key, 'bla')]);
$detect->setUserAgent('iPhone');
$detect->isMobile();
}
}

0 comments on commit 807f5ee

Please sign in to comment.