Skip to content

Commit 5e1f4a0

Browse files
committed
feat: add request lifecylce caching optimization for configuration loading
1 parent 73edf20 commit 5e1f4a0

2 files changed

Lines changed: 164 additions & 3 deletions

File tree

src/View/Helper/ViteHelper.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
use Cake\Core\Configure;
77
use Cake\View\Helper;
8+
use CakeVite\Enum\Environment;
89
use CakeVite\Service\AssetService;
910
use CakeVite\Service\EnvironmentService;
1011
use CakeVite\Service\ManifestService;
@@ -32,19 +33,42 @@ class ViteHelper extends Helper
3233
*/
3334
private ?AssetService $assetService = null;
3435

36+
/**
37+
* Cached configuration (optimization #1: reduce Configure::read calls)
38+
*/
39+
private ?ViteConfig $cachedConfig = null;
40+
41+
/**
42+
* Cached environment detection (optimization #2: reduce environment detection overhead)
43+
*/
44+
private ?Environment $cachedEnvironment = null;
45+
3546
/**
3647
* Check if currently in development mode
3748
*
3849
* Backwards compatible with ViteScriptsHelper::isDev()
3950
*
51+
* Uses cached environment detection to avoid repeated cookie/query/host checks.
52+
*
4053
* @param \CakeVite\ValueObject\ViteConfig|array<string, mixed>|null $config Configuration
4154
*/
4255
public function isDev(array|ViteConfig|null $config = null): bool
4356
{
57+
// Return cached result if available (optimization #2)
58+
if ($this->cachedEnvironment instanceof Environment && $config === null) {
59+
return $this->cachedEnvironment->isDevelopment();
60+
}
61+
4462
$config = $this->resolveConfig($config);
4563
$envService = new EnvironmentService($this->getView()->getRequest());
64+
$environment = $envService->detect($config);
4665

47-
return $envService->detect($config)->isDevelopment();
66+
// Cache environment for subsequent calls (only when using default config)
67+
if ($config === $this->cachedConfig) {
68+
$this->cachedEnvironment = $environment;
69+
}
70+
71+
return $environment->isDevelopment();
4872
}
4973

5074
/**
@@ -151,6 +175,8 @@ public function pluginCss(
151175
/**
152176
* Resolve configuration from various input types
153177
*
178+
* Uses cached configuration to reduce Configure::read() overhead (optimization #1).
179+
*
154180
* @param \CakeVite\ValueObject\ViteConfig|array<string, mixed>|null $config Configuration
155181
*/
156182
private function resolveConfig(array|ViteConfig|null $config): ViteConfig
@@ -163,8 +189,8 @@ private function resolveConfig(array|ViteConfig|null $config): ViteConfig
163189
return ViteConfig::fromArray($config);
164190
}
165191

166-
// Load from Configure
167-
return ViteConfig::fromArray(Configure::read('CakeVite', []));
192+
// Load from Configure and cache for request lifetime (optimization #1)
193+
return $this->cachedConfig ??= ViteConfig::fromArray(Configure::read('CakeVite', []));
168194
}
169195

170196
/**

tests/TestCase/View/Helper/ViteHelperTest.php

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
use Cake\Http\ServerRequest;
77
use Cake\TestSuite\TestCase;
88
use Cake\View\View;
9+
use CakeVite\Enum\Environment;
910
use CakeVite\Service\ManifestService;
11+
use CakeVite\ValueObject\ViteConfig;
1012
use CakeVite\View\Helper\ViteHelper;
13+
use ReflectionClass;
1114

1215
/**
1316
* ViteHelper Test
@@ -302,4 +305,136 @@ public function testCssAcceptsStringShorthand(): void
302305
$result = $this->View->fetch('css');
303306
$this->assertStringContainsString('http://localhost:3000/src/style.css', $result);
304307
}
308+
309+
/**
310+
* Test that configuration is cached after first call
311+
*
312+
* Uses reflection to verify the cachedConfig property is populated.
313+
* This verifies optimization #1: configuration caching to reduce Configure::read calls.
314+
*/
315+
public function testConfigurationIsCachedInProperty(): void
316+
{
317+
$reflection = new ReflectionClass($this->Vite);
318+
$property = $reflection->getProperty('cachedConfig');
319+
320+
// Initially null
321+
$this->assertNull($property->getValue($this->Vite));
322+
323+
// Call isDev() without config - triggers Configure::read and caching
324+
$this->Vite->isDev();
325+
326+
// cachedConfig should now be populated
327+
$cachedConfig = $property->getValue($this->Vite);
328+
$this->assertInstanceOf(ViteConfig::class, $cachedConfig);
329+
330+
// Second call should return same cached instance
331+
$this->Vite->isDev();
332+
/** @phpstan-ignore argument.unresolvableType */
333+
$this->assertSame($cachedConfig, $property->getValue($this->Vite));
334+
}
335+
336+
/**
337+
* Test that custom config passed as argument is NOT cached
338+
*
339+
* Only default config from Configure should be cached.
340+
*/
341+
public function testCustomConfigIsNotCached(): void
342+
{
343+
$reflection = new ReflectionClass($this->Vite);
344+
$property = $reflection->getProperty('cachedConfig');
345+
346+
$customConfig = [
347+
'devServer' => ['hostHints' => ['localhost']],
348+
];
349+
350+
// Call with custom config - should NOT populate cache
351+
$this->Vite->isDev($customConfig);
352+
353+
// Cache should still be null (custom configs aren't cached)
354+
$this->assertNull($property->getValue($this->Vite));
355+
}
356+
357+
/**
358+
* Test that environment detection is cached after first call
359+
*
360+
* Uses reflection to verify the cachedEnvironment property is populated.
361+
* This verifies optimization #2: environment detection caching.
362+
*/
363+
public function testEnvironmentDetectionIsCachedInProperty(): void
364+
{
365+
$reflection = new ReflectionClass($this->Vite);
366+
$property = $reflection->getProperty('cachedEnvironment');
367+
368+
// Initially null
369+
$this->assertNull($property->getValue($this->Vite));
370+
371+
// Call isDev() - triggers environment detection and caching
372+
$result = $this->Vite->isDev();
373+
374+
// cachedEnvironment should now be populated
375+
$cachedEnv = $property->getValue($this->Vite);
376+
$this->assertInstanceOf(Environment::class, $cachedEnv);
377+
/** @phpstan-ignore instanceof.alwaysFalse */
378+
if ($cachedEnv instanceof Environment) {
379+
$this->assertSame($result, $cachedEnv->isDevelopment());
380+
}
381+
382+
// Second call should return same cached instance
383+
$this->Vite->isDev();
384+
/** @phpstan-ignore argument.unresolvableType */
385+
$this->assertSame($cachedEnv, $property->getValue($this->Vite));
386+
}
387+
388+
/**
389+
* Test that environment cache is only used with default config
390+
*
391+
* Custom configs should trigger fresh environment detection.
392+
*/
393+
public function testEnvironmentCacheOnlyUsedWithDefaultConfig(): void
394+
{
395+
$reflection = new ReflectionClass($this->Vite);
396+
$property = $reflection->getProperty('cachedEnvironment');
397+
398+
// First call with default config - caches environment
399+
$this->Vite->isDev();
400+
$cachedEnv = $property->getValue($this->Vite);
401+
$this->assertInstanceOf(Environment::class, $cachedEnv);
402+
403+
// Call with custom config - should still use cache for subsequent default calls
404+
$customConfig = [
405+
'devServer' => ['hostHints' => ['localhost']],
406+
];
407+
$this->Vite->isDev($customConfig);
408+
409+
// Cache should be preserved
410+
$this->assertSame($cachedEnv, $property->getValue($this->Vite));
411+
}
412+
413+
/**
414+
* Test that script() and css() methods work with cached configuration
415+
*/
416+
public function testScriptAndCssUseCachedConfiguration(): void
417+
{
418+
$config = [
419+
'devServer' => [
420+
'url' => 'http://localhost:3000',
421+
'hostHints' => ['localhost'],
422+
'entries' => [
423+
'script' => ['src/app.ts'],
424+
'style' => ['src/style.css'],
425+
],
426+
],
427+
];
428+
429+
// Call script() which internally calls isDev() - both should benefit from caching
430+
$this->Vite->script([], $config);
431+
$this->Vite->css([], $config);
432+
433+
// Verify both rendered correctly
434+
$scriptResult = $this->View->fetch('script');
435+
$cssResult = $this->View->fetch('css');
436+
437+
$this->assertStringContainsString('http://localhost:3000', $scriptResult);
438+
$this->assertStringContainsString('http://localhost:3000', $cssResult);
439+
}
305440
}

0 commit comments

Comments
 (0)