Skip to content

Commit 7f22153

Browse files
authored
fix(globals): correctly fetch $_REQUEST super global (#334)
* fix(globals): correctly fetch `$_REQUEST` super global Refs: #331 * fix(globals): fix request global fetching for php 8.0 Refs: #331 * refactor(globals): use symbol table from `ExecutorGlobals` Refs: #331 * chore(bindings): update doc.rs bindings Refs: #331 * refactor(globals): use `cfg_if` for php versions Refs: #331 * test(globals): add integration tests Refs: #331
1 parent 08501f7 commit 7f22153

File tree

6 files changed

+236
-51
lines changed

6 files changed

+236
-51
lines changed

allowed_bindings.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ bind! {
3939
_zend_new_array,
4040
_zval_struct__bindgen_ty_1,
4141
_zval_struct__bindgen_ty_2,
42+
_zend_known_string_id,
4243
// ext_php_rs_executor_globals,
4344
// ext_php_rs_php_build_id,
4445
// ext_php_rs_zend_object_alloc,
@@ -84,6 +85,8 @@ bind! {
8485
zend_execute_data,
8586
zend_function_entry,
8687
zend_hash_clean,
88+
zend_hash_find_known_hash,
89+
_zend_hash_find_known_hash,
8790
zend_hash_index_del,
8891
zend_hash_index_find,
8992
zend_hash_index_update,
@@ -95,6 +98,7 @@ bind! {
9598
zend_is_callable,
9699
zend_is_identical,
97100
zend_is_iterable,
101+
zend_known_strings,
98102
zend_long,
99103
zend_lookup_class_ex,
100104
zend_module_entry,

docsrs_bindings.rs

Lines changed: 138 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ where
8282
}
8383
pub const ZEND_DEBUG: u32 = 1;
8484
pub const _ZEND_TYPE_NAME_BIT: u32 = 16777216;
85+
pub const _ZEND_TYPE_LITERAL_NAME_BIT: u32 = 8388608;
8586
pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2;
8687
pub const HT_MIN_SIZE: u32 = 8;
8788
pub const IS_UNDEF: u32 = 0;
@@ -269,7 +270,7 @@ pub struct _IO_FILE {
269270
pub _wide_data: *mut _IO_wide_data,
270271
pub _freeres_list: *mut _IO_FILE,
271272
pub _freeres_buf: *mut ::std::os::raw::c_void,
272-
pub __pad5: usize,
273+
pub _prevchain: *mut *mut _IO_FILE,
273274
pub _mode: ::std::os::raw::c_int,
274275
pub _unused2: [::std::os::raw::c_char; 20usize],
275276
}
@@ -537,6 +538,83 @@ pub type zend_string_init_interned_func_t = ::std::option::Option<
537538
extern "C" {
538539
pub static mut zend_string_init_interned: zend_string_init_interned_func_t;
539540
}
541+
extern "C" {
542+
pub static mut zend_known_strings: *mut *mut zend_string;
543+
}
544+
pub const _zend_known_string_id_ZEND_STR_FILE: _zend_known_string_id = 0;
545+
pub const _zend_known_string_id_ZEND_STR_LINE: _zend_known_string_id = 1;
546+
pub const _zend_known_string_id_ZEND_STR_FUNCTION: _zend_known_string_id = 2;
547+
pub const _zend_known_string_id_ZEND_STR_CLASS: _zend_known_string_id = 3;
548+
pub const _zend_known_string_id_ZEND_STR_OBJECT: _zend_known_string_id = 4;
549+
pub const _zend_known_string_id_ZEND_STR_TYPE: _zend_known_string_id = 5;
550+
pub const _zend_known_string_id_ZEND_STR_OBJECT_OPERATOR: _zend_known_string_id = 6;
551+
pub const _zend_known_string_id_ZEND_STR_PAAMAYIM_NEKUDOTAYIM: _zend_known_string_id = 7;
552+
pub const _zend_known_string_id_ZEND_STR_ARGS: _zend_known_string_id = 8;
553+
pub const _zend_known_string_id_ZEND_STR_UNKNOWN: _zend_known_string_id = 9;
554+
pub const _zend_known_string_id_ZEND_STR_UNKNOWN_CAPITALIZED: _zend_known_string_id = 10;
555+
pub const _zend_known_string_id_ZEND_STR_EVAL: _zend_known_string_id = 11;
556+
pub const _zend_known_string_id_ZEND_STR_INCLUDE: _zend_known_string_id = 12;
557+
pub const _zend_known_string_id_ZEND_STR_REQUIRE: _zend_known_string_id = 13;
558+
pub const _zend_known_string_id_ZEND_STR_INCLUDE_ONCE: _zend_known_string_id = 14;
559+
pub const _zend_known_string_id_ZEND_STR_REQUIRE_ONCE: _zend_known_string_id = 15;
560+
pub const _zend_known_string_id_ZEND_STR_SCALAR: _zend_known_string_id = 16;
561+
pub const _zend_known_string_id_ZEND_STR_ERROR_REPORTING: _zend_known_string_id = 17;
562+
pub const _zend_known_string_id_ZEND_STR_STATIC: _zend_known_string_id = 18;
563+
pub const _zend_known_string_id_ZEND_STR_THIS: _zend_known_string_id = 19;
564+
pub const _zend_known_string_id_ZEND_STR_VALUE: _zend_known_string_id = 20;
565+
pub const _zend_known_string_id_ZEND_STR_KEY: _zend_known_string_id = 21;
566+
pub const _zend_known_string_id_ZEND_STR_MAGIC_INVOKE: _zend_known_string_id = 22;
567+
pub const _zend_known_string_id_ZEND_STR_PREVIOUS: _zend_known_string_id = 23;
568+
pub const _zend_known_string_id_ZEND_STR_CODE: _zend_known_string_id = 24;
569+
pub const _zend_known_string_id_ZEND_STR_MESSAGE: _zend_known_string_id = 25;
570+
pub const _zend_known_string_id_ZEND_STR_SEVERITY: _zend_known_string_id = 26;
571+
pub const _zend_known_string_id_ZEND_STR_STRING: _zend_known_string_id = 27;
572+
pub const _zend_known_string_id_ZEND_STR_TRACE: _zend_known_string_id = 28;
573+
pub const _zend_known_string_id_ZEND_STR_SCHEME: _zend_known_string_id = 29;
574+
pub const _zend_known_string_id_ZEND_STR_HOST: _zend_known_string_id = 30;
575+
pub const _zend_known_string_id_ZEND_STR_PORT: _zend_known_string_id = 31;
576+
pub const _zend_known_string_id_ZEND_STR_USER: _zend_known_string_id = 32;
577+
pub const _zend_known_string_id_ZEND_STR_PASS: _zend_known_string_id = 33;
578+
pub const _zend_known_string_id_ZEND_STR_PATH: _zend_known_string_id = 34;
579+
pub const _zend_known_string_id_ZEND_STR_QUERY: _zend_known_string_id = 35;
580+
pub const _zend_known_string_id_ZEND_STR_FRAGMENT: _zend_known_string_id = 36;
581+
pub const _zend_known_string_id_ZEND_STR_NULL: _zend_known_string_id = 37;
582+
pub const _zend_known_string_id_ZEND_STR_BOOLEAN: _zend_known_string_id = 38;
583+
pub const _zend_known_string_id_ZEND_STR_INTEGER: _zend_known_string_id = 39;
584+
pub const _zend_known_string_id_ZEND_STR_DOUBLE: _zend_known_string_id = 40;
585+
pub const _zend_known_string_id_ZEND_STR_ARRAY: _zend_known_string_id = 41;
586+
pub const _zend_known_string_id_ZEND_STR_RESOURCE: _zend_known_string_id = 42;
587+
pub const _zend_known_string_id_ZEND_STR_CLOSED_RESOURCE: _zend_known_string_id = 43;
588+
pub const _zend_known_string_id_ZEND_STR_NAME: _zend_known_string_id = 44;
589+
pub const _zend_known_string_id_ZEND_STR_ARGV: _zend_known_string_id = 45;
590+
pub const _zend_known_string_id_ZEND_STR_ARGC: _zend_known_string_id = 46;
591+
pub const _zend_known_string_id_ZEND_STR_ARRAY_CAPITALIZED: _zend_known_string_id = 47;
592+
pub const _zend_known_string_id_ZEND_STR_BOOL: _zend_known_string_id = 48;
593+
pub const _zend_known_string_id_ZEND_STR_INT: _zend_known_string_id = 49;
594+
pub const _zend_known_string_id_ZEND_STR_FLOAT: _zend_known_string_id = 50;
595+
pub const _zend_known_string_id_ZEND_STR_CALLABLE: _zend_known_string_id = 51;
596+
pub const _zend_known_string_id_ZEND_STR_ITERABLE: _zend_known_string_id = 52;
597+
pub const _zend_known_string_id_ZEND_STR_VOID: _zend_known_string_id = 53;
598+
pub const _zend_known_string_id_ZEND_STR_NEVER: _zend_known_string_id = 54;
599+
pub const _zend_known_string_id_ZEND_STR_FALSE: _zend_known_string_id = 55;
600+
pub const _zend_known_string_id_ZEND_STR_TRUE: _zend_known_string_id = 56;
601+
pub const _zend_known_string_id_ZEND_STR_NULL_LOWERCASE: _zend_known_string_id = 57;
602+
pub const _zend_known_string_id_ZEND_STR_MIXED: _zend_known_string_id = 58;
603+
pub const _zend_known_string_id_ZEND_STR_TRAVERSABLE: _zend_known_string_id = 59;
604+
pub const _zend_known_string_id_ZEND_STR_SLEEP: _zend_known_string_id = 60;
605+
pub const _zend_known_string_id_ZEND_STR_WAKEUP: _zend_known_string_id = 61;
606+
pub const _zend_known_string_id_ZEND_STR_CASES: _zend_known_string_id = 62;
607+
pub const _zend_known_string_id_ZEND_STR_FROM: _zend_known_string_id = 63;
608+
pub const _zend_known_string_id_ZEND_STR_TRYFROM: _zend_known_string_id = 64;
609+
pub const _zend_known_string_id_ZEND_STR_TRYFROM_LOWERCASE: _zend_known_string_id = 65;
610+
pub const _zend_known_string_id_ZEND_STR_AUTOGLOBAL_SERVER: _zend_known_string_id = 66;
611+
pub const _zend_known_string_id_ZEND_STR_AUTOGLOBAL_ENV: _zend_known_string_id = 67;
612+
pub const _zend_known_string_id_ZEND_STR_AUTOGLOBAL_REQUEST: _zend_known_string_id = 68;
613+
pub const _zend_known_string_id_ZEND_STR_COUNT: _zend_known_string_id = 69;
614+
pub const _zend_known_string_id_ZEND_STR_SENSITIVEPARAMETER: _zend_known_string_id = 70;
615+
pub const _zend_known_string_id_ZEND_STR_CONST_EXPR_PLACEHOLDER: _zend_known_string_id = 71;
616+
pub const _zend_known_string_id_ZEND_STR_LAST_KNOWN: _zend_known_string_id = 72;
617+
pub type _zend_known_string_id = ::std::os::raw::c_uint;
540618
extern "C" {
541619
pub fn zend_hash_clean(ht: *mut HashTable);
542620
}
@@ -575,6 +653,9 @@ extern "C" {
575653
extern "C" {
576654
pub fn zend_hash_index_find(ht: *const HashTable, h: zend_ulong) -> *mut zval;
577655
}
656+
extern "C" {
657+
pub fn zend_hash_find_known_hash(ht: *const HashTable, key: *const zend_string) -> *mut zval;
658+
}
578659
extern "C" {
579660
pub fn zend_hash_move_forward_ex(ht: *mut HashTable, pos: *mut HashPosition) -> zend_result;
580661
}
@@ -1992,7 +2073,7 @@ pub struct _php_stream {
19922073
pub wrapperthis: *mut ::std::os::raw::c_void,
19932074
pub wrapperdata: zval,
19942075
pub _bitfield_align_1: [u8; 0],
1995-
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>,
2076+
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 2usize]>,
19962077
pub mode: [::std::os::raw::c_char; 16usize],
19972078
pub flags: u32,
19982079
pub res: *mut zend_resource,
@@ -2011,105 +2092,122 @@ pub struct _php_stream {
20112092
}
20122093
impl _php_stream {
20132094
#[inline]
2014-
pub fn is_persistent(&self) -> u8 {
2015-
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u8) }
2095+
pub fn is_persistent(&self) -> u16 {
2096+
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u16) }
20162097
}
20172098
#[inline]
2018-
pub fn set_is_persistent(&mut self, val: u8) {
2099+
pub fn set_is_persistent(&mut self, val: u16) {
20192100
unsafe {
2020-
let val: u8 = ::std::mem::transmute(val);
2101+
let val: u16 = ::std::mem::transmute(val);
20212102
self._bitfield_1.set(0usize, 1u8, val as u64)
20222103
}
20232104
}
20242105
#[inline]
2025-
pub fn in_free(&self) -> u8 {
2026-
unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 2u8) as u8) }
2106+
pub fn in_free(&self) -> u16 {
2107+
unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 2u8) as u16) }
20272108
}
20282109
#[inline]
2029-
pub fn set_in_free(&mut self, val: u8) {
2110+
pub fn set_in_free(&mut self, val: u16) {
20302111
unsafe {
2031-
let val: u8 = ::std::mem::transmute(val);
2112+
let val: u16 = ::std::mem::transmute(val);
20322113
self._bitfield_1.set(1usize, 2u8, val as u64)
20332114
}
20342115
}
20352116
#[inline]
2036-
pub fn eof(&self) -> u8 {
2037-
unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u8) }
2117+
pub fn eof(&self) -> u16 {
2118+
unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u16) }
20382119
}
20392120
#[inline]
2040-
pub fn set_eof(&mut self, val: u8) {
2121+
pub fn set_eof(&mut self, val: u16) {
20412122
unsafe {
2042-
let val: u8 = ::std::mem::transmute(val);
2123+
let val: u16 = ::std::mem::transmute(val);
20432124
self._bitfield_1.set(3usize, 1u8, val as u64)
20442125
}
20452126
}
20462127
#[inline]
2047-
pub fn __exposed(&self) -> u8 {
2048-
unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u8) }
2128+
pub fn __exposed(&self) -> u16 {
2129+
unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u16) }
20492130
}
20502131
#[inline]
2051-
pub fn set___exposed(&mut self, val: u8) {
2132+
pub fn set___exposed(&mut self, val: u16) {
20522133
unsafe {
2053-
let val: u8 = ::std::mem::transmute(val);
2134+
let val: u16 = ::std::mem::transmute(val);
20542135
self._bitfield_1.set(4usize, 1u8, val as u64)
20552136
}
20562137
}
20572138
#[inline]
2058-
pub fn fclose_stdiocast(&self) -> u8 {
2059-
unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 2u8) as u8) }
2139+
pub fn fclose_stdiocast(&self) -> u16 {
2140+
unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 2u8) as u16) }
20602141
}
20612142
#[inline]
2062-
pub fn set_fclose_stdiocast(&mut self, val: u8) {
2143+
pub fn set_fclose_stdiocast(&mut self, val: u16) {
20632144
unsafe {
2064-
let val: u8 = ::std::mem::transmute(val);
2145+
let val: u16 = ::std::mem::transmute(val);
20652146
self._bitfield_1.set(5usize, 2u8, val as u64)
20662147
}
20672148
}
20682149
#[inline]
2069-
pub fn has_buffered_data(&self) -> u8 {
2070-
unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u8) }
2150+
pub fn has_buffered_data(&self) -> u16 {
2151+
unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u16) }
20712152
}
20722153
#[inline]
2073-
pub fn set_has_buffered_data(&mut self, val: u8) {
2154+
pub fn set_has_buffered_data(&mut self, val: u16) {
20742155
unsafe {
2075-
let val: u8 = ::std::mem::transmute(val);
2156+
let val: u16 = ::std::mem::transmute(val);
20762157
self._bitfield_1.set(7usize, 1u8, val as u64)
20772158
}
20782159
}
20792160
#[inline]
2161+
pub fn fclose_stdiocast_flush_in_progress(&self) -> u16 {
2162+
unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u16) }
2163+
}
2164+
#[inline]
2165+
pub fn set_fclose_stdiocast_flush_in_progress(&mut self, val: u16) {
2166+
unsafe {
2167+
let val: u16 = ::std::mem::transmute(val);
2168+
self._bitfield_1.set(8usize, 1u8, val as u64)
2169+
}
2170+
}
2171+
#[inline]
20802172
pub fn new_bitfield_1(
2081-
is_persistent: u8,
2082-
in_free: u8,
2083-
eof: u8,
2084-
__exposed: u8,
2085-
fclose_stdiocast: u8,
2086-
has_buffered_data: u8,
2087-
) -> __BindgenBitfieldUnit<[u8; 1usize]> {
2088-
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize]> = Default::default();
2173+
is_persistent: u16,
2174+
in_free: u16,
2175+
eof: u16,
2176+
__exposed: u16,
2177+
fclose_stdiocast: u16,
2178+
has_buffered_data: u16,
2179+
fclose_stdiocast_flush_in_progress: u16,
2180+
) -> __BindgenBitfieldUnit<[u8; 2usize]> {
2181+
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 2usize]> = Default::default();
20892182
__bindgen_bitfield_unit.set(0usize, 1u8, {
2090-
let is_persistent: u8 = unsafe { ::std::mem::transmute(is_persistent) };
2183+
let is_persistent: u16 = unsafe { ::std::mem::transmute(is_persistent) };
20912184
is_persistent as u64
20922185
});
20932186
__bindgen_bitfield_unit.set(1usize, 2u8, {
2094-
let in_free: u8 = unsafe { ::std::mem::transmute(in_free) };
2187+
let in_free: u16 = unsafe { ::std::mem::transmute(in_free) };
20952188
in_free as u64
20962189
});
20972190
__bindgen_bitfield_unit.set(3usize, 1u8, {
2098-
let eof: u8 = unsafe { ::std::mem::transmute(eof) };
2191+
let eof: u16 = unsafe { ::std::mem::transmute(eof) };
20992192
eof as u64
21002193
});
21012194
__bindgen_bitfield_unit.set(4usize, 1u8, {
2102-
let __exposed: u8 = unsafe { ::std::mem::transmute(__exposed) };
2195+
let __exposed: u16 = unsafe { ::std::mem::transmute(__exposed) };
21032196
__exposed as u64
21042197
});
21052198
__bindgen_bitfield_unit.set(5usize, 2u8, {
2106-
let fclose_stdiocast: u8 = unsafe { ::std::mem::transmute(fclose_stdiocast) };
2199+
let fclose_stdiocast: u16 = unsafe { ::std::mem::transmute(fclose_stdiocast) };
21072200
fclose_stdiocast as u64
21082201
});
21092202
__bindgen_bitfield_unit.set(7usize, 1u8, {
2110-
let has_buffered_data: u8 = unsafe { ::std::mem::transmute(has_buffered_data) };
2203+
let has_buffered_data: u16 = unsafe { ::std::mem::transmute(has_buffered_data) };
21112204
has_buffered_data as u64
21122205
});
2206+
__bindgen_bitfield_unit.set(8usize, 1u8, {
2207+
let fclose_stdiocast_flush_in_progress: u16 =
2208+
unsafe { ::std::mem::transmute(fclose_stdiocast_flush_in_progress) };
2209+
fclose_stdiocast_flush_in_progress as u64
2210+
});
21132211
__bindgen_bitfield_unit
21142212
}
21152213
}

src/zend/globals.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ use crate::ffi::{
2020
zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET,
2121
TRACK_VARS_POST, TRACK_VARS_SERVER,
2222
};
23+
#[cfg(not(php81))]
24+
use crate::ffi::{_zend_hash_find_known_hash, _zend_string};
25+
#[cfg(php81)]
26+
use crate::ffi::{
27+
_zend_known_string_id_ZEND_STR_AUTOGLOBAL_REQUEST, zend_hash_find_known_hash,
28+
zend_known_strings,
29+
};
2330

2431
use crate::types::{ZendHashTable, ZendObject, ZendStr};
2532

@@ -285,16 +292,39 @@ impl ProcessGlobals {
285292
/// Get the HTTP Request variables. Equivalent of $_REQUEST.
286293
///
287294
/// # Panics
288-
/// There is an outstanding issue with the implementation of this function.
289-
/// Until resolved, this function will always panic.
290-
///
291-
/// - <https://github.com/davidcole1340/ext-php-rs/issues/331>
292-
/// - <https://github.com/php/php-src/issues/16541>
293-
pub fn http_request_vars(&self) -> &ZendHashTable {
294-
todo!("$_REQUEST super global was erroneously fetched from http_globals which resulted in an out-of-bounds access. A new implementation is needed.");
295-
// self.http_globals[TRACK_VARS_REQUEST as usize]
296-
// .array()
297-
// .expect("Type is not a ZendArray")
295+
/// - If the request global is not found or fails to be populated.
296+
/// - If the request global is not a ZendArray.
297+
pub fn http_request_vars(&self) -> Option<&ZendHashTable> {
298+
cfg_if::cfg_if! {
299+
if #[cfg(php81)] {
300+
let key = unsafe {
301+
*zend_known_strings.add(_zend_known_string_id_ZEND_STR_AUTOGLOBAL_REQUEST as usize)
302+
};
303+
} else {
304+
let key = _zend_string::new("_REQUEST", false).as_mut_ptr();
305+
}
306+
};
307+
308+
// `$_REQUEST` is lazy-initted, we need to call `zend_is_auto_global` to make
309+
// sure it's populated.
310+
if !unsafe { zend_is_auto_global(key) } {
311+
panic!("Failed to get request global");
312+
}
313+
314+
let symbol_table = &ExecutorGlobals::get().symbol_table;
315+
cfg_if::cfg_if! {
316+
if #[cfg(php81)] {
317+
let request = unsafe { zend_hash_find_known_hash(symbol_table, key) };
318+
} else {
319+
let request = unsafe { _zend_hash_find_known_hash(symbol_table, key) };
320+
}
321+
};
322+
323+
if request.is_null() {
324+
return None;
325+
}
326+
327+
Some(unsafe { (*request).array() }.expect("Type is not a ZendArray"))
298328
}
299329

300330
/// Get the HTTP Environment variables. Equivalent of $_ENV.

tests/src/integration/globals.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
assert(test_globals_http_get() === []);
4+
assert(test_globals_http_post() === []);
5+
assert(test_globals_http_cookie() === []);
6+
assert(!empty(test_globals_http_server()));
7+
assert(test_globals_http_request() === []);
8+
assert(test_globals_http_files() === []);

tests/src/integration/globals.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[test]
2+
fn globals_works() {
3+
assert!(crate::integration::run_php("globals.php"));
4+
}

0 commit comments

Comments
 (0)