summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Peter Banyard <girgias@php.net>2020-10-19 15:19:15 +0100
committerGeorge Peter Banyard <girgias@php.net>2021-01-04 21:09:23 +0100
commit589bdf30b2bea10172a49bcad26d44b18f192556 (patch)
tree2f507c6d291d4e7195ba1886abeb130d5515b46d
parentab9f497b90bdbbe1f0b0ada1867d1007b7a5958d (diff)
downloadphp-git-589bdf30b2bea10172a49bcad26d44b18f192556.tar.gz
Implement Explicit octal notation for integers RFC
RFC: https://wiki.php.net/rfc/explicit_octal_notation Add an extensive test suits for other variants of integer literals Closes GH-6360
-rw-r--r--UPGRADING14
-rw-r--r--Zend/zend_language_scanner.l48
-rw-r--r--Zend/zend_strtod.c3
-rw-r--r--ext/filter/logical_filters.c7
-rw-r--r--ext/filter/tests/013.phpt8
-rw-r--r--ext/filter/tests/surprising_integer_literals.phpt36
-rw-r--r--ext/gmp/gmp.c3
-rw-r--r--ext/gmp/tests/gmp_init_integer_notations.phpt50
-rw-r--r--ext/gmp/tests/surprising_integer_literals.phpt33
-rw-r--r--ext/standard/tests/math/base_convert_variation2.phpt32
-rw-r--r--ext/standard/tests/math/base_convert_variation3.phpt18
-rw-r--r--ext/standard/tests/math/bindec_variation2.phpt14
-rw-r--r--ext/standard/tests/math/hexdec_variation2.phpt14
-rw-r--r--ext/standard/tests/math/octdec_variation2.phpt16
-rw-r--r--tests/lang/integer_literals/binary_32bit.phpt82
-rw-r--r--tests/lang/integer_literals/binary_64bit.phpt82
-rw-r--r--tests/lang/integer_literals/hexadecimal_32bit.phpt82
-rw-r--r--tests/lang/integer_literals/hexadecimal_64bit.phpt83
-rw-r--r--tests/lang/integer_literals/octal_32bit.phpt118
-rw-r--r--tests/lang/integer_literals/octal_64bit.phpt133
20 files changed, 872 insertions, 4 deletions
diff --git a/UPGRADING b/UPGRADING
index 953082a7a0..38dfda996a 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -65,6 +65,11 @@ PHP 8.1 UPGRADE NOTES
2. New Features
========================================
+- Core:
+ . It is now possible to specify octal integer by using the explicit "0o"/"0O"
+ prefix similar to hexadecimal ("0x"/"0X) and binary ("0b"/"0B") integer literals
+ RFC: https://wiki.php.net/rfc/explicit_octal_notation
+
- hash:
. The following functions have changed signatures:
@@ -101,6 +106,15 @@ PHP 8.1 UPGRADE NOTES
5. Changed Functions
========================================
+- Filter:
+ . The FILTER_FLAG_ALLOW_OCTAL flag of the FILTER_VALIDATE_INT filter now accept
+ octal string with the leading octal prefix ("0o"/"0O")
+ RFC: https://wiki.php.net/rfc/explicit_octal_notation
+
+- GMP:
+ . All GMP function now accept octal string with the leading octal prefix ("0o"/"0O")
+ RFC: https://wiki.php.net/rfc/explicit_octal_notation
+
========================================
6. New Functions
========================================
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index c381d50a28..d82f67d0c7 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -1366,6 +1366,7 @@ DNUM ({LNUM}?"."{LNUM})|({LNUM}"."{LNUM}?)
EXPONENT_DNUM (({LNUM}|{DNUM})[eE][+-]?{LNUM})
HNUM "0x"[0-9a-fA-F]+(_[0-9a-fA-F]+)*
BNUM "0b"[01]+(_[01]+)*
+ONUM "0o"[0-7]+(_[0-7]+)*
LABEL [a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*
WHITESPACE [ \n\r\t]+
TABS_AND_SPACES [ \t]*
@@ -1950,6 +1951,51 @@ NEWLINE ("\r"|"\n"|"\r\n")
}
}
+<ST_IN_SCRIPTING>{ONUM} {
+ /* The +/- 2 skips "0o" */
+ size_t len = yyleng - 2;
+ char *end, *octal = yytext + 2;
+ zend_bool contains_underscores = (memchr(octal, '_', len) != NULL);
+
+ /* Skip any leading 0s */
+ while (len > 0 && (*octal == '0' || *octal == '_')) {
+ ++octal;
+ --len;
+ }
+
+ if (contains_underscores) {
+ octal = estrndup(octal, len);
+ strip_underscores(octal, &len);
+ }
+
+ errno = 0;
+
+ ZVAL_LONG(zendlval, ZEND_STRTOL(octal, &end, 8));
+
+ ZEND_ASSERT(end == octal + len);
+
+ if (!errno) {
+ if (contains_underscores) {
+ efree(octal);
+ }
+ RETURN_TOKEN_WITH_VAL(T_LNUMBER);
+ }
+
+ /* Overflow */
+ ZEND_ASSERT(errno == ERANGE);
+ /* Reset errno */
+ errno = 0;
+
+ /* zend_oct_strtod skips leading '0' */
+ ZVAL_DOUBLE(zendlval, zend_oct_strtod(octal, (const char **)&end));
+ ZEND_ASSERT(!errno);
+ ZEND_ASSERT(end == octal + len);
+ if (contains_underscores) {
+ efree(octal);
+ }
+ RETURN_TOKEN_WITH_VAL(T_DNUMBER);
+}
+
<ST_IN_SCRIPTING>{LNUM} {
size_t len = yyleng;
char *end, *lnum = yytext;
@@ -2071,7 +2117,7 @@ string:
RETURN_TOKEN_WITH_VAL(T_NUM_STRING);
}
-<ST_VAR_OFFSET>{LNUM}|{HNUM}|{BNUM} { /* Offset must be treated as a string */
+<ST_VAR_OFFSET>{LNUM}|{HNUM}|{BNUM}|{ONUM} { /* Offset must be treated as a string */
if (yyleng == 1) {
ZVAL_INTERNED_STR(zendlval, ZSTR_CHAR((zend_uchar)*(yytext)));
} else {
diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c
index e88fbd001f..9173a15031 100644
--- a/Zend/zend_strtod.c
+++ b/Zend/zend_strtod.c
@@ -4467,9 +4467,6 @@ ZEND_API double zend_oct_strtod(const char *str, const char **endptr)
return 0.0;
}
- /* skip leading zero */
- s++;
-
while ((c = *s++)) {
if (c < '0' || c > '7') {
/* break and return the current value if the number is not well-formed
diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c
index 392156b539..1e4925b421 100644
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@ -240,6 +240,13 @@ void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
error = 1;
}
} else if (allow_octal) {
+ /* Support explicit octal prefix notation */
+ if (*p == 'o' || *p == 'O') {
+ p++; len--;
+ if (len == 0) {
+ RETURN_VALIDATION_FAILED
+ }
+ }
if (php_filter_parse_octal(p, len, &ctx_value) < 0) {
error = 1;
}
diff --git a/ext/filter/tests/013.phpt b/ext/filter/tests/013.phpt
index c9dd915d83..3f663ac762 100644
--- a/ext/filter/tests/013.phpt
+++ b/ext/filter/tests/013.phpt
@@ -38,6 +38,11 @@ var_dump(filter_var(345, FILTER_VALIDATE_INT, array("options" => array("min_rang
var_dump(filter_var("0ff", FILTER_VALIDATE_INT));
var_dump(filter_var("010", FILTER_VALIDATE_INT));
+// Explicit octal prefix
+var_dump(filter_var("0o16", FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL)));
+var_dump(filter_var("0O16", FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL)));
+var_dump(filter_var("0o016", FILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL)));
+
echo "Done\n";
?>
--EXPECT--
@@ -71,4 +76,7 @@ bool(false)
bool(false)
bool(false)
bool(false)
+int(14)
+int(14)
+int(14)
Done
diff --git a/ext/filter/tests/surprising_integer_literals.phpt b/ext/filter/tests/surprising_integer_literals.phpt
new file mode 100644
index 0000000000..abb7b9caea
--- /dev/null
+++ b/ext/filter/tests/surprising_integer_literals.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Surprising result with integer literals (hex/octal)
+--SKIPIF--
+<?php if (!extension_loaded("filter")) die("skip"); ?>
+--FILE--
+<?php
+echo 'Hex', \PHP_EOL;
+var_dump(filter_var('0x', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX));
+var_dump(filter_var('0xg', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX));
+var_dump(filter_var('0X', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX));
+var_dump(filter_var('0Xg', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX));
+var_dump(filter_var('', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX));
+echo 'Octal', \PHP_EOL;
+var_dump(filter_var('0o', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
+var_dump(filter_var('0og', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
+var_dump(filter_var('0O', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
+var_dump(filter_var('0Og', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
+var_dump(filter_var('O', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
+var_dump(filter_var('Og', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
+var_dump(filter_var('', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
+?>
+--EXPECT--
+Hex
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+Octal
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c
index 0f4b317fd0..da1ab3047f 100644
--- a/ext/gmp/gmp.c
+++ b/ext/gmp/gmp.c
@@ -605,6 +605,9 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui
if ((base == 0 || base == 16) && (numstr[1] == 'x' || numstr[1] == 'X')) {
base = 16;
skip_lead = 1;
+ } else if ((base == 0 || base == 8) && (numstr[1] == 'o' || numstr[1] == 'O')) {
+ base = 8;
+ skip_lead = 1;
} else if ((base == 0 || base == 2) && (numstr[1] == 'b' || numstr[1] == 'B')) {
base = 2;
skip_lead = 1;
diff --git a/ext/gmp/tests/gmp_init_integer_notations.phpt b/ext/gmp/tests/gmp_init_integer_notations.phpt
new file mode 100644
index 0000000000..0a705f7cd3
--- /dev/null
+++ b/ext/gmp/tests/gmp_init_integer_notations.phpt
@@ -0,0 +1,50 @@
+--TEST--
+gmp_init() with various integer notations
+--SKIPIF--
+<?php if (!extension_loaded("gmp")) print "skip"; ?>
+--FILE--
+<?php
+
+var_dump(gmp_init("0x16"));
+var_dump(gmp_init("0X16"));
+var_dump(gmp_init("0o16"));
+var_dump(gmp_init("0o16"));
+var_dump(gmp_init("016"));
+var_dump(gmp_init("016"));
+var_dump(gmp_init("0b11"));
+var_dump(gmp_init("0b11"));
+
+?>
+--EXPECT--
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(2) "22"
+}
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(2) "22"
+}
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(2) "14"
+}
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(2) "14"
+}
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(2) "14"
+}
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(2) "14"
+}
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(1) "3"
+}
+object(GMP)#1 (1) {
+ ["num"]=>
+ string(1) "3"
+}
diff --git a/ext/gmp/tests/surprising_integer_literals.phpt b/ext/gmp/tests/surprising_integer_literals.phpt
new file mode 100644
index 0000000000..de7289ea0b
--- /dev/null
+++ b/ext/gmp/tests/surprising_integer_literals.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Surprising result with integer literals (hex/binary/octal)
+--SKIPIF--
+<?php if (!extension_loaded("gmp")) print "skip"; ?>
+--FILE--
+<?php
+
+$values = [
+ '0x',
+ '0X',
+ '0b',
+ '0B',
+ '0o',
+ '0O',
+ ''
+];
+
+foreach ($values as $value) {
+ try {
+ var_dump(gmp_init($value));
+ } catch (\ValueError $e) {
+ echo $e->getMessage(), \PHP_EOL;
+ }
+}
+?>
+--EXPECT--
+gmp_init(): Argument #1 ($num) is not an integer string
+gmp_init(): Argument #1 ($num) is not an integer string
+gmp_init(): Argument #1 ($num) is not an integer string
+gmp_init(): Argument #1 ($num) is not an integer string
+gmp_init(): Argument #1 ($num) is not an integer string
+gmp_init(): Argument #1 ($num) is not an integer string
+gmp_init(): Argument #1 ($num) is not an integer string
diff --git a/ext/standard/tests/math/base_convert_variation2.phpt b/ext/standard/tests/math/base_convert_variation2.phpt
new file mode 100644
index 0000000000..279cff9920
--- /dev/null
+++ b/ext/standard/tests/math/base_convert_variation2.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Test base_convert() function : strange literals
+--FILE--
+<?php
+echo 'Binary to decimal:', \PHP_EOL;
+var_dump(base_convert('0b', 2, 10));
+var_dump(base_convert('0B', 2, 10));
+var_dump(base_convert('', 2, 10));
+echo 'Octal to decimal:', \PHP_EOL;
+var_dump(base_convert('0o', 8, 10));
+var_dump(base_convert('0O', 8, 10));
+var_dump(base_convert('0', 8, 10));
+var_dump(base_convert('', 8, 10));
+echo 'Hexadecimal to decimal:', \PHP_EOL;
+var_dump(base_convert('0x', 16, 10));
+var_dump(base_convert('0X', 16, 10));
+var_dump(base_convert('', 16, 10));
+?>
+--EXPECT--
+Binary to decimal:
+string(1) "0"
+string(1) "0"
+string(1) "0"
+Octal to decimal:
+string(1) "0"
+string(1) "0"
+string(1) "0"
+string(1) "0"
+Hexadecimal to decimal:
+string(1) "0"
+string(1) "0"
+string(1) "0"
diff --git a/ext/standard/tests/math/base_convert_variation3.phpt b/ext/standard/tests/math/base_convert_variation3.phpt
new file mode 100644
index 0000000000..27e0841108
--- /dev/null
+++ b/ext/standard/tests/math/base_convert_variation3.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Test base_convert() function: converting '0'
+--FILE--
+<?php
+echo 'Binary to decimal:', \PHP_EOL;
+var_dump(base_convert('0', 2, 10));
+echo 'Octal to decimal:', \PHP_EOL;
+var_dump(base_convert('0', 8, 10));
+echo 'Hexadecimal to decimal:', \PHP_EOL;
+var_dump(base_convert('0', 16, 10));
+?>
+--EXPECT--
+Binary to decimal:
+string(1) "0"
+Octal to decimal:
+string(1) "0"
+Hexadecimal to decimal:
+string(1) "0"
diff --git a/ext/standard/tests/math/bindec_variation2.phpt b/ext/standard/tests/math/bindec_variation2.phpt
new file mode 100644
index 0000000000..3fddc5d856
--- /dev/null
+++ b/ext/standard/tests/math/bindec_variation2.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test bindec() function : strange literals
+--FILE--
+<?php
+
+var_dump(bindec('0b'));
+var_dump(bindec('0B'));
+var_dump(bindec(''));
+
+?>
+--EXPECT--
+int(0)
+int(0)
+int(0)
diff --git a/ext/standard/tests/math/hexdec_variation2.phpt b/ext/standard/tests/math/hexdec_variation2.phpt
new file mode 100644
index 0000000000..81ebae5c3f
--- /dev/null
+++ b/ext/standard/tests/math/hexdec_variation2.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Test hexdec() function : strange literals
+--FILE--
+<?php
+
+var_dump(hexdec('0x'));
+var_dump(hexdec('0X'));
+var_dump(hexdec(''));
+
+?>
+--EXPECT--
+int(0)
+int(0)
+int(0)
diff --git a/ext/standard/tests/math/octdec_variation2.phpt b/ext/standard/tests/math/octdec_variation2.phpt
new file mode 100644
index 0000000000..bf26f45736
--- /dev/null
+++ b/ext/standard/tests/math/octdec_variation2.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Test octdec() function : strange literals
+--FILE--
+<?php
+
+var_dump(octdec('0o'));
+var_dump(octdec('0O'));
+var_dump(octdec('0'));
+var_dump(octdec(''));
+
+?>
+--EXPECT--
+int(0)
+int(0)
+int(0)
+int(0)
diff --git a/tests/lang/integer_literals/binary_32bit.phpt b/tests/lang/integer_literals/binary_32bit.phpt
new file mode 100644
index 0000000000..4318940446
--- /dev/null
+++ b/tests/lang/integer_literals/binary_32bit.phpt
@@ -0,0 +1,82 @@
+--TEST--
+Binary integer strings (32bit)
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only");
+?>
+--FILE--
+<?php
+/* Using binary prefix notation lowercase */
+/* Maximum value representable as integer */
+$binary = 0b1111111111111111111111111111111;
+var_dump($binary);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$binary = 0b111111010000101010101010101010111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+/* Integer */
+$binary = 0b1010110;
+var_dump($binary);
+
+/* underscore separator */
+$binary = 0b1_010110;
+var_dump($binary);
+
+/* Ignore leading 0 and _ */
+$binary = 0b0_01010110;
+var_dump($binary);
+$binary = 0b0_1010110;
+var_dump($binary);
+
+/* Overflow to infinity */
+$binary = 0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+/* Using binary prefix notation uppercase */
+/* Maximum value representable as integer */
+$binary = 0B1111111111111111111111111111111;
+var_dump($binary);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$binary = 0B111111010000101010101010101010111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+/* Integer */
+$binary = 0B1010110;
+var_dump($binary);
+
+/* underscore separator */
+$binary = 0B1_010110;
+var_dump($binary);
+
+/* Ignore leading 0 and _ */
+$binary = 0B0_01010110;
+var_dump($binary);
+$binary = 0B0_1010110;
+var_dump($binary);
+
+/* Overflow to infinity */
+$binary = 0B111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+?>
+--EXPECT--
+int(2147483647)
+int(2147483647)
+float(1.9119287772983036E+25)
+int(86)
+int(86)
+int(86)
+int(86)
+float(INF)
+int(2147483647)
+int(2147483647)
+float(1.9119287772983036E+25)
+int(86)
+int(86)
+int(86)
+int(86)
+float(INF)
diff --git a/tests/lang/integer_literals/binary_64bit.phpt b/tests/lang/integer_literals/binary_64bit.phpt
new file mode 100644
index 0000000000..9b6c3997ae
--- /dev/null
+++ b/tests/lang/integer_literals/binary_64bit.phpt
@@ -0,0 +1,82 @@
+--TEST--
+Binary integer strings (64bit)
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+/* Using binary prefix notation lowercase */
+/* Maximum value representable as integer */
+$binary = 0b111111111111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$binary = 0b111111010000101010101010101010111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+/* Integer */
+$binary = 0b1010110;
+var_dump($binary);
+
+/* underscore separator */
+$binary = 0b1_010110;
+var_dump($binary);
+
+/* Ignore leading 0 and _ */
+$binary = 0b0_01010110;
+var_dump($binary);
+$binary = 0b0_1010110;
+var_dump($binary);
+
+/* Overflow to infinity */
+$binary = 0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+/* Using binary prefix notation uppercase */
+/* Maximum value representable as integer */
+$binary = 0B111111111111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$binary = 0B111111010000101010101010101010111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+/* Integer */
+$binary = 0B1010110;
+var_dump($binary);
+
+/* underscore separator */
+$binary = 0B1_010110;
+var_dump($binary);
+
+/* Ignore leading 0 and _ */
+$binary = 0B0_01010110;
+var_dump($binary);
+$binary = 0B0_1010110;
+var_dump($binary);
+
+/* Overflow to infinity */
+$binary = 0B111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111;
+var_dump($binary);
+
+?>
+--EXPECT--
+int(9223372036854775807)
+int(9223372036854775807)
+float(1.9119287772983036E+25)
+int(86)
+int(86)
+int(86)
+int(86)
+float(INF)
+int(9223372036854775807)
+int(9223372036854775807)
+float(1.9119287772983036E+25)
+int(86)
+int(86)
+int(86)
+int(86)
+float(INF)
diff --git a/tests/lang/integer_literals/hexadecimal_32bit.phpt b/tests/lang/integer_literals/hexadecimal_32bit.phpt
new file mode 100644
index 0000000000..1793354a5d
--- /dev/null
+++ b/tests/lang/integer_literals/hexadecimal_32bit.phpt
@@ -0,0 +1,82 @@
+--TEST--
+Hexadecimal integer strings (32bit)
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only");
+?>
+--FILE--
+<?php
+/* Using hexadecimal prefix notation lowercase */
+/* Maximum value representable as integer */
+$hex = 0x7FFFFFFF;
+var_dump($hex);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$hex = 0x45FFFABCDE0000000;
+var_dump($hex);
+
+/* Integer */
+$hex = 0x1C;
+var_dump($hex);
+
+/* underscore separator */
+$hex = 0x1_C;
+var_dump($hex);
+
+/* Ignore leading 0 and _ */
+$hex = 0x0_01C;
+var_dump($hex);
+$hex = 0x0_1C;
+var_dump($hex);
+
+/* Overflow to infinity */
+$hex = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
+var_dump($hex);
+
+/* Using hexadecimal prefix notation uppercase */
+/* Maximum value representable as integer */
+$hex = 0X7FFFFFFF;
+var_dump($hex);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$hex = 0X45FFFABCDE0000000;
+var_dump($hex);
+
+/* Integer */
+$hex = 0X1C;
+var_dump($hex);
+
+/* underscore separator */
+$hex = 0X1_C;
+var_dump($hex);
+
+/* Ignore leading 0 and _ */
+$hex = 0X0_01C;
+var_dump($hex);
+$hex = 0X0_1C;
+var_dump($hex);
+
+/* Overflow to infinity */
+$hex = 0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
+var_dump($hex);
+
+?>
+--EXPECT--
+int(2147483647)
+int(2147483647)
+float(8.070441274821732E+19)
+int(28)
+int(28)
+int(28)
+int(28)
+float(INF)
+int(2147483647)
+int(2147483647)
+float(8.070441274821732E+19)
+int(28)
+int(28)
+int(28)
+int(28)
+float(INF)
diff --git a/tests/lang/integer_literals/hexadecimal_64bit.phpt b/tests/lang/integer_literals/hexadecimal_64bit.phpt
new file mode 100644
index 0000000000..780591d101
--- /dev/null
+++ b/tests/lang/integer_literals/hexadecimal_64bit.phpt
@@ -0,0 +1,83 @@
+--TEST--
+Hexadecimal integer strings (64bit)
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+/* Using hexadecimal prefix notation lowercase */
+/* Maximum value representable as integer */
+$hex = 0x7FFFFFFFFFFFFFFF;
+var_dump($hex);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$hex = 0x45FFFABCDE0000000;
+var_dump($hex);
+
+/* Integer */
+$hex = 0x1C;
+var_dump($hex);
+
+/* underscore separator */
+$hex = 0x1_C;
+var_dump($hex);
+
+/* Ignore leading 0 and _ */
+$hex = 0x0_01C;
+var_dump($hex);
+$hex = 0x0_1C;
+var_dump($hex);
+
+/* Overflow to infinity */
+$hex = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
+var_dump($hex);
+
+
+/* Using hexadecimal prefix notation uppercase */
+/* Maximum value representable as integer */
+$hex = 0X7FFFFFFFFFFFFFFF;
+var_dump($hex);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$hex = 0X45FFFABCDE0000000;
+var_dump($hex);
+
+/* Integer */
+$hex = 0X1C;
+var_dump($hex);
+
+/* underscore separator */
+$hex = 0X1_C;
+var_dump($hex);
+
+/* Ignore leading 0 and _ */
+$hex = 0X0_01C;
+var_dump($hex);
+$hex = 0X0_1C;
+var_dump($hex);
+
+/* Overflow to infinity */
+$hex = 0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
+var_dump($hex);
+
+?>
+--EXPECT--
+int(9223372036854775807)
+int(9223372036854775807)
+float(8.070441274821732E+19)
+int(28)
+int(28)
+int(28)
+int(28)
+float(INF)
+int(9223372036854775807)
+int(9223372036854775807)
+float(8.070441274821732E+19)
+int(28)
+int(28)
+int(28)
+int(28)
+float(INF)
diff --git a/tests/lang/integer_literals/octal_32bit.phpt b/tests/lang/integer_literals/octal_32bit.phpt
new file mode 100644
index 0000000000..472fe602c0
--- /dev/null
+++ b/tests/lang/integer_literals/octal_32bit.phpt
@@ -0,0 +1,118 @@
+--TEST--
+Octal integer strings (32bit)
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only");
+?>
+--FILE--
+<?php
+/* Using octal prefix notation lowercase */
+/* Maximum value representable as integer */
+$octal = 0o17777777777;
+var_dump($octal);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$octal = 0o45734321536435450000000000;
+var_dump($octal);
+
+/* Integer */
+$octal = 0o16;
+var_dump($octal);
+
+/* underscore separator */
+$octal = 0o1_6;
+var_dump($octal);
+
+/* Ignore leading 0 and _ */
+$octal = 0o0_016;
+var_dump($octal);
+$octal = 0o0_16;
+var_dump($octal);
+
+/* Overflow to infinity */
+$octal = 0o77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777;
+var_dump($octal);
+
+/* Using octal prefix notation uppercase */
+/* Maximum value representable as integer */
+$octal = 0O17777777777;
+var_dump($octal);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$octal = 0O45734321536435450000000000;
+var_dump($octal);
+
+/* Integer */
+$octal = 0O16;
+var_dump($octal);
+
+/* underscore separator */
+$octal = 0O1_6;
+var_dump($octal);
+
+/* Ignore leading 0 and _ */
+$octal = 0O0_016;
+var_dump($octal);
+$octal = 0O0_16;
+var_dump($octal);
+
+/* Overflow to infinity */
+$octal = 0O77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777;
+var_dump($octal);
+
+/* Using no dedicated prefix */
+/* Maximum value representable as integer */
+$octal = 017777777777;
+var_dump($octal);
+var_dump(PHP_INT_MAX);
+
+/* Floating number */
+$octal = 045734321536435450000000000;
+var_dump($octal);
+
+/* Integer */
+$octal = 016;
+var_dump($octal);
+
+/* underscore separator */
+$octal = 01_6;
+var_dump($octal);
+
+/* Ignore leading 0 and _ */
+$octal = 00_016;
+var_dump($octal);
+$octal = 0_16;
+var_dump($octal);
+
+/* Overflow to infinity */
+$octal = 077777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777;
+var_dump($octal);
+
+?>
+--EXPECT--
+int(2147483647)
+int(2147483647)
+float(1.7912166229916324E+23)
+int(14)
+int(14)
+int(14)
+int(14)
+float(INF)
+int(2147483647)
+int(2147483647)
+float(1.7912166229916324E+23)
+int(14)
+int(14)
+int(14)
+int(14)
+float(INF)
+int(2147483647)
+int(2147483647)
+float(1.7912166229916324E+23)
+int(14)
+int(14)
+int(14)
+int(14)
+float(INF)
diff --git a/tests/lang/integer_literals/octal_64bit.phpt b/tests/lang/integer_literals/octal_64bit.phpt
new file mode 100644
index 0000000000..742900e4e8
--- /dev/null
+++ b/tests/lang/integer_literals/octal_64bit.phpt
@@ -0,0 +1,133 @@
+--TEST--
+Octal integer strings (64bit)
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
+?>
+--FILE--
+<?php
+/* Using octal prefix notation lowercase */
+/* Maximum value representable as integer */
+$octal = 0o777777777777777777777;
+var_dump($octal);
+var_dump(PHP_INT_MAX);
+
+/* *technically* this should work but treat this as a degenerate case */
+$octal = 0o1000000000000000000000;
+var_dump($octal);
+
+/* Floating number */
+$octal = 0o45734321536435450000000000;
+var_dump($octal);
+
+/* Integer */
+$octal = 0o16;
+var_dump($octal);
+
+/* underscore separator */
+$octal = 0o1_6;
+var_dump($octal);
+
+/* Ignore leading 0 and _ */
+$octal = 0o0_016;
+var_dump($octal);
+$octal = 0o0_16;
+var_dump($octal);
+
+/* Overflow to infinity */
+$octal = 0o77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777;
+var_dump($octal);
+
+/* Using octal prefix notation uppercase */
+/* Maximum value representable as integer */
+$octal = 0O777777777777777777777;
+var_dump($octal);
+var_dump(PHP_INT_MAX);
+
+/* *technically* this should work but treat this as a degenerate case */
+$octal = 0O1000000000000000000000;
+var_dump($octal);
+
+/* Floating number */
+$octal = 0O45734321536435450000000000;
+var_dump($octal);
+
+/* Integer */
+$octal = 0O16;
+var_dump($octal);
+
+/* underscore separator */
+$octal = 0O1_6;
+var_dump($octal);
+
+/* Ignore leading 0 and _ */
+$octal = 0O0_016;
+var_dump($octal);
+$octal = 0O0_16;
+var_dump($octal);
+
+/* Overflow to infinity */
+$octal = 0O77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777;
+var_dump($octal);
+
+/* Using no dedicated prefix */
+/* Maximum value representable as integer */
+$octal = 0777777777777777777777;
+var_dump($octal);
+var_dump(PHP_INT_MAX);
+
+/* *technically* this should work but treat this as a degenerate case */
+$octal = 01000000000000000000000;
+var_dump($octal);
+
+/* Floating number */
+$octal = 045734321536435450000000000;
+var_dump($octal);
+
+/* Integer */
+$octal = 016;
+var_dump($octal);
+
+/* underscore separator */
+$octal = 01_6;
+var_dump($octal);
+
+/* Ignore leading 0 and _ */
+$octal = 00_016;
+var_dump($octal);
+$octal = 0_16;
+var_dump($octal);
+
+/* Overflow to infinity */
+$octal = 077777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777;
+var_dump($octal);
+
+?>
+--EXPECT--
+int(9223372036854775807)
+int(9223372036854775807)
+float(9.223372036854776E+18)
+float(1.7912166229916324E+23)
+int(14)
+int(14)
+int(14)
+int(14)
+float(INF)
+int(9223372036854775807)
+int(9223372036854775807)
+float(9.223372036854776E+18)
+float(1.7912166229916324E+23)
+int(14)
+int(14)
+int(14)
+int(14)
+float(INF)
+int(9223372036854775807)
+int(9223372036854775807)
+float(9.223372036854776E+18)
+float(1.7912166229916324E+23)
+int(14)
+int(14)
+int(14)
+int(14)
+float(INF)