@@ -1635,7 +1635,10 @@ static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_inf
1635
1635
}
1636
1636
}
1637
1637
1638
- static zend_never_inline zend_long zend_check_string_offset (zval * dim , int type EXECUTE_DATA_DC )
1638
+ /* Compared to the behaviour of array offsets, isset()/empty() did not throw
1639
+ * TypeErrors for invalid offsets, or warn on type coercions.
1640
+ * The coalesce operator did throw on invalid offset types but not for type coercions so we need to be aware of it. */
1641
+ static zend_never_inline zend_long zend_check_string_offset (zval * dim , int type , bool * is_type_valid , bool is_coalesce EXECUTE_DATA_DC )
1639
1642
{
1640
1643
zend_long offset ;
1641
1644
@@ -1646,15 +1649,17 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
1646
1649
case IS_STRING :
1647
1650
{
1648
1651
bool trailing_data = false;
1649
- /* For BC reasons we allow errors so that we can warn on leading numeric string */
1652
+ /* For BC reasons we allow errors so that we can warn on leading numeric string,
1653
+ * however, empty() and isset() never allowed errors */
1654
+ bool allow_errors = (type != BP_VAR_IS ) || is_coalesce ;
1650
1655
if (IS_LONG == is_numeric_string_ex (Z_STRVAL_P (dim ), Z_STRLEN_P (dim ), & offset , NULL ,
1651
- /* allow errors */ true , NULL , & trailing_data )) {
1656
+ allow_errors , NULL , & trailing_data )) {
1652
1657
if (UNEXPECTED (trailing_data ) && type != BP_VAR_UNSET ) {
1653
1658
zend_error (E_WARNING , "Illegal string offset \"%s\"" , Z_STRVAL_P (dim ));
1654
1659
}
1655
1660
return offset ;
1656
1661
}
1657
- zend_illegal_string_offset ( dim , type ) ;
1662
+ goto invalid_type ;
1658
1663
return 0 ;
1659
1664
}
1660
1665
case IS_UNDEF :
@@ -1664,13 +1669,25 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
1664
1669
case IS_NULL :
1665
1670
case IS_FALSE :
1666
1671
case IS_TRUE :
1667
- zend_error (E_WARNING , "String offset cast occurred" );
1672
+ if (type != BP_VAR_IS ) {
1673
+ zend_error (E_WARNING , "String offset cast occurred" );
1674
+ }
1668
1675
break ;
1669
1676
case IS_REFERENCE :
1670
1677
dim = Z_REFVAL_P (dim );
1671
1678
goto try_again ;
1672
1679
default :
1673
- zend_illegal_string_offset (dim , type );
1680
+ invalid_type :
1681
+ if (is_type_valid ) {
1682
+ * is_type_valid = false;
1683
+ }
1684
+ if (type != BP_VAR_IS ) {
1685
+ zend_illegal_string_offset (dim , type );
1686
+ } else if (is_coalesce ) {
1687
+ /* is_coalesce used to have the same logic as BP_VAR_R */
1688
+ zend_illegal_string_offset (dim , BP_VAR_R );
1689
+ }
1690
+ /* BP_VAR_IS emits no warning and throws noe exception */
1674
1691
return 0 ;
1675
1692
}
1676
1693
@@ -1759,19 +1776,22 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
1759
1776
if (EXPECTED (Z_TYPE_P (dim ) == IS_LONG )) {
1760
1777
offset = Z_LVAL_P (dim );
1761
1778
} else {
1779
+ bool is_type_valid = true;
1762
1780
/* The string may be destroyed while throwing the notice.
1763
1781
* Temporarily increase the refcount to detect this situation. */
1764
- GC_ADDREF (s );
1765
- offset = zend_check_string_offset (dim , BP_VAR_W EXECUTE_DATA_CC );
1766
- if (UNEXPECTED (GC_DELREF (s ) == 0 )) {
1782
+ if (!(GC_FLAGS (s ) & IS_STR_INTERNED )) {
1783
+ GC_ADDREF (s );
1784
+ }
1785
+ offset = zend_check_string_offset (dim , BP_VAR_W , & is_type_valid , /* is_coalesce */ false EXECUTE_DATA_CC );
1786
+ if (!(GC_FLAGS (s ) & IS_STR_INTERNED ) && UNEXPECTED (GC_DELREF (s ) == 0 )) {
1767
1787
zend_string_efree (s );
1768
1788
if (UNEXPECTED (RETURN_VALUE_USED (opline ))) {
1769
1789
ZVAL_NULL (EX_VAR (opline -> result .var ));
1770
1790
}
1771
1791
return ;
1772
1792
}
1773
1793
/* Illegal offset assignment */
1774
- if (UNEXPECTED (EG (exception ) != NULL )) {
1794
+ if (UNEXPECTED (! is_type_valid || EG (exception ) != NULL )) {
1775
1795
if (UNEXPECTED (RETURN_VALUE_USED (opline ))) {
1776
1796
ZVAL_UNDEF (EX_VAR (opline -> result .var ));
1777
1797
}
@@ -2322,7 +2342,7 @@ static ZEND_COLD void zend_binary_assign_op_dim_slow(zval *container, zval *dim
2322
2342
if (opline -> op2_type == IS_UNUSED ) {
2323
2343
zend_use_new_element_for_string ();
2324
2344
} else {
2325
- zend_check_string_offset (dim , BP_VAR_RW EXECUTE_DATA_CC );
2345
+ zend_check_string_offset (dim , BP_VAR_RW , /* is_type_valid */ NULL , /* is_coalesce */ false EXECUTE_DATA_CC );
2326
2346
zend_wrong_string_offset_error ();
2327
2347
}
2328
2348
} else {
@@ -2624,7 +2644,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
2624
2644
if (dim == NULL ) {
2625
2645
zend_use_new_element_for_string ();
2626
2646
} else {
2627
- zend_check_string_offset (dim , type EXECUTE_DATA_CC );
2647
+ zend_check_string_offset (dim , type , /* is_type_valid */ NULL , /* is_coalesce */ false EXECUTE_DATA_CC );
2628
2648
zend_wrong_string_offset_error ();
2629
2649
}
2630
2650
ZVAL_UNDEF (result );
@@ -2747,73 +2767,27 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
2747
2767
zend_string * str = Z_STR_P (container );
2748
2768
zend_long offset ;
2749
2769
2750
- try_string_offset :
2751
- if (UNEXPECTED (Z_TYPE_P (dim ) != IS_LONG )) {
2752
- switch (Z_TYPE_P (dim )) {
2753
- case IS_STRING :
2754
- {
2755
- bool trailing_data = false;
2756
- /* For BC reasons we allow errors so that we can warn on leading numeric string */
2757
- if (IS_LONG == is_numeric_string_ex (Z_STRVAL_P (dim ), Z_STRLEN_P (dim ), & offset ,
2758
- NULL , /* allow errors */ true, NULL , & trailing_data )) {
2759
- if (UNEXPECTED (trailing_data )) {
2760
- zend_error (E_WARNING , "Illegal string offset \"%s\"" , Z_STRVAL_P (dim ));
2761
- }
2762
- goto out ;
2763
- }
2764
- if (type == BP_VAR_IS ) {
2765
- ZVAL_NULL (result );
2766
- return ;
2767
- }
2768
- zend_illegal_string_offset (dim , BP_VAR_R );
2769
- ZVAL_NULL (result );
2770
- return ;
2771
- }
2772
- case IS_UNDEF :
2773
- /* The string may be destroyed while throwing the notice.
2774
- * Temporarily increase the refcount to detect this situation. */
2775
- if (!(GC_FLAGS (str ) & IS_STR_INTERNED )) {
2776
- GC_ADDREF (str );
2777
- }
2778
- ZVAL_UNDEFINED_OP2 ();
2779
- if (!(GC_FLAGS (str ) & IS_STR_INTERNED ) && UNEXPECTED (GC_DELREF (str ) == 0 )) {
2780
- zend_string_efree (str );
2781
- ZVAL_NULL (result );
2782
- return ;
2783
- }
2784
- ZEND_FALLTHROUGH ;
2785
- case IS_DOUBLE :
2786
- case IS_NULL :
2787
- case IS_FALSE :
2788
- case IS_TRUE :
2789
- if (type != BP_VAR_IS ) {
2790
- /* The string may be destroyed while throwing the notice.
2791
- * Temporarily increase the refcount to detect this situation. */
2792
- if (!(GC_FLAGS (str ) & IS_STR_INTERNED )) {
2793
- GC_ADDREF (str );
2794
- }
2795
- zend_error (E_WARNING , "String offset cast occurred" );
2796
- if (!(GC_FLAGS (str ) & IS_STR_INTERNED ) && UNEXPECTED (GC_DELREF (str ) == 0 )) {
2797
- zend_string_efree (str );
2798
- ZVAL_NULL (result );
2799
- return ;
2800
- }
2801
- }
2802
- break ;
2803
- case IS_REFERENCE :
2804
- dim = Z_REFVAL_P (dim );
2805
- goto try_string_offset ;
2806
- default :
2807
- zend_illegal_string_offset (dim , BP_VAR_R );
2808
- ZVAL_NULL (result );
2809
- return ;
2810
- }
2811
-
2812
- offset = zval_get_long_func (dim , /* is_strict */ false);
2813
- } else {
2770
+ if (EXPECTED (Z_TYPE_P (dim ) == IS_LONG )) {
2814
2771
offset = Z_LVAL_P (dim );
2772
+ } else {
2773
+ bool is_type_valid = true;
2774
+ /* The string may be destroyed while throwing the notice.
2775
+ * Temporarily increase the refcount to detect this situation. */
2776
+ if (!(GC_FLAGS (str ) & IS_STR_INTERNED )) {
2777
+ GC_ADDREF (str );
2778
+ }
2779
+ offset = zend_check_string_offset (dim , type , & is_type_valid , /* is_coalesce */ true EXECUTE_DATA_CC );
2780
+ if (!(GC_FLAGS (str ) & IS_STR_INTERNED ) && UNEXPECTED (GC_DELREF (str ) == 0 )) {
2781
+ zend_string_efree (str );
2782
+ ZVAL_NULL (result );
2783
+ return ;
2784
+ }
2785
+ /* Illegal offset assignment */
2786
+ if (UNEXPECTED (!is_type_valid || EG (exception ) != NULL )) {
2787
+ ZVAL_NULL (result );
2788
+ return ;
2789
+ }
2815
2790
}
2816
- out :
2817
2791
2818
2792
if (UNEXPECTED (ZSTR_LEN (str ) < ((offset < 0 ) ? - (size_t )offset : ((size_t )offset + 1 )))) {
2819
2793
if (type != BP_VAR_IS ) {
@@ -2954,16 +2928,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isset_dim_slow(zval *container,
2954
2928
return 0 ;
2955
2929
}
2956
2930
} else {
2957
- /*if (OP2_TYPE & (IS_CV|IS_VAR)) {*/
2958
- ZVAL_DEREF (offset );
2959
- /*}*/
2960
- if (Z_TYPE_P (offset ) < IS_STRING /* simple scalar types */
2961
- || (Z_TYPE_P (offset ) == IS_STRING /* or numeric string */
2962
- && IS_LONG == is_numeric_string (Z_STRVAL_P (offset ), Z_STRLEN_P (offset ), NULL , NULL , 0 ))) {
2963
- lval = zval_get_long_ex (offset , /* is_strict */ true);
2964
- goto str_offset ;
2931
+ bool is_type_valid = true;
2932
+ lval = zend_check_string_offset (offset , BP_VAR_IS , & is_type_valid , /* is_coalesce */ false EXECUTE_DATA_CC );
2933
+ if (!is_type_valid || UNEXPECTED (EG (exception ) != NULL )) {
2934
+ return false;
2965
2935
}
2966
- return 0 ;
2936
+ goto str_offset ;
2967
2937
}
2968
2938
} else {
2969
2939
return 0 ;
@@ -2993,16 +2963,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isempty_dim_slow(zval *containe
2993
2963
return 1 ;
2994
2964
}
2995
2965
} else {
2996
- /*if (OP2_TYPE & (IS_CV|IS_VAR)) {*/
2997
- ZVAL_DEREF (offset );
2998
- /*}*/
2999
- if (Z_TYPE_P (offset ) < IS_STRING /* simple scalar types */
3000
- || (Z_TYPE_P (offset ) == IS_STRING /* or numeric string */
3001
- && IS_LONG == is_numeric_string (Z_STRVAL_P (offset ), Z_STRLEN_P (offset ), NULL , NULL , 0 ))) {
3002
- lval = zval_get_long_ex (offset , /* is_strict */ true);
3003
- goto str_offset ;
2966
+ bool is_type_valid = true;
2967
+ lval = zend_check_string_offset (offset , BP_VAR_IS , & is_type_valid , /* is_coalesce */ false EXECUTE_DATA_CC );
2968
+ if (!is_type_valid || UNEXPECTED (EG (exception ) != NULL )) {
2969
+ return true;
3004
2970
}
3005
- return 1 ;
2971
+ goto str_offset ;
3006
2972
}
3007
2973
} else {
3008
2974
return 1 ;
0 commit comments