Skip to content

Commit 48d3515

Browse files
authored
Performance Improvements (#23)
* Cache resource manager base uris, and build resource uri manually * Improve external url check * Cache optimisation * Cache file hashes * Cache file hashes * Static file cache * Hash cache * Clear hash cache when changing salt
1 parent f538855 commit 48d3515

File tree

3 files changed

+60
-24
lines changed

3 files changed

+60
-24
lines changed

src/Dispatch.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ public static function destroy()
102102
public function setHashSalt(string $hashSalt)
103103
{
104104
$this->_hashSalt = $hashSalt;
105+
static::$_hashCache = [];
105106
return $this;
106107
}
107108

109+
protected static $_hashCache = [];
110+
108111
/**
109112
* Generate a hash against specific content, for a desired length
110113
*
@@ -115,12 +118,16 @@ public function setHashSalt(string $hashSalt)
115118
*/
116119
public function generateHash($content, int $length = null)
117120
{
118-
$hash = md5($content . $this->_hashSalt);
121+
if(!isset(static::$_hashCache[$content]))
122+
{
123+
static::$_hashCache[$content] = md5($content . $this->_hashSalt);
124+
}
125+
119126
if($length !== null)
120127
{
121-
return substr($hash, 0, $length);
128+
return substr(static::$_hashCache[$content], 0, $length);
122129
}
123-
return $hash;
130+
return static::$_hashCache[$content];
124131
}
125132

126133
public function getResourcesPath()
@@ -303,7 +310,7 @@ protected function _getClassLoader()
303310
{
304311
if($this->_classLoader === null)
305312
{
306-
foreach(spl_autoload_functions() as list($loader))
313+
foreach(spl_autoload_functions() as [$loader])
307314
{
308315
if($loader instanceof ClassLoader)
309316
{

src/ResourceManager.php

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use RuntimeException;
1212
use function apcu_fetch;
1313
use function apcu_store;
14-
use function array_merge;
1514
use function array_unshift;
1615
use function base_convert;
1716
use function count;
@@ -43,7 +42,7 @@ class ResourceManager
4342

4443
protected $_type = self::MAP_RESOURCES;
4544
protected $_mapOptions = [];
46-
protected $_baseUri = [];
45+
protected $_baseUri;
4746
protected $_componentPath;
4847
protected $_options = [];
4948

@@ -69,7 +68,17 @@ public function __construct($type, array $mapOptions = [], array $options = [])
6968
$this->setOption($option, $optionValue);
7069
}
7170
$this->_options = $options;
72-
$this->_baseUri = array_merge([$type], $mapOptions);
71+
}
72+
73+
public function getBaseUri()
74+
{
75+
if($this->_baseUri === null)
76+
{
77+
$this->_baseUri = (Dispatch::instance() ? Dispatch::instance()->getBaseUri() : '/')
78+
. '/' . $this->_type . '/' . implode('/', $this->_mapOptions);
79+
$this->_baseUri = trim($this->_baseUri, '/');
80+
}
81+
return $this->_baseUri;
7382
}
7483

7584
/**
@@ -297,20 +306,22 @@ public function getResourceUri($relativeFullPath, bool $allowComponentBubble = t
297306
{
298307
return null;
299308
}
300-
return Path::custom(
301-
'/',
302-
array_merge(
303-
[Dispatch::instance()->getBaseUri()],
304-
$this->_baseUri,
305-
[$hash . $relHash . ($bits > 0 ? '-' . base_convert($bits, 10, 36) : ''), $relativeFullPath]
306-
)
307-
);
309+
310+
return $this->getBaseUri()
311+
. '/' . $hash . $relHash . ($bits > 0 ? '-' . base_convert($bits, 10, 36) : '')
312+
. '/' . $relativeFullPath;
308313
}
309314

315+
protected $_optimizeWebP;
316+
310317
protected function _optimisePath($path, $relativeFullPath)
311318
{
312-
$optimise = ValueAs::bool(Dispatch::instance()->config()->getItem('optimisation', 'webp', false));
313-
if($optimise
319+
if($this->_optimizeWebP === null)
320+
{
321+
$this->_optimizeWebP = ValueAs::bool(Dispatch::instance()->config()->getItem('optimisation', 'webp', false));
322+
}
323+
324+
if($this->_optimizeWebP
314325
&& in_array(substr($path, -4), ['.jpg', 'jpeg', '.png', '.gif', '.bmp', 'tiff', '.svg'])
315326
&& file_exists($path . '.webp'))
316327
{
@@ -328,11 +339,11 @@ protected function _optimisePath($path, $relativeFullPath)
328339
*/
329340
public function isExternalUrl($path)
330341
{
331-
return strlen($path) > 8 && (
332-
Strings::startsWith($path, 'http://', true, 7) ||
333-
Strings::startsWith($path, 'https://', true, 8) ||
334-
Strings::startsWith($path, '//', true, 2)
335-
);
342+
return isset($path[8])
343+
&& (
344+
($path[0] == '/' && $path[1] == '/')
345+
|| strncasecmp($path, 'http://', 7) == 0
346+
|| strncasecmp($path, 'https://', 8) == 0);
336347
}
337348

338349
/**
@@ -377,16 +388,27 @@ public function getRelativeHash($filePath)
377388
return Dispatch::instance()->generateHash(Dispatch::instance()->calculateRelativePath($filePath), 4);
378389
}
379390

391+
protected static $_fileHashCache = [];
392+
380393
public function getFileHash($fullPath)
381394
{
382-
if(!file_exists($fullPath))
395+
$cached = static::$_fileHashCache[$fullPath] ?? null;
396+
397+
if($cached === -1 || ($cached === null && !file_exists($fullPath)))
383398
{
399+
self::$_fileHashCache[$fullPath] = -1;
384400
if($this->getOption(self::OPT_THROW_ON_FILE_NOT_FOUND, true))
385401
{
386402
throw new RuntimeException("Unable to find dispatch file '$fullPath'", 404);
387403
}
388404
return null;
389405
}
406+
407+
if(!empty($cached))
408+
{
409+
return $cached;
410+
}
411+
390412
$key = 'pdspfh-' . md5($fullPath) . '-' . filectime($fullPath);
391413

392414
if(function_exists("apcu_fetch"))
@@ -396,12 +418,13 @@ public function getFileHash($fullPath)
396418
if($exists && $hash)
397419
{
398420
// @codeCoverageIgnoreStart
421+
self::$_fileHashCache[$fullPath] = $hash;
399422
return $hash;
400423
// @codeCoverageIgnoreEnd
401424
}
402425
}
403426

404-
$hash = Dispatch::instance()->generateHash(md5_file($fullPath), 8);
427+
self::$_fileHashCache[$fullPath] = $hash = Dispatch::instance()->generateHash(md5_file($fullPath), 8);
405428
if($hash && function_exists('apcu_store'))
406429
{
407430
apcu_store($key, $hash, 86400);

tests/ResourceManagerTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,13 @@ public function testIsExternalUrl()
118118
{
119119
$manager = ResourceManager::external();
120120
$this->assertTrue($manager->isExternalUrl('http://www.google.com'));
121+
$this->assertTrue($manager->isExternalUrl('hTTp://www.google.com'));
121122
$this->assertFalse($manager->isExternalUrl('abhttp://www.google.com'));
123+
$this->assertTrue($manager->isExternalUrl('https://www.google.com'));
124+
$this->assertTrue($manager->isExternalUrl('httPS://www.google.com'));
125+
$this->assertFalse($manager->isExternalUrl('abhttps://www.google.com'));
126+
$this->assertTrue($manager->isExternalUrl('//www.google.com'));
127+
$this->assertFalse($manager->isExternalUrl('://www.google.com'));
122128

123129
//Check external still work on other resource types
124130
$manager = ResourceManager::public();

0 commit comments

Comments
 (0)