]> granicus.if.org Git - php/commitdiff
Add support for negative string offsets (syntax)
authorFrancois Laupretre <francois@php.net>
Tue, 21 Jul 2015 20:16:08 +0000 (22:16 +0200)
committerNikita Popov <nikic@php.net>
Wed, 9 Mar 2016 13:41:37 +0000 (14:41 +0100)
Zend/tests/bug29883.phpt
Zend/tests/bug31098.phpt
Zend/tests/empty_str_offset.phpt
Zend/tests/isset_str_offset.phpt
Zend/tests/str_offset_001.phpt
Zend/tests/str_offset_003.phpt [new file with mode: 0644]
Zend/tests/str_offset_004.phpt [new file with mode: 0644]
Zend/zend_execute.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index c92f147ff7a7b69fa743a31442176a50418f6d53..b6ad99aeaf618ecabaf39d62b51246e9f32c59cf 100644 (file)
@@ -3,7 +3,7 @@ Bug #29883 (isset gives invalid values on strings)
 --FILE--
 <?php
 $x = "bug";
-var_dump(isset($x[-1]));
+var_dump(isset($x[-10]));
 var_dump(isset($x["1"]));
 echo $x["1"]."\n";
 ?>
index 23cec9bbf493335316cb516946907af61954f916..31823a1aa5e8a45cb756c118ba9bbf1a28a2c0de 100644 (file)
@@ -18,7 +18,7 @@ var_dump(isset($a['b']));
 $simpleString = "Bogus String Text";
 echo isset($simpleString->wrong)?"bug\n":"ok\n";
 echo isset($simpleString["wrong"])?"bug\n":"ok\n";
-echo isset($simpleString[-1])?"bug\n":"ok\n";
+echo isset($simpleString[-20])?"bug\n":"ok\n";
 echo isset($simpleString[0])?"ok\n":"bug\n";
 echo isset($simpleString["0"])?"ok\n":"bug\n";
 echo isset($simpleString["16"])?"ok\n":"bug\n";
index 486c052dc4975b0fecf9b100b54c7a15b721d8d2..49e175dd214d6f4410331f87af0b103f6ecc7167 100644 (file)
@@ -8,6 +8,8 @@ print "- empty ---\n";
 $str = "test0123";
 
 var_dump(empty($str[-1]));
+var_dump(empty($str[-10]));
+var_dump(empty($str[-4])); // 0
 var_dump(empty($str[0]));
 var_dump(empty($str[1]));
 var_dump(empty($str[4])); // 0
@@ -17,6 +19,8 @@ var_dump(empty($str[10000]));
 // non-numeric offsets
 print "- string ---\n";
 var_dump(empty($str['-1']));
+var_dump(empty($str['-10']));
+var_dump(empty($str['-4'])); // 0
 var_dump(empty($str['0']));
 var_dump(empty($str['1']));
 var_dump(empty($str['4'])); // 0
@@ -31,6 +35,8 @@ print "- null ---\n";
 var_dump(empty($str[null]));
 print "- double ---\n";
 var_dump(empty($str[-1.1]));
+var_dump(empty($str[-10.5]));
+var_dump(empty($str[-4.1]));
 var_dump(empty($str[-0.8]));
 var_dump(empty($str[-0.1]));
 var_dump(empty($str[0.2]));
@@ -50,6 +56,8 @@ print "done\n";
 ?>
 --EXPECTF--
 - empty ---
+bool(false)
+bool(true)
 bool(true)
 bool(false)
 bool(false)
@@ -58,6 +66,8 @@ bool(false)
 bool(true)
 bool(true)
 - string ---
+bool(false)
+bool(true)
 bool(true)
 bool(false)
 bool(false)
@@ -72,6 +82,8 @@ bool(true)
 - null ---
 bool(false)
 - double ---
+bool(false)
+bool(true)
 bool(true)
 bool(false)
 bool(false)
index 7a9164a381795034dd854719a30e4f0225bd783a..d693f80a52e0cf25d1bad3bc9e944cc5c75242e3 100644 (file)
@@ -8,6 +8,7 @@ print "- isset ---\n";
 $str = "test0123";
 
 var_dump(isset($str[-1]));
+var_dump(isset($str[-10]));
 var_dump(isset($str[0]));
 var_dump(isset($str[1]));
 var_dump(isset($str[4])); // 0
@@ -17,6 +18,7 @@ var_dump(isset($str[10000]));
 // non-numeric offsets
 print "- string ---\n";
 var_dump(isset($str['-1']));
+var_dump(isset($str['-10']));
 var_dump(isset($str['0']));
 var_dump(isset($str['1']));
 var_dump(isset($str['4'])); // 0
@@ -31,6 +33,7 @@ print "- null ---\n";
 var_dump(isset($str[null]));
 print "- double ---\n";
 var_dump(isset($str[-1.1]));
+var_dump(isset($str[-10.5]));
 var_dump(isset($str[-0.8]));
 var_dump(isset($str[-0.1]));
 var_dump(isset($str[0.2]));
@@ -50,6 +53,7 @@ print "done\n";
 ?>
 --EXPECTF--
 - isset ---
+bool(true)
 bool(false)
 bool(true)
 bool(true)
@@ -58,6 +62,7 @@ bool(true)
 bool(false)
 bool(false)
 - string ---
+bool(true)
 bool(false)
 bool(true)
 bool(true)
@@ -72,6 +77,7 @@ bool(false)
 - null ---
 bool(true)
 - double ---
+bool(true)
 bool(false)
 bool(true)
 bool(true)
index 8a6b91b49ad9ca85c7a050a85142ab3696858dcd..3317674857026e260025486391675739d1a09ebd 100644 (file)
@@ -1,51 +1,46 @@
---TEST--\r
-string offset 001\r
---FILE--\r
-<?php\r
-function foo($x) {\r
-       var_dump($x);\r
-}\r
-\r
-$str = "abc";\r
-var_dump($str[-1]);\r
-var_dump($str[0]);\r
-var_dump($str[1]);\r
-var_dump($str[2]);\r
-var_dump($str[3]);\r
-var_dump($str[1][0]);\r
-var_dump($str[2][1]);\r
-\r
-foo($str[-1]);\r
-foo($str[0]);\r
-foo($str[1]);\r
-foo($str[2]);\r
-foo($str[3]);\r
-foo($str[1][0]);\r
-foo($str[2][1]);\r
-?>\r
---EXPECTF--\r
-Notice: Uninitialized string offset: -1 in %sstr_offset_001.php on line %d\r
-string(0) ""\r
-string(1) "a"\r
-string(1) "b"\r
-string(1) "c"\r
-\r
-Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d\r
-string(0) ""\r
-string(1) "b"\r
-\r
-Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d\r
-string(0) ""\r
-\r
-Notice: Uninitialized string offset: -1 in %sstr_offset_001.php on line %d\r
-string(0) ""\r
-string(1) "a"\r
-string(1) "b"\r
-string(1) "c"\r
-\r
-Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d\r
-string(0) ""\r
-string(1) "b"\r
-\r
-Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d\r
-string(0) ""\r
+--TEST--
+string offset 001
+--FILE--
+<?php
+// Test positive or null string offsets
+
+function foo($x) {
+       var_dump($x);
+}
+
+$str = "abc";
+var_dump($str[0]);
+var_dump($str[1]);
+var_dump($str[2]);
+var_dump($str[3]);
+var_dump($str[1][0]);
+var_dump($str[2][1]);
+
+foo($str[0]);
+foo($str[1]);
+foo($str[2]);
+foo($str[3]);
+foo($str[1][0]);
+foo($str[2][1]);
+?>
+--EXPECTF--
+string(1) "a"
+string(1) "b"
+string(1) "c"
+
+Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d
+string(0) ""
+string(1) "b"
+
+Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d
+string(0) ""
+string(1) "a"
+string(1) "b"
+string(1) "c"
+
+Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d
+string(0) ""
+string(1) "b"
+
+Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d
+string(0) ""
diff --git a/Zend/tests/str_offset_003.phpt b/Zend/tests/str_offset_003.phpt
new file mode 100644 (file)
index 0000000..e357ac0
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+string offset 003
+--FILE--
+<?php
+// Test negative string offsets
+
+function foo($x) {
+       var_dump($x);
+}
+
+$str = "abcdef";
+var_dump($str[-10]);
+var_dump($str[-3]);
+var_dump($str[2][-2]);
+var_dump($str[2][-1]);
+
+foo($str[-10]);
+foo($str[-3]);
+foo($str[2][-2]);
+foo($str[2][-1]);
+?>
+--EXPECTF--
+Notice: Uninitialized string offset: -10 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "d"
+
+Notice: Uninitialized string offset: -2 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "c"
+
+Notice: Uninitialized string offset: -10 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "d"
+
+Notice: Uninitialized string offset: -2 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "c"
diff --git a/Zend/tests/str_offset_004.phpt b/Zend/tests/str_offset_004.phpt
new file mode 100644 (file)
index 0000000..c8ce607
--- /dev/null
@@ -0,0 +1,49 @@
+--TEST--
+string offset 004
+--FILE--
+<?php
+// Test assignments using (positive and negative) string offsets
+
+$str = "abcdefghijklmno";
+$i = 3;
+$j = -4;
+
+$str{2} = 'C';
+var_dump($str);
+
+$str{$i} = 'Z';
+var_dump($str);
+
+$str{-5} = 'P';
+var_dump($str);
+
+$str{$j} = 'Q';
+var_dump($str);
+
+$str{-20} = 'Y';
+var_dump($str);
+
+$str{-strlen($str)} = strtoupper($str{0}); /* An exotic ucfirst() ;) */
+var_dump($str);
+
+$str{20} = 'N';
+var_dump($str);
+
+$str{-2} = 'UFO';
+var_dump($str);
+
+$str{-$i} = $str{$j*2};
+var_dump($str);
+?>
+--EXPECTF--
+string(15) "abCdefghijklmno"
+string(15) "abCZefghijklmno"
+string(15) "abCZefghijPlmno"
+string(15) "abCZefghijPQmno"
+
+Warning: Illegal string offset:  -20 in %sstr_offset_004.php on line %d
+string(15) "abCZefghijPQmno"
+string(15) "AbCZefghijPQmno"
+string(21) "AbCZefghijPQmno     N"
+string(21) "AbCZefghijPQmno    UN"
+string(21) "AbCZefghijPQmno   nUN"
index de5875fdba68ed17effb4a07e4f07989cc5998b5..3918ddd7ae7ae7e6117e0c6f6f97b178469ba507 100644 (file)
@@ -1172,7 +1172,7 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu
        zend_uchar c;
        size_t string_len;
 
-       if (offset < 0) {
+       if (offset < (zend_long)(-Z_STRLEN_P(str))) {
                /* Error on negative offset */
                zend_error(E_WARNING, "Illegal string offset:  " ZEND_LONG_FMT, offset);
                zend_string_release(Z_STR_P(str));
@@ -1204,6 +1204,10 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu
                return;
        }
 
+       if (offset < 0) { /* Handle negative offset */
+               offset += (zend_long)Z_STRLEN_P(str);
+       }
+
        old_str = Z_STR_P(str);
        if ((size_t)offset >= Z_STRLEN_P(str)) {
                /* Extend string if needed */
@@ -1849,7 +1853,7 @@ try_string_offset:
                        offset = Z_LVAL_P(dim);
                }
 
-               if (UNEXPECTED(offset < 0) || UNEXPECTED(Z_STRLEN_P(container) <= (size_t)offset)) {
+               if (UNEXPECTED(Z_STRLEN_P(container) < (size_t)((offset < 0) ? -offset : (offset + 1)))) {
                        if (type != BP_VAR_IS) {
                                zend_error(E_NOTICE, "Uninitialized string offset: %pd", offset);
                                ZVAL_EMPTY_STRING(result);
@@ -1857,12 +1861,17 @@ try_string_offset:
                                ZVAL_NULL(result);
                        }
                } else {
-                       zend_uchar c = (zend_uchar)Z_STRVAL_P(container)[offset];
+                       zend_uchar c;
+                       zend_long real_offset;
+
+                       real_offset = (UNEXPECTED(offset < 0)) /* Handle negative offset */
+                               ? (zend_long)Z_STRLEN_P(container) + offset : offset;
+                       c = (zend_uchar)Z_STRVAL_P(container)[real_offset];
 
                        if (CG(one_char_string)[c]) {
                                ZVAL_INTERNED_STR(result, CG(one_char_string)[c]);
                        } else {
-                               ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(container) + offset, 1, 0));
+                               ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(container) + real_offset, 1, 0));
                        }
                }
        } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
index de67318bed16c23d9ef7d4300dedb94ed9b15f99..627c2c188666630cb9fce4dd9baa96fa28765fae 100644 (file)
@@ -6742,6 +6742,9 @@ ZEND_VM_C_LABEL(num_index_prop):
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 ZEND_VM_C_LABEL(isset_str_offset):
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
index 40d75a7d2704c09eeec0ccadc0867d09865a902f..dea44311af71fdf06d603c916fa199c023c513d1 100644 (file)
@@ -6671,6 +6671,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -10200,6 +10203,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -11952,6 +11958,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -28296,6 +28305,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -31467,6 +31479,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -33711,6 +33726,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -39195,6 +39213,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -45424,6 +45445,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -48849,6 +48873,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -50895,6 +50922,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -52987,6 +53017,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;
@@ -54126,6 +54159,9 @@ num_index_prop:
                if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
                        lval = Z_LVAL_P(offset);
 isset_str_offset:
+                       if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+                               lval += (zend_long)Z_STRLEN_P(container);
+                       }
                        if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
                                if (opline->extended_value & ZEND_ISSET) {
                                        result = 1;