Skip to content

Commit

Permalink
added new internal functions bool(), int(), float(), string()
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 5, 2020
1 parent 5932800 commit 0af45e4
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/DI/Helpers.php
Expand Up @@ -192,4 +192,27 @@ public static function normalizeClass(string $type): string
? (new \ReflectionClass($type))->name
: $type;
}


/**
* Non data-loss type conversion.
* @param mixed
* @return mixed
* @throws Nette\InvalidStateException
*/
public static function convertType($val, string $type)
{
if (is_scalar($val)) {
$norm = ($val === false ? '0' : (string) $val);
if ($type === 'float') {
$norm = preg_replace('#\.0*$#D', '', $norm);
}
$orig = $norm;
settype($norm, $type);
if ($orig === ($norm === false ? '0' : (string) $norm)) {
return $norm;
}
}
throw new Nette\InvalidStateException('Cannot convert ' . (is_scalar($val) ? "'$val'" : gettype($val)) . " to $type.");
}
}
14 changes: 14 additions & 0 deletions src/DI/Resolver.php
Expand Up @@ -181,9 +181,23 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo
break;

case $entity === 'not':
if (count($arguments) > 1) {
throw new ServiceCreationException("Function $entity() expects at most 1 parameter, " . count($arguments) . ' given.');
}
$entity = ['', '!'];
break;

case $entity === 'bool':
case $entity === 'int':
case $entity === 'float':
case $entity === 'string':
if (count($arguments) > 1) {
throw new ServiceCreationException("Function $entity() expects at most 1 parameter, " . count($arguments) . ' given.');
}
$arguments = [$arguments[0], $entity];
$entity = [Helpers::class, 'convertType'];
break;

case is_string($entity): // create class
if (!class_exists($entity)) {
throw new ServiceCreationException("Class $entity not found.");
Expand Down
88 changes: 88 additions & 0 deletions tests/DI/Compiler.functions.phpt
@@ -0,0 +1,88 @@
<?php

/**
* Test: Nette\DI\Compiler: internal functions.
*/

declare(strict_types=1);

use Nette\DI;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


class Service
{
public function __construct()
{
}


public function __call($nm, $args)
{
$this->args[$nm] = $args;
}
}


const NUM = 231;

$compiler = new DI\Compiler;
$compiler->setDynamicParameterNames(['dynamic']);
$container = createContainer($compiler, '
parameters:
t: true
f: false
fn: ::constant(NUM)
not: not(%f%)
string: string(%f%)
services:
ok:
factory: Service
setup:
- not( not(%f%), not(%t%), not(%fn%), not(%dynamic%), %not% )
- string( string(%f%), string(%t%), string(%fn%), string(%dynamic%), %string% )
- bool( bool(%f%), bool(%t%) )
- int( int(%f%), int(%t%), int(%fn%), int(%dynamic%) )
- float( float(%f%), float(%t%), float(%fn%), float(%dynamic%) )
bad1: Service(bool(123))
bad2:
factory: Service
setup:
- method(bool(123))
', ['dynamic' => 123]);


$obj = $container->getByName('ok');

Assert::same(
[
'not' => [true, false, false, false, true],
'string' => ['0', '1', '231', '123', '0'],
'bool' => [false, true],
'int' => [0, 1, 231, 123],
'float' => [0.0, 1.0, 231.0, 123.0],
],
$obj->args
);

Assert::exception(function () use ($container) {
$container->getByName('bad1');
}, Nette\InvalidStateException::class, "Cannot convert '123' to bool.");

Assert::exception(function () use ($container) {
$container->getByName('bad2');
}, Nette\InvalidStateException::class, "Cannot convert '123' to bool.");


// wrong arguments count
Assert::exception(function () {
createContainer(new DI\Compiler, '
services:
- Service(bool(123, 10))
');
}, Nette\InvalidStateException::class, 'Service of type Service: Function bool() expects at most 1 parameter, 2 given. (used in __construct())');
94 changes: 94 additions & 0 deletions tests/DI/Helpers.convertType.phpt
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

use Nette\DI\Helpers;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


function testIt(string $type, $val, $res = null)
{
if (func_num_args() === 3) {
Assert::same($res, Helpers::convertType($val, $type));
} else {
Assert::exception(function () use ($val, $type) {
Helpers::convertType($val, $type);
}, Nette\InvalidStateException::class);
}
}


$obj = new stdClass;

testIt('string', null);
testIt('string', []);
testIt('string', $obj);
testIt('string', '', '');
testIt('string', 'a', 'a');
testIt('string', '0', '0');
testIt('string', '1', '1');
testIt('string', '1.0', '1.0');
testIt('string', '1.1', '1.1');
testIt('string', '1a', '1a');
testIt('string', true, '1');
testIt('string', false, '0');
testIt('string', 0, '0');
testIt('string', 1, '1');
testIt('string', 1.0, '1');
testIt('string', 1.2, '1.2');

testIt('int', null);
testIt('int', []);
testIt('int', $obj);
testIt('int', '');
testIt('int', 'a');
testIt('int', '0', 0);
testIt('int', '1', 1);
testIt('int', '1.0');
testIt('int', '1.1');
testIt('int', '1a');
testIt('int', true, 1);
testIt('int', false, 0);
testIt('int', 0, 0);
testIt('int', 1, 1);
testIt('int', 1.0, 1);
testIt('int', 1.2);

testIt('float', null);
testIt('float', []);
testIt('float', $obj);
testIt('float', '');
testIt('float', 'a');
testIt('float', '0', 0.0);
testIt('float', '1', 1.0);
testIt('float', '1.', 1.0);
testIt('float', '1.0', 1.0);
testIt('float', '1.00', 1.0);
testIt('float', '1..0');
testIt('float', '1.1', 1.1);
testIt('float', '1a');
testIt('float', true, 1.0);
testIt('float', false, 0.0);
testIt('float', 0, 0.0);
testIt('float', 1, 1.0);
testIt('float', 1.0, 1.0);
testIt('float', 1.2, 1.2);

testIt('bool', null);
testIt('bool', []);
testIt('bool', $obj);
testIt('bool', '');
testIt('bool', 'a');
testIt('bool', '1', true);
testIt('bool', '1.0');
testIt('bool', '1.1');
testIt('bool', '1a');
testIt('bool', true, true);
testIt('bool', false, false);
testIt('bool', 0, false);
testIt('bool', 1, true);
testIt('bool', 1.0, true);
testIt('bool', 1.2);

0 comments on commit 0af45e4

Please sign in to comment.