If I have an array like :
["006"] => int(100) [700] => int(150) ["008"] => int(200) ["a"] => int(300)
I cannot retrieve the values from the keys that are numeric and starts with ZERO.
{{ test['006'] }} ---> null !!!!
{{ test['700'] }} ---> 150
{{ test['a'] }} ---> 300
The problem is in the getAttribute() method in Twig_Template class when there's a INTEGER CAST
Class : Twig_Template
protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
{
$item = ctype_digit((string) $item) ? (int) $item : (string) $item;
...
This is a bug or not?
And if not why exists this CAST?
To reproduce the error:
{% set data = {'01':'123', '09':'456'} %}
{% set key = '09' %}
{{ data[key] }}
and produce the error :
Twig_Error_Runtime: Key "9" for array with keys "01, 09" does not exist
When using a string with numbers greater than the max of an int, i get this message :
Twig_Error_Runtime # 0 - Key "2147483647" for array with keys "9900270000" does not exist in ...
Stangely i only have this behavior on windows. Commenting the line of the ctype_digit test of the getAttribute method of the Twig_Template class make it work.
I've prefixed my keys with '_' to solve my issue.
var_dump(filter_var("2147483648", FILTER_VALIDATE_INT));
false
var_dump(filter_var("2147483647", FILTER_VALIDATE_INT));
2147483647
I suppose using filter_var is better than using ctype_digit for detecting integer key.
var_dump(filter_var("09", FILTER_VALIDATE_INT));
false
So filter_var is a proper solution.
Replacing ctype_digit by filter_var in Template::getAttribute() would break this test: regression/simple_xml_element.test. Not sure what to do here.
filter_var returns the filtered value, so to replace ctype_digit, the return value should be compared with false
$item = filter_var((string) $item, FILTER_VALIDATE_INT) !== false ? (int) $item : (string) $item;
That's almost perfect but not quite as +4 is now considered as an integer; so it is implicitly converted to 4. This is probably an edge case, but that prevents us from using filter_var. The following would fail:
{% set data = {'+1':'123'} %}
{% set key = '+1' %}
{{ data[key] }}
I suppose there is no choice but using both functions :
$item = (filter_var((string) $item, FILTER_VALIDATE_INT) !== false) && (ctype_digit((string) $item)) ? (int) $item : (string) $item;
The problem I see is that this method is critical in terms of performance. So, complexifying the code is most of time a no-go. Of course, we need to benchmark first.
I know this is 2 function calls instead of one but i don't see any other possibility to solve this problem.
Perhaps there's a way to reduce the performance issue by making this test in ext/twig.
PHP already converts string key to number automatically, so why don't we leave it to PHP.
>> $a = [4 => 'x'];
array (
4 => 'x',
)
>> $a[4];
'x'
>> $a['4'];
'x'
>> $a['04'];
Exception (code: 0) got thrown
exception 'Exception' with message 'phar:///root/bin/php-shell.phar/php-shell-cmd.php(121) : eval()'d code:1
Undefined index: 04' in phar:///root/bin/php-shell.phar/php-shell-cmd.php:54
Stack trace:
#0 phar:///root/bin/php-shell.phar/php-shell-cmd.php(121) : eval()'d code(1): __shell_default_error_handler(8, 'Undefined index...', 'phar:///root/bi...', 1, Array)
#1 phar:///root/bin/php-shell.phar/php-shell-cmd.php(121): eval()
#2 /root/bin/php-shell.phar(1): include('phar:///root/bi...')
#3 {main}
>> $a['+4'];
Exception (code: 0) got thrown
exception 'Exception' with message 'phar:///root/bin/php-shell.phar/php-shell-cmd.php(121) : eval()'d code:1
Undefined index: +4' in phar:///root/bin/php-shell.phar/php-shell-cmd.php:54
Stack trace:
#0 phar:///root/bin/php-shell.phar/php-shell-cmd.php(121) : eval()'d code(1): __shell_default_error_handler(8, 'Undefined index...', 'phar:///root/bi...', 1, Array)
#1 phar:///root/bin/php-shell.phar/php-shell-cmd.php(121): eval()
#2 /root/bin/php-shell.phar(1): include('phar:///root/bi...')
#3 {main}
I have tried to replace
$item = filter_var((string) $item, FILTER_VALIDATE_INT) !== false ? (int) $item : (string) $item;
with
$item = (string) $item;
in Twig_Template::getAttribute and then running the tests, and the result is no failing test case.
That's what we had before, but it definitely break one test: regression/simple_xml_element.test
For the SimpleXML case, I propose that we localize the integer conversion to the object property case
// object property
if (Twig_TemplateInterface::METHOD_CALL !== $type) {
if (isset($object->$item) || (false !== ($int_item = filter_var($item, FILTER_VALIDATE_INT)) && isset($object->$int_item) && (($item = $int_item) || true)) || array_key_exists($item, $object)) {
if ($isDefinedTest) {
return true;
}
if ($this->env->hasExtension('sandbox')) {
$this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
}
return $object->$item;
}
}
PHPUnit 3.7.1 by Sebastian Bergmann.
.S............................................................. 63 / 829 ( 7%)
............................................................... 126 / 829 ( 15%)
............................................................... 189 / 829 ( 22%)
............................................................... 252 / 829 ( 30%)
............................................................... 315 / 829 ( 37%)
............................................................... 378 / 829 ( 45%)
............................................................... 441 / 829 ( 53%)
............................................................... 504 / 829 ( 60%)
............................................................... 567 / 829 ( 68%)
............................................................... 630 / 829 ( 75%)
............................................................... 693 / 829 ( 83%)
............................................................... 756 / 829 ( 91%)
............................................................... 819 / 829 ( 98%)
..........
Time: 4 seconds, Memory: 13.00Mb
OK, but incomplete or skipped tests!
Tests: 829, Assertions: 2575, Skipped: 1.
I opened a PR in #878 so you can see my idea to fix it.
see #878
There is still no fix for that ?
it is a bug
Commenting on a closed issue that has been fixed in the past does not help. If you think there is still a bug, you should rather open a new issue with some steps describing the behaviour you experience an what you did expect instead.