@@ -1635,9 +1635,14 @@ 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 ;
1644
+ /* isset()/empty() are more strict in what they allow/warn */
1645
+ bool allow_errors = (type != BP_VAR_IS ) || is_coalesce ;
1641
1646
1642
1647
try_again :
1643
1648
switch (Z_TYPE_P (dim )) {
@@ -1646,15 +1651,22 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
1646
1651
case IS_STRING :
1647
1652
{
1648
1653
bool trailing_data = false;
1649
- /* For BC reasons we allow errors so that we can warn on leading numeric string */
1654
+ /* For BC reasons we allow errors so that we can warn on leading numeric string,
1655
+ * however, empty() and isset() never allowed errors */
1650
1656
if (IS_LONG == is_numeric_string_ex (Z_STRVAL_P (dim ), Z_STRLEN_P (dim ), & offset , NULL ,
1651
- /* allow errors */ true , NULL , & trailing_data )) {
1657
+ allow_errors , NULL , & trailing_data )) {
1652
1658
if (UNEXPECTED (trailing_data ) && type != BP_VAR_UNSET ) {
1653
1659
zend_error (E_WARNING , "Illegal string offset \"%s\"" , Z_STRVAL_P (dim ));
1654
1660
}
1655
1661
return offset ;
1656
1662
}
1657
- zend_illegal_string_offset (dim , type );
1663
+ if (is_type_valid ) {
1664
+ * is_type_valid = false;
1665
+ }
1666
+ if (type != BP_VAR_IS ) {
1667
+ zend_illegal_string_offset (dim , type );
1668
+ }
1669
+ /* BP_VAR_IS emits no warning and throws no exception */
1658
1670
return 0 ;
1659
1671
}
1660
1672
case IS_UNDEF :
@@ -1664,17 +1676,29 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type
1664
1676
case IS_NULL :
1665
1677
case IS_FALSE :
1666
1678
case IS_TRUE :
1667
- zend_error (E_WARNING , "String offset cast occurred" );
1679
+ if (type != BP_VAR_IS ) {
1680
+ zend_error (E_WARNING , "String offset cast occurred" );
1681
+ }
1668
1682
break ;
1669
1683
case IS_REFERENCE :
1670
1684
dim = Z_REFVAL_P (dim );
1671
1685
goto try_again ;
1672
1686
default :
1673
- zend_illegal_string_offset (dim , type );
1687
+ if (is_type_valid ) {
1688
+ * is_type_valid = false;
1689
+ }
1690
+ if (type != BP_VAR_IS ) {
1691
+ zend_illegal_string_offset (dim , type );
1692
+ } else if (is_coalesce ) {
1693
+ /* is_coalesce used to have the same logic as BP_VAR_R */
1694
+ zend_illegal_string_offset (dim , BP_VAR_R );
1695
+ }
1696
+ /* isset()/empty() emits no warning and throws no exception */
1674
1697
return 0 ;
1675
1698
}
1676
1699
1677
- return zval_get_long_func (dim , /* is_strict */ false);
1700
+ /* Being strict here means that incompatible floats emit a deprecation notice */
1701
+ return zval_get_long_func (dim , /* is_strict */ !allow_errors );
1678
1702
}
1679
1703
1680
1704
ZEND_API ZEND_COLD void zend_wrong_string_offset_error (void )
@@ -1759,19 +1783,22 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
1759
1783
if (EXPECTED (Z_TYPE_P (dim ) == IS_LONG )) {
1760
1784
offset = Z_LVAL_P (dim );
1761
1785
} else {
1786
+ bool is_type_valid = true;
1762
1787
/* The string may be destroyed while throwing the notice.
1763
1788
* 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 )) {
1789
+ if (!(GC_FLAGS (s ) & IS_STR_INTERNED )) {
1790
+ GC_ADDREF (s );
1791
+ }
1792
+ offset = zend_check_string_offset (dim , BP_VAR_W , & is_type_valid , /* is_coalesce */ false EXECUTE_DATA_CC );
1793
+ if (!(GC_FLAGS (s ) & IS_STR_INTERNED ) && UNEXPECTED (GC_DELREF (s ) == 0 )) {
1767
1794
zend_string_efree (s );
1768
1795
if (UNEXPECTED (RETURN_VALUE_USED (opline ))) {
1769
1796
ZVAL_NULL (EX_VAR (opline -> result .var ));
1770
1797
}
1771
1798
return ;
1772
1799
}
1773
1800
/* Illegal offset assignment */
1774
- if (UNEXPECTED (EG (exception ) != NULL )) {
1801
+ if (UNEXPECTED (! is_type_valid || EG (exception ) != NULL )) {
1775
1802
if (UNEXPECTED (RETURN_VALUE_USED (opline ))) {
1776
1803
ZVAL_UNDEF (EX_VAR (opline -> result .var ));
1777
1804
}
@@ -2322,7 +2349,7 @@ static ZEND_COLD void zend_binary_assign_op_dim_slow(zval *container, zval *dim
2322
2349
if (opline -> op2_type == IS_UNUSED ) {
2323
2350
zend_use_new_element_for_string ();
2324
2351
} else {
2325
- zend_check_string_offset (dim , BP_VAR_RW EXECUTE_DATA_CC );
2352
+ zend_check_string_offset (dim , BP_VAR_RW , /* is_type_valid */ NULL , /* is_coalesce */ false EXECUTE_DATA_CC );
2326
2353
zend_wrong_string_offset_error ();
2327
2354
}
2328
2355
} else {
@@ -2624,7 +2651,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
2624
2651
if (dim == NULL ) {
2625
2652
zend_use_new_element_for_string ();
2626
2653
} else {
2627
- zend_check_string_offset (dim , type EXECUTE_DATA_CC );
2654
+ zend_check_string_offset (dim , type , /* is_type_valid */ NULL , /* is_coalesce */ false EXECUTE_DATA_CC );
2628
2655
zend_wrong_string_offset_error ();
2629
2656
}
2630
2657
ZVAL_UNDEF (result );
@@ -2747,73 +2774,27 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
2747
2774
zend_string * str = Z_STR_P (container );
2748
2775
zend_long offset ;
2749
2776
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 {
2777
+ if (EXPECTED (Z_TYPE_P (dim ) == IS_LONG )) {
2814
2778
offset = Z_LVAL_P (dim );
2779
+ } else {
2780
+ bool is_type_valid = true;
2781
+ /* The string may be destroyed while throwing the notice.
2782
+ * Temporarily increase the refcount to detect this situation. */
2783
+ if (!(GC_FLAGS (str ) & IS_STR_INTERNED )) {
2784
+ GC_ADDREF (str );
2785
+ }
2786
+ offset = zend_check_string_offset (dim , type , & is_type_valid , /* is_coalesce */ true EXECUTE_DATA_CC );
2787
+ if (!(GC_FLAGS (str ) & IS_STR_INTERNED ) && UNEXPECTED (GC_DELREF (str ) == 0 )) {
2788
+ zend_string_efree (str );
2789
+ ZVAL_NULL (result );
2790
+ return ;
2791
+ }
2792
+ /* Illegal offset assignment */
2793
+ if (UNEXPECTED (!is_type_valid || EG (exception ) != NULL )) {
2794
+ ZVAL_NULL (result );
2795
+ return ;
2796
+ }
2815
2797
}
2816
- out :
2817
2798
2818
2799
if (UNEXPECTED (ZSTR_LEN (str ) < ((offset < 0 ) ? - (size_t )offset : ((size_t )offset + 1 )))) {
2819
2800
if (type != BP_VAR_IS ) {
@@ -2954,16 +2935,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isset_dim_slow(zval *container,
2954
2935
return 0 ;
2955
2936
}
2956
2937
} 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 ;
2938
+ bool is_type_valid = true;
2939
+ lval = zend_check_string_offset (offset , BP_VAR_IS , & is_type_valid , /* is_coalesce */ false EXECUTE_DATA_CC );
2940
+ if (!is_type_valid || UNEXPECTED (EG (exception ) != NULL )) {
2941
+ return false;
2965
2942
}
2966
- return 0 ;
2943
+ goto str_offset ;
2967
2944
}
2968
2945
} else {
2969
2946
return 0 ;
@@ -2993,16 +2970,12 @@ static zend_never_inline bool ZEND_FASTCALL zend_isempty_dim_slow(zval *containe
2993
2970
return 1 ;
2994
2971
}
2995
2972
} 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 ;
2973
+ bool is_type_valid = true;
2974
+ lval = zend_check_string_offset (offset , BP_VAR_IS , & is_type_valid , /* is_coalesce */ false EXECUTE_DATA_CC );
2975
+ if (!is_type_valid || UNEXPECTED (EG (exception ) != NULL )) {
2976
+ return true;
3004
2977
}
3005
- return 1 ;
2978
+ goto str_offset ;
3006
2979
}
3007
2980
} else {
3008
2981
return 1 ;
0 commit comments