Skip to content

Commit c06913f

Browse files
[9.x] Recompiles views when necessary (#44737)
* Recompiles views when necessary * Fixes when filesystem cache file exists * Apply fixes from StyleCI * More tests * Apply fixes from StyleCI Co-authored-by: StyleCI Bot <[email protected]>
1 parent a56be42 commit c06913f

File tree

2 files changed

+235
-5
lines changed

2 files changed

+235
-5
lines changed

src/Illuminate/View/Engines/CompilerEngine.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,27 @@ public function get($path, array $data = [])
6262
$this->compiler->compile($path);
6363
}
6464

65-
$this->compiledOrNotExpired[$path] = true;
66-
6765
// Once we have the path to the compiled file, we will evaluate the paths with
6866
// typical PHP just like any other templates. We also keep a stack of views
6967
// which have been rendered for right exception messages to be generated.
70-
$results = $this->evaluatePath($this->compiler->getCompiledPath($path), $data);
68+
69+
try {
70+
$results = $this->evaluatePath($this->compiler->getCompiledPath($path), $data);
71+
} catch (ViewException $e) {
72+
if (! str($e->getMessage())->contains(['No such file or directory', 'File does not exist at path'])) {
73+
throw $e;
74+
}
75+
76+
if (! isset($this->compiledOrNotExpired[$path])) {
77+
throw $e;
78+
}
79+
80+
$this->compiler->compile($path);
81+
82+
$results = $this->evaluatePath($this->compiler->getCompiledPath($path), $data);
83+
}
84+
85+
$this->compiledOrNotExpired[$path] = true;
7186

7287
array_pop($this->lastCompiled);
7388

tests/View/ViewCompilerEngineTest.php

Lines changed: 217 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
namespace Illuminate\Tests\View;
44

5+
use ErrorException;
6+
use Exception;
7+
use Illuminate\Contracts\Filesystem\FileNotFoundException;
58
use Illuminate\Filesystem\Filesystem;
69
use Illuminate\View\Compilers\CompilerInterface;
710
use Illuminate\View\Engines\CompilerEngine;
11+
use Illuminate\View\ViewException;
812
use Mockery as m;
913
use PHPUnit\Framework\TestCase;
1014

@@ -55,8 +59,219 @@ public function testThatViewsAreNotAskTwiceIfTheyAreExpired()
5559
$engine->get(__DIR__.'/fixtures/foo.php');
5660
}
5761

58-
protected function getEngine()
62+
public function testViewsAreRecompiledWhenCompiledViewIsMissingViaFileNotFoundException()
5963
{
60-
return new CompilerEngine(m::mock(CompilerInterface::class), new Filesystem);
64+
$compiled = __DIR__.'/fixtures/basic.php';
65+
$path = __DIR__.'/fixtures/foo.php';
66+
67+
$files = m::mock(Filesystem::class);
68+
$engine = $this->getEngine($files);
69+
70+
$files->shouldReceive('getRequire')
71+
->once()
72+
->with($compiled, [])
73+
->andReturn('compiled-content');
74+
75+
$files->shouldReceive('getRequire')
76+
->once()
77+
->with($compiled, [])
78+
->andThrow(new FileNotFoundException(
79+
"File does not exist at path {$path}."
80+
));
81+
82+
$files->shouldReceive('getRequire')
83+
->once()
84+
->with($compiled, [])
85+
->andReturn('compiled-content');
86+
87+
$engine->getCompiler()
88+
->shouldReceive('getCompiledPath')
89+
->times(3)
90+
->with($path)
91+
->andReturn($compiled);
92+
93+
$engine->getCompiler()
94+
->shouldReceive('isExpired')
95+
->once()
96+
->andReturn(true);
97+
98+
$engine->getCompiler()
99+
->shouldReceive('compile')
100+
->twice()
101+
->with($path);
102+
103+
$engine->get($path);
104+
$engine->get($path);
105+
}
106+
107+
public function testViewsAreRecompiledWhenCompiledViewIsMissingViaRequireException()
108+
{
109+
$compiled = __DIR__.'/fixtures/basic.php';
110+
$path = __DIR__.'/fixtures/foo.php';
111+
112+
$files = m::mock(Filesystem::class);
113+
$engine = $this->getEngine($files);
114+
115+
$files->shouldReceive('getRequire')
116+
->once()
117+
->with($compiled, [])
118+
->andReturn('compiled-content');
119+
120+
$files->shouldReceive('getRequire')
121+
->once()
122+
->with($compiled, [])
123+
->andThrow(new ErrorException(
124+
"require({$path}): Failed to open stream: No such file or directory",
125+
));
126+
127+
$files->shouldReceive('getRequire')
128+
->once()
129+
->with($compiled, [])
130+
->andReturn('compiled-content');
131+
132+
$engine->getCompiler()
133+
->shouldReceive('getCompiledPath')
134+
->times(3)
135+
->with($path)
136+
->andReturn($compiled);
137+
138+
$engine->getCompiler()
139+
->shouldReceive('isExpired')
140+
->once()
141+
->andReturn(true);
142+
143+
$engine->getCompiler()
144+
->shouldReceive('compile')
145+
->twice()
146+
->with($path);
147+
148+
$engine->get($path);
149+
$engine->get($path);
150+
}
151+
152+
public function testViewsAreRecompiledJustOnceWhenCompiledViewIsMissing()
153+
{
154+
$compiled = __DIR__.'/fixtures/basic.php';
155+
$path = __DIR__.'/fixtures/foo.php';
156+
157+
$files = m::mock(Filesystem::class);
158+
$engine = $this->getEngine($files);
159+
160+
$files->shouldReceive('getRequire')
161+
->once()
162+
->with($compiled, [])
163+
->andReturn('compiled-content');
164+
165+
$files->shouldReceive('getRequire')
166+
->once()
167+
->with($compiled, [])
168+
->andThrow(new FileNotFoundException(
169+
"File does not exist at path {$path}."
170+
));
171+
172+
$files->shouldReceive('getRequire')
173+
->once()
174+
->with($compiled, [])
175+
->andThrow(new FileNotFoundException(
176+
"File does not exist at path {$path}."
177+
));
178+
179+
$engine->getCompiler()
180+
->shouldReceive('getCompiledPath')
181+
->times(3)
182+
->with($path)
183+
->andReturn($compiled);
184+
185+
$engine->getCompiler()
186+
->shouldReceive('isExpired')
187+
->once()
188+
->andReturn(true);
189+
190+
$engine->getCompiler()
191+
->shouldReceive('compile')
192+
->twice()
193+
->with($path);
194+
195+
$engine->get($path);
196+
197+
$this->expectException(ViewException::class);
198+
$this->expectExceptionMessage("File does not exist at path {$path}.");
199+
$engine->get($path);
200+
}
201+
202+
public function testViewsAreNotRecompiledOnRegularViewException()
203+
{
204+
$compiled = __DIR__.'/fixtures/basic.php';
205+
$path = __DIR__.'/fixtures/foo.php';
206+
207+
$files = m::mock(Filesystem::class);
208+
$engine = $this->getEngine($files);
209+
210+
$files->shouldReceive('getRequire')
211+
->once()
212+
->with($compiled, [])
213+
->andThrow(new Exception(
214+
'Just an regular error...'
215+
));
216+
217+
$engine->getCompiler()
218+
->shouldReceive('isExpired')
219+
->once()
220+
->andReturn(false);
221+
222+
$engine->getCompiler()
223+
->shouldReceive('compile')
224+
->never();
225+
226+
$engine->getCompiler()
227+
->shouldReceive('getCompiledPath')
228+
->once()
229+
->with($path)
230+
->andReturn($compiled);
231+
232+
$this->expectException(ViewException::class);
233+
$this->expectExceptionMessage('Just an regular error...');
234+
$engine->get($path);
235+
}
236+
237+
public function testViewsAreNotRecompiledIfTheyWereJustCompiled()
238+
{
239+
$compiled = __DIR__.'/fixtures/basic.php';
240+
$path = __DIR__.'/fixtures/foo.php';
241+
242+
$files = m::mock(Filesystem::class);
243+
$engine = $this->getEngine($files);
244+
245+
$files->shouldReceive('getRequire')
246+
->once()
247+
->with($compiled, [])
248+
->andThrow(new FileNotFoundException(
249+
"File does not exist at path {$path}."
250+
));
251+
252+
$engine->getCompiler()
253+
->shouldReceive('isExpired')
254+
->once()
255+
->andReturn(true);
256+
257+
$engine->getCompiler()
258+
->shouldReceive('compile')
259+
->once()
260+
->with($path);
261+
262+
$engine->getCompiler()
263+
->shouldReceive('getCompiledPath')
264+
->once()
265+
->with($path)
266+
->andReturn($compiled);
267+
268+
$this->expectException(ViewException::class);
269+
$this->expectExceptionMessage("File does not exist at path {$path}.");
270+
$engine->get($path);
271+
}
272+
273+
protected function getEngine($filesystem = null)
274+
{
275+
return new CompilerEngine(m::mock(CompilerInterface::class), $filesystem ?: new Filesystem);
61276
}
62277
}

0 commit comments

Comments
 (0)