Skip to content

Commit

Permalink
Merge pull request #3 from Dhii/fix/convert-context-values
Browse files Browse the repository at this point in the history
Normalize Context Values
  • Loading branch information
XedinUnknown authored Feb 22, 2021
2 parents fcdb9e1 + 98cb498 commit 7c361e2
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 7 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"phpunit/phpunit": "^6.0 | ^7.0 | ^8.0",
"psr/container": "^1.0",
"vimeo/psalm": "^3.11.7 | ^4.0",
"slevomat/coding-standard": "^6.0"
"slevomat/coding-standard": "^6.0",
"symfony/polyfill-php80": "^1.19"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 37 additions & 5 deletions src/Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

use ArrayAccess;
use Dhii\Output\Template\TemplateInterface;
use Exception;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use RangeException;
use Stringable;

/**
Expand Down Expand Up @@ -98,6 +100,8 @@ public function render($context = null)
* @return string A string with tokens replaced by values.
* If value not found in context, token will remain unchanged.
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
*
* @throws Exception If problem replacing.
*/
protected function replaceTokens(string $template, $context): string
{
Expand All @@ -115,6 +119,7 @@ protected function replaceTokens(string $template, $context): string
if ($value === null) {
continue;
}
$value = $this->normalizeValue($value);

$result = str_replace($token, $value, $result);
}
Expand Down Expand Up @@ -197,13 +202,15 @@ protected function getContextValue($context, string $key, $default)
}

/** @psalm-suppress RedundantConditionGivenDocblockType Not guaranteed by typehint */
if ($context instanceof ContainerInterface) {
return $context->has($key)
? $context->get($key)
: $default;
if (!($context instanceof ContainerInterface)) {
throw new InvalidArgumentException('Invalid context');
}

throw new InvalidArgumentException('Invalid context');
$value = $context->has($key)
? $context->get($key)
: $default;

return $value;
}

/**
Expand Down Expand Up @@ -252,4 +259,29 @@ protected function cleanTokens(array $tokens): array

return $newTokens;
}

/**
* Normalize a value to string.
*
* @param scalar|Stringable|mixed $value The value to normalize.
* @return string The normalized value.
*
* @throws RangeException If cannot normalize.
*/
protected function normalizeValue($value): string
{
if (is_bool($value)) {
$value = (int) $value;
}

if (is_scalar($value) || (is_object($value) && method_exists($value, '__toString'))) {
$value = (string) $value;
}

if (!is_string($value)) {
throw new RangeException(sprintf('Could not normalize value: %1$s', print_r($value, true)));
}

return $value;
}
}
88 changes: 88 additions & 0 deletions tests/functional/TemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
use Dhii\Output\DelimitedTokenTemplate\Template as TestSubject;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use RangeException;
use stdClass;
use Stringable;

class TemplateTest extends TestCase
{
Expand Down Expand Up @@ -110,4 +113,89 @@ public function testRenderDelimiterInMiddle()
$this->assertEquals("Good night, $value!", $result);
}
}

/**
* Tests that values of allowed types may be used, and disallowed throw.
*
* @dataProvider provideContext
* @param mixed $context The context to render with.
* @param mixed $expect What to expect.
* @param string|null $shouldExpectException The exception type to expect, if any.
*/
public function testValueTypes($context, $expect, ?string $shouldExpectException)
{
{
$e = '\\';
$d = '%';
$key = 'key';
$template = "{$d}{$key}{$d}";
$subject = $this->createInstance($template, $d, $d, $e);
}

{
if ($shouldExpectException) {
$this->expectException($shouldExpectException);
}

$result = $subject->render($context);
$this->assertEquals($expect, $result);
}
}

/**
* @return array[]
*/
public function provideContext(): array
{
return [
[ // Set
['key' => $value = uniqid('value1')], // Context
$value, // Expect
null, // Should throw
],

[ // Set
['key' => $value = rand(1, 99)], // Context
$value, // Expect
null, // Should throw
],

[ // Set
['key' => $value = rand(0, 100) < 50], // Context
(string) (int) $value, // Expect
null, // Should throw
],

[ // Set
['key' => $value = $this->createStringable(uniqid('value2'))], // Context
$value, // Expect
null, // Should throw
],

[ // Set
['key' => new stdClass()], // Context
null, // Expect
RangeException::class, // Should throw
],
];
}

/**
* Creates a new stringable that represents the specified content.
*
* @param string $content The content of the stringable.
*
* @return Stringable|MockObject The new stringable.
*/
protected function createStringable(string $content): Stringable
{
$mock = $this->getMockBuilder(Stringable::class)
->setMethods(['__toString'])
->getMockForAbstractClass();

$mock->method('__toString')
->will($this->returnValue($content));

return $mock;
}
}

0 comments on commit 7c361e2

Please sign in to comment.