Skip to content

Commit

Permalink
Ensure fetchTemplate method always returns string (#107)
Browse files Browse the repository at this point in the history
* Fix doc blocks and code style
* Ensure fetchTemplate method always returns string
* Update section Exceptions
* Optimize tests
* Change phpstan level from 5 to 9
  • Loading branch information
odan committed Jul 13, 2024
1 parent 45a4b88 commit ee71d44
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 46 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,7 @@ Hello <?= html($name) ?>

## 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'
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 5
level: 8
paths:
- src
- tests
52 changes: 28 additions & 24 deletions src/PhpRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,24 @@

use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use RuntimeException;
use Slim\Views\Exception\PhpTemplateNotFoundException;
use Throwable;

class PhpRenderer
{
protected string $templatePath;

/**
* @var array<string, mixed>
*/
protected array $attributes;

protected string $layout;

/**
* @param string $templatePath
* @param array $attributes
* @param array<string, mixed> $attributes
* @param string $layout
*/
public function __construct(string $templatePath = '', array $attributes = [], string $layout = '')
Expand All @@ -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<string, mixed> $data
*
* @throws Throwable
*
* @return ResponseInterface
*/
public function render(ResponseInterface $response, string $template, array $data = []): ResponseInterface
{
Expand All @@ -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
{
Expand All @@ -80,15 +80,15 @@ public function setLayout(string $layout): void
}

/**
* @return array
* @return array<string, mixed>
*/
public function getAttributes(): array
{
return $this->attributes;
}

/**
* @param array $attributes
* @param array<string, mixed> $attributes
*
* @return void
*/
Expand All @@ -99,7 +99,7 @@ public function setAttributes(array $attributes): void

/**
* @param string $key
* @param $value
* @param mixed $value
*
* @return void
*/
Expand Down Expand Up @@ -140,12 +140,12 @@ public function setTemplatePath(string $templatePath): void

/**
* @param string $template
* @param array $data
* @param bool $useLayout
*
* @return string
* @param array<string, mixed> $data
* @param bool $useLayout
*
* @throws Throwable
*
* @return string
*/
public function fetch(string $template, array $data = [], bool $useLayout = false): string
{
Expand All @@ -160,11 +160,11 @@ public function fetch(string $template, array $data = [], bool $useLayout = fals

/**
* @param string $template
* @param array $data
*
* @return string
* @param array<string, mixed> $data
*
* @throws Throwable
*
* @return string
*/
public function fetchTemplate(string $template, array $data = []): string
{
Expand All @@ -173,15 +173,19 @@ 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);
try {
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;
Expand All @@ -205,7 +209,7 @@ public function templateExists(string $template): bool

/**
* @param string $template
* @param array $data
* @param array<string, mixed> $data
*
* @return void
*/
Expand Down
59 changes: 39 additions & 20 deletions tests/PhpRendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
use Slim\Views\Exception\PhpTemplateNotFoundException;
use Slim\Views\PhpRenderer;
use Throwable;
use UnexpectedValueException;

class PhpRendererTest extends TestCase
{
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();
Expand All @@ -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();
Expand All @@ -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'
Expand All @@ -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'
]);
Expand All @@ -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', [
Expand All @@ -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', []);
Expand All @@ -105,37 +105,43 @@ 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('<html><head><title>Hello - My App</title></head><body>Hi<footer>This is the footer'
. '</footer></body></html>', $newResponse->getBody()->getContents());
$this->assertEquals(
'<html><head><title>Hello - My App</title></head><body>Hi<footer>This is the footer'
. '</footer></body></html>',
$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('<html><head><title>Hello - My App</title></head><body>Hi<footer>This is the footer'
. '</footer></body></html>', $newResponse->getBody()->getContents());
$this->assertEquals(
'<html><head><title>Hello - My App</title></head><body>Hi<footer>This is the footer'
. '</footer></body></html>',
$newResponse->getBody()->getContents()
);
}

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', [
Expand All @@ -159,22 +165,35 @@ 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,
'template.phtml',
['title' => 'Hello - My App', 'hello' => 'Hi', 'content' => 'Ho']
);
$newResponse->getBody()->rewind();
$this->assertEquals('<html><head><title>Hello - My App</title></head><body>Hi<footer>This is the footer'
. '</footer></body></html>', $newResponse->getBody()->getContents());
$this->assertEquals(
'<html><head><title>Hello - My App</title></head><body>Hi<footer>This is the footer'
. '</footer></body></html>',
$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);
}
}

0 comments on commit ee71d44

Please sign in to comment.