Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions lib/Transphpile/Tests/Functional/tests/typehint006.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: test if scalar typehint works
stdout: |
523
stderr: |
Argument \$arg passed to test\(\) must be of the type int, string given
code: |
declare(strict_types=1);

function test(int ...$arg) {
foreach ($arg as $v) {
print $v;
}
}

test(5, 2);
test();
test(3);
test(1, "6");
2 changes: 1 addition & 1 deletion lib/Transphpile/Tests/FunctionalTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function assertTranspile($yamlPath)

// Check output
$config['stdout'] = trim($config['stdout']);
$this->assertRegExp('{'.$config['stdout'].'}', $stdout, isset($config['name']) ? $config['name'] : "");
$this->assertRegExp('{'.$config['stdout'].'}', $stdout, (isset($config['name']) ? $config['name'] : "") . ": stderr:\n$stderr");

// Check stderr if any
if (isset($config['stderr'])) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Transphpile/Transpile/Visitors/Php70/ReturnVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public function leaveNode(Node $node)
$returnType, $returnType
);
} else {
// Otherwise use is_a for check against classes
// Otherwise use instanceof for check against classes
$code = sprintf(
'<?php '."\n".
' if ('.$nullCheck.' ! $'.$retVar.' instanceof %s) { '."\n".
Expand Down
32 changes: 26 additions & 6 deletions lib/Transphpile/Transpile/Visitors/Php70/TypehintVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public function leaveNode(Node $node)
'arg' => $param->name,
'func' => $node->name,
'nullable' => $canBeNull,
'variadic' => $param->variadic,
);
$param->type = null;
} else if ($canBeNull && $param->default === null) {
Expand Down Expand Up @@ -107,12 +108,14 @@ public function leaveNode(Node $node)

// Add code for checking scalar types
foreach (array_reverse($params) as $param) {
$code = sprintf(
'<?php if (! is_%s($%s) %s) { throw new \InvalidArgumentException("Argument \$%s passed to %s() must be of the type %s, ".(gettype($%s) == "object" ? get_class($%s) : gettype($%s))." given"); }',
$param['type'], $param['arg'],
($param['nullable'] ? 'and ! is_null($'.$param['arg'].')' : ""),
$param['arg'], $param['func'], $param['type'], $param['arg'], $param['arg'], $param['arg'], $param['arg']
);
$isVariadic = $param['variadic'];
if ($isVariadic) {
$argInner = '__transpiler_iter_' . $param['arg'];
$innerCode = self::generateParamValidationSnippet($param['type'], $argInner, $param['nullable'], $param['func'], $param['arg']);
$code = sprintf('<?php foreach ($%s as $%s) { %s }', $param['arg'], $argInner, $innerCode);
} else {
$code = '<?php ' . self::generateParamValidationSnippet($param['type'], $param['arg'], $param['nullable'], $param['func'], $param['arg']);
}

$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
$stmts = $parser->parse($code);
Expand All @@ -124,4 +127,21 @@ public function leaveNode(Node $node)
}
}
}

/**
* @param string $type (scalar type hint not found in php 5)
* @param string $argInner (arg name in the generated code)
* @param bool $nullable
* @param string $func
* @param string $arg (arg name for the error message)
* @return string
*/
private static function generateParamValidationSnippet($type, $argInner, $nullable, $func, $arg) {
return sprintf(
'if (! is_%s($%s) %s) { throw new \InvalidArgumentException("Argument \$%s passed to %s() must be of the type %s, ".(gettype($%s) == "object" ? get_class($%s) : gettype($%s))." given"); }',
$type, $argInner,
($nullable ? 'and ! is_null($'.$arg.')' : ""),
$arg, $func, $type, $argInner, $argInner, $argInner, $argInner
);
}
}