diff --git a/README.md b/README.md index 05cd234..2bf515c 100644 --- a/README.md +++ b/README.md @@ -199,5 +199,7 @@ Hello ## Exceptions -* `\RuntimeException` - If template does not exist +* `\Slim\Views\Exception\PhpTemplateNotFoundException` - If template layout does not exist +* `\Slim\Views\Exception\PhpTemplateNotFoundException` - If template does not exist +* `\RuntimeException` - If the template output could not be fetched * `\InvalidArgumentException` - If $data contains 'template' diff --git a/phpstan.neon b/phpstan.neon index b35f3d4..7f33c04 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 5 + level: 8 paths: - src - tests diff --git a/src/PhpRenderer.php b/src/PhpRenderer.php index ffec787..a73ac25 100644 --- a/src/PhpRenderer.php +++ b/src/PhpRenderer.php @@ -12,6 +12,7 @@ use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; +use RuntimeException; use Slim\Views\Exception\PhpTemplateNotFoundException; use Throwable; @@ -19,13 +20,16 @@ class PhpRenderer { protected string $templatePath; + /** + * @var array + */ protected array $attributes; protected string $layout; /** * @param string $templatePath - * @param array $attributes + * @param array $attributes * @param string $layout */ public function __construct(string $templatePath = '', array $attributes = [], string $layout = '') @@ -37,12 +41,12 @@ public function __construct(string $templatePath = '', array $attributes = [], s /** * @param ResponseInterface $response - * @param string $template - * @param array $data - * - * @return ResponseInterface + * @param string $template + * @param array $data * * @throws Throwable + * + * @return ResponseInterface */ public function render(ResponseInterface $response, string $template, array $data = []): ResponseInterface { @@ -59,16 +63,12 @@ public function getLayout(): string return $this->layout; } - /** - * @param string $layout - */ - /** * @param string $layout * - * @return void - * * @throws PhpTemplateNotFoundException + * + * @return void */ public function setLayout(string $layout): void { @@ -80,7 +80,7 @@ public function setLayout(string $layout): void } /** - * @return array + * @return array */ public function getAttributes(): array { @@ -88,7 +88,7 @@ public function getAttributes(): array } /** - * @param array $attributes + * @param array $attributes * * @return void */ @@ -99,7 +99,7 @@ public function setAttributes(array $attributes): void /** * @param string $key - * @param $value + * @param mixed $value * * @return void */ @@ -140,12 +140,12 @@ public function setTemplatePath(string $templatePath): void /** * @param string $template - * @param array $data - * @param bool $useLayout - * - * @return string + * @param array $data + * @param bool $useLayout * * @throws Throwable + * + * @return string */ public function fetch(string $template, array $data = [], bool $useLayout = false): string { @@ -160,11 +160,11 @@ public function fetch(string $template, array $data = [], bool $useLayout = fals /** * @param string $template - * @param array $data - * - * @return string + * @param array $data * * @throws Throwable + * + * @return string */ public function fetchTemplate(string $template, array $data = []): string { @@ -173,8 +173,9 @@ public function fetchTemplate(string $template, array $data = []): string } if (!$this->templateExists($template)) { - throw new PhpTemplateNotFoundException('View cannot render "' . $template - . '" because the template does not exist'); + throw new PhpTemplateNotFoundException( + 'View cannot render "' . $template . '" because the template does not exist' + ); } $data = array_merge($this->attributes, $data); @@ -182,6 +183,9 @@ public function fetchTemplate(string $template, array $data = []): string ob_start(); $this->protectedIncludeScope($this->templatePath . $template, $data); $output = ob_get_clean(); + if ($output === false) { + throw new RuntimeException('Failed to fetch the template output'); + } } catch (Throwable $e) { ob_end_clean(); throw $e; @@ -205,7 +209,7 @@ public function templateExists(string $template): bool /** * @param string $template - * @param array $data + * @param array $data * * @return void */ diff --git a/tests/PhpRendererTest.php b/tests/PhpRendererTest.php index b3e68bd..0e2284e 100644 --- a/tests/PhpRendererTest.php +++ b/tests/PhpRendererTest.php @@ -18,6 +18,7 @@ use Slim\Views\Exception\PhpTemplateNotFoundException; use Slim\Views\PhpRenderer; use Throwable; +use UnexpectedValueException; class PhpRendererTest extends TestCase { @@ -25,7 +26,7 @@ public function testRenderer(): void { $renderer = new PhpRenderer(__DIR__ . '/_files/'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $newResponse = $renderer->render($response, 'template.phtml', ['hello' => 'Hi']); $newResponse->getBody()->rewind(); @@ -36,7 +37,7 @@ public function testRenderConstructor(): void { $renderer = new PhpRenderer(__DIR__ . '/_files'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $newResponse = $renderer->render($response, 'template.phtml', ['hello' => 'Hi']); $newResponse->getBody()->rewind(); @@ -45,12 +46,11 @@ public function testRenderConstructor(): void public function testAttributeMerging(): void { - $renderer = new PhpRenderer(__DIR__ . '/_files/', [ 'hello' => 'Hello' ]); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $newResponse = $renderer->render($response, 'template.phtml', [ 'hello' => 'Hi' @@ -63,12 +63,12 @@ public function testExceptionInTemplate(): void { $renderer = new PhpRenderer(__DIR__ . '/_files/'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); try { $newResponse = $renderer->render($response, 'exception_layout.phtml'); } catch (Throwable $t) { - // Simulates an error template + // Simulates an error template $newResponse = $renderer->render($response, 'template.phtml', [ 'hello' => 'Hi' ]); @@ -82,7 +82,7 @@ public function testExceptionForTemplateInData(): void { $renderer = new PhpRenderer(__DIR__ . '/_files/'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $this->expectException(InvalidArgumentException::class); $renderer->render($response, 'template.phtml', [ @@ -94,7 +94,7 @@ public function testTemplateNotFound(): void { $renderer = new PhpRenderer(__DIR__ . '/_files/'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $this->expectException(PhpTemplateNotFoundException::class); $renderer->render($response, 'adfadftemplate.phtml', []); @@ -105,24 +105,30 @@ public function testLayout(): void $renderer = new PhpRenderer(__DIR__ . '/_files/', ['title' => 'My App']); $renderer->setLayout('layout.phtml'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $newResponse = $renderer->render($response, 'template.phtml', ['title' => 'Hello - My App', 'hello' => 'Hi']); $newResponse->getBody()->rewind(); - $this->assertEquals('Hello - My AppHi
This is the footer' - . '
', $newResponse->getBody()->getContents()); + $this->assertEquals( + 'Hello - My AppHi
This is the footer' + . '
', + $newResponse->getBody()->getContents() + ); } public function testLayoutConstructor(): void { $renderer = new PhpRenderer(__DIR__ . '/_files', ['title' => 'My App'], 'layout.phtml'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $newResponse = $renderer->render($response, 'template.phtml', ['title' => 'Hello - My App', 'hello' => 'Hi']); $newResponse->getBody()->rewind(); - $this->assertEquals('Hello - My AppHi
This is the footer' - . '
', $newResponse->getBody()->getContents()); + $this->assertEquals( + 'Hello - My AppHi
This is the footer' + . '
', + $newResponse->getBody()->getContents() + ); } public function testExceptionInLayout(): void @@ -130,12 +136,12 @@ public function testExceptionInLayout(): void $renderer = new PhpRenderer(__DIR__ . '/_files/'); $renderer->setLayout('exception_layout.phtml'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); try { $newResponse = $renderer->render($response, 'template.phtml'); } catch (Throwable $t) { - // PHP 7+ + // PHP 7+ // Simulates an error template $renderer->setLayout(''); $newResponse = $renderer->render($response, 'template.phtml', [ @@ -159,7 +165,7 @@ public function testContentDataKeyShouldBeIgnored(): void $renderer = new PhpRenderer(__DIR__ . '/_files/'); $renderer->setLayout('layout.phtml'); $headers = new Headers(); - $body = new Stream(fopen('php://temp', 'r+')); + $body = $this->createStream(); $response = new Response(200, $headers, $body); $newResponse = $renderer->render( $response, @@ -167,14 +173,27 @@ public function testContentDataKeyShouldBeIgnored(): void ['title' => 'Hello - My App', 'hello' => 'Hi', 'content' => 'Ho'] ); $newResponse->getBody()->rewind(); - $this->assertEquals('Hello - My AppHi
This is the footer' - . '
', $newResponse->getBody()->getContents()); + $this->assertEquals( + 'Hello - My AppHi', + $newResponse->getBody()->getContents() + ); } - public function testTemplateExists() + public function testTemplateExists(): void { $renderer = new PhpRenderer(__DIR__ . '/_files/'); $this->assertTrue($renderer->templateExists('layout.phtml')); $this->assertFalse($renderer->templateExists('non-existant-template')); } + + private function createStream(): Stream + { + $resource = fopen('php://temp', 'r+'); + if ($resource === false) { + throw new UnexpectedValueException(); + } + + return new Stream($resource); + } }