ID:               41118
 Updated by:       [EMAIL PROTECTED]
 Reported By:      mahesh dot vemula at in dot ibm dot com
-Status:           Bogus
+Status:           Open
-Bug Type:         Output Control
+Bug Type:         Scripting Engine problem
 Operating System: RHEL 4
 PHP Version:      5CVS-2007-04-17 (snap)
 New Comment:

Hi Ilia,
        Mahesh is one of a team in IBM who are looking at contributing
new PHP tests to the PHP community to improve the coverage of the PHPT
tests. This defect reports one of a number of issues that this work 
has so far uncovered. So as to try and avoid us rasing bogus defects 
all issues they uncover are first reviewed internally by myself and 
others.  I  myself reviewed this issue (and what the PHP manual says 
on the matter) and considered the issue raised here to be a valid
defect. Interpreting a literal expressed in octal notation which is 
outside the range of a signed 32 bit int as a decimal integer without
any warning makes no sense to me at all and certainly is not the
behaviour suggested by the PHP manual: 

    http://uk2.php.net/manual/en/language.types.integer.php

It says on the subject of integers: 

  "Integers can be specified in decimal (10-based), hexadecimal 
  (16-based) or octal (8-based) notation,  optionally preceded by a
   sign (- or +). If you use the octal notation, you must precede the 
   number with a 0 (zero), to use hexadecimal notation precede the 
   number with 0x" 

and 

     "If you specify a number beyond the bounds of the integer type,
     it will be interpreted as a float instead. Also, if you perform
     an operation that results in a number beyond the bounds of the
     integer type, a float will be returned instead. 

and indeed this is what happens if an integer is specified in decimal
or hexadecimal notation but not when its specified in octal so at the 
very least the manual needs updating to document the different
behaviour for octal.  

However, better still would be to fix the code in some way to at least
warn users this is happening. 

The code which is causing this bad/unexpected behaviour is in
zend_scanner

<ST_IN_SCRIPTING>{LNUM} {
        if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */
                zendlval->value.lval = strtol(yytext, NULL, 0);
        } else {
                errno = 0;
                zendlval->value.lval = strtol(yytext, NULL, 0);
                if (errno == ERANGE) { /* Overflow */
                        zendlval->value.dval = zend_strtod(yytext, NULL);
                        zendlval->type = IS_DOUBLE;
                        return T_DNUMBER;
                }
        }

        zendlval->type = IS_LONG;
        return T_LNUMBER;
}

If "yytext" contains an octal value > 017777777777  (i.e decimal 
2147483647 )then strtol will return with errno == ERANGE and we will
call zend_strtod. Unfortunately zend_strtod does not support octal
notation; just decimal and hexadecimal and as a result the number
gets interpreted as decimal.

To fix the behaviour to be inline with expectations zend_strtod would
need to be modified to support octal. I can appreciate this may not be
a straightforward change and what one that would need a fare amount of
testing so as a temporary/simpler fix could the scanner code not be
changed to issue a NOTICE when a integer is expressed in OCTAL that is
too large  to be expressed as a 32 bit signed integer. This was until 
a recent change in PHP 5 what happened in the case of large hexadecimal
integers and indeed is what still happens on PHP6 as the fix as not yet
been applied there, i.e if I run the following code
on PHP6:

<?php
$i = 0xFFFFFFFFFFFFFFFFFFF); 
var_dump($i); 
?>

you get 

>php -f testmod.php
PHP Notice:  Hex number is too big: 0xFFFFFFFFFFFFFFFFFFF in <path to
testcase> on line 7
int(2147483647)

i.e a notice is issued and the number set to maximum value for a 32 
bit signed integer.

Using a similar technique the PHP code could easily be changed as
follows:   

<ST_IN_SCRIPTING>{LNUM} {
        if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */
                zendlval->value.lval = strtol(yytext, NULL, 0);
        } else {
                errno = 0;
                zendlval->value.lval = strtol(yytext, NULL, 0);
                //check for octal       
                 if (*yytext == '0') {
                                zendlval->value.lval = LONG_MAX;
                        zendlval->type = IS_LONG;
                        zend_error(E_NOTICE,"Octal number is too big: %s", 
yytext);
                } else {
                                zendlval->value.dval = zend_strtod(yytext, 
NULL);
                          zendlval->type = IS_DOUBLE;
                }
        }

        zendlval->type = IS_LONG;
        return T_LNUMBER;
}

Assuming that most  PHP developers test programs with 
   error_reporting =E_ALL 
this would at least warn the application writer that the script is not
going to behave as expected rather than just silently convert the 
octal numbers to decimal as the code does today.

I am therefore re-eopning the defect for further consideration in light
of the above.

Regards
        Andy Wharmby
        IBM United Kingdom Limited


Previous Comments:
------------------------------------------------------------------------

[2007-04-17 14:15:57] [EMAIL PROTECTED]

Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

.

------------------------------------------------------------------------

[2007-04-17 13:28:59] mahesh dot vemula at in dot ibm dot com

Description:
------------
Octal integer values which are beyond MAX_INT or MIN_INT value are not
converted to respective float values as expected, instead they are
treated as decimal values. Please see the example attached.

Environment:
Linux Kernal: Linux 2.6.9
PHP Version: PHP 5.2 (Built on Apr 17, 2007 from snaps.php.net)
PHP Configure Setup: ./configure



Reproduce code:
---------------
<?php
var_dump(017777777777);
var_dump(020000000000);
var_dump(020000000007);
var_dump(-017777777777);
var_dump(-020000000000);
var_dump(-020000000007);
?>


Expected result:
----------------
int(2147483647)
float(2147483648)
float(2147483655)
int(-2147483647)
int(-2147483648)
float(-2147483655)


Actual result:
--------------
int(2147483647)
float(2.0E+10)
float(20000000007)
int(-2147483647)
float(-2.0E+10)
float(-20000000007)



------------------------------------------------------------------------


-- 
Edit this bug report at http://bugs.php.net/?id=41118&edit=1

Reply via email to